epoll详解及坑

        epoll是select、poll 的改进版。

        使用select、poll的缺点:

        (1)调用select 时,需要将用户空间的所有fd集合拷贝进内核空间。

        (2)调用select 时,需要在内核空间遍历所有fd的状态。

        (3)select 支持的fd 数目有限,不超过1024。

 

        关于epoll的三个系统调用:epoll_create、epoll_ctl、epoll_wait:

epoll_create:

        #include <sys/epoll.h> 
        int epoll_create ( int size );

        功能:创建一个文件描述符作为“监听的一大堆fd”的标识。

        返回值:返回文件描述符epollfd。注意:使用完epoll后记得close(epollfd)。

        参数介绍:这里的size是早期设计时产生的,那时所有需监测的文件描述符放入hash表中,而现在时放入红黑树中,所以该参数现在并没有实际用到,但是必须 >0。

 

epoll_ctl:

        #include <sys/epoll.h>
        int epoll_ctl ( int epfd, int op, int fd, struct epoll_event *event );

        功能:将需监听的fd添加到epfd对应的红黑树上,其中形参中指定:(1)对fd的操作类型:是删除还是增加。(2)监听fd的哪些事件:读事件还是写事件。(3)监听事件的方式:是水平触发还是边沿触发。

        返回值:成功返回0,不成功返回-1。

        参数介绍:

        epfd:epoll_create 的返回值。

        op:操作方式。有三种:(1)向事件表中注事件,EPOLL_CTL_ADD。(2)修改fd上事件,EPOLL_CTL_MOD。(3)删除fd上事件,EPOLL_CTL_DEL。

        fd:要操作的文件描述符。

        event:指定事件。介绍epoll_event:

        struct epoll_event

        {
            __unit32_t events;    // epoll事件
            epoll_data_t data;     // 用户数据
        };

        介绍epoll_event 的两个成员:
events:
        EPOLLIN :  表示监听对应文件描述符,读事件(包括对端SOCKET正常关闭);
        EPOLLOUT:表示监听对应文件描述符,写事件;
        EPOLLPRI:  表示监听对应文件描述符,紧急数据可读事件(这里应该表示有带外数据到来);
        EPOLLERR: 表示监听文件描述符,发生错误事件;
        EPOLLHUP:表示监听文件描述符,被挂断事件;
        EPOLLET:   将epoll设为边沿触发(Edge Triggered)模式,该参数缺省状态为水平触发LT。
        EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socketfd的话,需重新把这个socketfd加入到EPOLL队列里。
data:在实际应用中多只会用到fd。
        typedef union epoll_data
        {
            void* ptr;              //fd相关用户数据
            int fd;                   //事件从属的文件描述符
            uint32_t u32;
            uint64_t u64;
        } epoll_data_t;

        epoll的水平触发和边沿触发:

        (1)水平触发LT:接受缓冲区不为空,对应fd一直处于“读就绪”状态;发送缓冲区不满,对应fd一直处于“写就绪”状态关于这里的缓冲区:读多少就少多少,写多少就多多少。所以水平触发,只要数据没读完,那么在epoll_wait中,就一直认为该fd处于就绪状态。

        (2)边沿触发ET:当fd对应的读缓冲区从”有“变为了”无“时,对应fd才处于”读就绪“。当fd对应的写缓冲区从”无“变为了”有“时,对应fd才处于”写就绪“。注意:一定是在”有“和”无“之间变化才能触发。

        上述是自己的总结。想通过代码了解到两种方式不同的,推荐大牛博客:https://www.cnblogs.com/lojunren/p/3856290.html

        epoll中的event什么时候移除:不处于就绪状态的fd,就会被移除,根据 LT/ET 而不同。

