第六章:高级字符驱动程序操作(续2)
以下为第三部分:poll/select系统调用
poll方法执行了两个任务:
第一项任务:调用poll_wait方法向poll_table结构添加一个等待队列
第二项任务:返回一个位掩码(mask),该位掩码秒数了哪个操作可以立即执行而不会被阻塞。
对于位掩码,定义在asm-generic/poll.h中,为了以后方便查阅,记录如下:
[cpp] view plain copy print ?
-
- #define POLLIN 0x0001 //如果设备可以无阻塞的读取,就设置该位
- #define POLLPRI 0x0002 //设置该位可以无阻塞地读取高优先级的数据
- #define POLLOUT 0x0004 //如果设备可以无阻塞地写入,就设置该位
- #define POLLERR 0x0008 //设备发生了错误
- #define POLLHUP 0x0010 //当读取进程到达文件尾时,则必须设置该位挂起
- #define POLLNVAL 0x0020
-
-
- #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
/* 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实现:
[cpp] view plain copy print ?
- static unsigned int scull_p_poll(struct file *filp, poll_table *wait)
- {
- struct scull_pipe *dev = filp->private_data;
- unsigned int mask = 0;
-
-
-
-
-
-
- down(&dev->sem);
-
- poll_wait(filp, &dev->inq, wait);
- poll_wait(filp, &dev->outq, wait);
-
-
- if (dev->rp != dev->wp)
- mask |= POLLIN | POLLRDNORM;
- if (spacefree(dev))
- mask |= POLLOUT | POLLWRNORM;
- up(&dev->sem);
- return mask;
- }
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方法来保证输出缓冲区的数据确实已经被传送出去了。
===============================================================================================
原文出处:http://blog.csdn.net/ypoflyer/article/details/6131324