网络I/O_04 IO模型

一.本质

1.本质

        用户角度:网卡-网卡之间通信

        内核角度:socket-socket之间数据输入输出

2.与文件IO区别

(1)数据位置:

        文件:数据是已确定在磁盘上的,但是

        网络:只是连接了一个端口,是否有数据到达,未确定

(2)读取步骤:

        网络IO:需要先查,确定有数据才发起真正IO操作,2步

        文件IO:直接读,1步

3.问题与解决

(1)核心问题:查

        假设一个服务同时连接多个socket

        Q:怎么判断什么时候socket有数据到达?

        Q:是哪一个socket的数据到达?

(2)解决办法:

        方案1:轮询:        

                用户进程层面轮询,拿fd一个一个扔给内核帮忙查,查到了告诉我,我去拷贝

                内核层面轮询,用户进程扔一堆给内核,让内核轮询,哪个有结果了,通知去拷贝       (select,poll)

        方案2:基于信号(事件) epoll,异步

小知识:

        A.每个socket连接都返回一个fd,linux一切皆文件

        B.用户进程无法直接问网卡是否有数据,需要拿sokcet连接分配的fd问内核,内核帮我们去查,得到内核返回的结果,再进行下一步

        C.用户进程问内核,用户态到内核态切换,剩下交给内核处理

二、IO模型

1.IO生命周期

        一次IO生命周期一般涉及两个阶段与两个对象

(1)两个阶段与两个对象

        对象:用户进程和内核进程,

        阶段:网卡-内核阶段,内核-用户阶段(针对接收,发送反之)

(2)周期

        发送数据: 用户空间->内核->网卡-网络

        接收数据: 网络->网卡->内核->用户空间

        A.用户进程创建一个socket连接,然后开始把fd传给内核,开始监听数据

        B.socket有一个接收缓冲区:recive buffer和写缓冲区writh buffer, 一个数据包到达网卡(NIC)时,首先会以DMA(存储器直接访问, 允许在 外部设备存储器 之间直接读写数据)的方式把网卡的帧写入到内存,然后向CPU发送一个软中断,告诉内核去处理下,内核这是就会用select,poll,epoll_wait等来收包

        C.内核监测到哪个fd有数据包到了,再通知用户,用户再调用read()系统调用读取数据

  (3)接收的具体流程是:先等后取

        A:用户进程在创建socket连接时会返回一个fd,然后将fd加入到内核监听集合fds,有数据包到,内核会收到软中断信号,去遍历fds,看是哪个fd数据到达,然后标记好通知用户进程,

B:用户进程等到通知后,再次发起read()系统调用。内核去拷贝该socket的数据到用户进程缓存区,再通知用户进程,可以访问数据了

(4)小结:网络IO生命周期,先等后取

2.IO模型

        如何批量处理socket数据到达,常见5种IO模型:

        阻塞IO

        非阻塞IO

        多路复用IO

        信号驱动IO

        异步IO

(1)阻塞IO:(linux默认所有IO都是阻塞的)

        kernel会检查socket是否有数据到达,如果fd数据还没到达,kernel会一直阻塞,直到有数据才返回。

        这是同步阻塞(BIO)模型

(2)非阻塞IO

        kernel检查fd的数据到了没,如果fd数据还没到达,返回error给kernel。然后过段时间,kernel再来问,不停轮询,直到有数据,结果返回给用户。

这是同步非阻塞(NIO)模型:是用户进程不停循环fds问kernel有没数据。

(3)多路复用IO

        多个IO可以注册到一个复用器上,用户进程问kernel,一组fds的数据到了没,fds数据都没到达,kernel一等。任一fd数据到达,返回结果通知用户进程拷贝这个fd的数据。

循环fds有:select,poll这两种多路复用模型。

 另一种:epoll则是把每个fd都注册一个回调函数,当一个fd有数据到达,回调通知这个fd的进程,过来读数据

(4)信号驱动IO

        在信号驱动IO模型中,当⽤户线程发起⼀个IO请求操作,会给对应的socket注册⼀个信号函数,然后⽤户线程会继续执⾏,当内核数据就绪时会发送⼀个信号给⽤户线程,⽤户线程接收到信号之后,便在信号函数中调⽤IO读写操作来进⾏实际的IO请求操作。这个⼀般⽤于UDP中,对TCP套接⼝⼏乎是没⽤的,原因是该信号产⽣得过于频繁,并且该信号的出现并没有告诉我们发⽣了什么事情

该模型很少使用

这是同步非阻塞(NIO)模型

前4中都可以归结为:同步IO

从整个IO过程来看,他们都是顺序执行的,进程主动等待且向内核检查状态

(5)异步IO

        当进程发起一个IO操作,马上得到内核返回,可以先去处理其他事情了,注意数据可能不是马上得到;内核会一直查询fd数据是否到达,到达之后,顺便把数据拷贝到用户和内核都能访问得内存共享缓存区,然后告诉用户进程,可以直接拿到数据了。

实际上是信号驱动+拷贝数据。

这是异步非阻塞模型(AIO)

读写操作由内核完成,完成后内核将数据放到指定用户进程的缓冲区,通知应用程序来取

        扩展:

        Windows的IOCP实现了真正的异步IO

        Linux系统下,异步IO模型在2.6版本才引入,目前并不完善,其底层实现仍使用epoll

        大多数的高并发服务器端的程序,一般都是基于Linux系统的,因此大多还采用IO多路复用模型

3.Java 中的BIO,NIO,AIO

(1)BIO:同步阻塞IO

        卡那里的问题:用户进程-内核-socket

        A. 用户进程卡在内核这里(同步)

        B. 内核卡在socket这里(阻塞)

(2)NIO: I/O 多路复用模型

        卡那里的问题:用户进程-内核-socket

        A. 用户进程卡在内核这里(同步)

        B. 内核不卡在socket这里(非阻塞,kernel定时轮询)

        select,poll

(3)AIO:异步非阻塞

异步 IO 是基于事件和回调机制实现

epoll

先理解这几个概念:

异步:用户进程-(内核-socket)(用户马上可以得到内核查询socket有没数据的结果,然后去处理其他事情)

4.同步和异步:

(1)用户有没第一时间获取查询结果。用户进程-内核之间

        同步:用户拿fd问内核,这个fd有数据了没?内核查看fd的数据,有,返回给用户。若内核查到fd没数据,让用户进程等着,先不返回。定期再次查fd,循环往复,直到有数据才返回给用户进程。用户进程收到消息,再次发起读取系统调用,让内核拷贝数据到用户缓存区。

        异步:用户拿fd问内核,这个fd有数据了没?内核查看fd的数据,有,返回给用户,没有返回err。用户若得到err,就先不等了,先去处理其他事情。剩下得事情交给内核了(fd数据到达,会主动通知用户)

(2)区别:

返回结果得时机和复制fd数据的时机

同步是内核告诉用户进程数据来了。用户需再次发起调用,内核去复制fd的数据到用户缓存区

异步是内核直接fd的数据都给复制到了用户缓存区了,然后通知用户进程好了(缓存共享技术)。

5.阻塞和非阻塞:

(1)内核有没第一时间获取查询结果。内核-fd之间

阻塞:是指内核查fd数据是否到达,有数据才返回结果,否则一直在等待;

非阻塞:是指内核查fd数据是否到达,有数据返回结果,没数据返回err

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值