Windows与类Unix平台上各种IO模型工作模式的讨论?

时间:2008-05-13 15:24:43   来源:论坛整理  作者:  编辑:chinaitzhe
我现在在移植nginx到Windows平台(现在把该版本的nginx称为ngwsx),
并对Windows上的各种IO模型都编写了相应的事件处理模块,
具体请到我的博客去看看:http://blog.csdn.net/ngwsx/。

现在有个疑问,就是具体各种IO模型的工作模式是什么?

这里说的工作模式有两种:
Level Triggered (LT)(条件触发)
Edge Triggered (ET)(边缘触发)

Level Triggered 工作模式:
通过调用某种IO方法,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。
假如你不对该描述符作任何操作,当你再次调用该IO方法,内核还是会继续通知你的。

Edge Triggered 工作模式:
当描述符从未就绪变为就绪时,内核通过你调用IO方法时告诉你。然后它会假设你知道文件描述符已经就绪,
并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了
(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。
但是请注重,假如一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。

我想知道的是以下这些IO模型的工作模式是上面这两种的那一种,大家来讨论讨论吧。

Windows平台:
select
WSAAsyncSelect
WSAEventSelect
OverlappedIO
IO Completion Port
WSAPoll

类Unix平台:
select
poll
devpoll
epoll
kqueue
aio
eventport
rtsig

现在ngwsx是这样的工作模式:
(1)主线程通过调用select/WSAAsyncSelect/WSAEventSelect/OverlappedIO/IOCP/WSAPoll
这些函数来检测IO事件是否就绪或IO操作是否已经完成;假如已经预备好,
就投递相应的事件到内部一个队列中。
(2)多个工作线程同时从队列中取出事件并处理它。

因为这种工作模式,ngwsx要确保不能投递重复的事件到队列中,
所以需要了解各种IO模型的工作模式。
网友回复:谁来说说吗?
网友回复:怎么没人发言呀,我发错地方吗?
网友回复:跨平台的移植,一般都不敢随便说话的,何况还涉及到IO操作...
网友回复:我来说话,用SELECT好了,所有平台都有

(1)主线程通过调用select/WSAAsyncSelect/WSAEventSelect/OverlappedIO/IOCP/WSAPoll
这些函数来检测IO事件是否就绪或IO操作是否已经完成;假如已经预备好,
就投递相应的事件到内部一个队列中。
(2)多个工作线程同时从队列中取出事件并处理它。
你这个方法很有创意,效率如何
网友回复:这个不是我的创意,官方nginx就有的,不过它现在还不支持这种工作模式,
现在nginx是一个主进程多个工作进程这种模式,
假如比较两者的效率,那肯定是主线程多工作线程这种模式。

里面实现的事件队列是这样子的:
C/C code





Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/







/* 首先ngx_event_t结构的定义是:*/



typedef struct ngx_event_s  ngx_event_t;



struct ngx_event_s {

    ... /* 其它结构字段 */



    ngx_event_t   *next;

    ngx_event_t  **prev;

};





/* 这个互斥体用于保护事件队列 */

ngx_mutex_t                      *ngx_posted_events_mutex;

/*

 * ngx_posted_events事件队列,官方nginx还有一个事件队列ngx_posted_accept_events。

 */

ngx_thread_volatile ngx_event_t  *ngx_posted_events;





/* 事件入队 */



/*

 * ev参数传入检测到的IO事件(用ngx_event_t结构变量来保存)的指针,

 * queue参数传入ngx_posted_events的指针,也就是ngx_event_t二级指针。

 */



#define ngx_locked_post_event(ev, queue)                                      \

                                                                              \

    if ((ev)->prev == NULL) {                                                 \

        (ev)->next = (ngx_event_t *) *(queue);                                \

        (ev)->prev = (ngx_event_t **) (queue);                                \

        *(queue) = ev;                                                        \

                                                                              \

        if ((ev)->next) {                                                     \

            (ev)->next->prev = &(ev)->next;                                   \

        }                                                                     \

                                                                              \

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, (ev)->log, 0,                     \

                       "post event %p", ev);                                  \

                                                                              \

    } else {                                                                  \

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, (ev)->log, 0,                     \

                       "update posted event %p", ev);                         \

    }





#define ngx_post_event(ev, queue)                                             \

                                                                              \

    ngx_mutex_lock(ngx_posted_events_mutex);                                  \

    ngx_locked_post_event(ev, queue);                                         \

    ngx_mutex_unlock(ngx_posted_events_mutex);





/* 事件出队 */



#define ngx_delete_posted_event(ev)                                           \

                                                                              \

    *(ev)->prev = (ev)->next;                                                 \

                                                                              \

    if ((ev)->next) {                                                         \

        (ev)->next->prev = (ev)->prev;                                        \

    }                                                                         \

                                                                              \

    (ev)->prev = NULL;                                                        \

                                                                              \

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, (ev)->log, 0,                         \

                   "delete posted event %p", ev);






网友回复:这个还真不好说
这几天看开源的web server
感觉就是,linux 2.6以前的轻量级一般都选择poll/select,
性能高一点的都选择kqueue,现在2.6以后的一般都选择epoll
poll/select的缺点都是轮询,必须遍历所有点fd, 而且select监听fd还是有数量限制的
epoll是2.6以后的API,据说性能比poll高的相当多,
网上有这几种复用方式的评估结果,你可以参考一下

网友回复:ET,LT是epoll里的
似乎ET的性能更好,这个网上也有评估结果供参考
网友回复:你说的这些我都有所了解,
EPOLL的工作模式ET与LT我也知道,
我就是通过EPOLL才知道这两种工作模式的,
我认为其它的IO模型也应该是具有这两种工作模式的一种或二种的?
就拿select来说吧,它应该是LT这种工作模式的,
当调用select检测到一个可读套接字描述符时,
然后假如不调用recv去读,
当下次调用select还是会检测到相同的可读套接字描述符。
poll也应该是LT工作模式,
Windows的IO完成端口应该是ET工作模式的。
其它的就不太确定。

虽然可以通过编写程序来了解所有这些IO模型的工作模式
但在这里希望对这些IO模型有过相关使用经验的朋友来说说。
网友回复:
引用 8 楼 ngwsx 的回复:
你说的这些我都有所了解,
EPOLL的工作模式ET与LT我也知道,
我就是通过EPOLL才知道这两种工作模式的,
我认为其它的IO模型也应该是具有这两种工作模式的一种或二种的?
就拿select来说吧,它应该是LT这种工作模式的,
当调用select检测到一个可读套接字描述符时,
然后假如不调用recv去读,
当下次调用select还是会检测到相同的可读套接字描述符。
poll也应该是LT工作模式,
Windows的IO完成端口应该是ET工作模式的。


你这样理解也是可以的,但是select/poll, 就算是和epoll中的LT比还是有差距的
二者并不一样
网友回复:你是说具体实现吗?
那肯定不一样!
网友回复:没人?
网友回复:up
关键字:Windows,Unix,平台,各种,IO,

文章评论

共有 0 位网友发表了评论 此处只显示部分留言 点击查看完整评论页面