epoll_wait:

        #include <sys/epoll.h>
        int epoll_wait ( int epfd, struct epoll_event* events, int maxevents, int timeout );

        功能:发现并获得”就绪“状态的fd。

        返回值:成功,返回就绪文件描述符个数。失败返回-1,并设置errno(使用errno需#include <errno.h>)。

        参数介绍:

        (1) epfd:epoll_create创建的epollfd。

        (2) events:一个数组,若检测到某fd处于就绪状态,将事件从内核事件表复制到该数组。

        (3) maxevents:指定最多监听多少事件。

        (4) 坑一:timeout:epoll超时时间,单位毫秒。为-1表示阻塞,为0表示非阻塞,其他值表示超时时间。这里一定要注意,程序涉猎多线程和多进程时,这个timeout的设置初学时也容易出错。我们来说明以下几种设置方式:

1. 设置为-1,程序阻塞在此,后续任务没法执行。

2. 设置为0,程序能继续跑,但即使没事件时,程序也在空转,十分占用cpu时间片,我测试时每个进程都是60+%的cpu占用时间。

3. 综上,我们给出比较好的设置方法:将其设置为1,但还没完,因为即使这样设置,处理其它任务时,
在每次循环都会在这浪费1ms的阻塞时间,多次循环后性能损失就比较明显了。为了避免该现象,我们通常
向epoll再添加一个fd,我们有其它任务要执行时直接向该fd随便写入一个字节,将epoll唤醒从而跳过
阻塞时间。没任务时epoll超过阻塞时间1ms也会自动挂起,不会占用cpu,两全其美。

 

        坑二:在多进程中,epoll的创建和添加,最好放在同一进程中进行。本人就因此遇上了问题:

        坑的创建:父进程创建epoll,子进程有4步操作:(1) socket;(2) 设置reuseport避免“惊群”;(3) 将socket_fd添加进epoll;(4) 子进程通过epoll_wait等待然后accept;这样做是错误的,运行程序会报错:Resource temporarily unavailable。

        产生坑的原因:多个子进程socket产生的socket_fd是相同的数值,但表示不同的套接字(详见“网络编程”),所以实际上加入父进程epoll的,应该只是某一子进程的套接字,如果有客户端来connect,由于设置了reuseport,只有一个子进程来处理该事件,调用accept,至于能否accept成功取决于:添加入epoll的套接字是否是该子进程创建的套接字,那么只有 1/(进程数) 的概率accept 成功。

        综上述:epoll的三个系统调用最好在同一进程内使用,会方便很多。

        注:accept本身已经有效避免“惊群”了,引起“惊群”的是:epoll/select/poll等。

       

 

好的,我可以回答你关于 epoll 的问题。epoll 是 Linux 内核提供的一种 I/O 事件通知机制,是高性能网络编程中常用的技术之一。 epoll 提供了三个函数:epoll_create、epoll_ctl 和 epoll_wait。 1. epoll_create 函数 epoll_create 函数用于创建一个 epoll 实例,它的原型如下: ```c int epoll_create(int size); ``` 参数 size 表示要监听的文件描述符数目,这个参数实际上是一个提示,内核会根据这个提示值来调整 epoll 实例的大小,但是这并不意味着 epoll 实例不能监听更多的文件描述符。 函数返回一个非负整数,表示 epoll 实例的文件描述符(类似于文件描述符,用于标识 epoll 实例)。 2. epoll_ctl 函数 epoll_ctl 函数用于向 epoll 实例添加、修改或删除事件,它的原型如下: ```c int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); ``` 参数 epfd 表示 epoll 实例的文件描述符,参数 op 表示要进行的操作,可以是 EPOLL_CTL_ADD、EPOLL_CTL_MOD 或 EPOLL_CTL_DEL,分别表示添加、修改或删除事件。 参数 fd 表示要监听的文件描述符,参数 event 为一个指向 epoll_event 结构体的指针,它用于设置事件类型和其他属性。 3. epoll_wait 函数 epoll_wait 函数用于等待事件的发生,它的原型如下: ```c int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); ``` 参数 epfd 表示 epoll 实例的文件描述符,参数 events 表示一个指向 epoll_event 结构体数组的指针,用于存放已经发生的事件。 参数 maxevents 表示 events 数组的大小,即最多可以存放多少个事件。 参数 timeout 表示等待事件的超时时间(以毫秒为单位),当 timeout 为 -1 时表示一直等待,直到有事件发生。 函数返回值表示发生事件的文件描述符数目,如果返回值为 0 表示超时,没有事件发生。 关于 epoll 的详细介绍还有很多,我只是简单地介绍了一下 epoll 的三个函数。希望对你有帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值