5种IO模型

从TCP发送数据的流程说起

产生各种IO的原因是什么。本质问题,一条消息是如何从一个人发送到另一个人。

以两个应用程序为例,当‘A’向‘B’发送一条消息:
1.应用A把消息发送到TCP缓冲区。
2.TCP发送缓冲区把消息发送出去,经过网络传递后,消息会发送到B服务器得TCP接收缓冲区。
3.B在从TCP缓冲区去读取属于自己的数据。

在这里插入图片描述

阻塞IO(BIO)/非阻塞IO(NIO)

应用B从TCP缓冲区中读取数据。

应用之间发送消息是间断性的,如果应用B端的接收缓冲区还没接收到属于B该读取的数据时,此时B向TCP缓冲区发起读取申请,TCP接收换冲区让应用B在这里等着直到有数据再把数据交给应用B,就是阻塞,马上告诉你没有数据就是非阻塞。

什么是阻塞IO:应用B调用recvfrom读取数据,在有结果返回之前,应用B一直处于等待状态,不能干其他的事。

就是当应用B发起读取申请时,在内核数据没有准备好之前,应用B会一直处于等待状态,直到内核把数据准备好了交给应用B才结束。换句话说,应用B调用读取数据,在没有得到结果之前,不能干别的事。

术语描述

在应用调用recfrom读取数据时,其系统调用知道数据包到达且被复制到应用缓冲区中或者发送错误时才返回,在此期间值会等待,进程从调用到返回这段时间内都是被阻塞的称为阻塞IO‘

流程:
1.应用进程向内核发起recfrom读取数据。
2.准备数据报(应用进程阻塞)
3.将数据从内核负责到应用空间。
4.复制完成后,返回成功提示。

应用B申请读取TCP接收缓存中的数据,调用recvfrom方法,因为要访问网络资源,用户态切换为内核态,从内核中读取数据,读取后复制到用户态,并返回结果。期间,进程一直等待,直到返回结果。
在这里插入图片描述

什么是非阻塞 应用B调用recfrom后,系统会立即返回结果,期间可以去干别的事,不会一直等着结果什么也不干,但是需要反复调用recfrom等待成功, 因为recfrom成功后不会通知调用它的线程。

就是当应用B发起读取申请时,如果内核数据没有准备好会立刻告诉应用B,不会让B在这里等待。

**术语:**非阻塞IO是在应用调用recvfrom读取数据时,如果该缓冲区没有数据的话,就会直接返回一个EWOULDBLOCK错误,不会让应用一直等待中。在没有数据的时候会即刻返回错误标识,那也意味着如果应用要读取数据就需要不断的调用recvfrom请求,直到读取到它数据要的数据为止。

在这里插入图片描述

IO复用模型 一个或几个线程监视询问数据,当有数据准备就绪之后再分配个对用的线程去读数据,节省大量的线程资源(提供一种函数可以同时监多个fd操作,select、poll、epoll)

应用程序调用SELECT函数就可以同时监控多个fd, SELECT函数监控的fd中只要有一个只要有一个数据准备好了,SELECT就会返回可读状态,询问线程通知处理数据的线程,对应线程此时再次发起recfrom请求去读取数据。

复用IO总的思路就是通过select或poll,epoll来监控多fd,不必为每个fd创建一个对应的监控线程,从而减少线程资源创建的目的。
在这里插入图片描述

IO复用模型的产生原因和思路

在应用B从TCP缓冲区中读取数据环节中,如果是并发环境,N个人向应用B发送消息,应该为每个请求创建一个线程去读取数据,每个线程自己调用recvfrom去读取数据。
在这里插入图片描述
若有上百万个请求,需要创建上百万个线程去读取数据,线程不知道什么时候有数据,需要自己不断的调用recvfrom去询问。

太多线程,线程是宝贵的资源。

能不能专门新建一个线程去监视多个网络请求,linux中一个网络请求为一个fd文件描述符。这样就只需要一个或几个线程就可以完成数据状态的询问操作,当有数据准备就绪之后,再分配对应的线程去读取数据。这样一个或几个线程监视询问数据,当有数据准备就绪之后再分配个对用的线程去读数据,节省大量的线程资源,这就是IO复用模型的思路

相当于这几个线程是处理数据的应用线程和TCP数据缓存的一个中间商,以前是处理数据的线程发出recfrom请求读取数据,系统返回的结构直接返回给这些处理数据的线程,而现在不是直接给调用recfrom的线程,而是给监视他的线程。
在这里插入图片描述

信号驱动IO模型 开启套接口信号驱动IO功能,并通过系统调用signaction执行一个信号处理函数,当数据准备就绪时,就生成对应进程的信号,信号回调通知应用线程调用recvfrom来读取数据。

