Yongbing's Blog

A personal technical note.

Sending a Signal From Linux Kernel

| Comments

Send SIGUSR1 from kernel

I want to use a signal to inform an asynchronous event from one kernel module to a user space application.

To achieve this, add below code in kernel driver:

patch on kernel module
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
+#include <linux/sched.h>
+#include <asm/siginfo.h>
+#include <linux/pid_namespace.h>
+#include <linux/pid.h>

typedef enum
 {
+       THIS_MODULE_IOCTL_SET_OWNER = 0x111,
 }MODULE_IOCTL_CMD;


+static int owner = 0;
+static struct task_struct * current_task;

+static void send_signal(int sig_num)
+{
+       if (owner == 0)
+               return;
+       printk("%s,%d.sending to owner %d\n",__func__, __LINE__, owner);
+       struct siginfo info;
+       memset(&info, 0, sizeof(struct siginfo));
+       info.si_signo = sig_num;
+       info.si_code = 0;
+       info.si_int = 1234;
+       if (current_task == NULL){
+               rcu_read_lock();
+               current_task = pid_task(find_vpid(owner), PIDTYPE_PID);
+               rcu_read_unlock();
+       }
+       int ret = send_sig_info(sig_num, &info, current_task);
+       if (ret < 0) {
+               printk("error sending signal\n");
+       }
+}
+
 static int device_event_handler(struct snd_pcm_substream *substream, int cmd)
 {
      if (cmd == INTRESTED_EVENT) {
+                       send_signal(SIGUSR1);



@@ -325,6 +352,13 @@ static long device_ioctl(struct file *file,
+       case THIS_MODULE_IOCTL_SET_OWNER:
+               printk("%s, owner pid %d\n",__func__, owner);
+               if(copy_from_user(&owner, (int *)arg, sizeof(int))){
+                       ret = -EFAULT;
+               }
+               current_task = NULL;
+               break;

+MODULE_LICENSE("GPL");

In user space application, set its pid to kernel module, then listen on the signal.

patch on user application
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+static sem_t event_sem;
+static volatile sig_atomic_t intrested_event = 0;
+
+void sig_handler_event1(int sig)
+{
+       interested_event = 1;
+       sem_post(&event_sem);
+}

+static void * event_handler_thread_func()
+{
+        while(1){
+                sem_wait(&event_sem);
+                if (intrested_event){
+                        LOGD("%s,%d, received intrested_event signal.\n",__func__, __LINE__);
+                        intrested_event = 0;
+                }
+        }
+        pthread_exit(NULL);
+}

int main(int argc, char *argv[])
+               pthread_t event_thread;
+               if (pthread_create(&event_thread, NULL, event_handler_thread_func, NULL) != 0){
                        printf("Thread create failed%s.\n", strerror(errno));
+                  exit(1);
+               }
+               sem_init(&event_sem, 0, 0);

+               struct sigaction usr_action;
+               sigset_t block_mask;
+               sigfillset (&block_mask);
+               usr_action.sa_handler = sig_handler_event1;
+               usr_action.sa_mask = block_mask;//block all signal inside signal handler.
+               usr_action.sa_flags = SA_NODEFER;//do not block SIGUSR1 within sig_handler_int.
+               sigaction (SIGUSR1, &usr_action, NULL);
+               int fd = open("/dev/target_device_name", O_RDWR);
+               int my_pid = getpid();
+               ioctl(fd, 0x111, &my_pid);
+               close(fd);

A signal could interrupt below primitive calls: close, fcntl (operation F_SETLK), open, read, recv, recvfrom, select, send, sendto, tcdrain, waitpid, wait, and write, POXIS and BSD will handle this situation differently. POXIS will fail these primitive call with EINTR, caller need to use macro TEMP_FAILURE_RETRY (expression) to retry. Programmer need to take care of this issue.

Reference:

Sending Signal Across Processes

In receiver process, create a share memory, write receiver’s pid to it, then wait for the signal.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
+#include  <signal.h>
+#include  <sys/ipc.h>
+#include  <sys/shm.h>
+#include <pthread.h>
+#include <semaphore.h>

+#define MY_IPC_KEY_PATH "/data/misc/bluedroid"

+static sem_t event_sem;
+static volatile sig_atomic_t intrested_event = 0;
+
+static void sig_handler_int(int sig)
+{
+       interested_event = 1;
+       sem_post(&event_sem);
+}
+
+static void wait_for_signal(void)
+{
+       pid_t pid = getpid();
+       key_t my_ipc_key   = ftok(MY_IPC_KEY_PATH, 's');
+       int share_mem_id   = shmget(my_ipc_key, sizeof(pid_t), IPC_CREAT | 0666);
+       pid_t *share_mem  = (pid_t *) shmat(share_mem_id, NULL, 0);
+       *share_mem = pid;
+
+       printf("%s,%d, set my pid %d\n", __func__, __LINE__, pid);
+
+       struct sigaction usr_action;
+       sigset_t block_mask;
+       sigfillset (&block_mask);
+       usr_action.sa_handler = sig_handler_int;
+       usr_action.sa_mask = block_mask;//block all signal inside signal handler.
+       usr_action.sa_flags = SA_NODEFER;//do not block SIGUSR1 within sig_handler_int.
+       sigaction (SIGINT, &usr_action, NULL);
+
+       sem_init(&event_sem, 0, 0);
+       while(intrested_event == 0){
+               sem_wait(&event_sem);
+               if (intrested_event){
+                       printf("%s,%d, received intrested_event signal.\n",__func__, __LINE__);
+               }
+       }
+       intrested_event = 0;
+       shmdt(share_mem);
+       shmctl(share_mem_id, IPC_RMID, NULL);
+}

int receiver_process(int argc, char *argv[])
{

+    wait_for_signal();

In sender process, acquire the receiver process’spid through the share memory object, then send the signal.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
+#include  <signal.h>
+#include  <sys/ipc.h>
+#include  <sys/shm.h>

+#define MY_IPC_KEY_PATH "/data/misc/bluedroid"

+static void wakeup_receiver(void)
+{
+       key_t my_ipc_key;
+       int share_mem_id;
+       pid_t *share_mem;
+       pid_t pid;
+
+       my_ipc_key   = ftok(MY_IPC_KEY_PATH, 's');
+       share_mem_id   = shmget(my_ipc_key, sizeof(pid_t), 0666);
+       share_mem  = (pid_t *) shmat(share_mem_id, NULL, 0);
+       pid     = *share_mem;
+       shmdt(share_mem);
+
+       if (pid > 0){
+               int ret = sigquue(pid, SIGINT, 0);
                if (ret != 0 ){
                        printf("send signal failed %s.\n", strerror(errno));
                        return;
                }
+               printf("%s,%d, send signale to pid %d\n", __func__, __LINE__, pid);
+       }
+       else{
+               printf("%s,%d, pid %d not ready\n", __func__, __LINE__, pid);
+       }
+
+}
void sender(void)
{
+    wakeup_receiver();
}

A note: shmget() is not available in Android, so this is not a valid IPC for Android.

Reference:

http://www.csl.mtu.edu/cs4411.ck/www/NOTES/signal/kill.html

Footnote: signal lost

1 unreliable signal: earlier version of UNIX signal is unreliable

2 real-time signal:

POSIX added signals, and

3 signal lost:

. when signal handler is running, blocked signals is “lost” (?!) . when signal handler is running, the same signal is blocked by default, add SA_NODEFER in sigaction.sa_flags unblock it.

Comments