高阶IO
五种IO模型
1.阻塞IO
2.非阻塞IO
3.信号驱动IO
4.多路转接IO
5.异步IO
阻塞IO与非阻塞IO
阻塞IO | 非阻塞IO |
---|---|
为了完成功能发起调用,但是如果当前不具备完成条件,则等待。 | 为了完成功能发起调用,但是如果当前不具备完成条件,则立即报错返回 |
- 阻塞IO与非阻塞IO之间最大的区别在于当不具备完成条件时,是否立即返回
同步与异步
同步 | 异步 |
---|---|
为了完成功能发起调用但是如果当前不具备完成条件,则等待,直到完成。 | 为了完成功能发起调用,但是如果当前不具备完成条件,则立即返回(将完成功能的操作交给操作系统,当操作系统完成操作之后会以一些其他的方式(信号通知等)告诉我们功能完成) |
- 同步与异步最大的区别:当不具备完成功能的条件下,是否会阻塞完成。
多路转接(多路复用模型)
- 让别人替我们监控等待的整个过程,看现在有哪一个就绪好了,直到完成相应的操作。
使用多路转接模型替用户完成监控多个描述符的等待过程,如果哪一个描述符就绪,则完成监控过程返回,并通知我们有描述符就绪,接下来用户直接读取数据即可。
信号驱动IO
非阻塞IO的实现
这里需要使用一个函数:fcntl()
使用的头文件:
#include <unistd.h>
#include <fcntl.h>
函数结构:
int fcntl(int fd, int cmd, ... /* arg */ );
//fd : 文件描述符
//cmd:
// F_GETFL 获取属性状态 (通过返回值返回当前属性)
// F_SETFL 设置属性状态 (替换原有属性为当前arg属性)
// arg: 要设置的属性信息
// O_NONBLOCK 设置描述符为非阻塞
// 返回值:F_GETFL:当前属性
// F_SETFL:成功:0 失败:-1
多路转接模型的实现
使用函数:select()
使用这个函数需要使用的头文件
#include <sys/select.h>
#include <unistd.h>
函数的结构:
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
// nfds: 最大的描述符+1
// readfds: 可读事件的监控集合
// writefds: 可写事件的监控集合
// exceptfds: 异常事件的监控集合
// timeout: 超时等待事件
// NULL 没有描述符就绪则永久阻塞等待
// struct timeval
// tv_sec 秒
// tv_usec 微秒
// 返回值:<0:出错 ==0:等待超时了 >0 就绪的描述符个数
//还使用到了
//int FD_ISSET(int fd, fd_set *set)
//判断描述符是否存在于集合中
多路转接模型实现的过程描述
多路转接的功能:
- 对大量描述符进行事件阻塞监控,当描述符状态发生改变时,则返回
select:
- 功能:同时对大量描述符进行事件阻塞监控(可读 可写 异常),状态改变时返回
select:创建的三个描述符集合,分别对可读事件,可写事件,异常事件进行监控。
集合:
- 集合实际上就是一个位图,添加描述符实际上就是修改位图对应比特位,位图大小取决于FD_SETSIZE。向集合中添加描述符,对描述符关注什么样的状态就添加到对应的描述符集合中。
然后将集合中的数据拷贝到内核中,进行阻塞监控(间隔事件,轮询遍历,判断是否有描述福就绪)
就绪:
- 描述符对应缓冲区中数据大小/空闲空间大小是否大于低水位标记
若是编译没有描述符就绪,则继续休眠等待/判断是否超时,超时则返回0。
若是有描述就绪,将集合中没有就绪的描述符全部从集合中移除(集合中保留的都是就绪描述符)
因为对集合进行了修改,因此需要每次清空集合,向集合重新添加描述符)
遍历从0~maxfd的描述符,判断是否在集合中(目的:为了找到具体是哪一个描述符就绪)
对就绪描述符进行相应的操作
select优缺点
缺点 | 优点 |
---|---|
能够监控的描述符有最大上限(因为位图最大取决于FD_SETSZIE) | 跨平台 |
因为select判断集合中描述符就绪后会修改集合内容,因此需要每次重新添加描述符(编程麻烦,随着描述符的增多,效率会随之降低) | 超时时间的控制比较精细 |
因为select不会告诉我们具体哪一个描述符准备就绪,因此需要用户进行遍历判断(编程麻烦,随着描述符的增多,效率会随之降低) | |
因为select每次都需要将集合数据拷贝到内核,并且在内核是轮训遍历实现监控,因此(性能随着描述符的增多而降低) |
poll模型原理
缺点 | 相较于select优点 |
---|---|
因为select判断集合中描述符就绪后会修改集合内容,因此需要每次重新添加描述符(编程麻烦,随着描述符的增多,效率会随之降低) | 描述符无上限 |
因为select不会告诉我们具体哪一个描述符准备就绪,因此需要用户进行遍历判断(编程麻烦,随着描述符的增多,效率会随之降低) | 监控集合只有一个,每个节点可以关注不同事件,不用针对不同的事件进行多次遍历 |
因为select每次都需要将集合数据拷贝到内核,并且在内核是轮训遍历实现监控,因此(性能随着描述符的增多而降低) |
后续:
高阶IO 2