Linux 设备驱动中的 I/O模型(二)—— 异步通知和异步I/O

来源:互联网 发布:js脚本注入方法 编辑:程序博客网 时间:2024/05/24 05:03

 阻塞和非阻塞访问、poll() 函数提供了较多地解决设备访问的机制,但是如果有了异步通知整套机制就更加完善了。

      异步通知的意思是:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上“中断”的概念,比较准确的称谓是“信号驱动的异步I/O”。信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。

    阻塞I/O意味着移植等待设备可访问后再访问,非阻塞I/O中使用poll()意味着查询设备是否可访问,而异步通知则意味着设备通知自身可访问,实现了异步I/O。由此可见,这种方式I/O可以互为补充。


1、异步通知的概念和作用

影响:阻塞–应用程序无需轮询设备是否可以访问

           非阻塞–中断进行通知

即:由驱动发起,主动通知应用程序


2、linux异步通知编程

2.1 linux信号

作用:linux系统中,异步通知使用信号来实现

函数原型为:

void (*signal(int signum,void (*handler))(int)))(int)

原型比较难理解可以分解为

typedef void(*sighandler_t)(int); sighandler_t signal(int signum,sighandler_t handler);

第一个参数是指定信号的值,第二个参数是指定针对前面信号的处理函数


2.2 信号的处理函数(在应用程序端捕获信号)

signal()函数

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //启动信号机制  
  2.    
  3. void sigterm_handler(int sigo)  
  4. {  
  5.     char data[MAX_LEN];  
  6.     int len;  
  7.     len = read(STDIN_FILENO,&data,MAX_LEN);  
  8.     data[len] = 0;  
  9.     printf("Input available:%s\n",data);  
  10.     exit(0);  
  11.  }  
  12.    
  13. int main(void)  
  14. {  
  15.    
  16.     int oflags;  
  17.   
  18.     //启动信号驱动机制  
  19.     signal(SIGIO,sigterm_handler);  
  20.     fcntl(STDIN_FILENO,F_SETOWN,getpid());  
  21.     oflags = fcntl(STDIN_FILENO,F_GETFL);  
  22.     fctcl(STDIN_FILENO,F_SETFL,oflags | FASYNC);  
  23.       
  24.     //建立一个死循环,防止程序结束       
  25.     whlie(1);  
  26.    
  27.     return 0;  
  28. }  

2.3 信号的释放 (在设备驱动端释放信号)

      为了是设备支持异步通知机制,驱动程序中涉及以下3项工作

(1)、支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应的进程ID。不过此项工作已由内核完成,设备驱动无须处理。
(2)、支持F_SETFL命令处理,每当FASYNC标志改变时,驱动函数中的fasync()函数得以执行。因此,驱动中应该实现fasync()函数
(3)、在设备资源中可获得,调用kill_fasync()函数激发相应的信号

     

     设备驱动中异步通知编程比较简单,主要用到一项数据结构和两个函数。这个数据结构是fasync_struct 结构体,两个函数分别是:

a -- 处理FASYNC标志变更

int fasync_helper(int fd,struct file *filp,int mode,struct fasync_struct **fa);

b -- 释放信号用的函数

void kill_fasync(struct fasync_struct **fa,int sig,int band);

和其他结构体指针放到设备结构体中,模板如下

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. struct xxx_dev{  
  2.     struct cdev cdev;  
  3.     ...  
  4.     struct fasync_struct *async_queue;  
  5.     //异步结构体指针  
  6. };  

      在设备驱动中的fasync()函数中,只需简单地将该函数的3个参数以及fasync_struct结构体指针的指针作为第四个参数传入fasync_helper()函数就可以了,模板如下

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. static int xxx_fasync(int fd,struct file *filp, int mode)  
  2. {  
  3.     struct xxx_dev *dev = filp->private_data;  
  4.     return fasync_helper(fd, filp, mode, &dev->async_queue);  
  5. }  
     在设备资源可获得时应该调用kill_fasync()函数释放SIGIO信号,可读时第三个参数为POLL_IN,可写时第三个参数为POLL_OUT,模板如下
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. static ssize_t xxx_write(struct file *filp,const char __user *buf,size_t count,loff_t *ppos)  
  2. {  
  3.     struct xxx_dev *dev = filp->private_data;  
  4.     ...  
  5.        
  6.     if(dev->async_queue)  
  7.    
  8.     kill_fasync(&dev->async_queue,GIGIO,POLL_IN);  
  9.     ...  
  10. }  

    最后在文件关闭时,要将文件从异步通知列表中删除
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int xxx_release(struct inode *inode,struct file *filp)  
  2. {  
  3.     xxx_fasync(-1,filp,0);  
  4.    
  5.     ...  
  6.     return 0;  
  7.    
  8. }  

