九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 4031|回复: 0
打印 上一主题 下一主题

x4412&ibox项目实战39-轮询操作

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-10 14:45:54 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在用户程序中,使用非阻塞I/O的应用程序通常会使用select()和poll()系统调用查询是否可对设备进行无阻塞的访问。select()和poll()系统调用最终会引发设备驱动中的poll()函数被执行。select()和poll()系统调用的本质一样,前者在 BSD UNIX 中引入的,后者在 System V中引入的。应用程序中最广泛用到的是BSD UNIX中引入的select()系统调用,其原型如下:
  1. int select(int numfds, fd _ set *readfds, fd _ set *writefds, fd _ set *exceptfds,struct timeval *timeout);
复制代码
其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合,numfds的值是文件描述符加1。timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout时间后若没有文件描述符准备好则返回。
下列操作用来设置、清除、判断文件描述符集合。
  1. FD_ZERO(fd_set *set)
复制代码
清除一个文件描述符集。
  1. FD_SET(int fd,fd_set *set)
复制代码
将一个文件描述符加入文件描述符集中。
  1. FD_CLR(int fd,fd_set *set)
复制代码
将一个文件描述符从文件描述符集中清除。
  1. FD_ISSET(int fd,fd_set *set)
复制代码
判断文件描述符是否被置位。
设备驱动中 poll()函数的原型如下:
  1. unsigned int(*poll)(struct file * filp, struct poll_table* wait);
