epoll原理学习

转自:https://zhuanlan.zhihu.com/p/119400472

https://zhuanlan.zhihu.com/p/187463036

1.相关函数

#include <sys/epoll.h> 

int epoll_create(int size);

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

2.介绍

转自:https://zhuanlan.zhihu.com/p/116901360

epoll_create的做法是创建出一个内核事件表,实际上就是创建文件,这其中包括文件描述符的分配、文件实体的分配等等。 

进程中的文件描述符指向文件结构体file,其中有元素指向epoll文件描述符,epoll中有指针指向eventpoll结构体,其中有等待队列,就绪队列,所有感兴趣的socket描述符存放在红黑树中。

那么简单来说,在epoll wait时,过程如下:

asmlinkage long sys_epoll_wait(int epfd, struct epoll_event __user *events,
                   int maxevents, int timeout)
{
    ...
    error = ep_poll(ep, events, maxevents, timeout);
    return error;
}

static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
           int maxevents, long timeout)
{        
        if (list_empty(&ep->rdllist)) {
        // 加入阻塞队列
        init_waitqueue_entry(&wait, current);
        add_wait_queue(&ep->wq, &wait);//
用于将等待元素wait插入到等待队列的头部

        for (;;) {
            // 挂起
            set_current_state(TASK_INTERRUPTIBLE);
            // 超时或者有就绪事件了,则跳出返回
            if (!list_empty(&ep->rdllist) || !jtimeout)
                break;
            // 被信号唤醒返回EINTR
            if (signal_pending(current)) {
                res = -EINTR;
                break;
            }

            // 设置定时器,然后进程挂起,等待超时唤醒(超时或者信号唤醒)
            jtimeout = schedule_timeout(jtimeout);
        }
        // 移出阻塞队列
        remove_wait_queue(&ep->wq, &wait);
        // 设置就绪
        set_current_state(TASK_RUNNING);
    }

本人认为的过程:调用epoll_wait将所有事件加入等待队列,如果有就绪事件,即就绪队列上不为空了,那么就返回。那么这个线程被阻塞在检查是否有就绪事件,就绪事件又是谁负责放上去的呢?答:是向epoll事件表中加入文件描述符时设置的回调函数:

static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *key)
{
    int pwake = 0;
    unsigned long flags;
    struct epitem *epi = EP_ITEM_FROM_WAIT(wait);
    struct eventpoll *ep = epi->ep;
    // 插入就绪队列 //它负责将当前文件描述符加入就绪队列
    list_add_tail(&epi->rdllink, &ep->rdllist);
    // 唤醒因epoll_wait而阻塞的进程  //紧接着唤醒epollwaite的进程/线程
    if (waitqueue_active(&ep->wq))
        wake_up(&ep->wq);
    if (waitqueue_active(&ep->poll_wait))
        pwake++;
    return 1;
}

在文件对应的inode上注册一个回调。当文件满足条件的时候,就会唤醒因为epoll_wait而阻塞的进程。接着epoll_wait会收集事件返回给用户。

原文地址:https://www.cnblogs.com/BlueBlueSea/p/14817502.html