当应用程序需要进行对多文件读写时,常用到 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函数