字符驱动学习四(异步通知 signal)

原创 2016年08月31日 12:10:43
    异步通知意思是:一旦设备就绪,则主动通知应用程序,这样应用程序不需要查询设备状态,类似硬件上的“中断”概念。准确的称谓是“信号驱动的异步I/O”.信号是在软件层次上对中断机制的一种模拟。
    在驱动框架中需要实现xxx_fasync函数,主要涉及一个结构体 struct fasync_struct 和fasync_helper kill_fasync 两个函数。这个结构体和两个函数的声明在include/linux/fs.h中。

  1. struct fasync_struct {
  2.     spinlock_t        fa_lock;
  3.     int            magic;
  4.     int            fa_fd;
  5.     struct fasync_struct    *fa_next; /* singly linked list */
  6.     struct file        *fa_file;
  7.     struct rcu_head        fa_rcu;
  8. };
  1. /* SMP safe fasync helpers: */
  2. extern int fasync_helper(int, struct file *, int, struct fasync_struct **);
  3. /* can be called from interrupts */
  4. extern void kill_fasync(struct fasync_struct **, int, int);
函数的实现是在:fs/fcntl.c: fasync_helper函数经常在字符设备中用来申请和删除异步队列。如果删除则 on= -1;在release函数中使用删除模式。
  1. /*
  2.  * fasync_helper() is used by almost all character device drivers
  3.  * to set up the fasync queue, and for regular files by the file
  4.  * lease code. It returns negative on error, 0 if it did no changes
  5.  * and positive if it added/deleted the entry.
  6.  */
  7. int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
  8. {
  9.     if (!on)
  10.         return fasync_remove_entry(filp, fapp);
  11.     return fasync_add_entry(fd, filp, fapp);
  12. }

  13. EXPORT_SYMBOL(fasync_helper);
xxx_fasync的驱动基本构架是:
1. 在设备抽象的数据结构中增加一个struct fasync_struct的指针
  1. struct cfifo_t{
  2.     struct cdev cdev;
  3.     unsigned char mem[GLOBALMEM_SIZE];
  4.     struct semaphore sem;

  5.     unsigned int current_len;
  6.     wait_queue_head_t r_wait;
  7.     wait_queue_head_t w_wait;

  8.     struct fasync_struct *r_async_queue; /* 异步指针结构体指针 读*/
  9. };
2. 实现设备操作中的fasync函数,这个函数很简单,其主体就是调用内核的fasync_helper函数。

设备的fasync函数:
  1. static int cfifo_fasync(int fd, struct file *filp, int mode)
  2. {
  3.     struct cfifo_t *dev = filp->private_data;
  4.     return fasync_helper(fd, filp, mode, &dev->r_async_queue);
  5. }
3. 在需要向用户空间通知的地方(例如wrie方法中)调用内核的kill_fasync函数。
  1. static ssize_t cfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
  2. {
  3.     int ret = 0;
  4.     struct cfifo_t *dev = filp->private_data;
  5.     DECLARE_WAITQUEUE(wait, current);

  6.     down(&dev->sem);
  7.     add_wait_queue(&dev->w_wait, &wait);

  8.     while(dev->current_len >= GLOBALMEM_SIZE){
  9.         if(filp->f_flags & O_NONBLOCK){
  10.             ret = -EAGAIN;
  11.             goto out;
  12.         }

  13.         __set_current_state(TASK_INTERRUPTIBLE); /* 进程状态,定义在 linux/sched.h */
  14.         up(&dev->sem); /* 释放信号量,避免死锁 */

  15.         schedule(); /* 调度其他进程运行*/
  16.         if(signal_pending(current)){
  17.             ret = -ERESTARTSYS;
  18.             goto out2;
  19.         }

  20.         down(&dev->sem);
  21.     }

  22.     if(count > GLOBALMEM_SIZE - dev->current_len)
  23.         count = GLOBALMEM_SIZE - dev->current_len;


  24.     if(copy_from_user(dev->mem+dev->current_len, buf, count)){
  25.         ret = - EFAULT;
  26.         goto out;
  27.     }
  28.     else{
  29.         dev->current_len += count;
  30.         *fpos = dev->current_len;
  31.         ret = count;

  32.         wake_up_interruptible(&dev->r_wait);
  33.         printk(KERN_INFO "write %s %d bites \n", buf, count);

  34.         /* 产生异步信号 */
  35.         if(NULL != dev->r_async_queue)
  36.             kill_fasync(&dev->r_async_queue, SIGIO, POLL_IN);
  37.     }

  38. out:
  39.     up(&dev->sem);
  40. out2:
  41.     remove_wait_queue(&dev->w_wait, &wait);
  42.     set_current_state(TASK_RUNNING);

  43.     return ret;
  44. }
