poll的作用:同时探测n个drivers,找到可以直接使用的driver,从而尽量block进程。
以下kernel源代码来自于:<linux/poll.h> 与 fs/select.c
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 */
if (spacefree(dev))
mask |= POLLOUT | POLLWRNORM; /* writable */
up(&dev->sem);
return mask;
}
poll调用之后,kernel针对每个driver进入其相应的poll函数。poll_wait负责将当前进程放入wait_queue,(对于每个driver,每个queue,申请wait_queue_t, 放入相应的queue,由kernel完成),但是现在并不阻塞current进程,直到所有的driver最后都没有合适的mask的时候,阻塞poll系统调用,当有信号将当前进程唤醒后,说明某一条件满足了。阻塞结束,返回将current从wait_queue挪出来。
具体poll系统调用的内部数据结构为:
struct poll_wqueues{
poll_table pt;
struct poll_table_page * table;
int error;
};
//每一个poll系统调用只有一个poll_wqueues。是总的结构。
//但是对外接口是poll_table pt的地址。进入此模块后,使用container_of求出poll_wqueues的地址。绝对,面向对象的用法。
减少了耦合性。
上面poll_table的的数据结构。
typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);
typedef struct poll_table_struct {
poll_queue_proc qproc;
}poll_table;
//poll_table的变量是一个函数指针,先不用管。
然后poll_table_page, poll_table_entry.
struct poll_table_entry {
struct file * filp;
wait_queue_t wait;
wait_queue_head_t * wait_address;
};
struct poll_table_page {
struct poll_table_page * next;
struct poll_table_entry * entry;
struct poll_table_entry entries[0]; //指向结构体的下一个地址,不占空间。
};
#define POLL_TABLE_FULL(table) \
((unsigned long)((table)->entry+1) > PAGE_SIZE + (unsigned long)(table))
具体poll的数据结构的示意图。
struct poll_wqueues ---> struct poll_table_page // poll_wqueues指向一个poll_table_page的单链表,每一个page占1个PAGE_SIZE大小的区域。
struct poll_table_page ---> struct poll_table_entry // 每个poll_table_page在申请的一个PAGE_SIZE的头,entry的空间都在剩下的空间中。
请看poll_wait是怎么操作poll的数据结构的。
void __pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *_p)
{
struct poll_wqueues *p = container_of(_p, struct poll_wqueues, pt);
struct poll_table_page *table = p->table;
if (!table || POLL_TABLE_FULL(table)) {
struct poll_table_page *new_table;
new_table = (struct poll_table_page *) __get_free_page(GFP_KERNEL); //如果空间不够,一下子申请PAGE_SIZE的大小。
if (!new_table) {
p->error = -ENOMEM;
__set_current_state(TASK_RUNNING);
return;
}
new_table->entry = new_table->entries;
new_table->next = table;
p->table = new_table;
table = new_table;
}
/* Add a new entry */
{
struct poll_table_entry * entry = table->entry;
table->entry = entry+1; //直接在当前页中往下给entry分配内存。
get_file(filp);
entry->filp = filp;
entry->wait_address = wait_address;
init_waitqueue_entry(&entry->wait, current);
add_wait_queue(wait_address,&entry->wait); //将当前进程放入wait_queue,并且entry中都有wait_queue_head_t记录。
}
}