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

3.app-write.c

  1. #include <stdio.h>
  2. int main()
  3. {
  4. FILE *fp = NULL;
  5. char Buf[128];
  6. /*打开设备文件*/
  7. fp = fopen("/dev/memdev0","r+");
  8. if (fp == NULL)
  9. {
  10. printf("Open Dev memdev Error!\n");
  11. return -1;
  12. }
  13. /*写入设备*/
  14. strcpy(Buf,"memdev is char dev!");
  15. printf("Write BUF: %s\n",Buf);
  16. fwrite(Buf, sizeof(Buf), 1, fp);
  17. sleep(5);
  18. fclose(fp);
  19. return 0;
  20. }

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. int main()
  12. {
  13. int fd;
  14. fd_set rds;
  15. int ret;
  16. char Buf[128];
  17. /*初始化Buf*/
  18. strcpy(Buf,"memdev is char dev!");
  19. printf("BUF: %s\n",Buf);
  20. /*打开设备文件*/
  21. fd = open("/dev/memdev0",O_RDWR);
  22. FD_ZERO(&rds);
  23. FD_SET(fd, &rds);
  24. /*清除Buf*/
  25. strcpy(Buf,"Buf is NULL!");
  26. printf("Read BUF1: %s\n",Buf);
  27. ret = select(fd + 1, &rds, NULL, NULL, NULL);
  28. if (ret < 0)
  29. {
  30. printf("select error!\n");
  31. exit(1);
  32. }
  33. if (FD_ISSET(fd, &rds))
  34. read(fd, Buf, sizeof(Buf));
  35. /*检测结果*/
  36. printf("Read BUF2: %s\n",Buf);
  37. close(fd);
  38. return 0;
  39. }
时间: 2024-12-12 10:08:21

Linux高级字符设备之Poll操作的相关文章

Linux高级字符设备驱动 poll方法(select多路监控原理与实现)

1.什么是Poll方法,功能是什么? 2.Select系统调用(功能)      Select系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用进程.      int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set *exceptfds, const struct timeval *timeout)     Select系统调用(参数)     1)Maxfd:           文件描述符的范围,比

linux 高级字符设备驱动 ioctl操作介绍 例程分析实现【转】

转自:http://my.oschina.net/u/274829/blog/285014 1,ioctl介绍 ioctl控制设备读写数据以及关闭等. 用户空间函数原型:int ioctl(int fd,unsinged long cmd,...) fd-文件描述符 cmd-对设备的发出的控制命令 ...表示这是一个可选的参数,存在与否依赖于cmd,如cmd为修改波特率,那么....就表示波特率的值.如果cmd表示关闭,则不需要参数 内核函数原型 file_operations结构体里面long

linux kernel 字符设备详解

有关Linux kernel 字符设备分析: 参考:http://blog.jobbole.com/86531/ 一.linux kernel 将设备分为3大类,字符设备,块设备,网络设备. 字符设备是指只能一个字节一个字节读写的设备, 常见的外设基本上都是字符设备. 块设备:常见的存储设备,硬盘,SD卡都归为块设备,块设备是按一块一块读取的. 网络设备:linux 将对外通信的一个机制抽象成一个设备, 通过套接字对其进行相关的操作. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.l

Linux 简单字符设备驱动程序 (自顶向下)

第零章:扯扯淡 特此总结一下写的一个简单字符设备驱动程序的过程,我要强调一下“自顶向下”这个介绍方法,因为我觉得这样更容易让没有接触过设备驱动程序的童鞋更容易理解,“自顶向下”最初从<计算机网络 自顶向下方法>这本书学到的,我觉得有时候这是一种很好的方式. 第一章:测试程序 咦?你怎么跟别人的思路不一样???自顶向下嘛,我就直接从测试程序来说啦,这样那个不是更熟悉吗?看看下面的测试程序的代码,是不是很熟悉? 1 #include <stdio.h> 2 #include <u

Linux混杂字符设备

混杂设备驱动模型 混杂设备概念 在Linux系统中,存在一类字符设备,它们拥有相同的主设备号(10),但次设备号不同,我们称这类设备为混杂设备(miscdevice).所有的混杂设备形成一个链表,对设备访问时内核根据次设备号查找到相应的混杂设备. 1.设备描述 Linux中使用struct miscdevice来描述一个混杂设备.struct miscdevice {int minor; /* 次设备号*/const char *name; /* 设备名*/const struct file_o

Linux实现字符设备驱动的基础步骤

Linux应用层想要操作kernel层的API,比方想操作相关GPIO或寄存器,能够通过写一个字符设备驱动来实现. 1.先在rootfs中的 /dev/ 下生成一个字符设备.注意主设备号 和 从设备号.可用例如以下shell脚本生成: if [ ! -e audioIN ];then sudo mknod audioIN c 240 0 fi 生成的设备为 /dev/audioIN ,主设备号240,从设备号0. 2.写audioINdriver.ko ,audioINdriver.c 基本代码

[Linux驱动]字符设备驱动学习笔记(一)

一,主设备号和次设备号代表的含义?linu内核是如果根据主设备号找驱动,次设备号找设备的. 答:通常一个主设备号代表一个驱动,比如在block设备中,一个主设备号代表一个emmc设备,不同次设备号代表的是不同的分区 Linux内核允许多个驱动共享一个主设备号,但更多的设备都遵循一个驱动对一个主设备号的原则.内核维护者一个以主设备号为key的全局哈希表,而哈希表中数据部分则为与该主设备号设备对应的驱动程序(只有一个次设备)的指针或者多个次设备驱动程序组成的数组的指针(次设备共享主设备号) 二,编写

[Linux驱动]字符设备驱动学习笔记(二)———实例

一,注册字符设备 [cpp] view plaincopy #define GLOBALMEM_MAJOR 256 #define GLOBALMEM_SIZE 0X1000 //4k static int char_major=GLOBALMEM_MAJOR;//主设备号 struct chartest_dev { struct cdev cdev; unsigned char mem[GLOBALMEM_SIZE]; }; struct chartest_dev glob_char_dev;

[Linux驱动]字符设备驱动学习笔记(三)———高级

一,ioctl使用实例 ioctl使用实例 驱动程序.h文件  memdev.h [cpp] view plaincopy /* 定义幻数 */ #define MEMDEV_IOC_MAGIC  'k' /* 定义命令 */ #define MEMDEV_IOCPRINT   _IO(MEMDEV_IOC_MAGIC, 1) #define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int) #define MEMDEV_IOCSETDATA _I