通常应用程序跟驱动之间交互,主要是通过轮询的方式。这种方式效率非常低下。
下面介绍一种异步通知的方式。在这种模式下,应用程序不需要查询驱动,当驱动需要应用程序读取数据时,发送一个信号给应用程序,应用程序收到信号后,再去从驱动读取数据。
驱动代码:
#include <linux/miscdevice.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/mm.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/delay.h> #include <linux/moduleparam.h> #include <linux/errno.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/string.h> #include <linux/list.h> #include <linux/poll.h> #include <linux/fcntl.h> #include <linux/signal.h> //for SIGIO,POLL_IN #include <linux/timer.h> #include <linux/jiffies.h> //设备名 #define DEVICE_NAME "sync" //主设备号 #define DEVICE_MAJOR 228 //异步通知结构变量 struct fasync_struct *async_queue; //内核定时器 struct timer_list my_timer; //定时器超时函数 static void timer_function(unsigned long arg) { //发送信号到用户空间,POLL_IN表示可写 kill_fasync(&async_queue, SIGIO, POLL_IN); } //用户空间调用fcntl函数时会调用这个函数 static int async_fasync(int fd, struct file *filp, int mode) { //根据用户空间的需要,获取或设置相应的属性 return fasync_helper(fd, filp, mode, &async_queue); } static int async_open(struct inode *node,struct file *flip) { //初始化定时器 init_timer(&my_timer); my_timer.data = 0; //定时器超时函数 my_timer.function = &timer_function; //定时器超时时间(当前时间的后1000个时钟滴答) my_timer.expires = jiffies + 5*200; //启动定时器 add_timer(&my_timer); return 0; } static struct file_operations async_fops = { .owner = THIS_MODULE, .fasync = async_fasync, .open = async_open, }; static int __init dev_init(void) { int ret; //注册字符设备 ret = register_chrdev(DEVICE_MAJOR , DEVICE_NAME, &async_fops); if (ret < 0) { printk(DEVICE_NAME " can't register\n"); return ret; } else { printk(DEVICE_NAME " register successful\n"); return 0; } } static void __exit dev_exit(void) { //撤销字符设备 unregister_chrdev(DEVICE_MAJOR , DEVICE_NAME); printk("unregister success \n"); } module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("LKN@SCUT");
编译成功后会生成fasync.ko文件,先加载该驱动:
#insmod fasync.ko
然后,在/dev目录下创建设备文件:
#mknod sync c 228 1
接着,编写应用程序:
#include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <signal.h> #include <string.h> //是否已接收到信号标志 int flag = 0; //信号处理函数定义,由内核调用执行 void sig_handler(int signo) { if (signo==SIGIO) { printf("receive successful!!!\n"); flag = 1; } } int main() { struct sigaction action; int async_fd; memset(&action, 0, sizeof(action)); //信号处理函数 action.sa_handler = sig_handler; action.sa_flags = 0; //注册信号类型 sigaction(SIGIO, &action, NULL); async_fd = open("/dev/sync", O_RDONLY); if(async_fd < 0) { printf("can not open /dev/sync \n"); return -1; } //告诉驱动当前进程的PID fcntl(async_fd, F_SETOWN, getpid()); //设置驱动的FASYNC属性,支持异步通知 fcntl(async_fd, F_SETFL, fcntl(async_fd, F_GETFL) | FASYNC); printf("waiting for receive...\n"); while(!flag) { } close(async_fd); return 0; }