4. 在驱动的release方法中调用前面定义的fasync函数
  1. static int cfifo_release(struct inode *inode, struct file *filp)
  2. {
  3.     cfifo_fasync(-1, filp, 0); /* 文件从异步通知队列中删除 */
  4.     filp->private_data = NULL;
  5.     return 0;
  6. }

测试程序:
  1. /**
  2.  * =====================================================================================
  3.  * Filename: cfifo_signal_test.c
  4.  * Description: 测试cfifo 驱动信号
  5.  *
  6.  * Created: 2011年07月02日 17时13分32秒
  7.  * Revision: none
  8.  * Compiler: gcc
  9.  *
  10.  * Author: wanghuan
  11.  * Company:
  12.  * =====================================================================================
  13.  */

  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <sys/types.h>
  17. #include <sys/stat.h>
  18. #include <fcntl.h>
  19. #include <unistd.h>
  20. #include <signal.h>

  21. void input_handler(int signum)
  22. {
  23.     printf("[%s %s %d] Sinnal Num = %d\n", __FUNCTION__, __FILE__, __LINE__, signum);
  24. }

  25. int main()
  26. {
  27.     int fd, oflags;
  28.     fd = open("/dev/cfifo0", O_RDWR, S_IRUSR| S_IRUSR);
  29.     if(fd != -1)
  30.     {
  31.         signal(SIGIO, input_handler);
  32.         fcntl(fd, F_SETOWN, getpid());
  33.         oflags = fcntl(fd, F_GETFL);
  34.         fcntl(fd, F_SETFL, oflags | FASYNC);
  35.         while(1)
  36.         {
  37.             sleep(100);
  38.         }
  39.     }
  40.     else
  41.         perror("open");

  42.     return 0;
  43. }
分析一下测试程序看signal工作的流程:
1.设置信号的处理函数。  signal(SIGIO, input_handler);
2.将当文件和进程号关联,确定信号发射对象。fcntl(fd, F_SETOWN, getpid());
3.设置当前的文件支持异步通知,这一步实际上就是调用驱动中的xxx_fasync 函数,使fasync_helper分配异步通知队列。
4.当向文件中写入数据时在wrtie方法中调到
  1.  if(NULL != dev->r_async_queue)
  2.             kill_fasync(&dev->r_async_queue, SIGIO, POLL_IN);
  3. 这样就会产生SIGIO信号。
