linux基本IO模型
Linux的IO模型
阻塞式IO模型(默认)
特点
- 结构简单容易同步
- 进程可能永远阻塞或阻塞时间过长
- 阻塞时进程效率低
产生阻塞的函数-读操作:
read、readv、recv、recvfrom和recvmsg
TCP协议以字节为单位,只要接收缓冲区中出现数据,进程被唤醒
UDP协议以数据报为单位,当完整的数据报到达时,进程被唤醒
产生阻塞的函数-写操作
write、writev、send、sendto和sendmsg
- 写操作发生阻塞的几率低于读操作
- UDP协议写操作永远不会阻塞
产生阻塞的函数-建立连接
connect
- 客户机TCP协议接收到服务器TCP协议返回的对SYN数据段的确认时,函数connect成功返回
- TCP协议的连接操作至少需要一个往返时间
- UDP的connect操作不产生连接,因此不阻塞
产生阻塞的函数-TCP协议接受连接
accept
阻塞式I/O模型的超时控制
调用alarm函数
- 超时到达时产生SIGALARM信号中断I/O函数阻塞,对于4种产生阻塞的函数均有效
- 多次调用alarm时,产生的SIGALARM信号无法区分是哪一次超时引发的,无法实现超时控制
设置socket选项
设置SO_RCVTIMEO和SO_SNDTIMEO选项,设置了这两个选项之后,所有的读写操作可以保证在超时范围内返回。只需设置一次选项,对以后的读写操作均有效
,不适用于accept
和connect
。
非阻塞IO模型
可以设置socket为非阻塞模式,在非阻塞模式socket上进行I/O操作时,如果操作不能完成,将以错误返回。4种I/O操作在非阻塞式socket下均不会阻塞。
一个示例如下图:
优缺点
优点:
- 不会产生阻塞
- 输入方式效率比较高
缺点:
- 长时间占用CPU
设置为非阻塞模式
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
其中request
参数类别实在很多,就不总结了。简单说,如果设置非阻塞,就这样写:
ioctl(sockfd,FIONBIO,&on);
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
同样,设置套接字为非阻塞IO的方法如下:
int flags;
flag=fcntl(sockfd,F_GETFL,0);//获取套接字sockfd的属性值
fcntl(sockfd,F_SETFL,flag|O_NONBLOCK);//将属性值或上非阻塞标志
对四种IO操作返回的错误
- 读操作——接收缓冲区无数据时返回EWOULDBLOCK
- 写操作——发送缓冲区无空间时返回EWOULDBLOCK;空间不够时部分拷贝,返回实际拷贝字节数
- 建立连接——启动3次握手,立刻返回错误EINPROGRESS;服务器客户端在同一主机上connect立即返回成功
- 接受连接——没有新连接返回EWOULDBLOCK
检查IO是否完成
- 轮询
select
函数
多路复用模型
简单说就是在等待的时候加入超时的时间,超时时间未到时和阻塞IO一致,到达超时时间后若仍然无数据就返回。
特点
- 只检查一个socket描述符时和阻塞式I/O模型类似,只是阻塞的位置不同,但效率低于阻塞式I/O模型
- 在多个socket描述符上进行I/O操作时效率高于阻塞式I/O
socket描述符读就绪
- 接收缓冲区中的数据量≥接收最低限度(默认接收限度为1(TCP为1字节,UDP为1个数据报),可以用SO_RCVLOWAT修改默认值)。
- 读通道被关闭,收到FIN字段
- 侦听socket的完成连接队列不为空
- 非阻塞式socket的connect操作过程中出现错误
socket描述符写就绪
- 发送缓冲区中可用空间≥发送最低限度(TCP默认发送最低限度为2048字节,可以用SO_SNDLOWAT修改默认值;UDP协议没有实际的发送缓冲区,其发送缓冲区空间总是大于发送下限,所以UDP socket总是写就绪)。
- 写通道被关闭。
- 非阻塞式socket的connect操作成功
socket描述符异常就绪条件
用于带外数据。
信号驱动IO模型
在进程开始的时候注册一个信号处理的回调函数,当函数到达时,发送信号SIGIO,回调函数进行处理。
特点
- 等待I/O操作可以进行的过程中不用阻塞,可以执行其他操作
- 程序结构简单
- 更适用于UDP协议
- TCP协议在很多环节上会产生SIGIO信号,难以区分产生信号的原因
- UDP只在收到数据包或错误时产生SIGIO信号
异步IO模型
与信号驱动IO模型类似,区别在于在数据复制完成时才发送信号,而信号驱动IO是数据到达就发送信号。