3、下面是个实例:

hello.c

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #include <linux/module.h>  
  2. #include <linux/fs.h>  
  3. #include <linux/cdev.h>  
  4. #include <linux/device.h>  
  5. #include <asm/uaccess.h>  
  6. #include <linux/fcntl.h>  
  7.   
  8. static int major = 250;  
  9. static int minor=0;  
  10. static dev_t devno;  
  11. static struct class *cls;  
  12. static struct device *test_device;  
  13.   
  14. static char temp[64]={0};  
  15. static struct fasync_struct *fasync;  
  16.   
  17. static int hello_open (struct inode *inode, struct file *filep)  
  18. {  
  19.     return 0;  
  20. }  
  21. static int hello_release(struct inode *inode, struct file *filep)  
  22. {  
  23.     return 0;  
  24. }  
  25.   
  26. static ssize_t hello_read(struct file *filep, char __user *buf, size_t len, loff_t *pos)  
  27. {  
  28.     if(len>64)  
  29.     {  
  30.         len =64;  
  31.     }  
  32.     if(copy_to_user(buf,temp,len))  
  33.     {  
  34.         return -EFAULT;  
  35.     }     
  36.     return len;  
  37. }  
  38. static ssize_t hello_write(struct file *filep, const char __user *buf, size_t len, loff_t *pos)  
  39. {  
  40.     if(len>64)  
  41.     {  
  42.         len = 64;  
  43.     }  
  44.   
  45.     if(copy_from_user(temp,buf,len))  
  46.     {  
  47.         return -EFAULT;  
  48.     }  
  49.     printk("write %s\n",temp);  
  50.   
  51.     kill_fasync(&fasync, SIGIO, POLL_IN);  
  52.     return len;  
  53. }  
  54.   
  55. static int hello_fasync (int fd, struct file * file, int on)  
  56. {  
  57.     return fasync_helper(fd, file, on, &fasync);  
  58.   
  59. }  
  60. static struct file_operations hello_ops=  
  61. {  
  62.     .open = hello_open,  
  63.     .release = hello_release,  
  64.     .read =hello_read,  
  65.     .write=hello_write,  
  66. };  
  67. static int hello_init(void)  
  68. {  
  69.     int ret;      
  70.     devno = MKDEV(major,minor);  
  71.     ret = register_chrdev(major,"hello",&hello_ops);  
  72.   
  73.     cls = class_create(THIS_MODULE, "myclass");  
  74.     if(IS_ERR(cls))  
  75.     {  
  76.         unregister_chrdev(major,"hello");  
  77.         return -EBUSY;  
  78.     }  
  79.     test_device = device_create(cls,NULL,devno,NULL,"hello");//mknod /dev/hello  
  80.     if(IS_ERR(test_device))  
  81.     {  
  82.         class_destroy(cls);  
  83.         unregister_chrdev(major,"hello");  
  84.         return -EBUSY;  
  85.     }     
  86.     return 0;  
  87. }  
  88. static void hello_exit(void)  
  89. {  
  90.     device_destroy(cls,devno);  
  91.     class_destroy(cls);   
  92.     unregister_chrdev(major,"hello");  
  93.     printk("hello_exit \n");  
  94. }  
  95. MODULE_LICENSE("GPL");  
  96. module_init(hello_init);  
  97. module_exit(hello_exit);  

test.c

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #include <sys/types.h>  
  2. #include <sys/stat.h>  
  3. #include <fcntl.h>  
  4. #include <stdio.h>  
  5. #include <signal.h>  
  6.   
  7. static int fd,len;  
  8. static char buf[64]={0};  
  9.   
  10. void func(int signo)  
  11. {  
  12.     printf("signo %d \n",signo);  
  13.     read(fd,buf,64);  
  14.     printf("buf=%s \n",buf);      
  15. }  
  16.   
  17. main()  
  18. {  
  19.   
  20.     int flage,i=0;  
  21.   
  22.     fd = open("/dev/hello",O_RDWR);  
  23.     if(fd<0)  
  24.     {  
  25.         perror("open fail \n");  
  26.         return ;  
  27.     }  
  28.   
  29.   
  30.     fcntl(fd,F_SETOWN,getpid());  
  31.     flage = fcntl(fd,F_GETFL);  
  32.     fcntl(fd,F_SETFL,flage|FASYNC);  
  33.   
  34.     signal(SIGIO,func);  
  35.   
  36.     while(1)  
  37.     {  
  38.   
  39.         sleep(1);  
  40.         printf("%d\n",i++);  
  41.     }  
  42.   
  43.     close(fd);  
  44. }  
0 0
原创粉丝点击