近期评论

网络IO模型理解和分类

1、关键词定义

在史蒂芬的Unix网络编程API里面,把I/O分为两大步骤:

1.等待数据准备好。

2.从内核到进程拷贝数据。

 

根据资料,我自己的理解是:

功能:            包括一些系统调用函数和其他函数,语句,变量。

卡住:            通用于进程的功能或者系统调用函数或者其他任何函数。

同步异步:     针对进程执行的某个功能是否会卡住而言;

阻塞非阻塞: 只针对关于socket的单个系统调用函数而言,比如select,recvfrom,write。

 

例如:

执行socket连接这个功能,获取客户端数据,如果没有收到数据,就等待,同一时刻只做一件事,不做完接续做,哪怕卡在那里。这就是同步机制,如果没做完卡住时收到一个消息说因为某种原因卡住了,进程继续接受别的socket连接,这就是异步,所以异步I/O比同步多了异步操作,就是返回一个是否准备好数据的消息。

显然如果这个功能需要执行一段较长时间,异步I/O效率比较高;

但是如果I/O很快,由于异步操作比同步多一些步骤,所以异步效率比较低。

 

执行socket连接这个功能中,需要执行好几个系统调用函数,

如果某个系统调用函数没有执行成功就一直等待,甚至进程睡眠,这就叫阻塞;

如果执行系统调用立即返回,就是非阻塞。

 

2、网络IO分类

AUNP把I/O分为五种类别:

1、阻塞式I/O;

2、非阻塞式I/O;

3、I/O复用(select和poll);

4、信号驱动式I/O;

5、异步I/O(POSIX的aio_系列函数)

 

并且前面四种I/O即,阻塞、非阻塞、复用、信号驱动都是同步IO。严格上讲,这四种同步I/O在第二步操作都阻塞了。即都在等待系统把内核数据拷贝到进程中。

 

所以可以把阻塞I/O分为下面四种,分别对应上面的前4种:

1、直接阻塞I/O,即系统调用函数(比如read,recv)直接阻塞了,一直等待数据,相当于史蒂芬书中的第一种I/O,这种I/O,阻塞后,进程处于睡眠状态,不做其他事情。

2、间接阻塞I/O,类似第一种,区别是执行系统调用函数后立即返回一个标记,表示是否已经准备好数据,并且进程不处于睡眠状态,还可以做别的事情。这里第一步I/O操作是没有不用等待的,第二步I/O操作是卡住的。

3、替换阻塞I/O,相当于第二种的升级版,由于不断的执行recv,recvfrom这类系统调用函数,会消耗大量CPU资源,改用另外一个系统调用函数来获取标记,并且不需要低效的反复执行,只要执行一次即可。这种I/O比第二种效率高些,区别是I/O复用第一步使用select进行轮训这步没有卡住,第二步I/O操作都是阻塞的。

4、信号阻塞I/O,建立信号出去程序,类似闹钟一样,收到信号就直接执行第二步。这里的I/O操作第一步是等待信号,不存在是否阻塞,第二步执行系统调用时,是在阻塞等待的。

 

根据上面的理解,select,poll是同步I/O,实际上相当于在第二种IO模型非阻塞式IO的前面执行一个非阻塞并且不会卡住的select函数判断是否准备好数据。然后再去读或写。

 

而 epoll 因为采用 mmap的机制, 使得内核socket buffer和用户空间的 buffer共享, 从而省去了 socket data copy, 这也意味着, 当epoll 回调上层的 callback函数来处理 socket 数据时, 数据已经从内核层 "自动" 到了用户空间, 虽然和 用poll 一样, 用户层的代码还必须要调用 read/write, 但这个函数内部实现所触发的深度不同了.
 

用 poll 时, poll通知用户空间的Appliation时, 数据还在内核空间, 所以Appliation调用 read API 时, 内部会做 copy socket data from kenel space to user space.

而用 epoll 时, epoll 通知用户空间的Appliation时, 数据已经在用户空间, 所以 Appliation调用 read API 时, 只是读取用户空间的 buffer, 没有 kernal space和 user space的switch了.

 

所以epoll的第二步I/O操作是非阻塞的,另外epoll是利用事件机制触发,没有收到通知,进程还可以做别的事情,所以epoll是异步非阻塞的。

 

另外异步I/O执行第二步操作即系统调用时都是非阻塞的,同步I/O执行系统调用时第二步操作都是阻塞的,只不过第一步操作有点区别而已。

2,265 Responses to “网络IO模型理解和分类”