完整的程序:
  1. /**
  2.  * =====================================================================================
  3.  * Filename: cfifo.c
  4.  * Description: 字符设备驱动模型 阻塞I/O
  5.  *
  6.  * Created: 2011年06月12日 17时19分50秒
  7.  * Revision: none
  8.  * Compiler: gcc
  9.  *
  10.  * Author: wanghuan,
  11.  * Company:
  12.  * =====================================================================================
  13.  */

  14. #include <linux/module.h>
  15. #include <linux/types.h>
  16. #include <linux/fs.h>         /* file_operation */
  17. #include <linux/errno.h>     /* Error number */
  18. #include <linux/mm.h>    
  19. #include <linux/sched.h>    
  20. #include <linux/slab.h>    
  21. #include <linux/init.h>     /* __init __exit */
  22. #include <linux/device.h>
  23. #include <linux/cdev.h>    
  24. #include <linux/poll.h>    
  25. #include <asm/io.h>    
  26. #include <asm/system.h>    
  27. #include <asm/uaccess.h>     /* copy_to_user, copy_from_user */
  28. #include <linux/kernel.h>     /* printk() */

  29. #define GLOBALMEM_SIZE 0x1000
  30. #define MEM_CLEAR 0x1
  31. #define DEV_NAME "cfifo"
  32. //#define MEM_CLEAR _IO(GLOBALMEM_SIZE, 0)


  33. static int MAJOR_NR = 255;        /* Driver Major Number */
  34. //static int MINOR_NR = 0;        /* Driver Major Number */

  35. struct cfifo_t{
  36.     struct cdev cdev;
  37.     unsigned char mem[GLOBALMEM_SIZE];
  38.     struct semaphore sem;

  39.     unsigned int current_len;
  40.     wait_queue_head_t r_wait;
  41.     wait_queue_head_t w_wait;

  42.     struct fasync_struct *r_async_queue; /* 异步指针结构体指针 读*/
  43.     struct fasync_struct *w_async_queue; /* 异步指针结构体指针 写*/
  44. };
  45. static struct cfifo_t *cfifo_p;

  46. static ssize_t cfifo_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
  47. {
  48.     int ret = 0;
  49.     struct cfifo_t *dev = filp->private_data;
  50.     DECLARE_WAITQUEUE(wait, current);
  51.     
  52.     down(&dev->sem);
  53.     add_wait_queue(&dev->r_wait, &wait);

  54.     while(dev->current_len == 0){
  55.         if(filp->f_flags & O_NONBLOCK){
  56.             ret = -EAGAIN;
  57.             goto out;
  58.         }

  59.         __set_current_state(TASK_INTERRUPTIBLE);
  60.         up(&dev->sem);

  61.         schedule(); /*调度其他进程运行*/
  62.         if(signal_pending(current)){
  63.             ret = -ERESTARTSYS;
  64.             goto out2;
  65.         }

  66.         down(&dev->sem);
  67.     }

  68.     if(count > dev->current_len)
  69.         count = dev->current_len;

  70.     if(copy_to_user(buf, (void*)(dev->mem), count)){
  71.         ret = -EFAULT;
  72.         goto out;
  73.     } else{
  74.         memcpy(dev->mem, dev->mem + count, dev->current_len - count);
  75.         dev->current_len -= count;
  76.         *fpos =dev->current_len;

  77.         wake_up_interruptible(&dev->w_wait);
  78.         ret =count;

  79.         printk(KERN_INFO "read %s %d bites \n", buf, count);
  80.     }

  81. out:
  82.     up(&dev->sem);
  83. out2:
  84.     remove_wait_queue(&dev->r_wait, &wait);
  85.     set_current_state(TASK_RUNNING);

  86.     return ret;
  87. }

  88. static ssize_t cfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
  89. {
  90.     int ret = 0;
  91.     struct cfifo_t *dev = filp->private_data;
  92.     DECLARE_WAITQUEUE(wait, current);

  93.     down(&dev->sem);
  94.     add_wait_queue(&dev->w_wait, &wait);

  95.     while(dev->current_len >= GLOBALMEM_SIZE){
  96.         if(filp->f_flags & O_NONBLOCK){
  97.             ret = -EAGAIN;
  98.             goto out;
  99.         }

  100.         __set_current_state(TASK_INTERRUPTIBLE); /* 进程状态,定义在 linux/sched.h */
  101.         up(&dev->sem); /* 释放信号量,避免死锁 */

  102.         schedule(); /* 调度其他进程运行*/
  103.         if(signal_pending(current)){
  104.             ret = -ERESTARTSYS;
  105.             goto out2;
  106.         }

  107.         down(&dev->sem);
  108.     }

  109.     if(count > GLOBALMEM_SIZE - dev->current_len)
  110.         count = GLOBALMEM_SIZE - dev->current_len;


  111.     if(copy_from_user(dev->mem+dev->current_len, buf, count)){
  112.         ret = - EFAULT;
  113.         goto out;
  114.     }
  115.     else{
  116.         dev->current_len += count;
  117.         *fpos = dev->current_len;
  118.         ret = count;

  119.         wake_up_interruptible(&dev->r_wait);
  120.         printk(KERN_INFO "write %s %d bites \n", buf, count);

  121.         /* 产生异步信号 */
  122.         if(NULL != dev->r_async_queue)
  123.             kill_fasync(&dev->r_async_queue, SIGIO, POLL_IN);
  124.     }

  125. out:
  126.     up(&dev->sem);
  127. out2:
  128.     remove_wait_queue(&dev->w_wait, &wait);
  129.     set_current_state(TASK_RUNNING);

  130.     return ret;
  131. }

  132. static loff_t cfifo_llseek(struct file *filp, loff_t offset, int orig)
  133. {
  134.     loff_t ret;
  135.     switch(orig){
  136.         case 0:
  137.             if(offset < 0){
  138.                 ret = -EFAULT;
  139.                 break;
  140.             }
  141.             if((unsigned int)offset > GLOBALMEM_SIZE){
  142.                 ret = -EFAULT;
  143.                 break;
  144.             }

  145.             filp->f_pos = (unsigned int)offset;
  146.             ret = filp->f_pos;
  147.             break;
  148.         case 1:
  149.             if(filp->f_pos + offset > GLOBALMEM_SIZE){
  150.                 ret = -EFAULT;
  151.                 break;
  152.             }

  153.             if(filp->f_pos + offset < 0){
  154.                 ret = -EFAULT;
  155.                 break;
  156.             }

  157.             filp->f_pos += offset;
  158.             ret = filp->f_pos;
  159.             break;
  160.         default:
  161.             ret = -EFAULT;
  162.     }
  163.     return ret;
  164. }

  165. static int cfifo_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
  166. {
  167.     struct cfifo_t *dev = filp->private_data;
  168.     switch(cmd){
  169.         case MEM_CLEAR:
  170.             if(down_interruptible(&dev->sem))
  171.                 return -ERESTARTSYS;

  172.             memset(dev->mem, 0, GLOBALMEM_SIZE);
  173.             dev->current_len = 0;
  174.             filp->f_pos = 0;

  175.             up(&dev->sem);
  176.             printk(KERN_INFO "globalmem is set to zero \n");
  177.             break;
  178.         default:
  179.             return -EINVAL;
  180.     }

  181.     return 0;
  182. }

  183. static unsigned int cfifo_poll(struct file *file, poll_table *wait)
  184. {
  185.     unsigned int mask = 0;
  186.     struct cfifo_t *dev = file->private_data;

  187.     down(&dev->sem);
  188.     poll_wait(file, &dev->r_wait, wait);
  189.     poll_wait(file, &dev->w_wait, wait);

  190.     if(dev->current_len != 0)
  191.         mask |= POLLIN | POLLRDNORM;

  192.     if(dev->current_len != GLOBALMEM_SIZE)
  193.         mask |= POLLOUT | POLLWRNORM;

  194.     up(&dev->sem);
  195.     return mask;
  196. }

  197. static int cfifo_fasync(int fd, struct file *filp, int mode)
  198. {
  199.     struct cfifo_t *dev = filp->private_data;
  200.     return fasync_helper(fd, filp, mode, &dev->r_async_queue);
  201. }

  202. static int cfifo_open(struct inode *inode, struct file *filp)
  203. {
  204.     struct cfifo_t *dev;
  205.     dev = container_of(inode->i_cdev, struct cfifo_t, cdev);
  206.     filp->private_data = dev;
  207.     return 0;
  208. }

  209. static int cfifo_release(struct inode *inode, struct file *filp)
  210. {
  211.     cfifo_fasync(-1, filp, 0); /* 文件从异步通知队列中删除 */
  212.     filp->private_data = NULL;
  213.     return 0;
  214. }

  215. static const struct file_operations cfifo_fops = {
  216.     .owner = THIS_MODULE,
  217.     .open = cfifo_open,
  218.     .release = cfifo_release,
  219.     .read = cfifo_read,
  220.     .write = cfifo_write,
  221.     .ioctl = cfifo_ioctl,
  222.     .llseek = cfifo_llseek,
  223.     .poll = cfifo_poll,
  224.     .fasync = cfifo_fasync,
  225. };

  226. static void cfifo_setup_cdev(struct cfifo_t *cfifo_cdev, int minor)
  227. {
  228.     int err;
  229.     dev_t devno = MKDEV(MAJOR_NR, minor);
  230.     
  231.     cdev_init(&cfifo_cdev->cdev, &cfifo_fops);
  232.     cfifo_p->cdev.owner = THIS_MODULE;
  233.     err = cdev_add(&cfifo_cdev->cdev, devno, 1);
  234.     if(err != 0)
  235.         printk(KERN_NOTICE "Error %d adding gmen", err);

  236.     cfifo_cdev->current_len = 0;
  237.     init_MUTEX(&cfifo_cdev->sem);
  238.     init_waitqueue_head(&cfifo_cdev->r_wait);
  239.     init_waitqueue_head(&cfifo_cdev->w_wait);
  240. }

  241. static struct class *cdev_class;
  242. static int __init cfifo_cdev_init(void)
  243. {
  244.     int result;
  245.     dev_t devno = MKDEV(MAJOR_NR, 0);

  246.     if(0 != MAJOR_NR){
  247.         result = register_chrdev_region(devno, 1, DEV_NAME); //注册设备 支持两个设备,此设备号从0开始
  248.     } else {
  249.         result = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
  250.         MAJOR_NR = MAJOR(devno);
  251.     }

  252.     printk(KERN_CRIT"hello cfifo\n");
  253.     if(result < 0)
  254.         return result;

  255.     cfifo_p = kmalloc(2*sizeof(struct cfifo_t), GFP_KERNEL);
  256.     if(NULL == cfifo_p){
  257.         unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 1);
  258.         result = -ENOMEM;
  259.     }

  260.     memset(cfifo_p, 0, 1*sizeof(struct cfifo_t));
  261.     cfifo_setup_cdev(&cfifo_p[0], 0);
  262.     cfifo_setup_cdev(&cfifo_p[0], 1);

  263.     cdev_class = class_create(THIS_MODULE, DEV_NAME);
  264.     if(IS_ERR(cdev_class))
  265.         return PTR_ERR(cdev_class);

  266.     device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 0),    NULL, DEV_NAME"%d", 0);
  267.     device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 1),    NULL, DEV_NAME"%d", 1);
  268.     return 0;
  269. }

  270. void cfifo_cdev_exit(void)
  271. {
  272.     cdev_del(&cfifo_p[0].cdev);
  273.     cdev_del(&cfifo_p[0].cdev);

  274.     device_destroy(cdev_class, MKDEV(MAJOR_NR, 0)); //delete device node under /dev
  275.     device_destroy(cdev_class, MKDEV(MAJOR_NR, 1)); //delete device node under /dev
  276.     class_destroy(cdev_class); //delete class created by us

  277.     kfree(cfifo_p);
  278.     unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 1); //由于注册了两个设备,最后一个参数为 2
  279. }

  280. module_init(cfifo_cdev_init);
  281. module_exit(cfifo_cdev_exit);
  282. MODULE_LICENSE("GPL");
  283. MODULE_AUTHOR("wanghuan");
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(586) | 评论(0) | 转发(1) |
相关热门文章
给主人留下些什么吧!~~
评论热议
版权声明:本文为博主原创文章,转载请注明出处。