复制代码
第一个参数为file结构体指针,第二个参数为轮询表指针。这个函数应该进行以下两项工作。
l  对可能引起设备文件状态变化的等待队列调用poll_wait()函数,将对应的等待队列头添加到poll_table
l  返回表示是否能对设备进行无阻塞读、写访问的掩码。
驱动程序poll()函数应该返回设备资源的可获取状态,即POLLIN、POLLOUT、POLLPRI、POLLERR、POLLNVAL等宏的位“或”结果。每个宏的含义都表明设备的一种状态,如POLLIN(定义为 0x0001)意味着设备可以无阻塞地读,POLLOUT(定义为0x0004)意味着设备可以无阻塞地写。
将上一小节的FIFO驱动修改为使用轮询操作,在kernel/drivers/char/x4412目录下新建x4412-globalfifo-poll.c,编辑内容如下:
  1. #include <linux/module.h>
  2. #include <linux/types.h>
  3. #include <linux/fs.h>
  4. #include <linux/errno.h>
  5. #include <linux/sched.h>
  6. #include <linux/init.h>
  7. #include <linux/cdev.h>
  8. #include <asm/uaccess.h>
  9. #include <linux/poll.h>
  10. #include <linux/slab.h>
  11. #include <linux/device.h>

  12. #define GLOBALFIFO_SIZE 0x1000 /*全局fifo最大4K字节*/
  13. #define FIFO_CLEAR 0x1  /*清0全局内存的长度*/
  14. DEFINE_SEMAPHORE(sem);//初始化并声明一个信号量
  15. DECLARE_WAIT_QUEUE_HEAD(r_wait);//初始化并声明一个等待队列r_wait
  16. DECLARE_WAIT_QUEUE_HEAD(w_wait);//初始化并声明一个等待队列w_wait
  17. static int globalfifo_major;
  18. static struct class *cdev_class;
  19. struct globalfifo_dev                                    
  20. {
  21.          struct cdev cdev; /*cdev结构体*/                     
  22.          unsigned int current_len;    /*fifo有效数据长度*/
  23.          unsigned char mem[GLOBALFIFO_SIZE]; /*全局内存*/
  24. };
  25. struct globalfifo_dev *globalfifo_devp; /*设备结构体指针*/
  26. int globalfifo_open(struct inode *inode, struct file *filp)
  27. {
  28.          /*将设备结构体指针赋值给文件私有数据指针*/
  29.          filp->private_data = globalfifo_devp;
  30.          return 0;
  31. }
  32. int globalfifo_release(struct inode *inode, struct file *filp)
  33. {
  34.          return 0;
  35. }
  36. static long globalfifo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  37. {
  38.          struct globalfifo_dev *dev = filp->private_data;/*获得设备结构体指针*/
  39.          switch (cmd)
  40.          {
  41.                    case FIFO_CLEAR:
  42.                             down(&sem); //获得信号量   
  43.                             dev->current_len = 0;
  44.                             memset(dev->mem,0,GLOBALFIFO_SIZE);
  45.                             up(&sem); //释放信号量
  46.                             printk(KERN_INFO "globalfifo is set to zero\n");     
  47.                    break;
  48.                    default:
  49.                             return  - EINVAL;
  50.          }
  51.          return 0;
  52. }
  53. static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
  54. {
  55.          unsigned int mask = 0;
  56.          struct globalfifo_dev *dev = filp->private_data; /*获得设备结构体指针*/
  57.          down(&sem);
  58.          poll_wait(filp, &r_wait, wait);//添加读等待队列头
  59.          poll_wait(filp, &w_wait, wait);//添加写等待队列头
  60.          if(dev->current_len != 0)/*fifo非空*/
  61.          {
  62.                    mask |= POLLIN | POLLRDNORM; /*标示数据可获得*/
  63.          }
  64.          if (dev->current_len != GLOBALFIFO_SIZE)/*fifo非满*/
  65.          {
  66.                    mask |= POLLOUT | POLLWRNORM; /*标示数据可写入*/
  67.          }
  68.          up(&sem);
  69.          return mask;
  70. }
  71. static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count,loff_t *ppos)
  72. {
  73.          int ret;
  74.          struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针
  75.          DECLARE_WAITQUEUE(wait, current); //定义等待队列
  76.          down(&sem); //获得信号量
  77.          add_wait_queue(&r_wait, &wait); //进入读等待队列头
  78.          if (dev->current_len == 0)/* 如果FIFO为空,则进程进入睡眠等待 */
  79.          {
  80.                    if (filp->f_flags &O_NONBLOCK)//非阻塞
  81.                    {
  82.                             ret =  - EAGAIN;
  83.                             goto out;
  84.                    }
  85.                    __set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠
  86.                    up(&sem);
  87.                    schedule(); //调度其他进程执行
  88.                    if (signal_pending(current))//如果是因为信号唤醒
  89.                    {
  90.                             ret =  - ERESTARTSYS;
  91.                             goto out2;
  92.                    }
  93.                    down(&sem);
  94.          }
  95.          if (count > dev->current_len)/* 拷贝到用户空间 */
  96.                    count = dev->current_len;
  97.          if (copy_to_user(buf, dev->mem, count)) //从内核空间拷贝到用户空间
  98.          {
  99.                    ret =  - EFAULT;
  100.                    goto out;
  101.          }
  102.          else
  103.          {
  104.                    memcpy(dev->mem, dev->mem + count, dev->current_len - count);  //fifo(管道)数据前移
  105.                    dev->current_len -= count;     //有效数据长度减少
  106.                    printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len);
  107.                    wake_up_interruptible(&w_wait); //唤醒写等待队列
  108.                    ret = count;
  109.          }
  110. out:
  111.          up(&sem); //释放信号量
  112. out2:
  113.          remove_wait_queue(&w_wait, &wait); //从附属的等待队列头移除
  114.          set_current_state(TASK_RUNNING);
  115.          return ret;
  116. }
  117. /*globalfifo写操作*/
  118. static ssize_t globalfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
  119. {
  120.          struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针
  121.          int ret;
  122.          DECLARE_WAITQUEUE(wait, current); //定义等待队列
  123.          down(&sem); //获取信号量
  124.          add_wait_queue(&w_wait, &wait); //进入写等待队列头
  125.          if (dev->current_len == GLOBALFIFO_SIZE)/* 如果FIFO已满则进入睡眠等待 */
  126.          {
  127.                    if (filp->f_flags &O_NONBLOCK)//如果是非阻塞访问
  128.                    {
  129.                             ret =  - EAGAIN;
  130.                             goto out;
  131.                    }
  132.                    __set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠
  133.                    up(&sem);
  134.                    schedule(); //调度其他进程执行
  135.                    if (signal_pending(current))//如果是因为信号唤醒
  136.                    {
  137.                             ret =  - ERESTARTSYS;
  138.                             goto out2;
  139.                    }
  140.                    down(&sem); //获得信号量
  141.          }
  142.          /*从用户空间拷贝到内核空间*/
  143.          if (count > GLOBALFIFO_SIZE - dev->current_len)
  144.                    count = GLOBALFIFO_SIZE - dev->current_len;
  145.          if (copy_from_user(dev->mem + dev->current_len, buf, count))
  146.          {
  147.                    ret =  - EFAULT;
  148.                    goto out;
  149.          }
  150.          else
  151.          {
  152.                    dev->current_len += count;
  153.                    printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev->current_len);
  154.                    wake_up_interruptible(&r_wait); //唤醒读等待队列
  155.                    ret = count;
  156.          }
  157. out:
  158.          up(&sem); //释放信号量
  159. out2:
  160.          remove_wait_queue(&w_wait, &wait); //从附属的等待队列头移除
  161.          set_current_state(TASK_RUNNING);
  162.          return ret;
  163. }
  164. /*文件操作结构体*/
  165. static const struct file_operations globalfifo_fops =
  166. {
  167.          .owner = THIS_MODULE,
  168.          .read = globalfifo_read,
  169.          .write = globalfifo_write,
  170.          .unlocked_ioctl = globalfifo_ioctl,
  171.          .poll = globalfifo_poll,
  172.          .open = globalfifo_open,
  173.          .release = globalfifo_release,
  174. };
  175. /*初始化并注册cdev*/
  176. static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
  177. {
  178.          int err, devno = MKDEV(globalfifo_major, index);
  179.          cdev_init(&dev->cdev, &globalfifo_fops);
  180.          dev->cdev.owner = THIS_MODULE;
  181.          dev->cdev.ops = &globalfifo_fops;
  182.          err = cdev_add(&dev->cdev, devno, 1);
  183.          if (err)
  184.                    printk(KERN_NOTICE "Error %d adding LED%d", err, index);
  185. }
  186. /*设备驱动模块加载函数*/
  187. int globalfifo_init(void)
  188. {
  189.          int ret;
  190.          dev_t devno;
  191.          ret = alloc_chrdev_region(&devno, 0, 1, "x4412-globalfifo-poll");
  192.          globalfifo_major = MAJOR(devno);
  193.          if (ret < 0)
  194.                    return ret;
  195.          /* 动态申请设备结构体的内存*/
  196.          globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
  197.          if (!globalfifo_devp)    /*申请失败*/
  198.          {
  199.                    ret =  - ENOMEM;
  200.                    goto fail_malloc;
  201.          }
  202.          memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));
  203.          globalfifo_setup_cdev(globalfifo_devp, 0);
  204.          cdev_class = class_create(THIS_MODULE,"x4412-globalfifo-poll");
  205.          if(IS_ERR(cdev_class))
  206.                    goto fail_class;
  207.          device_create(cdev_class,NULL,devno,NULL,"x4412-globalfifo-poll");
  208.          return 0;
  209. fail_class:
  210.          class_destroy(cdev_class);
  211. fail_malloc:
  212.          unregister_chrdev_region(devno, 1);
  213.          return ret;
  214. }
  215. /*模块卸载函数*/
  216. void globalfifo_exit(void)
  217. {
  218.          device_destroy(cdev_class,MKDEV(globalfifo_major,0));
  219.          class_destroy(cdev_class);
  220.          cdev_del(&globalfifo_devp->cdev);   /*注销cdev*/
  221.          kfree(globalfifo_devp);     /*释放设备结构体内存*/
  222.          unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*释放设备号*/
  223. }
  224. MODULE_LICENSE("Dual BSD/GPL");
  225. MODULE_AUTHOR("www.9tripod.com");
  226. module_init(globalfifo_init);
  227. module_exit(globalfifo_exit);
