通过十个问题助你彻底理解linux epoll工作原理

linux服务器开发相关视频解析:linux下的epoll实战揭秘——支撑亿级IO的底层基石
epoll的网络模型,从redis,memcached到nginx,一起搞定
epoll 是 linux 特有的一个 I/O 事件通知机制 。很久以来对 epoll 如何能够高效处理数以百万记的文件描述符很有兴趣 。近期学习、研究了 epoll 源码,在这个过程中关于 epoll 数据结构和作者的实现思路产生出不少疑惑,在此总结为了 10 个问题并逐个加以解答和分析 。
Question 1:是否所有的文件类型都可以被 epoll 监视?答案:不是 。看下面这个实验代码:
#include <stdio.h>#include <unistd.h>#include <sys/epoll.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <errno.h>#define MAX_EVENTS 1int main (void){    int epfd;    epfd = epoll_create(100); /* 创建epoll实例,预计监听100个fd */    if (epfd < 0) {        perror ("epoll_create");    }    struct epoll_event *events;    int nr_events, i;    events = malloc (sizeof (struct epoll_event) * MAX_EVENTS);    if (!events) {        perror("malloc");        return 1;    }    /* 打开一个普通文本文件 */    int target_fd = open ("./11.txt", O_RDONLY);    printf("target_fd %dn", target_fd);    int target_listen_type = EPOLLIN;    for (i = 0; i < 1; i++) {        int ret;        events[i].data.fd = target_fd; /* epoll调用返回后,返回给应用进程的fd号 */        events[i].events = target_listen_type; /* 需要监听的事件类型 */        ret = epoll_ctl (epfd, EPOLL_CTL_ADD, target_fd, &events[i]); /* 注册fd到epoll实例上 */        if (ret) {     printf("ret %d, errno %dn", ret, errno);            perror ("epoll_ctl");        }    }    /* 应用进程阻塞在epoll上,超时时长置为-1表示一直等到有目标事件才会返回 */    nr_events = epoll_wait(epfd, events, MAX_EVENTS, -1);    if (nr_events < 0) {        perror ("epoll_wait");        free(events);        return 1;    }    for (i = 0; i < nr_events; i++) {        /* 打印出处于就绪状态的fd及其事件 */        printf("event=%d on fd=%dn", events[i].events, events[i].data.fd);    }    free (events);    close(epfd);    return 0;}编译、运行上面的代码,会打印出下列信息:
gcc epoll_test.c -o epdemo./epdemotarget_fd 4ret -1, errno 1epoll_ctl: Operation not permitted正常打开了"txt"文件 fd=4, 但调用 epoll_ctl 监视这个 fd 时却 ret=-1 失败了, 并且错误码为 1,错误信息为"Operation not permitted" 。错误码指明这个 fd 不能够被 epoll 监视 。
那什么样的 fd 才可以被 epoll 监视呢?
只有底层驱动实现了 file_operations 中 poll 函数的文件类型才可以被 epoll 监视!socket 类型的文件驱动是实现了 poll 函数的,因此才可以被 epoll 监视 。struct file_operations 声明位置是在 include/linux/fs.h 中 。
Question 2:ep->wq 的作用是什么?答案:wq 是一个等待队列,用来保存对某一个 epoll 实例调用 epoll_wait()的所有进程 。
一个进程调用 epoll_wait()后,如果当前还没有任何事件发生,需要让当前进程挂起等待(放到 ep->wq 里);当 epoll 实例监视的文件上有事件发生后,需要唤醒 ep->wq 上的进程去继续执行用户态的业务逻辑 。之所以要用一个等待队列来维护关注这个 epoll 的进程,是因为有时候调用 epoll_wait()的不只一个进程,当多个进程都在关注同一个 epoll 实例时,休眠的进程们通过这个等待队列就可以逐个被唤醒了 。


推荐阅读