相关文章推荐

Linux设备驱动程序学习(6)-高级字符驱动程序操作[(4)异步通知fasync]

异步通知fasync 异步通知fasync是应用于系统调用signal和sigaction函数,下面我会使用signal函数。简单的说,signal函数就是让一个信号与与一个函数对应,没当接...
  • tigerly
  • tigerly
  • 2014年04月03日 15:23
  • 305

Linux设备驱动程序第三版学习(8)- 高级字符驱动程序操作(续3)- 异步通知

第六章:高级字符驱动程序操作(续3) 以下为第四部分:异步通知 使用poll轮询方式的时候,相当于应用程序在需要的时候询问设备“准备好了吗?”,如果有这样一种情况,一个进程在低优先级正在执行长的循...

07-S3C2440驱动学习(一)嵌入式linux字符设备驱动-按键驱动程序之异步通知机制+原子操作+互斥信号量+阻塞与非阻塞+定时器去抖

一、异步通知机制 从按键的实现方式来说,可以分为以下几种方式 查询方式,极度耗费CPU资源中断方式,平时休眠,按键按下,唤醒休眠poll机制,不需要一直read,根据poll返回值来决定是否rea...

Linux的异步通知字符设备驱动

Fasync字符驱动: 1、在我们用户程序下所做的工作: ⑴ 注册信号处理函数。 通过signal 或sigaction()实现。 ⑵ 使进程成为该文件的的属主进程。  通过fcntl 的F_SETO...

