标签:
异步通知:
阻塞与非阻塞访问、poll()函数提供了较好地解决设备访问的机制(应用程序主动访问)
异步通知:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上“中断”的概念,比较准确的称谓是“信号驱动的异步 I/O”
阻塞 I/O 意味着一直等待设备可访问后再访问,非阻塞 I/O 中使用 poll()意味着查询设备是否可访问,而异步通知则意味着设备通知自身可访问,实现了异步 I/O。由此可见,这几种方式 I/O可以互为补充。
阻塞、非阻塞 I/O、异步通知本身没有优劣,应该根据不同的应用场景合理选择。
linux异步通知编程:
1.信号的接受:
在用户程序中,为了捕获信号,可以使用 signal()函数来设置对应信号的处理函数:
void (*signal(int signum, void (*handler))(int)))(int);
该函数原型较难理解,它可以分解为:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));
第一个参数指定信号的值,第二个参数指定针对前面信号值的处理函数,若为SIG_IGN,表示 忽略该信号;若为 SIG_DFL,表示采用系统默认方式处理信号;若为用户自定义的函数,则信号 被捕获到后,该函数将被执行。 如果 signal()调用成功,它返回最后一次为信号 signum 绑定的处理函数 handler 值,失败则返 回 SIG_ERR。
使用信号实现异步通知的应用程序实例:
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <stdio.h> 4 #include <fcntl.h> 5 #include <signal.h> 6 #include <unistd.h> 7 #define MAX_LEN 100 8 void input_handler(int num) 9 { 10 char data[MAX_LEN]; 11 int len; 12 13 /* 读取并输出 STDIN_FILENO 上的输入 */ 14 len = read(STDIN_FILENO, &data, MAX_LEN); 15 data[len] = 0; 16 printf("input available:%s\n", data); 17 } 18 19 main() 20 { 21 int oflags; 22 23 /* 启动信号驱动机制 */ 24 signal(SIGIO, input_handler); 25 fcntl(STDIN_FILENO, F_SETOWN, getpid()); 26 oflags = fcntl(STDIN_FILENO, F_GETFL); 27 fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC); 28 29 /* 最后进入一个死循环,仅为保持进程不终止,如果程序中 30 没有这个死循会立即执行完毕 */ 31 while (1); 32 }
上述代码 24 行为 SIGIO 信号安装 input_handler()作为处理函数,第 25 行设置本进程为 STDIN_FILENO 文件的拥有者(owner),没有这一步内核不会知道应该将信号发给哪个进程。而 为了启用异步通知机制,还需对设备设置 FASYNC 标志,26~27 行代码实现此目的。
由此可见,为了在用户空间中能处理一个设备释放的信号,它必须完成 3 项工作。 (1)通过 F_SETOWN IO 控制命令设置设备文件的拥有者为本进程,这样从设备驱动发出的 信号才能被本进程接收到。 (2)通过 F_SETFL IO 控制命令设置设备文件支持 FASYNC,即异步通知模式。 (3)通过 signal()函数连接信号和信号处理函数。
相关链接:http://blog.csdn.net/bailyzheng/article/details/7463775
信号的释放:
在设备驱动和应用程序的异步通知交互中,仅仅在应用程序端捕获信号是不够的,因为信号 没有的源头在设备驱动端。因此,应该在合适的时机让设备驱动释放信号,在设备驱动程序中增 加信号释放的相关代码。 为了使设备支持异步通知机制,驱动程序中涉及 3 项工作。
(1)支持 F_SETOWN 命令,能在这个控制命令处理中设置 filp->f_owner 为对应进程 ID。不 过此项工作已由内核完成,设备驱动无需处理。
(2)支持 F_SETFL 命令的处理,每当FASYNC 标志改变时,驱动程序中的fasync()函数将得 以执行。因此,驱动中应该实现 fasync()函数。 (
3)在设备资源可获得时,调用 kill_fasync()函数激发相应的信号
设备驱动中异步通知编程比较简单,主要用到一项数据结构和两个函数。数据结构是
fasync_struct 结构体,两个函数分别是:
处理 FASYNC 标志变更的。
int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fa);
释放信号用的函数。
void kill_fasync(struct fasync_struct **fa, int sig, int band);
相关驱动:
#include <linux/module.h>//support module load and unload #include <linux/types.h>//special type definition,like dev_t off_t defined by typedef #include <linux/fs.h>//struct file_operations #include <linux/errno.h>//return value #include <linux/mm.h>//memory mannage ,include kmalloc.kfree and so on #include <linux/sched.h>//process schedule #include <linux/init.h>// #include <linux/cdev.h>//char device structure definition #include <linux/io.h>//io operation function ,like ioremap,iowrite #include <linux/poll.h> MODULE_AUTHOR("qigaohua") ; MODULE_LICENSE("Dual BSD/GPL") ; #define GLOBALFIFO_SIZE 0x1000 //1M #define CLEAR_ALL 0x01 //clear fifo #define GLOBALFIFO_MAJOR 0//major device No static int globalfifo_major = GLOBALFIFO_MAJOR ; /*define globalfifo structure*/ struct globalfifo_dev { struct cdev dev ; unsigned int current_len ; unsigned char mem[GLOBALFIFO_SIZE] ; struct semaphore sem ; wait_queue_head_t r_wait ; wait_queue_head_t w_wait ; struct fasync_struct fasync_queue; } ; struct globalfifo_dev *globalfifo_devp = NULL; static int globalfifo_open(struct inode *inode, struct file *filp) { filp->private_data = globalfifo_devp ; return 0 ; } static int globalfifo_release(struct inode *inode ,struct file *filp) { globalfifo_fasync(-1, filp, 0); return 0 ; } static int globalfifo_ioctl(struct inode *inode, struct file *filp, int cmd, unsigned long arg) { struct globalfifo_dev *dev = filp->private_data ; switch (cmd) { case CLEAR_ALL: /*note:down_intterruptible is more suitable,why?*/ down_interruptible(&dev->sem) ; dev->current_len = 0 ; memset(dev->mem,0,GLOBALFIFO_SIZE) ; up(&dev->sem) ; printk(KERN_INFO "clear globalfifo successfully!\n") ; break ; default: printk(KERN_INFO "invalid command!\n") ; return -EINVAL ; } } static unsigned int globalfifo_poll(struct file *filp, poll_table *wait) { unsigned int mask = 0 ; struct globalfifo_dev *dev = filp->private_data ; down_interruptible(&dev->sem) ; poll_wait(filp,&dev->r_wait,wait) ; poll_wait(filp,&dev->w_wait,wait) ; if(dev->current_len !=0 ) { mask |= POLLIN | POLLRDNORM ; } if(dev->current_len< GLOBALFIFO_SIZE ) { mask |= POLLOUT | POLLWRNORM ; } up(&dev->sem) ; return mask ; } static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { int ret ; struct globalfifo_dev *dev = filp->private_data ; DECLARE_WAITQUEUE(wait,current) ; down_interruptible(&dev->sem) ; add_wait_queue(&dev->r_wait,&wait) ; if(dev->current_len == 0 ) { if( filp->f_flags & O_NONBLOCK) { ret = -EAGAIN ; goto out ; } __set_current_state(TASK_INTERRUPTIBLE) ; up(&dev->sem) ; schedule() ; if(signal_pending(current)) { ret = - ERESTARTSYS ; goto out2 ; } down_interruptible(&dev->sem) ; } if(count>dev->current_len) { count = dev->current_len ; } if(copy_to_user(buf,dev->mem,count)) { ret = -EFAULT ; goto out ; }else { memcpy(dev->mem,dev->mem+count,dev->current_len-count) ; dev->current_len -= count ; printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count,dev->current_len) ; wake_up_interruptible(&dev->w_wait) ; ret = count ; } out: up(&dev->sem) ; out2: remove_wait_queue(&dev->w_wait,&wait) ; set_current_state(TASK_RUNNING) ; return ret ; } static int globalfifo_fasync(int fd, struct file *filp, int mode) { struct globalfifo_dev *dev = file->private_data; return fasync_helper(fd, filp, mode, &dev->fasync_queue); } static ssize_t globalfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { struct globalfifo_dev *dev = filp->private_data ; int ret = -1 ; DECLARE_WAITQUEUE(wait,current) ; down_interruptible(&dev->sem) ; add_wait_queue(&dev->w_wait,&wait) ; if(dev->current_len == GLOBALFIFO_SIZE) { if(filp->f_flags & O_NONBLOCK) { ret = -EAGAIN ; goto out ; } __set_current_state(TASK_INTERRUPTIBLE) ; up(&dev->sem) ; schedule() ; if(signal_pending(current)) { ret = -ERESTARTSYS ; goto out2 ; } down_interruptible(&dev->sem) ; } if(count>GLOBALFIFO_SIZE-dev->current_len) { count = GLOBALFIFO_SIZE - dev->current_len ; } if(copy_from_user(dev->mem+dev->current_len,buf,count)) { ret = - EFAULT ; goto out ; }else { dev->current_len += count ; printk(KERN_INFO "written %d bytes(s),cuurent_len:%d", count , dev->current_len ) ; wake_up_interruptible(&dev->r_wait) ; if(dev->fasync_queue) kill_fasync(&dev->fasync_queue, SIGIO, POLLIN); ret = count ; } out: up(&dev->sem) ; out2: remove_wait_queue(&dev->w_wait,&wait) ; set_current_state(TASK_RUNNING) ; return ret ; } static const struct file_operations globalfifo_fops = { .owner = THIS_MODULE , .open = globalfifo_open , .read = globalfifo_read , .write = globalfifo_write , .ioctl = globalfifo_ioctl , .poll = globalfifo_poll , .fasync = globalfifo_fasync, .release = globalfifo_release , } ; int __init globalfifo_init(void) { int ret ; dev_t devno = MKDEV(globalfifo_major, 0 ) ; if(globalfifo_major) { ret = register_chrdev_region(devno,1,"globalfifo") ; }else { ret = alloc_chrdev_region(&devno,0,1,"globalfifo") ; globalfifo_major = MAJOR(devno) ; } if(ret<0){ printk(KERN_INFO "apply for device No failed! major:%d,minor:%d\n",MAJOR(devno),MINOR(devno)) ; return ret ; } globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev),GFP_KERNEL) ; if(!globalfifo_devp){ ret = - ENOMEM ; goto fail_malloc ; } memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev)) ; cdev_init(&globalfifo_devp->dev, &globalfifo_fops) ; globalfifo_devp->dev.owner = THIS_MODULE ; globalfifo_devp->dev.ops = &globalfifo_fops ; if(cdev_add(&globalfifo_devp->dev, devno,1)) { printk(KERN_NOTICE "add cdev failed!\n") ; } INIT_MUTEX(&globalfifo_devp->sem) ; init_waitqueue_head(&globalfifo_devp->r_wait) ; init_waitqueue_head(&globalfifo_devp->w_wait) ; printk(KERN_INFO "init globalfifo device successfully!\n" ) ; return 0 ; fail_malloc: unregister_chrdev_region(devno,1) ; return ret ; } void __exit globalfifo_exit(void) { if(globalfifo_devp != NULL ){ cdev_del(&globalfifo_devp->dev) ; kfree(globalfifo_devp) ; unregister_chrdev_region(MKDEV(globalfifo_major,0),1) ; } printk(KERN_INFO "unload globalfifo device successfully!\n") ; } //module_param(globalfifo_major, int , S_IRUGO) ; module_init(globalfifo_init) ; module_exit(globalfifo_exit) ;
来自《Linux设备驱动开发详解》
标签:
原文地址:http://www.cnblogs.com/qigaohua/p/5448428.html