当应用程序需要进行对多文件读写时,常用到 poll(System V)、select(BSD Unix)、 epoll(linux2.5.45开始)(没验证)系统调用配合使用。当poll函数返回时,会给出一个文件是否可读写的标志,应用程序根据不同的标志读写相应的文件,实现阻塞或非阻塞的读写。这些系统调用功能相同: 允许进程来决定它是否可阻塞或非阻塞的读写一个或多个文件。这些调用都需要来自设备驱动中poll 方法的支持,poll返回不同的标志,告诉主进程文件是否可以读写,其原型(定义在<linux/poll.h> ):
实现这个设备方法分两步:
1.使用poll_wait将等待队列添加到poll_table中。
#include <linux/poll.h>
static inline void poll_wait (struct file *filp, wait_queue_head_t *wait_address, poll_table *P);
增加wait_queue_head_t等待队列到poll_tabl中。
2. 返回描述设备是否可读或可写的掩码。几个标志(通过 <linux/poll.h> 定义)用来指示可能的操作:
标志
|
含义
|
POLLIN
|
如果设备无阻塞的读,就返回该值(设备可读)
|
POLLRDNORM
|
通常的数据已经准备好,可以读了,就返回该值。通常的做法是会返回(POLLLIN|POLLRDNORA)
(数据可读)
|
POLLRDBAND
|
如果可 以从设备读出带外数据,就返回该值,它只可在linux内 核的某些网络代码中使用,通常不用在设备驱动程序中
|
POLLPRI
|
如果可 以无阻塞的读取高优先级(带外)数据,就返回该值,返回该值会导致select报告文件发生异常,以为select八带外数据当作异常处理
|
POLLHUP
|
当读设 备的进程到达文件尾时,驱动程序必须返回该值,依照select的功能描述,调用select的进程被告知进程时可读的。
|
POLLERR
|
如果设 备发生错误,就返回该值。
|
POLLOUT
|
如果设备可以无阻塞地写,就返回该值(设备可写)
|
POLLWRNORM
|
设备已经准备好,可以写了,就返回该值。通常地做法是(POLLOUT|POLLNORM)
(数据可写)
|
POLLWRBAND
|
于POLLRDBAND类似
|
设备和数据在使用上实际上没有任何差别,通常可读返回:POLLIN|POLLRDNORM,可写返回:POLLOUT|POLLWRNORM
应当重复一下 POLLRDBAND 和 POLLWRBAND 仅仅对关联到 socket 的文件描述符有意义: 通常设备驱动不使用这些标志。
poll 的描述使用了大量在实际使用中相对简单的东西. 考虑 poll 方法的 scullpipe 实现:
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_wait(filp, &dev->inq, wait);
poll_wait(filp, &dev->outq, wait);
if (dev->rp != dev->wp)
mask |= POLLIN | POLLRDNORM; /* readable */
up(&dev->sem);
return mask;
}
这个代码简单地增加了 2 个 scullpipe 等待队列到 poll_table, 接着设置正确的掩码位, 根据数据是否可以读或写.
所示的 poll 代码缺乏文件尾支持, 因为 scullpipe 不支持文件尾情况. 对大部分真实的设备, poll 方法应当返回 POLLHUP 如果没有更多数据(或者将)可用. 如果调用者使用 select 系统调用, 文件被报告为可读. 不管是使用 poll 还是 select, 应用程序知道它能够调用 read 而不必永远等待, 并且 read 方法返回 0 来指示文件尾.
范例
static unsigned int mem_poll(struct file *filp,poll_table *wait)
{
struct scull_piple *dev =filp->private_data;
?//为什么要使用scull_piple, scull_piple是针对一片内存实现一个环形缓,用缓冲的目的是存储队列。
unsigned int mask =0;
/*把进程添加到等待队列*/
poll_wait(filp,&dev->inq,wait);
/*返回掩码*/
If(数据有效)
mask=POLLIN|POLLRDNRM;/*设备可读*/
return mask;
}
Poll方法只是做一个登记,真正的阻塞发生在select.c中的do_select函数。