首页 > 代码库 > Linux高级字符设备之Poll操作

Linux高级字符设备之Poll操作

在用户程序中,select()和poll()也是与设备阻塞与非阻塞访问息息相关的,使用非阻塞I/O的应用程序通常会使用select和poll系统调用查询是否可对设备进行无阻塞的访问。select系统调用最终会引发设备驱动中的poll函数被执行。
一、select()系统调用
用于多路监控,当没有一个文件满足要求时,select将阻塞调用进程。
1.select()原型:
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,const struct timeval *timeout);
/*
*@maxfd : 需要检查的文件描述符个数,数值应该比是三组fd_set中最大数更大(即一般取所有文件描述符的最大值加1),而不是实际文件描述符的总数。
*@readfds: 用来检查可读性的一组文件描述符。
*@writesfds: 用来检查可写性的一组文件描述符。
*@exceptsfds:用来检查意外状态的文件描述符。(注:错误并不是意外状态)
*@timeout:NULL指针代表无限等待,否则是指向timeval结构的指针,代表最长等待时间。(如果其中tv_sec和tv_usec都等于0, 则文件描述符的状态不被影响,但函数并不挂起) 
返回值:
(1)正常情况下返回满足要求的文件描述符个数;
(2)经过了timeout等待后仍无文件满足要求,返回0;
(3)如果select被某个信号中断,将返回-1并设置errno为EINTR;
(4)若出错,返回-1并设置相应的errno;
2.select的使用方法
(1)将要监控的文件添加到文件描述符集;
(2)调用select开始监控;
(3)判断文件是否发生变化;
3.系统提供四个宏对描述符集进行操作
void FD_SET(int fd, fd_set *fdset); //将文件描述符fd添加到文件描述符集fdset中;
void FD_CLR(int fd, fd_set *fdset); //从文件描述符集fdset中清除文件描述符fd;
void FD_ISSET(int fd, fd_set *fdset); //在调用select后使用FD_ISSET来检测文件描述符集中的文件fd发生了变化
void FD_ZERO(fd_set *fdset);//清空文件描述符集
二、Poll方法:
1.poll函数原型:
unsigned int(*poll)(struct file *filp, struct poll_table *wait);
//第一个参数为file结构体指针,第二个参数为轮询表指针。
这个函数应该进行以下两项工作:
(1)对可能引起设备文件状态变化的等待队列调用poll_wait()函数,将对应等待队列添加到poll_table中; 
(2)返回表示是否能对设备进行无阻塞可读或可写访问的掩码;
  位掩码:POLLRDNORM, POLLIN,POLLOUT,POLLWRNORM
  设备可读,通常返回:(POLLIN | POLLRDNORM)
  设备可写,通常返回:(POLLOUT | POLLWRNORM)
三、调用过程:
Linux下select调用的过程:
1、用户层应用程序调用select(),底层调用poll())
2、核心层调用sys_select() ------> do_select()
  最终调用文件描述符fd对应的struct file类型变量的struct file_operations *f_op的poll函数。
  poll指向的函数返回当前可否读写的信息。
  1)如果当前可读写,返回读写信息。
  2)如果当前不可读写,则阻塞进程,并等待驱动程序唤醒,重新调用poll函数,或超时返回。
3、驱动需要实现poll函数。
当驱动发现有数据可以读写时,通知核心层,核心层重新调用poll指向的函数查询信息。
poll_wait(filp,&wait_q,wait) // 此处将当前进程加入到等待队列中,但并不阻塞

  在中断中使用wake_up_interruptible(&wait_q)唤醒等待队列。

四、实例分析:
1.memdev.h
  1. #ifndef _MEMDEV_H_  
  2. #define _MEMDEV_H_  
  3.   
  4. #ifndef MEMDEV_MAJOR  
  5. #define MEMDEV_MAJOR 0   /*预设的mem的主设备号*/  
  6. #endif  
  7.   
  8. #ifndef MEMDEV_NR_DEVS  
  9. #define MEMDEV_NR_DEVS 2    /*设备数*/  
  10. #endif  
  11.   
  12. #ifndef MEMDEV_SIZE  
  13. #define MEMDEV_SIZE 4096  
  14. #endif  
  15. /*mem设备描述结构体*/  
  16. struct mem_dev                                       
  17. {                                                          
  18.   char *data;                        
  19.   unsigned long size;   
  20.   wait_queue_head_t inq;    
  21. };  
  22.   
  23. #endif /* _MEMDEV_H_ */ 
