linux kernel 发送信号给user space
Sending Signal from Linux Device Driver to User Space
最近在看内核向用户空间发送信号,通知用户空间做一些操作。netlink更适用于双向通信,所以通过信号通知会是很好的方法。可能很多人的信号还和我之前一样,只熟悉用户空间向内核发信号,其实是内核也可以发送信号给用户空间。话不多说,我翻译了一篇比较好的博文,希望对你有帮助。
原文链接:
Sending Signal from Linux Device Driver to User Space
https://embetronicx.com/tutorials/linux/device-drivers/sending-signal-from-linux-device-driver-to-user-space/
什么是Linux中的信号呢? 信号是一种发送简单消息的方式,用于通知进程或线程特定的事件。 在Linux中,一次会运行许多进程。 我们可以将一个信号从一个进程发送到另一个进程。 信号是最古老的进程间通信方法之一。 这些信号是异步的。 像用户空间信号一样,我们可以从内核空间向用户空间发送信号吗? 是的,为什么不。 我们将在接下来的教程中看到完整的Signals。 在本教程中,我们将学习如何将信号从Linux设备驱动程序发送到用户空间。
从Linux设备驱动程序发送信号到用户空间
使用下面的步骤,我们可以很容易地发送信号。
1. 决定你想要发出的信号。
2. 向驱动程序注册用户空间应用程序。
3. 一旦发生什么事情(在我们的例子中我们使用中断),就向用户空间发送信号。
4. 在使用完用户空间应用程序后,注销它。
1. 决定你想要发出的信号。
首先,选择要发送的信号号。 在我们的例子中,我们将发送信号44。
#define SIGETX 44
2.向驱动程序注册用户空间应用程序
在发送信号之前,您的设备驱动程序应该知道它需要将信号发送给谁。 为此,我们需要将进程注册到驱动程序。
所以我们需要先将PID发送给驱动程序。 然后驱动程序将使用PID并发送信号。 你可以注册应用程序的PID在IOCTL,打开/读/写调用。 在我们的示例中,我们将使用IOCTL进行注册。
static long etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
if (cmd == REG_CURRENT_TASK) {
printk(KERN_INFO "REG_CURRENT_TASK\n");
task = get_current(); //得到当前进程号
signum = SIGETX;
}
return 0;
}
3.向用户空间发送信号
将应用程序注册到驱动程序后,驱动程序就可以在需要的时候发送信号了。 在我们的例子中,当我们得到中断时,我们将发送信号。
Example:
//Interrupt handler for IRQ 11.
static irqreturn_t irq_handler(int irq,void *dev_id) {
struct siginfo info;
printk(KERN_INFO "Shared IRQ: Interrupt Occurred");
//Sending signal to app
memset(&info, 0, sizeof(struct siginfo));
info.si_signo = SIGETX;
info.si_code = SI_QUEUE;
info.si_int = 1;
if (task != NULL) {
printk(KERN_INFO "Sending signal to app\n");
if(send_sig_info(SIGETX, &info, task) < 0) {
printk(KERN_INFO "Unable to send signal\n");
}
}
return IRQ_HANDLED;
}
4.注销用户空间应用程序
当您完成任务时,您可以注销您的应用程序。 在这里,当应用程序关闭驱动程序时,我们取消注册。
static int etx_release(struct inode *inode, struct file *file)
{
struct task_struct *ref_task = get_current();
printk(KERN_INFO "Device File Closed...!!!\n");
//delete the task
if(ref_task == task) {
task = NULL;
}
return 0;
}
附录: 源代码
设备驱动程序:
下面给出了完整的设备驱动程序代码。 在这个源代码中,当我们读取设备文件(/dev/etx_device)时,中断将会命中(要理解Linux中的中断,请参阅本教程)。 每当中断命中时,我就向已经注册的用户空间应用程序发送信号。 因为这是一个教程帖子,我不打算做任何工作在中断处理程序除了发送信号。
[Get the source code from the GitHub]
/***************************************************************************//**
* \file driver.c
*
* \details Simple Linux device driver (Signals)
*
* \author EmbeTronicX
*
* *******************************************************************************/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include<linux/slab.h> //kmalloc()
#include<linux/uaccess.h> //copy_to/from_user()
#include <linux/ioctl.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#define SIGETX 44
#define REG_CURRENT_TASK _IOW('a','a',int32_t*)
#define IRQ_NO 11
/* Signaling to Application */
static struct task_struct *task = NULL;
static int signum = 0;
int32_t value = 0;
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
static int etx_open(struct inode *inode, struct file *file);
static int etx_release(struct inode *inode, struct file *file);
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
static ssize_t etx_write(struct file *filp, const char *buf, size_t len, loff_t * off);
static long etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static struct file_operations fops =
{
.owner = THIS_MODULE,
.read = etx_read,
.write = etx_write,
.open = etx_open,
.unlocked_ioctl = etx_ioctl,
.release = etx_release,
};
//Interrupt handler for IRQ 11.
static irqreturn_t irq_handler(int irq,void *dev_id) {
struct siginfo info;
printk(KERN_INFO "Shared IRQ: Interrupt Occurred");
//Sending signal to app
memset(&info, 0, sizeof(struct siginfo));
info.si_signo = SIGETX;
info.si_code = SI_QUEUE;
info.si_int = 1;
if (task != NULL) {
printk(KERN_INFO "Sending signal to app\n");
if(send_sig_info(SIGETX, &info, task) < 0) {
printk(KERN_INFO "Unable to send signal\n");
}
}
return IRQ_HANDLED;
}
static int etx_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Device File Opened...!!!\n");
return 0;
}
static int etx_release(struct inode *inode, struct file *file)
{
struct task_struct *ref_task = get_current();
printk(KERN_INFO "Device File Closed...!!!\n");
//delete the task
if(ref_task == task) {
task = NULL;
}
return 0;
}
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Read Function\n");
asm("int $0x3B"); //Triggering Interrupt. Corresponding to irq 11
return 0;
}
static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Write function\n");
return 0;
}
static long etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
if (cmd == REG_CURRENT_TASK) {
printk(KERN_INFO "REG_CURRENT_TASK\n");
task = get_current();
signum = SIGETX;
}
return 0;
}
static int __init etx_driver_init(void)
{
/*Allocating Major number*/
if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){
printk(KERN_INFO "Cannot allocate major number\n");
return -1;
}
printk(KERN_INFO "Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
/*Creating cdev structure*/
cdev_init(&etx_cdev,&fops);
/*Adding character device to the system*/
if((cdev_add(&etx_cdev,dev,1)) < 0){
printk(KERN_INFO "Cannot add the device to the system\n");
goto r_class;
}
/*Creating struct class*/
if((dev_class = class_create(THIS_MODULE,"etx_class")) == NULL){
printk(KERN_INFO "Cannot create the struct class\n");
goto r_class;
}
/*Creating device*/
if((device_create(dev_class,NULL,dev,NULL,"etx_device")) == NULL){
printk(KERN_INFO "Cannot create the Device 1\n");
goto r_device;
}
if (request_irq(IRQ_NO, irq_handler, IRQF_SHARED, "etx_device", (void *)(irq_handler))) {
printk(KERN_INFO "my_device: cannot register IRQ ");
goto irq;
}
printk(KERN_INFO "Device Driver Insert...Done!!!\n");
return 0;
irq:
free_irq(IRQ_NO,(void *)(irq_handler));
r_device:
class_destroy(dev_class);
r_class:
unregister_chrdev_region(dev,1);
return -1;
}
static void __exit etx_driver_exit(void)
{
free_irq(IRQ_NO,(void *)(irq_handler));
device_destroy(dev_class,dev);
class_destroy(dev_class);
cdev_del(&etx_cdev);
unregister_chrdev_region(dev, 1);
printk(KERN_INFO "Device Driver Remove...Done!!!\n");
}
module_init(etx_driver_init);
module_exit(etx_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("A simple device driver - Signals");
MODULE_VERSION("1.20");
*用户空间程序:
这个应用程序使用IOCTL向驱动程序注册。 一旦注册,它将等待来自驱动的信号。 如果我们想关闭这个应用程序,我们需要按CTRL+C。 因为它是无限的。 我们已经安装了一个CTRL+C信号处理器。
/***************************************************************************//**
* \file test_app.c
*
* \details Userspace application to test the Device driver
*
* \author EmbeTronicX
*
* *******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>
#define REG_CURRENT_TASK _IOW('a','a',int32_t*)
#define SIGETX 44
static int done = 0;
int check = 0;
void ctrl_c_handler(int n, siginfo_t *info, void *unused)
{
if (n == SIGINT) {
printf("\nrecieved ctrl-c\n");
done = 1;
}
}
void sig_event_handler(int n, siginfo_t *info, void *unused)
{
if (n == SIGETX) {
check = info->si_int;
printf ("Received signal from kernel : Value = %u\n", check);
}
}
int main()
{
int fd;
int32_t value, number;
struct sigaction act;
printf("*********************************\n");
printf("*******WWW.EmbeTronicX.com*******\n");
printf("*********************************\n");
/* install ctrl-c interrupt handler to cleanup at exit */
sigemptyset (&act.sa_mask);
act.sa_flags = (SA_SIGINFO | SA_RESETHAND);
act.sa_sigaction = ctrl_c_handler;
sigaction (SIGINT, &act, NULL);
/* install custom signal handler */
sigemptyset(&act.sa_mask);
act.sa_flags = (SA_SIGINFO | SA_RESTART);
act.sa_sigaction = sig_event_handler;
sigaction(SIGETX, &act, NULL);
printf("Installed signal handler for SIGETX = %d\n", SIGETX);
printf("\nOpening Driver\n");
fd = open("/dev/etx_device", O_RDWR);
if(fd < 0) {
printf("Cannot open device file...\n");
return 0;
}
printf("Registering application ...");
/* register this task with kernel for signal */
if (ioctl(fd, REG_CURRENT_TASK,(int32_t*) &number)) {
printf("Failed\n");
close(fd);
exit(1);
}
printf("Done!!!\n");
while(!done) {
printf("Waiting for signal...\n");
//blocking check
while (!done && !check);
check = 0;
}
printf("Closing Driver\n");
close(fd);
}
Makefile
obj-m += driver.o
KDIR = /lib/modules/$(shell uname -r)/build
all:
make -C $(KDIR) M=$(shell pwd) modules
clean:
make -C $(KDIR) M=$(shell pwd) clean
更多推荐
所有评论(0)