How to receive callbacks in python from kernel interrupts SIGIO

Hello,
Would like to receive callbacks for receiving interrupts from kernel. I was able to receive interrupts in user space C code with registering the SIGIO with sigaction & fcntl, but not sure how to receive the same way in python. Really appreciate any insights on this.

Not working python code:

import fcntl
import os
import signal

def receiveSignal(signalNumber, frame):
print(‘Received:’, signalNumber)

signal.signal(signal.SIGIO, receiveSignal)

test_fd = os.open("/dev/testDev", os.O_RDWR)
rv = fcntl.fcntl(test_fd, fcntl.F_SETOWN, os.getpid())
rv = fcntl.fcntl(test_fd, fcntl.F_SETFL, fcntl.fcntl(test_fd, fcntl.F_GETFL))

signal.signal(signal.SIGIO, receiveSignal)

while True:
pass

Working code using C:

void int_sighandler(int signo)
{
printf("\n int_sighandler called \n");
return;
}
int main(void)
{

    int test_fd  = open("/dev/testDev", O_RDWR);
    if (test_fd  < 0) {
            printf("[APP] SDM dev open is failed! %d\n", rc);
            return -1;
    }
    struct sigaction action;
    memset(&action, 0, sizeof(action));
    action.sa_handler = int_sighandler;
    action.sa_flags = 0;

    sigaction(SIGIO, &action, NULL);

    fcntl(test_fd , F_SETOWN, getpid());
    fcntl(test_fd , F_SETFL, fcntl(test_fd , F_GETFL) | FASYNC);
  
  while (1) {}

}

Most likely, you are facing permission issues since /dev/testDev is owned by root.

$ ls -lh /dev/testDev        
-rw-r--r-- 1 root root 0 Jun  8 10:46 /dev/testDev

When sending signals to that process, you have to act on behalf of the root user by switching to that user:

$ sudo su -

or you can add sudo in front of your command:

$ sudo kill -SIGIO $PID

It also could be that the file doesn’t exist in the first place. To solve this, you can tweak your program to handle this case:

import fcntl
import os
import signal
import time

def receiveSignal(signalNumber, frame):
  print('Received:', signalNumber)

signal.signal(signal.SIGIO, receiveSignal)

test_fn = "/dev/testDev"
if not os.path.exists(test_fn):
   counter_file = open(test_fn, 'w')
   counter_file.close()
test_fd = open(test_fn, "rt+")
fcntl.fcntl(test_fd, fcntl.F_SETOWN, os.getpid())
fcntl.fcntl(test_fd, fcntl.F_SETFL, fcntl.fcntl(test_fd, fcntl.F_GETFL) | fcntl.FASYNC)
print(f"Process ID: {os.getpid()}")

while True:
  pass
  time.sleep(3)

You can run this program as a root user:

$ sudo python3 file1.py

Now, the program is listening to a SIGIO signal that would trigger the callback function:

Process ID: 18930

Open up a new terminal session to send that signal:

$ sudo kill -SIGIO 18930

Your program should output:

Received: 29

That’s pretty much it. I hope it helps.

Thanks Mahmoud Harmouch.
Actually I am in root user only when I execute the test. but thanks for your hint, I am using kill_pid() in kernel driver now to send signal to userspace PID and received the signal in python now.