关闭

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

111人阅读 评论(0) 收藏 举报
分类:
    异步通知意思是:一旦设备就绪,则主动通知应用程序,这样应用程序不需要查询设备状态,类似硬件上的“中断”概念。准确的称谓是“信号驱动的异步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) |
给主人留下些什么吧!~~
评论热议
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:38030次
    • 积分:459
    • 等级:
    • 排名:千里之外
    • 原创:90篇
    • 转载:10篇
    • 译文:0篇
    • 评论:1条
    文章分类
    最新评论