浅谈I/O模型

一:概念的理解: 

首先在网络编程的时候,我们常常见到同步/异步,阻塞/非阻塞四中调用方式:

1同步和异步主要针对C(client)端 

同步: 

所谓的同步,就是在c端发出一个功能调用时,在没有得到结果之前,该调用步返回,也就是说必须一件一件事做,等前一件事完了之后才做后一件事。 

如:普通的B/S模式(同步):提交请求->等待服务器处理->处理完毕返回,这期间客户端浏览器不能干任何事

异步: 

与同步相对。当C端一个异步过程调用发出之后,调用者不能立即得到结果,实际处理这个调用的部件在完成后,通过状态,通知和回调来通知调用者。 

如:请求通过事件触发->服务器处理(浏览器仍然可以做其他事情)->处理完毕

阻塞和非阻塞主要针对S端(server)

阻塞 

阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态,cpu不会分配时间片,线程暂停运行)函数只有得到结果返回。 

阻塞调用和同步调用的区别:对同步来说,很多时候当前线程还是激活的,只是逻辑上没有返回,如,在socket编程中调用recv函数,如果缓冲区没有数据,这个函数就会一直等待,直到有数据返回。而此前当前线程还有可能继续处理各种各样的消息。

阻塞的例子:比如去取A楼一层(假设是内核缓冲区)取快递,但是比不知道什么时候来,你有不能干别的事情,只能死等着但是可以睡觉(进程处于休眠状态),因为你知道快递把货送来时一定会给比大电话

非阻塞: 

非阻塞与阻塞概念想对应,指在不能立即得到结果之前,该函数不会阻塞当前线程,而会立即返回。

非阻塞的例子:还是等快递,如果用轮询的方式,每隔5分钟去A楼一层(内核缓冲区)去看快递来了没,没来,立即返回,如果快递来了,就放到A楼一层,等你去取。

对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但不是一一对应的。阻塞对象上可以有非阻塞的调用方式,我们可以通过轮询状态,在适当的时候调用阻塞函数,就可以避免阻塞,而对于非阻塞对象,调用函数可以进入阻塞调用,对于select: 

1:同步 

我客户端(C端调用者)一个功能,该功能没有结束前,我死等结果。 

2:异步,我(c端调用者)调用一个功能,不知道该功能结果,该功能有结果后通知我,即回调通知

同步和异步主要针对c端,但是跟s端不是完全没关系,同步和异步必须s端配合才能实现,同步和异步由c端控制,但是s端是否为阻塞还是非阻塞,c端不关心。

3:阻塞,就是调用我(s端被调用者,函数),我(s端被调用者,函数)没有完全接受完数据或者没有得到结果之前,我不会返回。

4:非阻塞,就是调用我(s端被调用者,函数),我(s端被调用者,函数)立即返回,通过select通知调用者

二:同步I/O和异步I/O 

同步I/O与异步I/O的区别在与数据访问的时候进程是否阻塞 

阻塞I/O与非阻塞I/O的区别在与:应该程序的调用是否立即返回。

阻塞和非阻塞是指server端的进程访问的数据如果尚未就绪,进程是否需要等待,简单说这相当于函数内部的实现区别,也就是未就绪时时直接返回还是等待就绪。

就同步和异步是指client端访问数据的机制,同步一般指主动请求并等待I/O操作完毕的方式,当数据就绪后再读写额时候必须阻塞,异步则指主动请求数据后便可以继续处理其他任务,随后等待I/O,操作完毕的通知。

三:五种I/O模型: 

1)阻塞I/O 

2)非阻塞I/O 

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

4)信号驱动I/O(SIGIO) 

5)异步I/O 

前四中是同步,最后一种是异步。 

阻塞I/O模型: 

简介:进程会一直阻塞,直到数据拷贝完成 

应用程序调用一个I/O函数,导致应用程序阻塞,等待数据准备好,如果数据没有准备好,一直等待。。数据准备好,从内核拷贝到用户空间,I/O函数返回成功

