第六章:高级字符驱动程序操作(续2)
以下为第三部分:poll/select系统调用
poll方法执行了两个任务:
第一项任务:调用poll_wait方法向poll_table结构添加一个等待队列
第二项任务:返回一个位掩码(mask),该位掩码秒数了哪个操作可以立即执行而不会被阻塞。
对于位掩码,定义在asm-generic/poll.h中,为了以后方便查阅,记录如下:
看一下scullpipe中的poll实现:
===============================================================================================
总结:当应用程序调用poll或select时就会知道接下来要进行的操作是否会阻塞。
通常将poll和read/write一起使用,综合分析前面的read和write方法可以知道从设备读取数据和向设备写入数据的流程。
从设备读取数据:
*如果输入缓冲区有数据,那么及时就绪的数据比程序所请求的少,并且驱动程序保证剩下的数据马上就能到达,read调用仍然应该以难以察觉的延迟立即返回。read甚至可以一直返回比所请求数目少的数据(至少返回一个字节)。
*如果输入缓冲区没有数据,那么默认情况read必须阻塞等待,直到至少一个字节到达。而如果设置了O_NONBLOCK标志,则read不阻塞,应立即返回,返回值是-EAGAIN。这时poll必须报告设备不可读,直到至少一个字节到达。一旦缓冲区有了数据,就回到了上一种情况
*如果已经到达文件为,则read应该立即返回0,无论是否阻塞。此时poll应该报告POLLHUP(挂起)
向设备写数据:
*如果输出缓冲区有空间,则write应该无延迟地立即返回。它可以接收比请求少的数据(知道接收一个字节)。这时poll报告设备可写。
*如果输出缓冲区已满,那么默认情况write被阻塞直到有空间。如果设置了O_NONBLOCK标志,则write不阻塞,应立即返回,返回值是-EAGAIN。这时poll报告文件不可写。另一方面,如果设备不能再接收任何数据,则write返回-ENOSPC(no space left on device 设备无可用空间),而不管是否阻塞。
*永远不要让write在返回前等待数据的传输结束,即使O_NONBLOCK被清除。因为应用程序用select来检查write是否会阻塞,如果poll报告设备可以写入,则write就不会被阻塞。驱动程序可以实现fsync方法来保证输出缓冲区的数据确实已经被传送出去了。
===============================================================================================
以下为第三部分:poll/select系统调用
poll方法执行了两个任务:
第一项任务:调用poll_wait方法向poll_table结构添加一个等待队列
第二项任务:返回一个位掩码(mask),该位掩码秒数了哪个操作可以立即执行而不会被阻塞。
对于位掩码,定义在asm-generic/poll.h中,为了以后方便查阅,记录如下:
- /* These are specified by iBCS2 */
- #define POLLIN 0x0001 //如果设备可以无阻塞的读取,就设置该位
- #define POLLPRI 0x0002 //设置该位可以无阻塞地读取高优先级的数据
- #define POLLOUT 0x0004 //如果设备可以无阻塞地写入,就设置该位
- #define POLLERR 0x0008 //设备发生了错误
- #define POLLHUP 0x0010 //当读取进程到达文件尾时,则必须设置该位挂起
- #define POLLNVAL 0x0020
- /* The rest seem to be more-or-less nonstandard. Check them! */
- #define POLLRDNORM 0x0040 //如果“通常”的数据已经就绪,可以读取,就设置该位
- #define POLLRDBAND 0x0080 //这一位指示可以从设备读取频带之外(out of band)的数据
- #ifndef POLLWRNORM
- #define POLLWRNORM 0x0100 //该位和POLLOUT的意义一样
- #endif
- #ifndef POLLWRBAND
- #define POLLWRBAND 0x0200 //与POLLRDBAND类似,这一位表示具有非零优先级的数据可以被写入设备
- #endif
- #ifndef POLLMSG
- #define POLLMSG 0x0400
- #endif
- #ifndef POLLREMOVE
- #define POLLREMOVE 0x1000
- #endif
- #ifndef POLLRDHUP
- #define POLLRDHUP 0x2000
- #endif
看一下scullpipe中的poll实现:
- static unsigned int scull_p_poll(struct file *filp, poll_table *wait)
- {
- struct scull_pipe *dev = filp->private_data;
- unsigned int mask = 0;
- /*
- * The buffer is circular; it is considered full
- * if "wp" is right behind "rp" and empty if the
- * two are equal.
- */
- down(&dev->sem); //加锁
- //下面是poll的第一项任务
- poll_wait(filp, &dev->inq, wait);
- poll_wait(filp, &dev->outq, wait);
- //下面是poll的第二项任务
- if (dev->rp != dev->wp)
- mask |= POLLIN | POLLRDNORM; /* readable */
- if (spacefree(dev))
- mask |= POLLOUT | POLLWRNORM; /* writable */
- up(&dev->sem); //解锁
- return mask; //返回位掩码
- }
===============================================================================================
总结:当应用程序调用poll或select时就会知道接下来要进行的操作是否会阻塞。
通常将poll和read/write一起使用,综合分析前面的read和write方法可以知道从设备读取数据和向设备写入数据的流程。
从设备读取数据:
*如果输入缓冲区有数据,那么及时就绪的数据比程序所请求的少,并且驱动程序保证剩下的数据马上就能到达,read调用仍然应该以难以察觉的延迟立即返回。read甚至可以一直返回比所请求数目少的数据(至少返回一个字节)。
*如果输入缓冲区没有数据,那么默认情况read必须阻塞等待,直到至少一个字节到达。而如果设置了O_NONBLOCK标志,则read不阻塞,应立即返回,返回值是-EAGAIN。这时poll必须报告设备不可读,直到至少一个字节到达。一旦缓冲区有了数据,就回到了上一种情况
*如果已经到达文件为,则read应该立即返回0,无论是否阻塞。此时poll应该报告POLLHUP(挂起)
向设备写数据:
*如果输出缓冲区有空间,则write应该无延迟地立即返回。它可以接收比请求少的数据(知道接收一个字节)。这时poll报告设备可写。
*如果输出缓冲区已满,那么默认情况write被阻塞直到有空间。如果设置了O_NONBLOCK标志,则write不阻塞,应立即返回,返回值是-EAGAIN。这时poll报告文件不可写。另一方面,如果设备不能再接收任何数据,则write返回-ENOSPC(no space left on device 设备无可用空间),而不管是否阻塞。
*永远不要让write在返回前等待数据的传输结束,即使O_NONBLOCK被清除。因为应用程序用select来检查write是否会阻塞,如果poll报告设备可以写入,则write就不会被阻塞。驱动程序可以实现fsync方法来保证输出缓冲区的数据确实已经被传送出去了。
===============================================================================================
本文解析了Linux字符设备驱动中的poll/select系统调用实现原理及应用。重点介绍了位掩码的作用和scullpipe中的poll实现过程,包括如何通过检查缓冲区状态来决定哪些操作可以立即执行。
- 高级字符驱动程序操作(续2)- pollselect .&spm=1001.2101.3001.5002&articleId=7264690&d=1&t=3&u=dbc5948b4ee8412bbcddefdfb3548b85)
1692

被折叠的 条评论
为什么被折叠?



