六、异步通知
在此之前的代码,我们都是应用程序主动去read()按键值
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。这章所要涉及的异步通知是一旦设备就绪,则驱动主动通知应用程序,这样应用程序不需要查询设备状态。这一点类似于“中断”
一、应用程序异步通知signal()
这章的异步通知使用的是UNIX环境高级编程第十章的信号。由于是异步通知,因此我们还需要使用fcntl(fd, F_SETFL, oflag | FASYNC)将应用程序的文件描述符设置为异步模式
fcntl()函数最终会调用驱动程序中的fasync() -> fasync_helper(),因此在驱动程序中我们只需要调用fasync_helper()函数即可
fasync_helper()函数的作用就是初始化fasync_struct,包括分配内存和设置属性,最后需要在驱动的release()函数里把fasync_helper()初始化的东西free掉
应用程序的示例代码如下:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/stat.h> 5 #include <fcntl.h> 6 #include <string.h> 7 #include <signal.h> 8 9 int fd; 10 11 static void sig_handler(int signo) 12 { 13 unsigned char key_val; 14 read(fd, &key_val, 1); 15 printf("key_val = 0x%x\n", key_val); 16 } 17 18 int main(int argc, char** argv) 19 { 20 int oflags = 0; 21 22 signal(SIGIO, sig_handler); 23 24 fd = open("/dev/key", O_RDWR); 25 if (fd < 0) { 26 printf("can't open /dev/key\n"); 27 return -1; 28 } 29 30 fcntl(fd, F_SETOWN, getpid()); 31 oflags = fcntl(fd, F_GETFL); 32 fcntl(fd, F_SETFL, oflags | FASYNC); 33 34 while (1) { 35 sleep(1000); 36 } 37 38 close(fd); 39 40 return 0; 41 }
应用程序完成的有以下一个任务:
1. fcntl(fd, F_SETOWN, getpid())设置文件拥有者为本进程,这样驱动程序发出的信号才能被本进程接到
2. fcntl(fd, F_SETFL, oflags | FASYNC)设置应用程序支持异步通知
3. 通过signal()连接信号和信号处理函数
二、驱动程序异步通知fasync()
在分析完应用程序之后,我们需要确定驱动程序需要做什么,如下图:
由图,可得出驱动需要做的有以下几点:
1. 设置file->f_owner,这个已经由内核帮我们完成了
2. 编写fasync()函数,里面调用fasync_helper()函数
3. 在资源可获得时,调用kill_fasync()函数释放信号
我们现在来看一下fasync()系统调用过程:
SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) -> do_fcntl(fd, cmd, arg, f.file); -> case F_SETFL: -> setfl(fd, filp, arg); -> filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0); /* 驱动程序的fasync() */ -> case F_SETOWN: -> f_setown(filp, arg, 1);
异步通知所使用保存属性的结构体是fasync_struct
fasync()、fasync_helper()和kill_fasync()函数声明如下:
int (*fasync) (int fd, struct file *filp, int mode); int fasync_helper(int fd, struct file * filp, int mode, struct fasync_struct **fapp) void kill_fasync(struct fasync_struct **fp, int sig, int band)
驱动程序模版如下:
struct key_device { ... struct fasync_struct *fasync; }; static int key_fasync(int fd, struct file *filp, int mode) { struct key_device *dev = filp->private_data; // 初始化fasync return fasync_helper(fd, filp, mode, &dev->fasync); } static struct file_operations key_fops = { ... .fasync = key_fasync, }; static ssize_t key_read(struct file *filp, char __user *buf, size_t len, loff_t * loff) { ... schedule(); // 休眠 // 有数据,释放信号 kill_fasync(&dev->fasync, SIGIO, POLL_IN); mutex_lock(&dev->lock); copy_to_user(buf, &key_val, 1); mutex_unlock(&dev->lock); ... } static int key_release(struct inode *nodep, struct file *filp) { key_fasync(-1, filp, 0); // 将文件从异步通知列表中删除 ... }
若需要完整驱动程序代码,点击查看:源代码
