文章目录
linux内核代码版本:v2.6.26.8
当系统启动时,epoll
会进行初始化操作:
//用于实现安全的poll唤醒,避免在wake_up()内部再次进入poll回调
struct poll_safewake {
struct list_head wake_task_list;
spinlock_t lock;
};
//用来序列化ep_free()和eventpoll_release_file()的互斥量
static struct mutex epmutex;
/* Safe wake up implementation */
static struct poll_safewake psw;
static int __init eventpoll_init(void)
{
mutex_init(&epmutex); //初始化互斥量
/* Initialize the structure used to perform safe poll wait head wake ups */
ep_poll_safewake_init(&psw);
//slab动态分配内存,用于分配struct epitm
epi_cache = kmem_cache_create("eventpoll_epi", sizeof(struct epitem),
0, SLAB_HWCACHE_ALIGN|EPI_SLAB_DEBUG|SLAB_PANIC,
NULL);
//slab动态分配内存,用于分配struct eppoll_entry
pwq_cache = kmem_cache_create("eventpoll_pwq",
sizeof(struct eppoll_entry), 0,
EPI_SLAB_DEBUG|SLAB_PANIC, NULL);
return 0;
}
fs_initcall(eventpoll_init);
epoll
需要三个级别的锁。
1.epmutex(mutex);
2.ep->mtx(mutex);
3.ep->lock(spinlock);
对于自旋锁ep->lock(spinlock)
,因为我们在poll
回调内部操作对象,该回调可能是由wake_up
触发的,而wake_up
有可能从中断请求上下文调用。所以我们不能在poll
回调中休眠,因此我们需要一个spinlock
。
在事件传输循环(从内核到用户空间)期间,由于copy_to_user()
,我们可能需要一个允许我们进入睡眠状态的锁。这个锁是一个互斥锁。epoll_ctl(EPOLL_CTL_DEL)
和eventpoll_release_file()
期间获取的。然后我们还需要一个全局互斥锁来序列化eventpoll_release_file()
和ep_free()
。
这个互斥锁是在epoll
文件清理路径期间由ep_free()
获取的,如果文件已被push
到epoll set
中,则它也由eventpoll_release_file()
获取,然后在没有之前调用epoll_ctl(EPOLL_CTL_DEL)
的情况下关闭它。
epoll_filefd
struct epoll_filefd {
struct file *file;
int fd;
};
list_head
struct list_head {
struct list_head *next, *prev;
};
epoll_event
struct epoll_event {
__u32 events;
__u64 data;
} EPOLL_PACKED;
红黑树的节点
struct rb_root
{
struct rb_node *rb_node;
};
struct rb_node
{
unsigned long rb_parent_color; //父节点颜色
#define RB_RED 0
#define RB_BLACK 1
struct rb_node *rb_right; //左子树
struct rb_node *rb_left; //右子树
} __attribute__((aligned(sizeof(long))));
epitem
//添加到eventpoll的每一个文件描述符都会有一个链接到红黑树的epitem条目
struct epitem {
//红黑树节点用于将epitm链接到eventpoll红黑树
struct rb_node rbn;
//用于将epitem链接到eventpoll就绪列表的列表头
struct list_head rdllink;
/*
* Works together "struct eventpoll"->ovflist in keeping the
* single linked chain of items.
*/
struct epitem *next;
//文件描述符的信息
struct epoll_filefd ffd;
//附加在poll操作的活动等待队列数
int nwait;
//包含poll wait queue列表
struct list_head pwqlist;
//包含epitem的容器
struct eventpoll *ep;
//将epitem链接到“struct file”项列表的列表头
struct list_head fllink;
//描述感兴趣事件的结构和源文件描述符的结构
struct epoll_event event;
};
eventpoll
//该结构存储在文件结构的“private_data”成员中,并代表eventpoll接口的主要数据结构
struct eventpoll {
//保护这个结构
spinlock_t lock;
//该互斥锁用于确保epoll使用文件时不会删除这些文件
//在事件收集循环,文件清理路径,epoll文件退出代码和ctl操作时持有
struct mutex mtx;
//sys_epoll_wait()使用的等待队列
wait_queue_head_t wq;
//file->poll()使用的等待队列
wait_queue_head_t poll_wait;
//就绪文件描述符列表
struct list_head rdllist;
//红黑树树根用于存储受监控的fd结构
struct rb_root rbr;
//这是一个单链表,它链接了在将就绪事件传输到用户空间时发生的所有“epitem”
//不需要持有锁
struct epitem *ovflist;
};
eppoll_entry
/* poll hooks使用的等待结构 */
struct eppoll_entry {
/* 将eppoll_entry链接到“struct epitem”的队列头 */
struct list_head llink;
/* base指针指向"struct epitem"的容器 */
void *base;
/*
* wait queue项链接到目标文件等待队列头
*/
wait_queue_t wait;
/* 链接等待队列项的等待队列头 */
wait_queue_head_t *whead;
};
sys_epoll_create(int size)
/*
* 打开一个eventepoll文件描述符。size参数现在已经被抛弃
*/
asmlinkage long sys_epoll_create(int size)
{
int error, fd = -1;
struct eventpoll *ep;
DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d)\n",
current, size));
/*
* 对size参数进行完整性检查
*/
error = -EINVAL; //EINVAL表示无效的参数
if (size <= 0 || (error = ep_alloc(&ep)) < 0) {
//ep_alloc初始化eventpoll结构
fd = error;
goto error_return;
}
/*
* 创建设置eventpoll文件所需的所有项目。即,文件结构和空闲文件描述符
* 将创建的eventpoll文件和文件描述符关联起来
* static const struct file_operations eventpoll_fops = {
* .release = ep_eventpoll_release,
* .poll = ep_eventpoll_poll
* };
*/
fd = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep);
if (fd < 0)
ep_free(ep);
error_return:
DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d) = %d\n",
current, size, fd));
return fd;
}
ep_alloc
static int ep_alloc(struct eventpoll **pep)
{
struct