linux字符设备驱动-异步通知

异步通知关键步骤: 1,应用注册信号处理函数,使用signal函数; 2,谁来发:驱动发送通知信号; 3,发给谁:驱动发送通知给特定的应用程序,驱动需要知道应用程序的PID号; 4,怎么发:驱...

高级字符驱动程序操作之异步通知IO(实践篇)基于内核2.6.35-30

1. async.c 主要展示异步通知机制在驱动程序中的实现 #include #include #include /* everything... */ #includ...

Linux 进程间通信 --- 信号通信 --- signal --- signal(SIGINT, my_func); --- 按键驱动异步通知

信号  ( signal ) 机制是 UNIX 系统中最为古老的进程间通信机制,很多条件可以产生一个信号. 信号的产生:           1,当用户按下某些按键时,产生信号.    ...

Linux字符设备驱动之异步通知

在linux中,异步通知是使用信号来实现的,而在linux,大概有30种信号,比如大家熟悉的ctrl+c的SIGINT信号,进程能够忽略或者捕获除过SIGSTOP和SIGKILL的全部信号,当信号背捕...
  • Lyanzh
  • Lyanzh
  • 2017年08月10日 10:09
  • 171

字符设备驱动之Buttons-异步通知(fasync)

buttons.c #include #include #include #include #include #include #include #i...

linux字符驱动之异步通知按键驱动

在上一节里,我们在中断的基础上添加poll机制来实现有数据的时候就去读,没数据的时候,自己规定一个时间,如果还没有数据,就表示超时时间。在此以前,我们都是让应用程序主动去读,那有没有一种情况,当驱动程...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:字符驱动学习四(异步通知 signal)
举报原因:
原因补充:

(最多只允许输入30个字)