复制代码
       程序中声明了两个等待队列,在读和写函数中实现数据同步,避免读写数据错乱。同时,在file_operations结构体中增加了轮询成员poll,在对应的函数globalfifo_poll中对读写等待队列调用poll_wait函数,返回可对设备操作权限的掩码。
       编辑kernel/drivers/char/x4412/Kconfig文件,添加如下内容:
  1. config X4412_GLOBALFIFO_POLL_DRIVER
  2.          tristate "x4412 globalfifo by poll driver"
  3.          default m
  4.          help
  5.          compile for x4412 globalfifo by poll driver,y for kernel,m for module.
复制代码
       编辑kernel/drivers/char/x4412/Makefile文件,添加如下内容:
  1. obj-$(CONFIG_X4412_GLOBALFIFO_DRIVER) += x4412-globalfifo-poll.o
复制代码
       编译内核,在kernel/drivers/char/x4412目录将会生成目标映像x4412-globalfifo-poll.ko文件。
       ubuntu用户目录或samba目录下新建应用程序x4412-globalfifo-poll-app.c文件,编辑内容如下:
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <stdio.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <sys/time.h>

  7. main()
  8. {
  9.          int fd;
  10.          fd_set rfds,wfds;//读写文件描述符集
  11.          /*以非阻塞方式打开设备文件*/
  12.          fd = open("/dev/x4412-globalfifo-poll", O_RDONLY | O_NONBLOCK);
  13.          if (fd !=  - 1)
  14.          {
  15.                    while (1)
  16.                    {
  17.                             FD_ZERO(&rfds);//清除读文件描述符
  18.                             FD_ZERO(&wfds);//清除写文件描述符
  19.                             FD_SET(fd, &rfds);//将rfds加入文件描述符集
  20.                             FD_SET(fd, &wfds);//将wfds加入文件描述符集
  21.                             //监控读写文件描述符
  22.                             select(fd + 1, &rfds, &wfds, NULL, NULL);
  23.                             /*数据可获得*/
  24.                             if (FD_ISSET(fd, &rfds))
  25.                             {
  26.                                      printf("Poll monitor:can be read\n");
  27.                             }
  28.                             /*数据可写入*/
  29.                             if (FD_ISSET(fd, &wfds))
  30.                             {
  31.                                      printf("Poll monitor:can be written\n");
  32.                             }     
  33.                    }
  34.          }
  35.          else
  36.          {
  37.                    printf("Device open failure\n");
  38.          }
  39. }
