0x01 什么是IO?
IO
模型中,先讨论下什么是IO
?
IO
在计算机中指的就是Input/Output
(输入/输出)。Input/Output
(输入/输出)的内容当然就是data
(数据)了。
那么数据被Input
到哪,Output
到哪呢?
-
Input
(输入)数据到内存中,Output
(输出)数据到IO
设备(磁盘、网络等需要与内存进行数据交互的设备)中; -
IO
设备与内存直接的数据传输通过IO
接口,操作系统封装了IO
接口,我们编程时可以直接使用
0x02 阻塞/非阻塞与同步/异步
阻塞/非阻塞
针对的对象是调用者自己本身的情况
☞ 阻塞
指调用者在调用某一个函数后,一直在等待该函数的返回值,线程处于挂起状态。
☞ 非阻塞
指调用者在调用某一个函数后,不等待该函数的返回值,线程继续运行其他程序(执行其他操作或者一直遍历该函数是否返回了值)
同步/异步
针对的对象是被调用者的情况
☞ 同步
指的是被调用者在被调用后,操作完函数所包含的所有动作后,再返回返回值
☞ 异步
指的是被调用者在被调用后,先返回返回值,然后再进行函数所包含的其他动作。
0x03 五种IO模型
recvfrom
/recv
都是操作系统的内核函数,用于从(已连接)socket
上接收数据,并捕获数据发送源的地址。
recv
函数原型:ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags)
sockfd
:接收端套接字描述符buff
:用来存放recv函数接收到的数据的缓冲区nbytes
:指明buff的长度flags
:一般置为0
阻塞IO(BIO)

recv
函数默认是阻塞的,直到对方socket
发送过来的数据被填充到用户提供的接收缓冲区(buff
参数)之后,才解除阻塞。
对方socket
发送过来的数据最先到达内核空间的缓冲区,然后从内核空间拷贝到用户空间提供的接收缓冲区(buff
),以供用户态程序使用。
从等待对方发送数据到接收数据,并把数据从内核空间复制到用户空间的这段时间,recv
函数都是阻塞的。
非阻塞IO

可用fcntl
函数设置recv
为非阻塞方式。
设置为非阻塞模式后,即使没有数据到来,recv
函数也不会阻塞,而是立即返回-1
,错误代码为EWOULDBLOCK
。为了得到对方发送的数据,只能不停的判断recv
返回值是否为-1
,以及返回码是否为EWOULDBLOCK
,直到数据到来,然后把数据从内核空间复制到用户态空间,复制完成后recv
函数返回。
非阻塞IO
模型的应用范围较窄,因为当数据没有到来时需要不断的循环测试recv
的返回值和返回码,这会消耗CPU
资源,于是把这种循环测试称作忙等待。换句话说就是这种模型没啥用。
IO多路复用

这种模型其实和BIO
是一模一样的,都是阻塞的,只不过在socket
上加了一层代理select
,select
可以通过监控多个socekt
是否有数据,通过这种方式来提高性能。
一旦检测到一个或多个文件描述有数据到来,select
函数就返回,这时再调用recv
函数就不会阻塞了,数据从内核空间拷贝到用户空间,recv
函数返回。
信号驱动IO

在用户态程序安装SIGIO
信号处理函数(用sigaction
函数或者signal
函数来安装自定义的信号处理函数),即recv
函数。然后用户态程序可以执行其他操作不会被阻塞。
一旦有数据到来,操作系统以信号的方式来通知用户态程序,用户态程序跳转到自定义的信号处理函数。
在信号处理函数中调用recv
函数,接收数据。数据从内核空间拷贝到用户态空间后,recv
函数返回。recv
函数不会因为等待数据到来而阻塞。
这种方式使异步处理成为可能,信号是异步处理的基础。
异步IO

异步IO
的效率是最高的。
异步IO
通过aio_read
函数实现,aio_read
提交请求,并递交一个用户态空间下的缓冲区。即使内核中没有数据到来,aio_read
函数也立刻返回,应用程序就可以处理其他的事情。
当数据到来后,操作系统自动把数据从内核空间拷贝到aio_read
函数递交的用户态缓冲区。拷贝完成以信号的方式通知用户态程序,用户态程序拿到数据后就可以执行后续操作。
异步IO
和信号驱动IO
的不同?
在于信号通知用户态程序时数据所处的位置。异步
IO
已经把数据从内核空间拷贝到用户空间了;而信号驱动IO
的数据还在内核空间,等着recv
函数把数据拷贝到用户态空间。异步
IO
主动把数据拷贝到用户态空间,主动推送数据到用户态空间,不需要调用recv
方法把数据从内核空间拉取到用户态空间。异步IO
是一种推数据的机制,相比于信号处理IO
拉数据的机制效率更高。推数据是直接完成的,而拉数据是需要调用
recv
函数,调用函数会产生额外的开销,故效率低。
欢迎关注微信公众号(coder0x00)或扫描下方二维码关注,我们将持续搜寻程序员必备基础技能包提供给大家。
网友评论