作者:董昊 (要转载的同学帮忙把名字和博客链接http://donghao.org/uii/带上,多谢了!)
ldd3上已经讲了如何开发linux下的驱动程序,怎么让该设备支持poll(和epoll),但是不够详细,这里给个例子。假设实现一个misc设备,为了实现poll,当然要有个wait_queue,注意,是dev带wait_queue,我一疏忽把wait_queue带到file上去了,调了半天才发现这个低级错误。
struct sample_dev
{
struct miscdevice misc;
wait_queue_head_t wait;
};
static struct sample_dev s_dev;
s_dev这个设备现在既可以当miscdevice用,同时又有了wait_queue
struct file_operations sample_fops =
{
.owner = THIS_MODULE,
.read = sample_read,
.write = sample_write,
.poll = sample_poll,
};
static int __init init_sample(void)
{
s_dev.misc.minor = MISC_DYNAMIC_MINOR;
s_dev.misc.name = "poll_device_sample";
s_dev.misc.fops = &sample_fops;
init_waitqueue_head(&s_dev.wait);
return misc_register(&s_dev.misc);
}
static void __exit exit_sample(void)
{
misc_unregister(&s_dev.misc);
}
这是设备的注册和注销。下面看sample_poll的做法,和ldd3上说的一样:
unsigned int sample_poll(struct file* file, poll_table* wait)
{
unsigned int mask = 0;
poll_wait(file, &s_dev.wait, wait);
/* if have something to read (代码省略)*/
mask |= POLLIN;
/* if have something to write (代码省略)*/
mask |= POLLOUT;
/* if some error occur (代码省略)*/
mask |= POLLERR;
return mask;
}
poll_wait是linux内核提供的,标准做法,所以最好这么用。在poll_wait里,current进程挂在了s_dev的wait_queue里,只有两种情况让他醒来:一个是poll系统调用超时(poll_table负责),另一个是读写唤醒他(后面的代码)。
sszie_t sample_read(struct file* file, char __user* buff, loff_t* pos)
{
/* do what you want to read(代码省略)*/
wake_up_interruptible(&s_dev.wait);
}
由于读走了一些数据,缓冲区(代码没有详细写)有位置了,可以往里面写了,上面标红的行便唤醒随眠的进程,他(进程)醒来后就到了poll_wait语句的后面,开始查看缓冲区并置mask,最后返回。sample_write也是同样的实现方式。
以上代码只是例子,并不完整,但原理已经充分。这样实现的设备已经可以支持poll和epoll调用,当然,epoll的原理更为复杂,和上面说的过程有一些差异,详情可以参考这里。