epoll源码分析---sys_epoll_create()函数

来源:互联网 发布:淘宝官方折扣 编辑:程序博客网 时间:2024/06/06 15:01

 http://blog.csdn.net/moonvs2010/article/details/8506940

eventpoll的优点就不用说了,网上的资料很多,eventpoll的使用也很广泛,特别是在Web服务器中。因为最近要用到epoll,所以好好地看了一下它的实现,把学到的一些东西做下整理,做个记录。

一、sys_epoll_create()
  其源码如下:
[cpp] view plaincopy
  1. SYSCALL_DEFINE1(epoll_create, int, size)  
  2. {  
  3.     if (size <= 0)  
  4.         return -EINVAL;  
  5.   
  6.     return sys_epoll_create1(0);  
  7. }  
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()函数
[cpp] view plaincopy
  1. SYSCALL_DEFINE1(epoll_create1, int, flags)  
  2. {  
  3.     int error;  
  4.     struct eventpoll *ep = NULL;  
  5.   
  6.     /* 
  7.      * 如果(EPOLL_CLOEXEC != O_CLOEXEC)成立,在编译时就会报错。这种方式 
  8.      *  
  9.      */  
  10.     BUILD_BUG_ON(EPOLL_CLOEXEC != O_CLOEXEC);  
  11.   
  12.     /* flags要么为0,要么为EPOLL_CLOEXEC,否则返回EINVAL错误 */  
  13.     if (flags & ~EPOLL_CLOEXEC)  
  14.         return -EINVAL;  
  15.     /* 
  16.      * 分配eventpoll实例并初始化,存储在file结构的private_data成员中。 
  17.      * private_data成员用来存储文件描述符真正对应的对象。例如 
  18.      * 如果文件描述符是一个套接字的话,其对应的file实例的private_data 
  19.      * 成员存储的就是一个socket实例。 
  20.      */  
  21.     error = ep_alloc(&ep);  
  22.     if (error < 0)  
  23.         return error;  
  24.   
  25.     /* 
  26.      * 创建eventpoll文件,这个文件的file_operations为eventpoll_fops, 
  27.      * 私有的数据为eventpoll实例 
  28.      */  
  29.     error = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep,  
  30.                  flags & O_CLOEXEC);  
  31.     if (error < 0)  
  32.         ep_free(ep);  
  33.   
  34.     return error;  
  35. }  
首先看一看BUILD_BUG_ON宏,该宏用来在编译时检查condition是否为true,如果是true,会报编译错误,
  1. #define BUILD_BUG_ON(condition) ((void)BUILD_BUG_ON_ZERO(condition))
BUILD_BUG_ON_ZERO的定义如下:
[cpp] view plaincopy
  1. #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)。内核使用的是下限值,还以使用上限值,即指定的位数超过对应成员的类型,例如下面的例子:
[cpp] view plaincopy
  1. #define BUILD_BUG_ON_ZERO1(e) (sizeof(struct {char:(!!e << 9);}))  
