(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的主要内容,如果未能解决你的问题,请参考以下文章