• DPDK(二):准备知识10 --- epoll


    https://www.cnblogs.com/skyfsm/p/7079458.html
    一、select & poll
    1、select API介绍:
    #include <sys/select.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    int select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
    maxfdp:被监听的文件描述符的总数,它比所有文件描述符集合中的文件描述符的最大值大1,因为文件描述符是从0开始计数的;
    readfds、writefds、exceptset:分别指向可读、可写和异常等事件对应的描述符集合。
    timeout:用于设置select函数的超时时间,即告诉内核select等待多长时间之后就放弃等待。timeout == NULL 表示等待无限长的时间
    返回值:超时返回0;失败返回-1;成功返回大于0的整数,这个整数表示就绪描述符的数目。
    以下介绍与select函数相关的常见的几个宏:
    #include <sys/select.h>
    int FD_ZERO(int fd, fd_set *fdset); //一个 fd_set类型变量的所有位都设为 0
    int FD_CLR(int fd, fd_set *fdset); //清除某个位时可以使用
    int FD_SET(int fd, fd_set *fd_set); //设置变量的某个位置位
    int FD_ISSET(int fd, fd_set *fdset); //测试某个位是否被置位
    2、深入理解select模型:
    理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
    (1)执行fd_set set; FD_ZERO(&set); 则set用位表示是0000,0000。
    (2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)
    (3)若再加入fd=2,fd=1,则set变为0001,0011
    (4)执行select(6,&set,0,0,0)阻塞等待
    (5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。

    select的缺点:
    1、单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,当然可以更改数量,但由于select采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越 差;(在linux内核头文件中,有这样的定义:#define __FD_SETSIZE    1024)
    2、内核 / 用户空间内存拷贝问题,select需要复制大量的句柄数据结构,产生巨大的开销;
    3、select返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件;
    4、select的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次select调用还是会将这些文件描述符通知进程
    相比select模型,poll使用链表保存文件描述符,因此没有了监视文件数量的限制,但其他三个缺点依然存在。

    select和poll的用武之地越来越有限,风头已经被epoll占尽
    二、该epoll上场了
    ---------------------
    原文:https://blog.csdn.net/davidsguo008/article/details/73556811
    当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中。
    eventpoll结构体如下所示:

    struct eventpoll{
    ....
    /*红黑树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/
    struct rb_root rbr;
    /*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/
    struct list_head rdlist;
    ....
    }
    epoll_create 创建一个epoll对象,一般epollfd = epoll_create()
    epoll_ctl (epoll_add/epoll_del的合体),往epoll对象中增加/删除某一个流的某一个事件
    比如
    epoll_ctl(epollfd, EPOLL_CTL_ADD, socket, EPOLLIN);//有缓冲区内有数据时epoll_wait返回
    epoll_ctl(epollfd, EPOLL_CTL_DEL, socket, EPOLLOUT);//缓冲区可写入时epoll_wait返回
    epoll_wait(epollfd,...)等待直到注册的事件发生
    每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中。通过红黑树和双链表数据结构,并结合回调机制,造就了epoll的高效。
    三、kqueue & kevent
    linux有epoll,Free BSD都有它们自己的实现kqueue。有一种说法是kqueue技术上比epoll更优。
    kqueue()函数行为有点类似于epoll_create()。但是,kevent()却集成了epoll_ctl()(用于调整兴趣集)和epoll_wait()(获取事件) 的角色。注册一批socket描述符到 kqueue 以后,当其中的描述符状态发生变化时,kqueue 将一次性通知应用程序哪些描述符可读、可写或出错了。
    int kqueue(void);

    int kevent(int kq, const struct kevent *changelist, int nchanges,

    struct kevent *eventlist, int nevents, const struct timespec *timeout);
    struct kevent {

    uintptr_t ident; /* 事件 ID */

    short filter; /* 事件过滤器 */

    u_short flags; /* 行为标识 */

    u_int fflags; /* 过滤器标识值 */

    intptr_t data; /* 过滤器数据 */

    void *udata; /* 应用透传数据 */

    };
    kqueue 中,{ident, filter} 确定一个唯一的事件。
    ident:事件的 id,一般设置为文件描述符。
    filter:可以将 kqueue filter 看作事件,例如:EVFILT_READ,EVFILT_WRIT。
    行为标志flags:
    EV_ADD:指示加入事件到 kqueue
    EV_DELETE:指示将传入的事件从 kqueue 中移除
    过滤器标识值:
    EV_ENABLE:过滤器事件可用,注册一个事件时,默认是可用的。
    EV_DISABLE:过滤器事件不可用,当内部描述可读或可写时,将不通知应用程序。

  • 相关阅读:
    抓老鼠啊
    币值转换
    打印沙漏
    秋季学习总结
    第五周课程总结&试验报告(三)
    第四周课程总结&试验报告2
    实验报告一 &第三周课程总结
    Java第二周总结
    2019春总结作业
    第十六周
  • 原文地址:https://www.cnblogs.com/xiaomayi-cyj/p/10543032.html
Copyright © 2020-2023  润新知