信号驱动IO通过信号关联数据与线程,只要数据处于就绪状态,信号就会通知对应的线程去调用recfrom,

复用IO解决了一个线程可以同时监控多个fd问题,但是select采用轮询的方式来监控多个fd,通过不断的轮询fd的可读状态来知道是否有可读的数据。有点残暴。大部分情况下的询问都是无效的。

能不能不要我总去问数据是否就绪,你好了通知我就行了。衍生出信号驱动IO模型。

信号驱动IO不是用循环来询问的方式去监控数据就绪状态,而是在调用sigaction时候建立一个SIGIO的信号联系,当内核数据准备好之后再通过SIGIO信号通知线程数据准备好后的可读状态,但线程接收到可读状态的信号后,此时再想内核发起recfrom读取数据的请求。

信号驱动IO模型下应用线程在发出信号监控后即可返回,不会阻塞,所以一个线程也可以同时监控多个fd.

在这里插入图片描述
**术语描述:**首先开启套接口信号驱动IO功能,并通过系统调用sigaction执行一个信号处理函数,此时请求立即返回,当数据准备继续时,就生成对应进程的SIGIO,通过信号回调通知应用线程调用recfrom来读取数据。
在这里插入图片描述

总结 IO复用模型的select虽然可以监控多个fd,但是是通过轮询fds来监控数据状态,因为大部分请求都是无效的,所以信号驱动IO通过信号关联数据与线程,只要数据处于就绪状态,信号就会通知对应的线程去调用recfrom。

异步IO

不管是IO复用还是信号驱动,我们要读取一个数据总是要发起两阶段的请求。第一次发送select请求,询问数据状态是否准备好,第二次发送recfrom请求读取数据。

之前,发请求询问数据状态,发请求读数据。
能不能我只发送一个请求告诉内核我要读取的数据,之后就可以什么都不管了,让内核去帮我完成剩下的所有事情。

方案:应用只需要向内核发送一个read请求,告诉内核它要读取的数据后立即返回结果,内核收到请求后会建立一个信号联系,当数据准备就绪,内核会主动把数据从内核复制到用户空间,等所有操作都完成之后,内核会发起一个通知告诉应用。一劳永逸的异步IO模型。
在这里插入图片描述
术语: 应用告诉内核启动某个操作,并让内核在整个操作完成之后,通知应用,这种模型与信号驱动模型的主要区别在于:信号驱动IO只是由内核通知我们数据就绪啦,可以调用recfrom去读了,而异步IO模型会处理完您调用我时要求我做的所有工作。

在这里插入图片描述
总结:异步IO的优化思路解决了应用程序需要两步操作:询问数据是否已经准备好,调用recfrom去读已经准备好的就绪数据。在异步IO模式下,值需要发送一次请求就可以完成状态的询问和数据拷贝的所有操作。

再谈IO模型里面的同步异步

IO阻塞就是调用recfrom读取数据成功之前,不会返回结果,期间,一直处于等待状态,不能干别的事。没有立即返回结果

IO非阻塞就是调用recfrom读取数据时,会立刻返回一个结果。线程可以去干别的事。

总结

数据就绪,数据还在内核空间,select只是判断内核中的数据是否就绪,当数据就绪后,再告诉应用线程调用recfrom去读。
数据可读,数据已经被系统复制到了用户空间。应用线程调用recfrom,系统将内核空间中的数据复制到用户空间。

阻塞IO:调用recfrom读取数据,会一直等待可读结果或异常返回。期间不能干其他的事。

非阻塞IO: 调用recfrom读取数据,可以去看其他的事情,因为调用recfrom会立马得到结果(行还是不行)。但需要应用线程不断去recfrom判断数据是否可读(内核中的数据已复制到用户空间中)。

多路复用IO:针对并发,当有大量并发请求,不可能去为每个请求创建个应用线程去直接读数据,而是创建一个或多个线程先去监视这些请求,当等到数据为就绪状态,监视线程再去通知应用线程去读取数据。解决并发问题。

信号IO:多路复用的监视数据状态是通过轮询的方式判断数据是否就绪。而信号IO是通过信号将每个数据与之对应的应用线程关联起来,当信号所在的数据为就绪状态时,就回调通知应用线程去读。解决了轮询的问题。

异步调用:不论是多路复用还是信号IO,都是应用程序先发起一个请求先判断数据是否就绪,数据就绪之后再去通知应用线程去读,应用线程再去调用recfrom去读数据。需要两步。异步让应用线程只需要调用一次read,告诉内核我要读取数据,内核完成询问数据是否就绪,读数据(将数据从内核空间复制到用户空间)。

https://www.zhihu.com/collection/518470152

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值