1. poll
从内核的角度看来,借助于VFS
, 一切皆file
// 文件表示 include/linux/fs.h
struct file {
const struct file_operations *f_op;
spinlock_t f_lock;
// 文件内部实现细节
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
// 其他细节....
};
// 文件操作 include/linux/fs.h
struct file_operations {
// 文件提供给poll/select/epoll
// 调用poll_table_struct中指定的函数并获取文件的当前状态
unsigned int (*poll) (struct file *, struct poll_table_struct *);
// 其他方法read/write 等... ...
};
/**
* 通常文件poll方法的实现
* 调用poll_table_struct中指定的函数,并获得文件当前就绪事件的掩码
* @param flip 文件的指针
* @param wait 指向poll_table_struct的指针
* @return 返回文件当前就绪事件掩码
*/
unsigned int XXX_poll (struct file *filp, struct poll_table_struct *wait)
{
unsigned int mask = 0;
wait_queue_head_t * wait_queue;
// 1. 根据事件掩码wait->key_和文件实现filp->private_data 取得事件掩码对应的一个或多个wait queue head
some_code();
// 2. 调用poll_wait,目的是向获得的等待队列中添加等待队列项
poll_wait(filp, wait_queue, wait);
// 3. 取得文件当前就绪事件的掩码并保存到mask
some_code();
return mask;
}
// select/poll/epoll 向文件注册就绪后回调节点的接口结构
typedef struct poll_table_struct {
// 向指定等待队列(wait_queue_head)添加等待队列项的(wait_queue_t)的接口函数
poll_queue_proc _qproc;
// 关注的事件掩码, 文件的实现利用此掩码将对应的等待队列传递给_qproc
unsigned long _key;
} poll_table;
//
typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);
// 通用的poll_wait 函数, 文件的f_ops->poll 通常会调用此函数
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
if (p && p->_qproc && wait_address) {
// 调用poll_table_struct 中指定的函数_qproc
// qproc一般的作用是向指定事件等待队列中添加等待队列项
// 如果是select或poll 则是 __pollwait, 如果是 epoll 则是 ep_ptable_queue_proc
p->_qproc(filp, wait_address, p);
}
}
2. upd的poll
2.1 poll
net/ipv4/af_inet.c
const struct proto_ops inet_dgram_ops = {
// 其它细节
.poll = udp_poll,
// 其它细节
};
2.2 udp_poll
net/ipv4/udp.c
/**
* udp_poll - wait for a UDP event.
* @