设置一个阻塞型字符设备驱动
- 在设备的读操作中加入阻塞函数:
...
While(!have_data)
{
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
wait_event_interruptible(dev->inq,have_data);
}
...
- 在设备的写操作完成后唤醒设备
have_data = true; /*有新的数据可读了*/
Wake_up(&dev->inq); /*唤醒读进程*/
...
- 另外,需要在设备的初始化初始化等待队列。
init_waitqueue_head(&(mem_devp[i].inq));
以上方法可以满足一般的需求,但是当一个应用程序需要同时监控多个设备文件的IO状态时,仅仅使用上面的read和write就不够了,需要引入poll和select方法。
设备的poll select方法
select 系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用进程。
相应地,设备的select 方法由驱动程序的poll方法实现,当应用程序调用设备的select 时,poll 首先使用poll_wait将等待队列添加到 poll_table中,当条件成熟时(poll_wait 返回),再将描述设备是否可读或可写的掩码返回给select。
工作原理,poll方法只是做一个登记,真正的阻塞发生在select.c中的do_select函数。
方法:
- 在一个阻塞型设备的基础上增加poll方法
- 在设备的fops中增加mem_poll方法:
#include<linux/poll.h>
unsigned int mem_poll(struct file *filp,poll_table *wait)
{
struct mem_dev *dev = filp->private_data;
unsigned int mask = 0;
/*将等待队列添加到poll_table */
poll_wait(filp, &dev->inq, wait);
if (have_data)
mask|= POLLIN | POLLRDNORM; /* readable */
return mask;
}
- 在应用程序中的使用:
#include<sys/select.h>
….
int fd;
fd_set rds;
fd = open ("/dev/memdevo", O_RDWR);
FD_ZERO(&rds); //初始化rds结构
FD_SET(fd, &rds); //将设备文件与rds结构关联起来
ret = select (fd +1, &rds, NULL, NULL, NULL);
if (ret < 0) { … }
if (FD_ISSET(fd, &rds ))
read (fd, Buf, sizeof(Buf));
...
疑惑:这里只是给设备增加了一个mem_poll方法,而设备的读和写还是和阻塞型设备的实现一样。用户直接使用read, write还是可以实现设备的阻塞的。那还要poll为什么。
解惑:poll和select 更重要的意义在于它们可以使应用程序同时等待多个数据流,它们是read和write的补充。