接下来看ep_alloc()函数,源码和注释如下如下:
[cpp] view plaincopy
  1. /* 
  2.  * 分配eventpoll实例并初始化 
  3.  */  
  4. static int ep_alloc(struct eventpoll **pep)  
  5. {  
  6.     int error;  
  7.     struct user_struct *user;  
  8.     struct eventpoll *ep;  
  9.   
  10.     user = get_current_user();  
  11.     error = -ENOMEM;  
  12.     ep = kzalloc(sizeof(*ep), GFP_KERNEL);  
  13.     if (unlikely(!ep))  
  14.         goto free_uid;  
  15.   
  16.     /* 
  17.      *  
  18.      */  
  19.     spin_lock_init(&ep->lock);  
  20.     /* 
  21.      * 初始化用于向用户空间传递事件和移除epoll中的文件之间 
  22.      * 的互斥锁 
  23.      */  
  24.     mutex_init(&ep->mtx);  
  25.     /* 
  26.      * 初始化epoll文件的登对队列。调用epoll_wait的进程 
  27.      * 可能在此队列上睡眠, 等待ep_poll_callback()函数唤醒或超时 
  28.      */  
  29.     init_waitqueue_head(&ep->wq);  
  30.     /* 
  31.      * poll_wait是eventpoll文件本身的唤醒队列,该队列上睡眠 
  32.      * 的进程是等待eventpoll文件本身的某些事件发生。 
  33.      */  
  34.     init_waitqueue_head(&ep->poll_wait);  
  35.     /* 
  36.      * 初始化就绪队列,如果当某个文件指定的事件发生时, 
  37.      * 会防止到该队列中。 
  38.      */  
  39.     INIT_LIST_HEAD(&ep->rdllist);  
  40.     /* 
  41.      * 用于存储文件描述符的红黑树根节点 
  42.      */  
  43.     ep->rbr = RB_ROOT;  
  44.     /* 
  45.      * 如果正在向用户空间传递事件,此时状态就绪的文件 
  46.      * 描述符相关的结构会暂时放在该队列上,否则会直接 
  47.      * 添加到就绪队列rdllist中。 
  48.      */  
  49.     ep->ovflist = EP_UNACTIVE_PTR;  
  50.     ep->user = user;  
  51.   
  52.     *pep = ep;  
  53.   
  54.     return 0;  
  55.   
  56. free_uid:  
  57.     free_uid(user);  
  58.     return error;  
  59. }  
上面的注释比较详细了,不再多叙。
最后一个关心的函数是anon_inode_getfd(),该函数的作用类似于sock_map_fd(),就是将eventpoll实例映射到一个文件中,
[cpp] view plaincopy
  1. int anon_inode_getfd(const char *name, const struct file_operations *fops,  
  2.          void *priv, int flags)  
  3. {  
  4.     int error, fd;  
  5.     struct file *file;  
  6.   
  7.     /* 
  8.      * 分配一个空闲的文件描述符。 
  9.      */  
  10.     error = get_unused_fd_flags(flags);  
  11.     if (error < 0)  
  12.         return error;  
  13.     fd = error;  
  14.   
  15.     file = anon_inode_getfile(name, fops, priv, flags);  
  16.     if (IS_ERR(file)) {  
  17.         error = PTR_ERR(file);  
  18.         goto err_put_unused_fd;  
  19.     }  
  20.     fd_install(fd, file);  
  21.   
  22.     return fd;  
  23.   
  24. err_put_unused_fd:  
  25.     put_unused_fd(fd);  
  26.     return error;  
  27. }  
