Tornado 源码阅读笔记(三)- kqueue

继续学习并翻译 (Kqueue: A generic and scalable event notification facility) 这篇论文

接着上篇 Tornado 源码阅读笔记(二) ,把原论文第2章余下的部分翻译完。

参考了 《socket 的几种工作模式》 一文中的流程图之后,我对 select() poll() 的工作模式有了清晰的认识。(一图胜千言,这话没错。)系统内核在复制了需要监控的 descriptor 列表之后,先遍历该列表,监测是否有 pending activity on the descriptor. 此次遍历之后,如果一个 active descriptor 都没有,内核将更新 descriptor's selinfo entry(用于唤醒内核监测 descriptor 列表的进程). 之后,该监控进程调用 tsleep()。当有 active descriptor 出现时,监控进程将被唤醒。醒了就得干活啊,于是,再遍历 descriptor 列表一遍,查找当前处于 active 状态的 descriptors。

在 poll or select actually sleep 的情况下,这个过程需要遍历 descriptor 列表 3 次:

第一次,内核,遍历列表以查找 pending events 并记录 select 信息;

第二次,内核,遍历列表以查找哪个 descriptor 的 activity 唤醒了检测进程;

第三次,user space,遍历列表以查找哪些 descriptor 被内核标记为 active 状态。

可见,poll() select() 低效的根源在于:

1。内核不能记录每个 application 所关注的 descriptor 列表,只能靠调用时,通过参数传入。如果内核能记录这些信息,再次调用时,就不需要传递 descriptor 列表了。

2。内核每次都返回完整的 descriptor 列表。如果只返回 actived descriptors,岂不是更好。

3. 设计目标

设计目标1:在处理几千个 descriptors 时,高效、可扩展;

设计目标2:make the system flexible(不解)。

基于 UNIX 的系统缺乏健壮的 event notification 处理机制。poll() select() 接口局限于 socket 和 pipe 这两种 descriptor,以致我们无法等待其他类型的 events,例如文件的创建和删除。若要监测其他类型的 events,只能使用其他接口 (必须使用 siginfo 和 family 来获取 notification of signal events, 调用 aiowait 来判断 if an AIO call has completed)。

设计目标3:保持接口简单,易于理解,并且便于将基于 poll() select() 的应用使用新 API 改写。

增加接口的返回信息。例如,对于 readable sockets,我们有时同样想知道 how many bytes are actually pending in the  socket buffer in order to avoid multiple read() calls. 

level-triggered.

4. Kqueue API

int kqueue(void) 
 
int kevent(int kq, 
                const struct kevent *changelist, int nchanges,  
                struct kevent *eventlist, int nevents,  
                const struct timespec *timeout) 
 
struct kevent { 
      uintpt_t   ident;        // identifier for event 
      short        filter;        // filter for event 
      u_short    flags;        // action flags for kq 
      u_int        fflags;       // filter flag value 
      intptr_t    data;         // filter data value 
      void          *udata;    // opaque identifier 

     
EV SET(&kev, ident, filter, flags, fflags, data, udata)  

kqueue API 引入了两个新的系统接口,如上所示。kqueue 接口创建一个新的 kqueue,它是一个 notification channel, 或者 queue。应用程序可以在其上 register 关注的 events,同时用来接收来自 kernel 的 events。kqueue() 的返回值可以作为一个普通的 descriptor 来处理,可以被传入 poll(), select(), 甚至 registered in another kqueue.

kevent 接口被应用程序用来 register 新的 events with the kqueue, 并接收任何 pending events。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。