前段时间把游戏里的epoll从LT弄成ET的,这几天空下来就看了下linux里的epoll实现,网上有蛮多源码的介绍,结合那些介绍自己整理了一下思路做个笔记~
和epoll相关的数据结构比较绕,其实搞清楚了这些数据结构基本上就了解它的脉络了,有一幅别人画的数据结构图比较清楚,这里转贴一下:
1 、eventpoll_init
这是系统自己的初始化函数,主要做一些内存分配和函数地址的初始化,就不多说了。
2、 epoll_create1
用户调用epoll_create创建一个新的epoll,之后会走到内核的SYSCALL_DEFINE1(epoll_create1, int, flags)
该函数新生成一个file的结构体,返回id即fd,注意这里生成的fd对于epoll是唯一的,之后插入的sock或其他文件会有其各自对应的fd。主要代码片段如下:
int error;
struct eventpoll *ep = NULL;
error = ep_alloc(&ep);
error = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep,flags & O_CLOEXEC);
eventpoll类似于一个mng,所有的epitem都可以从这里开始索引到。
anon_inode_getfd创建一个file,其中 file->private_data = ep,而file_operations是文件的操作集合,eventpoll_fops定义了其中的release和poll两个方法,分别为 ep_eventpoll_release,ep_eventpoll_poll,linux有很多这种所谓的多路复用写法,其实有点类似于面向对象里的接口。
3、 epoll_ctrl
内核对应的是SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event __user *, event)
其中epfd是epoll的fd,fd则是要变更的sock的fd,主要代码片段如下:
file = fget(epfd);
tfile = fget(fd);
ep = file->private_data;
epi = ep_find(ep, tfile, fd);
switch (op) {
case EPOLL_CTL_ADD:
if (!epi) {
epds.events |= POLLERR | POLLHUP;
error = ep_insert(ep, &epds, tfile, fd);
} else
error = -EEXIST;
break;
case EPOLL_CTL_DEL:
if (epi)
error = ep_remove(ep, epi);
else
error = -ENOENT;
break;
case EPOLL_CTL_MOD:
if (epi) {
epds.events |= POLLERR | POLLHUP;
error = ep_modify(ep, epi, &epds);
} else
error = -ENOENT;