2.memdev.c
  1. #include <linux/module.h>  
  2. #include <linux/types.h>  
  3. #include <linux/fs.h>  
  4. #include <linux/errno.h>  
  5. #include <linux/mm.h>  
  6. #include <linux/sched.h>  
  7. #include <linux/init.h>  
  8. #include <linux/cdev.h>  
  9. #include <asm/io.h>  
  10. #include <asm/system.h>  
  11. #include <asm/uaccess.h>  
  12.   
  13. #include <linux/poll.h>  
  14. #include "memdev.h"  
  15.   
  16. static mem_major = MEMDEV_MAJOR;  
  17. bool have_data = false/*表明设备有足够数据可供读*/  
  18.   
  19. module_param(mem_major, int, S_IRUGO);  
  20.   
  21. struct mem_dev *mem_devp; /*设备结构体指针*/  
  22.   
  23. struct cdev cdev;   
  24.   
  25. /*文件打开函数*/  
  26. int mem_open(struct inode *inode, struct file *filp)  
  27. {  
  28.     struct mem_dev *dev;  
  29.       
  30.     /*获取次设备号*/  
  31.     int num = MINOR(inode->i_rdev);  
  32.   
  33.     if (num >= MEMDEV_NR_DEVS)   
  34.             return -ENODEV;  
  35.     dev = &mem_devp[num];  
  36.       
  37.     /*将设备描述结构指针赋值给文件私有数据指针*/  
  38.     filp->private_data = dev;  
  39.       
  40.     return 0;   
  41. }  
  42.   
  43. /*文件释放函数*/  
  44. int mem_release(struct inode *inode, struct file *filp)  
  45. {  
  46.   return 0;  
  47. }  
  48.   
  49. /*读函数*/  
  50. static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)  
  51. {  
  52.   unsigned long p =  *ppos;  
  53.   unsigned int count = size;  
  54.   int ret = 0;  
  55.   struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/  
  56.   
  57.   /*判断读位置是否有效*/  
  58.   if (p >= MEMDEV_SIZE)  
  59.     return 0;  
  60.   if (count > MEMDEV_SIZE - p)  
  61.     count = MEMDEV_SIZE - p;  
  62.       
  63.   while (!have_data) /* 没有数据可读,考虑为什么不用if,而用while */  
  64.   {  
  65.         if (filp->f_flags & O_NONBLOCK)  
  66.             return -EAGAIN;  
  67.       
  68.     wait_event_interruptible(dev->inq,have_data);  
  69.   }  
  70.   
  71.   /*读数据到用户空间*/  
  72.   if (copy_to_user(buf, (void*)(dev->data + p), count))  
  73.   {  
  74.     ret =  - EFAULT;  
  75.   }  
  76.   else  
  77.   {  
  78.     *ppos += count;  
  79.     ret = count;  
  80.      
  81.     printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);  
  82.   }  
  83.     
  84.   have_data = false/* 表明不再有数据可读 */  
  85.   /* 唤醒写进程 */  
  86.   return ret;  
  87. }  
  88.   
  89. /*写函数*/  
  90. static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)  
  91. {  
  92.   unsigned long p =  *ppos;  
  93.   unsigned int count = size;  
  94.   int ret = 0;  
  95.   struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/  
  96.     
  97.   /*分析和获取有效的写长度*/  
  98.   if (p >= MEMDEV_SIZE)  
  99.     return 0;  
  100.   if (count > MEMDEV_SIZE - p)  
  101.     count = MEMDEV_SIZE - p;  
  102.   
  103.   /*从用户空间写入数据*/  
  104.   if (copy_from_user(dev->data + p, buf, count))  
  105.     ret =  - EFAULT;  
  106.   else  
  107.   {  
  108.     *ppos += count;  
  109.     ret = count;  
  110.       
  111.     printk(KERN_INFO "written %d bytes(s) from %d\n", count, p);  
  112.   }  
  113.     
  114.   have_data = true/* 有新的数据可读 */  
  115.       
  116.     /* 唤醒读进程 */  
  117.     wake_up(&(dev->inq));  
  118.   
  119.   return ret;  
  120. }  
  121.   
  122. /* seek文件定位函数 */  
  123. static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)  
  124. {   
  125.     loff_t newpos;  
  126.   
  127.     switch(whence) {  
  128.       case 0: /* SEEK_SET */  
  129.         newpos = offset;  
  130.         break;  
  131.   
  132.       case 1: /* SEEK_CUR */  
  133.         newpos = filp->f_pos + offset;  
  134.         break;  
  135.   
  136.       case 2: /* SEEK_END */  
  137.         newpos = MEMDEV_SIZE -1 + offset;  
  138.         break;  
  139.   
  140.       default/* can‘t happen */  
  141.         return -EINVAL;  
  142.     }  
  143.     if ((newpos<0) || (newpos>MEMDEV_SIZE))  
  144.         return -EINVAL;  
  145.           
  146.     filp->f_pos = newpos;  
  147.     return newpos;  
  148.   
  149. }  
  150. unsigned int mem_poll(struct file *filp, poll_table *wait)  
  151. {  
  152.     struct mem_dev  *dev = filp->private_data;   
  153.     unsigned int mask = 0;  
  154.       
  155.    /*将等待队列添加到poll_table */  
  156.     poll_wait(filp, &dev->inq,  wait);  
  157.    
  158.       
  159.     if (have_data)         mask |= POLLIN | POLLRDNORM;  /* readable */  
  160.   
  161.     return mask;  
  162. }  
  163.   
  164.   
  165. /*文件操作结构体*/  
  166. static const struct file_operations mem_fops =  
  167. {  
  168.   .owner = THIS_MODULE,  
  169.   .llseek = mem_llseek,  
  170.   .read = mem_read,  
  171.   .write = mem_write,  
  172.   .open = mem_open,  
  173.   .release = mem_release,  
  174.   .poll = mem_poll,  
  175. };  
  176.   
  177. /*设备驱动模块加载函数*/  
  178. static int memdev_init(void)  
  179. {  
  180.   int result;  
  181.   int i;  
  182.   
  183.   dev_t devno = MKDEV(mem_major, 0);  
  184.   
  185.   /* 静态申请设备号*/  
  186.   if (mem_major)  
  187.     result = register_chrdev_region(devno, 2, "memdev");  
  188.   else  /* 动态分配设备号 */  
  189.   {  
  190.     result = alloc_chrdev_region(&devno, 0, 2, "memdev");  
  191.     mem_major = MAJOR(devno);  
  192.   }    
  193.     
  194.   if (result < 0)  
  195.     return result;  
  196.   
  197.   /*初始化cdev结构*/  
  198.   cdev_init(&cdev, &mem_fops);  
  199.   cdev.owner = THIS_MODULE;  
  200.   cdev.ops = &mem_fops;  
  201.     
  202.   /* 注册字符设备 */  
  203.   cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);  
  204.      
  205.   /* 为设备描述结构分配内存*/  
  206.   mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);  
  207.   if (!mem_devp)    /*申请失败*/  
  208.   {  
  209.     result =  - ENOMEM;  
  210.     goto fail_malloc;  
  211.   }  
  212.   memset(mem_devp, 0, sizeof(struct mem_dev));  
  213.     
  214.   /*为设备分配内存*/  
  215.   for (i=0; i < MEMDEV_NR_DEVS; i++)   
  216.   {  
  217.         mem_devp[i].size = MEMDEV_SIZE;  
  218.         mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);  
  219.         memset(mem_devp[i].data, 0, MEMDEV_SIZE);  
  220.     
  221.       /*初始化等待队列*/  
  222.      init_waitqueue_head(&(mem_devp[i].inq));  
  223.      //init_waitqueue_head(&(mem_devp[i].outq));  
  224.   }  
  225.      
  226.   return 0;  
  227.   
  228.   fail_malloc:   
  229.   unregister_chrdev_region(devno, 1);  
  230.     
  231.   return result;  
  232. }  
  233.   
  234. /*模块卸载函数*/  
  235. static void memdev_exit(void)  
  236. {  
  237.   cdev_del(&cdev);   /*注销设备*/  
  238.   kfree(mem_devp);     /*释放设备结构体内存*/  
  239.   unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/  
  240. }  
  241.   
  242. MODULE_AUTHOR("David Xie");  
  243. MODULE_LICENSE("GPL");  
  244.   
  245. module_init(memdev_init);  
  246. module_exit(memdev_exit); 

 

