(dpdk f-stack)-Nginx使用kqueue

Posted ygmdream

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(dpdk f-stack)-Nginx使用kqueue相关的知识,希望对你有一定的参考价值。

概述

     kqueue 是 FreeBSD 上的一种的多路复用机制。
    它是针对传统的 select/poll 处理大量的文件描述符性能较低效而开发出来的。
    注册一堆描述符到 kqueue 以后,当其中的描述符状态发生变化时, kqueue 将一次性通知应用程序哪些描述符可读、可写或出错了。

kqueue APIs
    int kqueue(void)
        生成一个内核事件队列,返回该队列的文件描述索。其它 API 通过该描述符操作这个 kqueue 。

    kevent() 提供三个主要的行为功能。在下面小节中将会用到这两个主要功能。
        Ø注册/反注册
        注意 kevent() 中的 neventlist 这个输入参数,当将其设为 0 ,且传入合法的 changelist 和 nchangelist ,就会将 changelist 中的事件注册到 kqueue 中
        当关闭某文件描述符时,与之关联的事件会被自动地从 kqueue 移除。

        Ø允许/禁止过滤器事件
        通过 flags EV_ENABLE 和 EV_DISABLE 使过滤器事件有效或无效。这个功能在利用 EVFILT_WRITE 发送数据时非常有用。

        Ø等待事件通知
        将 nchangelist 设置成 0 ,当然要传入其它合法的参数,当 kevent 非错误和超时返回时,在 eventlist 和 neventlist 中就保存可用事件集合

        功能: kevent 向内核注册事件,返回就绪事件或错误事件;
        int
        kevent(int kq, const struct kevent *changelist, int nchanges,
            struct kevent *eventlist, int nevents, const struct timespec *timeout)

        函数参数:
            kq: kqueue 的文件描述符。
            changelist: 要注册的事件数组;
            nchanges: changelist 的元素个数。
            eventlist: 满足条件的通知事件数组;
            nevents: eventlist 的元素个数。
            timeout: 等待事件到来时的超时时间, 0 立刻返回; NULL 一直等待;有一个具体值则等待 timespec 时间值。

        返回值:可用事件个数。

    struct kevent:
        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 看作事件。内核检测 ident 上注册的 filter 的状态,状态发生了变化,就通知应用程序。 
        kqueue 定义了较多的 filter ,本文只介绍 Socket 读写相关的 filter;
            EVFILT_READ:
            TCP 监听 socket ,如果在完成的连接队列 ( 已收三次握手最后一个 ACK) 中有数据,此事件将被通知。
            收到该通知的应用一般调用 accept() ,且可通过 data 获得完成队列的节点个数。
            流或数据报 socket ,当协议栈的 socket 层接收缓冲区有数据时,该事件会被通知,并且 data 被设置成可读数据的字节数。

            EVFILT_WRITE:当 socket 层的写入缓冲区可写入时,该事件将被通知; data 指示目前缓冲区有多少字节空闲空间。

        Ø flags
        EV_ADD 指示加入事件到 kqueue 。
        EV_DELETE 指示将传入的事件从 kqueue 中移除。
        EV_ENABLE 过滤器事件可用,注册一个事件时,默认是可用的。
        EV_DISABLE 过滤器事件不可用,当内部描述可读或可写时,将不通知应用程序。
        EV_ERROR 一个输出参数,当 changelist 中对应的描述符处理出错时,将输出这个 flag 。应用程序要判断这个 flag ,否则可能出现 kevent 不断地提示某个描述符出错,却没将这个描述符从 kq 中清除。
        处理 EV_ERROR 类似下面的代码:
            if (events[i].flags & EV_ERROR)
                  close(events[i].ident);

        Ø fflags
        过滤器相关的一个输入输出类型标识,有时候和 data 结合使用。

        Ø data
        过滤器相关的数据值,请看 EVFILT_READ 和 EVFILT_WRITE 描述。

        Ø udata
        应用自定义数据,注册的时候传给 kernel , kernel 不会改变此数据,当有事件通知时,此数据会跟着返回给应用。

        Ø EV_SET
        EV_SET(&kev, ident, filter, flags, fflags, data, udata);
        struct kevent 的初始化的辅助操作。

使用:
    https://hub.fastgit.org/F-Stack/f-stack/blob/dev/app/nginx-1.16.1/conf/nginx.conf
    events
        worker_connections  102400;
        use kqueue;
    

    events 块解析时执行 ngx_events_block, use 指令解析时执行 ngx_event_use 遍历所有的 NGX_EVENT_MODULE 类型模块得到对应的模块;
    use kqueue 即使用 ngx_kqueue_module 模块;

    初始化:
        ngx_kqueue_init--ngx_kqueue=kqueue();

    添加或移除事件:
        ngx_kqueue_set_event--kevent;

        //注册    
        kevent()    
          ->kqueue_register()                          //注册要监听的TCP的事件    
            ->knote_attach                          //分配 knote 结点,并和相对应的socket 连接    
            ->fops->f_attach()==filt_sorattach()      //调用TCP socketfilter里的attach函数    
               ->SLIST_INSERT_HEAD()                   // 将knotes结点挂入该socket的knotes 列表  

    TCP 收报流程:    
        tcp_input()    
          ->sowwakeup()    
            ->sowakeup()        
              ->knote()             //kern_event.c 遍历连接到自己的knotes结点list    
                ->kn_fop->f_even()  //事件满足唤醒条件    
                ->knote_enqueue()   //将knote插入kqueue的active队列    
                  ->wakeup();       //唤醒kqueue

    事件处理:
        ngx_kqueue_process_events--kevent--ff_kevent--ff_kevent_do_each(epfd, NULL, 0, events, maxevents, NULL, ff_event_to_epoll)--kern_kevent--kern_kevent_fp--kqueue_kevent--kqueue_scan
        (执行 k_ops->k_copyout 即 kevent_copyout 拷贝发生的事件到用户的 eventlist 中);

        ngx_kqueue_process_events--遍历发生的事件执行 ev->handler(ev);

以上是关于(dpdk f-stack)-Nginx使用kqueue的主要内容,如果未能解决你的问题,请参考以下文章

(dpdk f-stack)-Nginx使用kqueue

(dpdk f-stack)-Nginx使用kqueue

(dpdk f-stack)-Nginx使用kqueue

(dpdk f-stack)-Nginx使用kqueue

(dpdk f-stack)-Nginx使用kqueue

(dpdk f-stack)-整体框架