1、认识POLL
用户态的poll会等待一个fd可写或可读,要能正常使用这个功能需要内核驱动支持
我们经常会用到的一个地方就是编写网络应用,我们需要等待SOCKET可读或可写时返回,
以便后我们做进一步的操作。
我们知道,一个常规的字符设备驱动会实现open, release, write,read方法,
对应于用户态的操作就是open, close, write, read,这些只是常规的一些操作,当
我们的设备不是随时可写或随时可读时,我们就需要实现poll的功能。
下面我们来看一下poll的一些实现细节。
2、实现细节
struct file_operations {
...
unsigned int (*poll)(struct file *, struct poll_table_struct *);
....
}
从回调的函数的参数我们可以看到,重要的是struct poll_table_struct
这是在poll被调用时,由上级函数传递过来的,这里我们可以先不关心它的实现细节
我们只管用它。
那我要怎么来实现这个回调呢?
struct my_dev {
struct cdev cdev;
bool is_open;
wait_queue_head_t wait
struct list_head tx_reqs;
};
static int my_dev_open(struct inode *inode, struct file *file)
{
struct my_dev *dev = container_of(inode->i_cdev, struct my_dev, cdev);
init_waitqueue_head(&dev->wait);
INIT_LIST_HEAD(&dev->tx_reqs);
dev->is_open = true;
file->private_data = dev;
}
static unsigned int my_dev_open(struct file *file, struct poll_table_struct *pts)
{
unsigned int mask = 0;
struct my_dev *dev = file->private_data;
poll_wait(file, &dev->wait, pts);
if (list_empty(&dev->tx_reqs)) {
mask |= POLLIN | POLLRDNORM ;
}
return mask;
}
3、设计思路
每一次poll被调用时,都会检查tx_reqs链表是否为空,当发送为空是,给用户态返回
返回可写,用户态就可以往设备中写入,写完之后,添加到tx_reqs中,当下一次再次调用时
因为tx_reqs不为空,返回0,用户态就会有可写的指示。
经测试,poll回调如果用户的poll函数第三个填(-1)时,内核中只会被调用一次,后
期是能否正常调用检查,还有待测试,当用户态设置超时间,poll的回调会被反复调用。