异步通知和MISC设备驱动
异步通知:
驱动程序中的异步通知是指驱动程序可以向应用程序发送通知,告知应用程序发生了某种事件。这种通知是异步的,即应用程序可以继续执行其他操作,而不需要主动轮询或等待事件的发生。
在Linux内核中,实现驱动异步通知的一种常见方式是使用fasync机制。这个机制的核心是利用fasync_helper函数和kill_fasync函数来实现驱动程序和应用程序之间的异步通信。
- fasync_helper函数:
在驱动程序中,通过调用fasync_helper函数可以将应用程序的PID添加到驱动程序的异步通知列表中,从而告知内核在特定事件发生时通知这些应用程序。
- kill_fasync函数:
当驱动程序需要向应用程序发送通知时,可以调用kill_fasync函数向所有在异步通知列表中的应用程序发送信号。
应用程序在接收到信号后,可以注册信号处理程序来处理异步通知,从而实现在事件发生时做出相应的处理
fsync函数说明:
int fasync_helper(int fd, struct file *filp, int on, struct fasync_struct **fapp); // 参数说明 fd:文件描述符,指定要进行异步通知的文件描述符。 filp:指向表示文件的结构体指针,即struct file结构体的指针。 on:指定是否开启异步通知,非零值表示开启,零值表示关闭。 fapp:指向指针的指针,用于操作异步通知队列。 // 返回值说明 成功,返回0;失败,返回负数错误码,如-EINVAL等。 int kill_fasync(struct fasync_struct **fapp, int sig, int poll_event); // 参数说明 fapp:指向指针的指针,用于操作异步通知队列。 sig:要发送的信号类型,通常是SIGIO。 poll_event:指定事件类型,通常是POLL_IN(输入事件)或POLL_OUT(输出事件)。 // 返回值说明 成功,返回0;失败,返回负数错误码,如-EINVAL等。
MISC设备驱动:
MISC 驱动叫做杂项驱动,就是开发板上的某些外设无法进行分类的时候就可以使用 MISC 驱动。所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。随着 Linux字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号,MISC 设备驱动就用于解决此问题。MISC 设备会自动创建 cdev。
int minor; // 次设备号,用于标识设备 const char *name; // 设备名称 const struct file_operations *fops; // 设备文件操作 struct list_head list; // 设备链表头 struct device *parent; // 父设备指针 struct device *this_device; // 当前设备指针 const struct attribute_group **groups; // 属性组指针数组 const char *nodename; // 设备节点名称 umode_t mode; // 设备访问权限模式 };
测试demo:
驱动测试demo:
#include <linux/module.h> #include <linux/fs.h> #include <linux/miscdevice.h> #include <linux/uaccess.h> #include <linux/sched.h> static struct miscdevice fasync_device; static int fasync_demo_fasync(int fd, struct file *filp, int mode) { return fasync_helper(fd, filp, mode, &fasync_device.this_device->async_queue); } static ssize_t fasync_demo_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { // 在此示例中,read函数仅作为占位符,不做实际操作 return 0; } static ssize_t fasync_demo_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { // 触发异步通知 kill_fasync(&fasync_device.this_device->fasync_list, SIGIO, POLL_OUT); return count; } static const struct file_operations fasync_demo_fops = { .owner = THIS_MODULE, .read = fasync_demo_read, .write = fasync_demo_write, .fasync = fasync_demo_fasync, // 将fasync函数指针指向实现的fasync函数 }; static int __init fasync_demo_init(void) { fasync_device.minor = MISC_DYNAMIC_MINOR; fasync_device.name = "fasync_demo_device"; fasync_device.fops = &fasync_demo_fops; misc_register(&fasync_device); printk(KERN_INFO "Fasync demo device registered\n"); return 0; } static void __exit fasync_demo_exit(void) { misc_deregister(&fasync_device); printk(KERN_INFO "Fasync demo device unregistered\n"); } module_init(fasync_demo_init); module_exit(fasync_demo_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("lethe1203"); MODULE_DESCRIPTION("Fasync && misc demo");
用户程序测试demo:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <signal.h> #define DEVICE_FILE "/dev/fasync_demo_device" void signal_handler(int signo) { printf("Asynchronous notification received!\n"); } int main() { int fd; struct sigaction sa; // 设置信号处理程序 sa.sa_handler = signal_handler; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGIO, &sa, NULL); // 打开设备文件 fd = open(DEVICE_FILE, O_RDWR); if (fd < 0) { perror("Failed to open the device..."); return -1; } // 将进程设置为接收SIGIO信号 fcntl(fd, F_SETOWN, getpid()); // 开启异步通知 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_ASYNC); // 写入数据以触发异步通知 write(fd, "Trigger asynchronous notification", sizeof("Trigger asynchronous notification")); // 等待异步通知 pause(); // 关闭设备文件 close(fd); return 0; }
在上面demo中,在驱动程序中使用fasync_helper函数来设置异步通知,并在应用程序中使用fcntl和信号处理程序来处理异步通知。当应用程序写入数据到设备文件时,会触发异步通知,驱动程序通过kill_fasync函数发送SIGIO信号通知应用程序。应用程序接收到SIGIO信号后,调用信号处理程序 signal_handler 来处理异步通知。