epoll_wait 源代码分析

在io多路复用中,有三个主要操作。

epoll_create

接口描述

int epoll_create(int size);
创建epoll的句柄,size是监听的数目,这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值,参数size并不是限制了epoll所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议。

当创建好epoll句柄后,它就会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

epoll_ctl

接口描述

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
对指定描述符fd执行op操作

接口参数解释

  • epfd:是epoll_create()的返回值。
  • op:表示op操作,用三个宏来表示:添加EPOLL_CTL_ADD,删除EPOLL_CTL_DEL,修改EPOLL_CTL_MOD。分别添加、删除和修改对fd的监听事件。
  • fd:是需要监听的fd(文件描述符)
  • epoll_event:是告诉内核需要监听什么事,struct epoll_event结构如下:
struct epoll_event {
  __uint32_t events;  /* Epoll events */
  epoll_data_t data;  /* User data variable */
};

events可以是以下几个宏的集合:

EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

epoll_wait

接口描述

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待epfd上的io事件,最多返回maxevents个事件。

参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size。

参数timeout是超时时间,毫秒,0会立即返回,非零则会
1.忙轮训一个固定时间(1ms),
2.忙轮训超时时间过去,则进入可中断休眠状态,等待唤醒条件)
3.可中断休眠状态,被唤醒时,若操作超时时间到了,或者内容ready,则返回需要处理的事件数目,如返回0表示已超时。

epoll_wait 源码分析

源码为C语言,此处仅摘取部分逻辑代码并加以解释


def ep():
     if (timeout > 0) {
          set_timeout_attr // 设置忙轮训超时时间属性,设置整个操作的超时时间属性
    } else if (timeout == 0) {
          ep_events_available(ep)
          goto send_events # 如果timeout 设置为零,则直接获取然后将结果返回。结果包括1. 获取成功,返回等待的个数。2.获取失败,返回失败的code
          }

  fetch_events:
        if(!ep_events_available(ep))
                ep_busy_loop(ep, timed_out) # 如果此时,没有事件就绪,且为阻塞,就去忙轮训,直到超时(1ms,与设置的超时时间不同,这里是忙轮训的超时时间)或者有事件返回
        if (eavail)
                goto send_events;

        for (;;) {
            set_current_state(TASK_INTERRUPTIBLE) # 可中断睡眠状态直到某些条件达成被唤醒,这里条件的检查由调度器完成,比如说检查信号量来同步状态等, identifies a process that is suspended (sleeping) until some condition becomes true.
}  
        if (pending or fatal_signal)
              break
        if (eavail)
              break
        if (timeout) # 操作超时时间到了
            break
        return res

附:
linux (本文使用linux4.8.4) 下进程状态,大致有7种。

进程状态    说明
TASK_RUNNING    可运行状态。未必正在使用CPU,也许是在等待调度
TASK_INTERRUPTIBLE  可中断的睡眠状态。正在等待某个条件满足
TASK_UNINTERRUPTIBLE    不可中断的睡眠状态。不会被信号中断
__TASK_STOPPED  暂停状态。收到某种信号,运行被停止
__TASK_TRACED   被跟踪状态。进程停止,被另一个进程跟踪
EXIT_ZOMBIE 僵尸状态。进程已经退出,但尚未被父进程或者init进程收尸
EXIT_DEAD   真正的死亡状态
在include/linux/sched.h中,进程状态的定义并没有那么少:

本文参考:
linux 中进程的状态
linux eventpoll内核代码
Linux IO模式及 select、poll、epoll详解

你可能感兴趣的:(epoll_wait 源代码分析)