复制代码
       应用程序首先定义了两个文件描述符rfdswfds,再以非阻塞的方式打开驱动模块,紧接着清除读写文件描述符后,将他们加入文件描述符集,再通过select函数监控这两个文件描述符。在整个while循环中不断使用FD_ISSET函数判断数据是否可读可写,并打印相应的提示信息。
       加载驱动模块后,在不写入任何内存的情况下运行应用程序,将不断打印“Pollmonitor:can be written”。
  1. [root@x4412 mnt]# insmod x4412-globalfifo-poll.ko
  2. [root@x4412 mnt]# ./x4412-globalfifo-poll
  3. Poll monitor:can be written
  4. Poll monitor:can be written
复制代码
       通过echo指令向/dev/x4412-globalfifo-poll写入一些指令后,将不断打印“Poll monitor:can be read”和“Poll monitor:can be written”。
  1. [root@x4412 mnt]# echo 'www.9tripod.com' > /dev/x4412-globalfifo-poll
  2. [ 1121.931135] written 16 bytes(s),current_len:16
  3. [root@x4412 mnt]# ./x4412-globalfifo-poll
  4. Poll monitor:can be read
  5. Poll monitor:can be written
  6. Poll monitor:can be read
  7. Poll monitor:can be written
复制代码
       反复通过echo指令向/dev/x4412-globalfifo-poll写指令,直到FIFO被写满4096bytes,再使用应用程序测试,将不断打印“Poll monitor:can beread”。
  1. [root@x4412 mnt]# echo 'www.9tripod.com' > /dev/x4412-globalfifo-poll
  2. [ 1419.245771] written 16 bytes(s),current_len:4096
  3. [root@x4412 mnt]#
  4. [root@x4412 mnt]# ./x4412-globalfifo-poll
  5. Poll monitor:can be read
  6. Poll monitor:can be read
复制代码
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-11-23 15:08 , Processed in 0.018477 second(s), 18 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表