网络IO模型

        网络IO会涉及到两个系统对象,一个是用户空间调用IO的进程或线程,另一个是内核空间的内核系统,网络IO本质是socket的读取,比如发生IO操作read时,会经历两个阶段:

  • 等待数据准备就绪;
  • 将数据从内核拷贝到进程或者线程;

        Unix的五种I/O网络模型:

  • 阻塞IO (blocking IO);
  • 非阻塞IO (non-blocking IO);
  • 多路复用IO (IO multiplexing);
  • 异步IO (Asynchronous IO);
  • 信号驱动IO (signal driven IO);

阻塞IO (blocking IO)

        在linux种,默认情况下所有socket都是阻塞的。

         当用户进程调用了read这个系统调用,内核就开始I/O的第一个阶段:准备数据。对于网络I/O来说,很多时候数据一开始还没到达(比如还未收到一个完整的数据包),此时内核就要等足够的数据到来。而在用户进程这边,整个进程会被阻塞。当内核一直等待数据准备就绪,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除阻塞状态,重新运行起来。

        所以,阻塞IO的特点是在IO执行的两个阶段(等待数据和拷贝数据)都被阻塞了。

一问一答服务器

        大部分的socket接口都是阻塞型的,只有当系统调用获得结果或者超时出错时才返回。

多线程的服务器

        一个简单的改进方案是在服务器端使用多线程(多进程),多线程(多进程)的目的时让每个连接都拥有独立的线程(进程),这样任何一个连接的阻塞都不会影响其他的连接。如果需要同时为较多客户机提供服务,不推荐使用多进程;如果单个服务执行需要消耗较多的CPU资源,譬如需要进行大规模或长时间的数据运算或访问,则进程较为安全。

        上述的图例中,主线程持续等待客户端的连接请求,如果有连接,则创建新线程, 并在新线程中提供为前例同样的问答服务。

        多线程的服务器模型似乎可以解决为多个客户提供问答服务的要求,但其实如果同时响应成百上千的连接请求时,该模型会严重占据系统资源,降低系统对外界的响应效率。针对上述问题可以使用非阻塞接口来解决。

非阻塞IO(no-blocking IO)

        linux下,可以通过设置socket使其变为non-blocking。当一个non-blocking socket执行读操作时,流程如下:

         如图所示,当用户进程发出read操作时,如果内核中的数据还没准备好,那么它不会阻塞用户进程,而是立刻返回一个error。从用户进程角度讲,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。所以,在非阻塞IO中,用户进程其实是需要不断的主动向内核询问数据是否准备好。

        在非阻塞IO中,recv()在被调用后立即返回,返回值代表不同含义:

  • recv() 返回值大于0,表示数据接收完毕,返回值即是接收到的字数;
  • recv() 返回值等于0,表示连接已经正常断开;
  • recv() 返回值等于-1,且error等于EAGAIN,表示recv还没执行完成;
  • recv() 返回值等于-1,且error不等于EAGAIN,表示recv遇到系统错误error;

         可以看到服务器线程可以通过循环调用recv()接口,在单个线程内实现对所有连接的数据接收工作。但是此模式不被推荐,循环调用recv()将大幅推高CPU占用率;此外,在这个方案中recv()更多的是起到监测“操作是否完成”的作用,实际操作系统提供了更为高效的监测接口,例如select()多路复用模式,可以一次监测多个连接是否活跃。

多路复用IO(IO multiplexing)

        IO multiplexing,也称这种方式为事件驱动IO(event driven IO)。例如select/epoll,单个process就可以同时处理多个网络连接的IO。它的基本原理是select/epoll 这个function会不断轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。

        当用户进程调用了select,那么整个进程会被阻塞,而同时,内核会监测所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回,这个时候用户进程再调用read操作,将数据从内核拷贝到用户进程。

        使用select最大的优势是用户可以在一个线程内同时处理多个socket,即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。(所以,如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading+ blocking IO的web server性能更好,可能延迟更高。select/epoll 的优势并不是对与单个连接能处理的更快,而是在于处理更多的连接。)

        在多路复用模型中,对于每一个socket,一般都设置称为non-blocking,但是,如上图所示,整个用户的进程其实一直被阻塞。只不过进程是被select这个函数阻塞,而不是被socket阻塞。因此select()与非阻塞IO类似。

fd_set set;
FD_ZERO(&set); /*将set清零使集合中不含任何fd*/
FD_SET(fd, &set); /*将fd加入set集合*/
FD_CLR(fd, &set); /*将fd从set集合中清除*/
FD_ISSET(fd, &set); /*在调用select()函数后,用FD_ISSET来检测fd是否在set集合中,当检测到fd在set中则返回真,否则,返回假(0)*/
以上式子中的fd为socket句柄。
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set
*exceptfds,struct timeval *timeout)

 

         上述模型只是描述了使用select()接口同时从多个客户端接收数据的过程;由于select()接口可以同时对多个句柄进行读状态、写状态和错误状态的探测,所以可以很容易构建为多个客户端提供独立问答服务的服务系统。

         

         客户端的一个connect()操作,将在服务器端激发一个“可读事件”,所以select()也能探测来自客户端的connect行为。

        相比其他模型,使用select()的事件驱动模型只用单线程执行,占用资源少,不消耗太多CPU,同时能够为多客户端提供服务。

        但是这个模型依旧有很多问题,select()本身需要消耗大量的时间去轮询各个句柄,使用epoll等更高效的接口更适合,其次,该模型将事件探测和事件响应夹杂在一起,一旦事件响应的执行体庞大,则对整个模型是灾难性的。

        使用高效的事件驱动库可以屏蔽上述困难,常见的事件驱动库有libevent、libev库。这些库会根据操作系统的特点选择最为合适的事件探测接口,并且加入了信号(signal)等技术以支持异步响应。

异步IO(Asynchronous I/O)

        linux下的asynchronous IO用在磁盘I/O读写操作,不用于网络,从内核2.6版本开始引入。

        用户进程发起read操作之后,立刻就可以开始去做其他事。从内核角度来看,当它收到一个asynchronous read之后,首先会立刻返回,所以不会对用户进程产生阻塞。然后,内核会等待数据准备完成,然后将数据拷贝至用户内存,当这一切完成后,内核会给用户进程发送一个signal,告诉它read操作完成了。

信号驱动IO(signal driven I/O,SIGIO)

        首先允许套接口进行信号驱动IO,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。当数据准备好时,内核就为该进程产生一个SIGIO信号。用户随后既可以在信号处理函数中调用read读取数据,并通知主循环数据已经准备好等待处理,也可以立即通知主循环,由它来读取数据。

        这种模型的优势在于等待数据报到达(第一阶段)期间,进程可以继续执行,不被阻塞。同时免去了select的阻塞与轮询,当有活跃套接字时,由注册的handler处理。

        需要注意,信号驱动IO和异步IO的区别:

  • 信号驱动IO是通知用户进程数据准备就绪,并开始读取数据;
  • 异步IO是通知用户进程数据拷贝完成 ,不需要用户进程主动拷贝数据;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值