Linux Kernel Send Signal to User Space

本文介绍了一个Linux内核模块如何通过信号通知用户空间应用程序特定事件的发生。该模块使用ioctl()进行任务ID注册,并通过write()API发送信号。应用程序则注册一个信号处理器来处理来自内核的信号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Signal is one way that Kenerl can notify User Space application that something happened.

Linux Version: Ubuntu 18.04

This demo includes:

1. kernel module + Makefile

2. application

Application will register it's task_id to kernel module by ioctl(). Kernel module write() API will send a signal to application. Application will register a handler to handle the signal from kernel. One char device is the bridge between kernel module and application.

First, it is kernel module -- signal.c

/*
 * sending signal from kernel module to user space. Usage:
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

#include <linux/sched.h>
#include <asm/siginfo.h>
#include <linux/pid_namespace.h>
#include <linux/pid.h>
#include <linux/sched/signal.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alex Xie");
MODULE_DESCRIPTION("A Linux module that sends signal to userspace.");
MODULE_VERSION("0.01");

#define DEVICE_NAME "signal_example"
#define EXAMPLE_MSG "Hello, World!\n"
#define MSG_BUFFER_LEN 15

typedef enum{
        THIS_MODULE_IOCTL_SET_OWNER = 0x111,
}MODULE_IOCTL_CMD;


#undef SIGRTMIN
#define SIGRTMIN 34


/* Prototypes for device functions */
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg);

static void send_signal(int sig_num);

static int major_num;
static int device_open_count = 0;


/* This structure points to all of the device functions */
static struct file_operations file_ops = {
        .read = device_read,
        .write = device_write,
        .open = device_open,
        .release = device_release,
        .unlocked_ioctl = device_ioctl,
        .compat_ioctl = device_ioctl,

};

static int owner = 0;

static void send_signal(int sig_num)
{
       struct kernel_siginfo info;
       struct task_struct *current_task=NULL;
       int ret;
       printk(KERN_INFO"signal_example: %s\n",__func__); 
       if (owner == 0)
               return;
       printk(KERN_INFO"%s,%d.signal_example: sending signal %d to owner %d\n",__func__, __LINE__, sig_num, owner);

       memset(&info, 0, sizeof(struct kernel_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();
       }
       printk(KERN_INFO"signal_example:send signal to task %p\n",current_task);

       ret = send_sig_info(sig_num, &info, current_task);
       if (ret < 0) {
               printk(KERN_INFO"signal_example:error sending signal\n");
       }
       printk(KERN_INFO"signal_example:send signal to task %p done\n",current_task);
}



/* When a process reads from our device, this gets called. */
static ssize_t device_read(struct file *flip, char *buffer, size_t len, loff_t *offset) {
	    /* not implemented yet */ 
        return 0;
}

/* Called when a process tries to write to our device */
static ssize_t device_write(struct file *flip, const char *buffer, size_t len, loff_t *offset) {
        /* This is a read-only device */
        printk(KERN_ALERT "signal_example: send signal to user space.\n");
        send_signal(SIGRTMIN+1);
        /*        return -EINVAL;*/
        return len;
}

/* Called when a process opens our device */
static int device_open(struct inode *inode, struct file *file) {
        /* If device is open, return busy */
	printk(KERN_INFO"signal_example:%s,%d\n",__func__,device_open_count);
        device_open_count++;
        try_module_get(THIS_MODULE);
        return 0;
}

/* Called when a process closes our device */
static int device_release(struct inode *inode, struct file *file) {
        /* Decrement the open counter and usage count. Without this, the module would not unload. */
        device_open_count--;
        module_put(THIS_MODULE);
        return 0;
}

static int __init signal_example_init(void) {

        /* Try to register character device */
        major_num = register_chrdev(0, "signal_example", &file_ops);
        if (major_num < 0) {
                printk(KERN_ALERT "signal_example:Could not register device: %d\n", major_num);
                return major_num;
        } else {
                printk(KERN_INFO "signal_example:module loaded with device major number %d\n", major_num);
                return 0;
        }
}

static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	printk(KERN_INFO "signal_example:%s cmd=0x%x,arg=0x%lx\n", __func__,cmd,arg);
        if(cmd == THIS_MODULE_IOCTL_SET_OWNER) {
                printk(KERN_INFO"%s, signal_example:owner pid at 0x%lx\n", __func__, arg);
#if 1
                if(copy_from_user(&owner, (int *)arg, sizeof(int))) {
			 printk(KERN_INFO"signal_example:copy from user failed\n");
                         return -EFAULT;
                }
#else
		__get_user(owner,(int*)arg);
#endif
		printk(KERN_INFO"signal_example: owner is %d\n",owner);
                return 0;
        } else
                return 0;
}

        
static void __exit signal_example_exit(void) {
        /* Remember — we have to clean up after ourselves. Unregister the character device. */
        unregister_chrdev(major_num, DEVICE_NAME);
        printk(KERN_INFO "signal_example:Goodbye, World!\n");
}

/* Register module functions */
module_init(signal_example_init);
module_exit(signal_example_exit);

Then Kernel module's Makefile

obj-m := signal.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

How to make Kernel module:

Put Makefile and signal.c in the same foler then make. As following:

Application code -- app.c :

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/ioctl.h>

static volatile sig_atomic_t interested_event = 0;

void sig_handler_event1(int sig)
{
        interested_event = 1;
	printf("received signal_example kernel signal\n");
}


int main(int argc, char *argv[])
{
	int fd=0;
	int my_pid;
        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.
        printf ("handle signal %d\n", SIGRTMIN+1);
        sigaction (SIGRTMIN+1, &usr_action, NULL);
        fd = open("/dev/signal_example", O_RDWR);
	printf("fd is %d\n",fd);
        my_pid = getpid();

        printf("in %s my_pid is %d\n", __func__, my_pid);
        ioctl(fd, 0x111, &my_pid);
	close(fd);
        while(1){
                sleep(60);
		printf("I am alive\n");
	}
}

Build application:

>gcc -o app app.c

Now we can start to test them.

1. insmod kernel module

2. create node /dev/signal_example with major device id 238 -- 238 is from the kernel log

3. start application

And we can see application send its task_id(5682) to kernel module by ioctl(), here is kernel log:

4. trigger signal

this operation will trigger device_write() of kernel module, then trigger send_signal() which will send signal to application. Here is the log:

as soon as kernel module send signal to application, application's handler will handle it as:

Here you can see 'received signal_example kernel signal' . This is from the handler.

 

Attention, we run all as root.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值