网络 IO 模型

IO 是计算机体系中重要的一部分 。不同的 IO 设备有着不同的特点:数据率不一样、传送单位不一样,数据表示不一样,等等。所以,很难实现一种统一的输入输出方法。

IO 有两种操作,同步 IO 和异步 IO

同步 IO :必须等待 IO 操作完成后,控制权才返回给用户进程。

异步 IO:无须等待 IO 操作完成,就将控制权返回给用户进程。

下面学习一下常见的 5 种 IO 模型

1、阻塞 IO 模型:

        Linux 默认情况下所有的 socket 都是阻塞的。

        阻塞和非阻塞的概念描述的是用户线程调用内核 IO 操作的方式:阻塞是指 IO 操作需要彻底完成后才返回到用户空间;而非阻塞是指 IO 操作被调用后立即返回给用户一个状态值,不需要等到 IO 操作彻底完成

        如上图所示,当应用进程调用了 recvfrom 这个系统调用后,系统内核就开始了 IO 的第一个阶段 :准备数据

        对于网络 IO 来说,很多时候数据在一开始还没到达时,系统内核就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。
       当系统内核一直等到数据准备好了,它就会将数据从系统内核中拷贝到用户内存中,然后系统内核返回结果,用户进程才解除阻塞的状态,重新运行起来。所以,阻塞 IO 模型的特点就是 IO 执行的两个阶段都被阻塞

        所谓阻塞型接口是指系统调用时却不返回调用结果,并让当前线程一直处于阻塞状态,只有当该系统调用获得结果或者超时出错时才返回结果

2、非阻塞 IO 模型 

 

        在Linux 下,可以通过设置 socket IO 变为非阻塞状态。当一个非阻塞的 socket执行 read 操作时,当用户进程发出 read 操作时,如果内核中的数据还没有准备好,那么它并不会 block 用户进程,而是立刻返回一个错误,只有在数据到达时需要等待复制数据到用户空间中,属于同步IO

        从用户进程角度讲,它发起 read 操作后,并不需要等待,而是马上就得到了一个结果 当用户进程判断结果是一个错误时,它就知道数据还没有准备好,于是它可以再次发送 read 操作。

        一旦内核中的数据准备好了,并且又再次收到了用户进程的系统调用,那么它马上就将数据复制到了用户内存中,然后返回正确的返回值。

        在非阻塞式 IO 中,用户进程其实需要不断地主动询问 kernel 数据是否准备好非阻塞的接口相比于阻塞型接口的显著差异在于被调用之后立即返回,使用如下的函数可以将某句柄归设为非阻塞状态:fcntl( fd , F_SETFL , O_NONBLOCK);     

在非阻塞状态下,recv 接口在被调用后立即返回,返回值代表了不同的含义,如下所述。      

  • recv 返回值大于 0,表示接收数据完毕,返回值即是接收到的字节数。

  • recv 返回 0,表示连接已经正常断开。

  • recv 返回 -1 ,且 errno 等于 EAGAIN ,表示 recv 操作还没执行完成。

  • recv 返回 -1,且 errno 不等于 EAGAIN ,表示 recv 操作遇到系统错误 errno。

3、多路 IO 复用模型 :

        多路 IO 复用,有时也称为事件驱动 IO。它的基本原理就是有个函数会不断地轮询所负责的所有 socket ,当某个 socket 有数据到达了,就通知用户进程。

 

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

       这个模型和阻塞 IO 的模型其实并没有太大的不同,事实上还更差一些 因为这里需要使用两个系统调用,而阻塞 IO 只调用了一个系统调用 recvfrom,即使将非阻塞IO定义在了recvfrom(读写前),在select时仍然要产生阻塞,所以也属于同步IO,用 select 的优势在于它可以同时处理多个连接

        如果处理的连接数不是很高的话,使用 select/epoll Web server不一 定比使用多线程的阻塞 IO Web server 性能更好,可能延迟还更大;select/poll 的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。

       select、poll、epoll都属于同步IO.

4、信号驱动IO模型:

      上面说过多路复用IO又称为事件驱动IO,信号驱动IO则通过调用sigaction注册信号函数,等内核数据准备好的时候系统中断当前程序,执行信号函数(在这里面调用recv)。

    

      信号驱动IO也是属于同步IO中的一种,实现和应用难度较大,一般较少使用。

5、异步 IO 模型

        

        

        用户进程发起 read 操作之后,立刻就可以开始去做其他的事;而另一方面,从内核的角度,当它收到一个异步的 read 请求操作之后,首先会立刻返回,所以不会对用户进程产生任何阻塞。

        然后,内核会等待数据准备完成,然后将数据拷贝到用户内存中,当这一切都完成之后,内核会给用户进程发送一个信号,返回 read 操作已完成的信息。

        调用阻塞 IO 一直阻塞住对应的进程直到操作完成,而非阻塞 IO 在内核还在准备数据的情况下会立刻返回。两者的区别就在于同步 IO 进行 IO 操作时会阻塞进程。

        非阻塞 IO 在执行 recvfrom 这个系统调用的时候,如果内核的数据没有准备好,这时候不会阻塞进程。但是当内核中数据准备好时,recvfrom 会将数据从内核拷贝到用户内存中,这个时候进程则被阻塞。

        而异步 IO 则不 样,当进程发起 IO 操作之后,就直接返回,直到内核发送一个信号,告诉进程 IO 已完成,则在这整个过程中,进程完全没有被阻塞

总结

        传统IO(还有零拷贝技术),分两阶段:

         1、 数据准备阶段

          2、内核空间复制回用户进程缓冲区阶段

        一般来讲:阻塞IO模型、非阻塞IO模型、IO复用模型(select/poll/epoll)、信号驱动IO模型都属于同步IO,因为阶段2是阻塞的(尽管时间很短)。只有异步IO模型是符合POSIX异步IO操作含义的,不管在阶段1还是阶段2都可以干别的事,完全没被阻塞。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值