3.app-write.c
  1. #include <stdio.h>  
  2.   
  3. int main()  
  4. {  
  5.     FILE *fp = NULL;  
  6.     char Buf[128];  
  7.       
  8.       
  9.     /*打开设备文件*/  
  10.     fp = fopen("/dev/memdev0","r+");  
  11.     if (fp == NULL)  
  12.     {  
  13.         printf("Open Dev memdev Error!\n");  
  14.         return -1;  
  15.     }  
  16.       
  17.     /*写入设备*/  
  18.     strcpy(Buf,"memdev is char dev!");  
  19.     printf("Write BUF: %s\n",Buf);  
  20.     fwrite(Buf, sizeof(Buf), 1, fp);  
  21.       
  22.     sleep(5);  
  23.     fclose(fp);  
  24.       
  25.     return 0;      
  26.   
4.app-read.c
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <sys/ioctl.h>  
  5. #include <sys/types.h>  
  6. #include <sys/stat.h>  
  7. #include <fcntl.h>  
  8. #include <sys/select.h>  
  9. #include <sys/time.h>  
  10. #include <errno.h>  
  11.   
  12. int main()  
  13. {  
  14.     int fd;  
  15.     fd_set rds;  
  16.     int ret;  
  17.     char Buf[128];  
  18.       
  19.     /*初始化Buf*/  
  20.     strcpy(Buf,"memdev is char dev!");  
  21.     printf("BUF: %s\n",Buf);  
  22.       
  23.     /*打开设备文件*/  
  24.     fd = open("/dev/memdev0",O_RDWR);  
  25.       
  26.     FD_ZERO(&rds);  
  27.     FD_SET(fd, &rds);  
  28.   
  29.     /*清除Buf*/  
  30.     strcpy(Buf,"Buf is NULL!");  
  31.     printf("Read BUF1: %s\n",Buf);  
  32.   
  33.     ret = select(fd + 1, &rds, NULL, NULL, NULL);  
  34.     if (ret < 0)   
  35.     {  
  36.         printf("select error!\n");  
  37.         exit(1);  
  38.     }  
  39.     if (FD_ISSET(fd, &rds))   
  40.         read(fd, Buf, sizeof(Buf));              
  41.       
  42.     /*检测结果*/  
  43.     printf("Read BUF2: %s\n",Buf);  
  44.       
  45.     close(fd);  
  46.       
  47.     return 0;      

Linux高级字符设备之Poll操作