该函数首先调用get_unused_fd_flags()分配一个空闲的文件描述符,然后创建一个匿名文件,附加上去。因为涉及到文件系统的操作,不做过多的分析。
三、find_next_zero_bit()函数
anon_inode_getfile()是一个宏定义,对应的函数时alloc_fd(),alloc_fd()中调用find_next_zero_bit()在文件描述符的位图中查找一个空闲的bit位,空闲的bit位的索引即为找到的文件描述符,我对这个函数比较感兴趣,特别研究一个一番,跟大家分享一下。
[cpp] view plaincopy
  1. /* 
  2.  * find_next_zero_bit返回的值的范围是0~(size-1),相当于是bit数组中的索引 
  3.  * @addr: 位图的地址 
  4.  * @size: 位图的bit位个数 
  5.  * @offset: 可以理解为bit数组中的索引,也就是说从这个bit位开始查找 
  6.  */  
  7. unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,  
  8.                  unsigned long offset)  
  9. {  
  10.     /* 
  11.      * 这里BITOP_WORD用来计算offset对应位图中的unsigned long元素在 
  12.      * addr数组中的索引,所以p为offset所在的unsigned long元素的地址 
  13.      */  
  14.     const unsigned long *p = addr + BITOP_WORD(offset);  
  15.     /* 
  16.      * 相当于是offset - (offset % BITS_PER_LONG),也就是offset所在的unsigned long 
  17.      * 之前所有unsigned long元素的bit位个数 
  18.      */  
  19.     unsigned long result = offset & ~(BITS_PER_LONG-1);  
  20.     unsigned long tmp;  
  21.   
  22.     /* 
  23.      * 如果偏移量大于等于位图的大小,则直接 
  24.      * 返回size。 
  25.      */  
  26.     if (offset >= size)  
  27.         return size;  
  28.     /* 
  29.      * 计算offset所在的unsigned long及其之后所有的unsigned long元素中 
  30.      * 的bit位个数 
  31.      */  
  32.     size -= result;  
  33.     /* 
  34.      * 计算offset对应的unsigned long中占用的bit位所在的位置,这个也可以理解 
  35.      * 为一个索引。假设计算前offset的值为67,计算后offset的值为3,也就是所在 
  36.      * unsigned long中的第4个bit位。 
  37.      */  
  38.     offset %= BITS_PER_LONG;  
  39.     if (offset) {  
  40.         /* 
  41.          * tmp的值为offset所在的unsigned long的值 
  42.          */  
  43.         tmp = *(p++);  
  44.         /* 
  45.          * BITS_PER_LONG - offset计算的offset所在的unsigned long元素中offset所在的 
  46.          * bit位及其之后的bit位个数。tmp中offset所对应的bit位及其之后的bit位都保留,而将 
  47.          * tmp中offset所对应的bit位之前的bit位都设置为1. 
  48.          */  
  49.         tmp |= ~0UL >> (BITS_PER_LONG - offset);  
  50.         /* 
  51.          * 如果size小于BITS_PER_LONG,说明offset在最后一个unsigned long元素。 
  52.          */  
  53.         if (size < BITS_PER_LONG)  
  54.             goto found_first;  
  55.         /* 
  56.          * 如果tmp取反后为不为0,则说明tmp中有为0的bit位,因此从 
  57.          * tmp中查找空闲的bit位。 
  58.          */  
  59.         if (~tmp)  
  60.             goto found_middle;  
  61.         /* 
  62.          * 如果offset所对应的bit位所在的unsigned long中没有空闲的bit位, 
  63.          * 开始从其之后得unsigned long元素中查找。计算剩余的bit位个数, 
  64.          * 修改已经查找的bit位个数 
  65.          */  
  66.         size -= BITS_PER_LONG;  
  67.         result += BITS_PER_LONG;  
  68.     }  
  69.     /* 
  70.      * 如果size小于BITS_PER_LONG,则退出循环 
  71.      */  
  72.     while (size & ~(BITS_PER_LONG-1)) {  
  73.         /* 
  74.          * 将下一个查找的元素存储在tmp中,如果tmp取反后不为0,则说明tmp中有为0的bit位,因此 
  75.          * 从tmp中查找空闲的bit位。 
  76.          */  
  77.         if (~(tmp = *(p++)))  
  78.             goto found_middle;  
  79.         /* 
  80.          * 计算剩余的bit位个数,修改已经查找的bit位个数 
  81.          */  
  82.         result += BITS_PER_LONG;  
  83.         size -= BITS_PER_LONG;  
  84.     }  
  85.     /* 
  86.      * 如果全部查找后,仍没有找到空闲的bit位,则直接返回result。 
  87.      * 此时result的值应该为位图的bit位的个数。 
  88.      */  
  89.     if (!size)  
  90.         return result;  
  91.     /* 
  92.      * 如果size不为0,则在剩余的最后的bit位(剩余的个数小于BITS_PER_LONG)中查找。 
  93.      */  
  94.     tmp = *p;  
  95.   
  96. found_first:  
  97.     /* 
  98.      * 因为剩余的bit位个数有可能小于BITS_PER_LONG,因此需要将unsigned long中 
  99.      * 不用的bit位置为1,以免干扰后续的查找 
  100.      */  
  101.     tmp |= ~0UL << size;  
  102.     /* 
  103.      * 如果所有bit位都为1,则说明没有空余的bit位, 
  104.      * 则返回所有的bit位的个数。 
  105.      */  
  106.     if (tmp == ~0UL)    /* Are any bits zero? */  
  107.         return result + size;    /* Nope. */  
  108. found_middle:  
  109.     /* 
  110.      * ffz(tmp)返回的是tmp中第一个为0的bit位的索引 
  111.      */  
  112.     return result + ffz(tmp);  
原创粉丝点击