异步通知是设备状态改变后主动通知应用程序,这样应用程序就不需要阻塞或查询设备了。应用通过信号来处理内核的异步通知,上次使用poll select来查询设备的可读状态,下面这个例子类似,不同的是当设备有数据时主动通知应用去读数据。
应用的C代码很简单,主要设置对信号的处理方式,内核有数据时会收到SIGIO信号,应用会自动调用signal设置的函数去读数据。
main.c
#include <stdio.h> #include <fcntl.h> #include <signal.h> #include <stdlib.h> unsigned char rdBuf[1024]; int fd; void hand_signal(int sign) { if(sign==SIGIO) { if (read(fd,rdBuf,1024)>0) printf("read data:%s\n",rdBuf); } } int main (int *argc,char**argv) { int fd,flags; /*处理SIGIO信号 驱动例子中收到数据时向应用发送的也是SIGIO信号*/ signal(SIGIO,hand_signal); fd=open("/dev/moduledev60",O_RDWR); if(fd<0) { printf("open file error\n"); return -1; } /*告诉驱动信号发送给谁 第三个参数传的是进程号*/ fcntl(fd, F_SETOWN, getpid()); /*设置FASYNC 执行后驱动的fasync方法被调用*/ flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | FASYNC); while(1); }
驱动主要代码,读操作只返回最后一次写入的字符串,写操作后向设置的进程发送SIGIO信号
struct file_operations ops= { .owner=THIS_MODULE , .read =fileops_read , .write=fileops_write, .fasync=fileops_fasync, .close=fileops_release }; int fileops_release(struct inode *inode,struct file *filp) { printk(KERN_ALERT "fileops_release\n"); fileops_fasync(-1,filp,0); //第三个参数为0时,第一个参数未用,用来释放申请的内存 return 0; } ssize_t fileops_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) { unsigned int len; if(rdFlag==0) return 0; len=strlen(rdBuf); copy_to_user(buff,rdBuf,len); rdFlag=0; return len; } ssize_t fileops_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp) { copy_from_user(rdBuf,buff,count); rdBuf[count]=0; rdFlag=1; kill_fasync(&fasync_queue,SIGIO ,POLL_IN); //有数据写入 向设置的进程发送SIGIO信号 printk(KERN_ALERT "signal %d \n",SIGIO); return count; } int fileops_fasync (int fd, struct file *filp, int mode) { int res=fasync_helper(fd,filp,mode,&fasync_queue); //初始化fasync_queue printk(KERN_ALERT "filp %x\n",(int)filp); if(res<0) return res; else return 0; }
测试结果
先编译加载驱动 [root@localhost ctest]# insmod moddev.ko 运行应用 [root@localhost ctest]# ./main 打开另一终端向设备写入数据 [root@localhost ctest]# echo 111 > /dev/moduledev60 应用输出,每次数据写入数据后,驱动通知应用读取设备 [root@localhost ctest]# ./main read data:111
附fasync_helper源码 /* * fasync_helper() is used by some character device drivers (mainly mice) * to set up the fasync queue. It returns negative on error, 0 if it did * no changes and positive if it added/deleted the entry. */ int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp) { /*这个函数维护一个线性表,用于创建和释放异步通知结构链表*/ struct fasync_struct *fa, **fp; struct fasync_struct *new = NULL; int result = 0; if (on) { new = kmem_cache_alloc(fasync_cache, GFP_KERNEL); //申请异步通知结构变量 if (!new) return -ENOMEM; } write_lock_irq(&fasync_lock); for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { if (fa->fa_file == filp) { if(on) {//已经存在filp节点 fa->fa_fd = fd; kmem_cache_free(fasync_cache, new); } else {//释放找到的节点 *fp = fa->fa_next; kmem_cache_free(fasync_cache, fa); result = 1; } goto out; } } if (on) {//将申请的节点加入链表 new->magic = FASYNC_MAGIC; new->fa_file = filp; new->fa_fd = fd; new->fa_next = *fapp; *fapp = new; result = 1; } out: write_unlock_irq(&fasync_lock); return result; }