阻塞I/O模型图:在调用recv()/recvfrom()函数,发生在内核中等待数据和复制数据过程。 

《浅谈I/O模型》

当调用recv()函数时,系统首先检查是否有准备好的数据,如果数据没有准备好,那么系统就处于等待状态,当数据准备好后,将数据从系统缓冲区复制到用户空间,然后函数返回。在套接应用程序中,当调用recv()函数时,未必用户空间就已经存在数据,那么此时recv()函数处于等待状态 

非阻塞I/O模型: 

简介:我们把一个套接口设置为非阻塞就是告诉内存,当所请求的I/O操作无法完成时,不要进程睡眠,而是返回一个错误,I/O函数会不断的测试数据是否准备好,没有准备好,继续测试,直到数据准备好为止。在测试的过程中会占用大量的CPU时间。 

《浅谈I/O模型》

I/O多路复用模型: 

简介:主要是select和epoll;对于一个I/O端口,两次调用,两次返回,比阻塞I/O并没有什么优势,只是能实现同时对多个I/O端口进行监听。

I/O多路复用模型会调用select,poll函数,这几个函数也会使进程阻塞,但是和阻塞I/O不同的,这个函数可以同时阻塞多个I/O操作,而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。 

《浅谈I/O模型》

IO多路复用适用场合:

当客户端处理多个描述符时(请求)(一般是交互式输入和网络套接口),必须使用I/O复用

当一个客户端同时处理多个套接字时,此情况可能的但很少出现

当一个TCP服务器既要处理监听套接字,又要处理已连接套接字,一般也要用到I/O复用 (使用最多的场景)

当一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用

当一个服务器要处理多个服务或多个协议,一般要使用I/O复用


信号驱动I/O 

简介:两次调用,两次返回 

首先允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。 

《浅谈I/O模型》

异步I/O模型: 

简介:数据拷贝的时候进程无需阻塞 

当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态,通知和回调通知调用者输入输出操作。 

《浅谈I/O模型》

四:五种I/O模型的比较

《浅谈I/O模型》

五:I/O模型的具体实现

主要实现方式有以下几种:

Select:Linux实现对应,I/O复用模型,BSD4.2最早实现

Poll:Linux实现,对应I/O复用模型,System V unix最早实现  (改进版的select)

Epoll:Linux实现,对应I/O复用模型,具有信号驱动I/O模型的某些特性

Kqueue:FreeBSD实现,对应I/O复用模型,具有信号驱动I/O模型某些特性

/dev/poll:SUN的Solaris实现,对应I/O复用模型,具有信号驱动I/O模型的某些特性

Iocp  Windows实现,对应第5种(异步I/O)模型

六:select/poll/epoll的比较:

《浅谈I/O模型》

1,select

优点

Select:POSIX所规定,目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理 

缺点

单个进程可监视的fd数量被限制,即能监听端口的数量有限  cat /proc/sys/fs/file-max 

对socket是线性扫描,即采用轮询的方法,效率较低 

select 采取了内存拷贝方法来实现内核将 FD 消息通知给用户空间,这样一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大 

2, poll

本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态

其没有最大连接数的限制,原因是它是基于链表来存储的

大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义

poll特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd  (通知多次)

边缘触发:只通知一次,性能更好

3,epoll

在Linux 2.6内核中提出的select和poll的增强版本

优点:

支持水平触发LT和边缘触发ET,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就需态,并且只会通知一次

使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知

没有最大并发连接的限制:能打开的FD的上限远大于1024(1G的内存能监听约10万个端口)

效率提升:非轮询的方式,不会随着FD数目的增加而效率下降;只有活跃可用的FD才会调用callback函数,即epoll最大的优点就在于它只管理“活跃”的连接,而跟连接总数无关

内存拷贝,利用mmap(Memory Mapping,内存映射)加速与内核空间的消息传递;即epoll使用mmap减少复制开销

    原文作者:千纸鹤V
    原文地址: https://www.jianshu.com/p/78e8a0755c90
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