eventpoll的优点就不用说了,网上的资料很多,eventpoll的使用也很广泛,特别是在Web服务器中。因为最近要用到epoll,所以好好地看了一下它的实现,把学到的一些东西做下整理,做个记录。一、sys_epoll_create()
其源码如下:
- SYSCALL_DEFINE1(epoll_create, int, size)
- {
- if (size <= 0)
- return -EINVAL;
- return sys_epoll_create1(0);
- }
SYSCALL_DEFINE1(epoll_create, int, size)在预处理之后就是long sys_epoll_create(int size)。从这里可以看到在用户层调用epoll_create时,传入的size参数没有使用。sys_epoll_create()在检查完参数后直接调用sys_epoll_create1()函数来完成主要的工作。因此接下来看看sys_epoll_create1()是怎么实现的。
二、sys_epoll_create1()函数
- SYSCALL_DEFINE1(epoll_create1, int, flags)
- {
- int error;
- struct eventpoll *ep = NULL;
- /*
- * 如果(EPOLL_CLOEXEC != O_CLOEXEC)成立,在编译时就会报错。这种方式
- *
- */
- BUILD_BUG_ON(EPOLL_CLOEXEC != O_CLOEXEC);
- /* flags要么为0,要么为EPOLL_CLOEXEC,否则返回EINVAL错误 */
- if (flags & ~EPOLL_CLOEXEC)
- return -EINVAL;
- /*
- * 分配eventpoll实例并初始化,存储在file结构的private_data成员中。
- * private_data成员用来存储文件描述符真正对应的对象。例如
- * 如果文件描述符是一个套接字的话,其对应的file实例的private_data
- * 成员存储的就是一个socket实例。
- */
- error = ep_alloc(&ep);
- if (error < 0)
- return error;
- /*
- * 创建eventpoll文件,这个文件的file_operations为eventpoll_fops,
- * 私有的数据为eventpoll实例
- */
- error = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep,
- flags & O_CLOEXEC);
- if (error < 0)
- ep_free(ep);
- return error;
- }
首先看一看
BUILD_BUG_ON宏,该宏用来在编译时检查condition是否为true,如果是true,会报编译错误,- #define BUILD_BUG_ON(condition) ((void)BUILD_BUG_ON_ZERO(condition))
宏
BUILD_BUG_ON_ZERO的定义如下:- #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
乍看之下,这个宏的定义好像有些乱,是不是不合语法啊?但是细看之下,你就会发现这个宏的巧妙之处。
先看!!(e),这个很容易看懂,就是将e转换为bool值,假设e为10,第一次取反时变为0,第二次取反时又变为1;如果e为0的话,两次取反仍是0. 接着看-!!(e)(注意前面的“-”,就是取负),如果e不为0时,!!(e)返回的是1,-!!(e)返回的就是-1,此时宏BUILD_BUG_ON_ZERO预处理后为(sizeof(struct { int:-1};)).我们知道定义结构体时,可以指定成员按位来存放,可以取的值为0到对应类型的bit位个数,小于0编译时会报错(在sizeof中可以为0,但是真正定义类型时不能为0)。内核使用的是下限值,还以使用上限值,即指定的位数超过对应成员的类型,例如下面的例子:
- #define BUILD_BUG_ON_ZERO1(e) (sizeof(struct {char:(!!e << 9);}))
接下来看ep_alloc()函数,源码和注释如下如下:
- /*
- * 分配eventpoll实例并初始化
- */
- static int ep_alloc(struct eventpoll **pep)
- {
- int error;
- struct user_struct *user;
- struct eventpoll *ep;
- user = get_current_user();
- error = -ENOMEM;
- ep = kzalloc(sizeof(*ep), GFP_KERNEL);
- if (unlikely(!ep))
- goto free_uid;
- /*
- *
- */
- spin_lock_init(&ep->lock);
- /*
- * 初始化用于向用户空间传递事件和移除epoll中的文件之间
- * 的互斥锁
- */
- mutex_init(&ep->mtx);
- /*
- * 初始化epoll文件的登对队列。调用epoll_wait的进程
- * 可能在此队列上睡眠, 等待ep_poll_callback()函数唤醒或超时
- */
- init_waitqueue_head(&ep->wq);
- /*
- * poll_wait是eventpoll文件本身的唤醒队列,该队列上睡眠
- * 的进程是等待eventpoll文件本身的某些事件发生。
- */
- init_waitqueue_head(&ep->poll_wait);
- /*
- * 初始化就绪队列,如果当某个文件指定的事件发生时,
- * 会防止到该队列中。
- */
- INIT_LIST_HEAD(&ep->rdllist);
- /*
- * 用于存储文件描述符的红黑树根节点
- */
- ep->rbr = RB_ROOT;
- /*
- * 如果正在向用户空间传递事件,此时状态就绪的文件
- * 描述符相关的结构会暂时放在该队列上,否则会直接
- * 添加到就绪队列rdllist中。
- */
- ep->ovflist = EP_UNACTIVE_PTR;
- ep->user = user;
- *pep = ep;
- return 0;
- free_uid:
- free_uid(user);
- return error;
- }
上面的注释比较详细了,不再多叙。
最后一个关心的函数是anon_inode_getfd(),该函数的作用类似于sock_map_fd(),就是将eventpoll实例映射到一个文件中,
- int anon_inode_getfd(const char *name, const struct file_operations *fops,
- void *priv, int flags)
- {
- int error, fd;
- struct file *file;
- /*
- * 分配一个空闲的文件描述符。
- */
- error = get_unused_fd_flags(flags);
- if (error < 0)
- return error;
- fd = error;
- file = anon_inode_getfile(name, fops, priv, flags);
- if (IS_ERR(file)) {
- error = PTR_ERR(file);
- goto err_put_unused_fd;
- }
- fd_install(fd, file);
- return fd;
- err_put_unused_fd:
- put_unused_fd(fd);
- return error;
- }
该函数首先调用
get_unused_fd_flags()分配一个空闲的文件描述符,然后创建一个匿名文件,附加上去。因为涉及到文件系统的操作,不做过多的分析。anon_inode_getfile()是一个宏定义,对应的函数时alloc_fd(),alloc_fd()中调用find_next_zero_bit()在文件描述符的位图中查找一个空闲的bit位,空闲的bit位的索引即为找到的文件描述符,我对这个函数比较感兴趣,特别研究一个一番,跟大家分享一下。
阅读(5119) | 评论(0) | 转发(0) |