1.概念
io
io:文件流的输入输出。网络IO,磁盘IO,都是io,一般只有内核才能访问。用户软件需要调用接口才能访问。
网络IO
- 等待网络数据到达网卡->读取到内核缓冲区
- 从内核缓冲区复制数据 –> 用户空间
磁盘IO
- 当进程IO系统调用后,内核会先看缓冲区中有没有相应的缓存数据。
- 如果没有的话,再到设备中读取,因为设备IO一般速度较慢、需要等待。
- 如果内核缓冲区有数据,则直接复制到用户空间
缓存IO
数据会先复制到内核,最后才从内核复制到用户进程。
- 缺点,重复了一次复制,
- 优点:如果有一样的数据重新操作,直接利用缓存
内核空间和用户空间
- 内核空间:即可以访问用户空间也可以访问内核空间,主要用户直接访问io的读写
- 用户空间:只能访问用户空间的内容,访问io只能调用内核api
同步与异步
同步:当调用方法时,只要没有返回会一直等待,直到返回才结束。(当等待过久操作系统有权切换cpu去执行其他进程) 异步:当调用方法时,立即返回,不用等待返回的结果,但是调用者会主动定期的轮询访问是否完成。(消耗cpu资源,cpu也不能切换其他进程)
2. 5种IO的模式
- 同步: 阻塞/非阻塞/多路复用/信号驱动 IO
- 异步: 异步IO
blocking 阻塞
用户进程 -> 调用内核方法 -> 内核无数据
-> 内核开始准备数据
-> 准备好数据
-> 内核复制数据到用户态
-> 内核复制完成
用户进程 <- 返回成功
用户进程需要等待内核"准备数据"和"复制数据到用户态"这两步。
- 优点:如果时间过去cpu可以切换到其他进程处理任务。
- 缺点:是完全阻塞,期间需要等待很久。
Non Blocking 非阻塞
用户进程 -> 调用内核方法/返回失败 -> 内核无数据
用户进程 <-> 返回失败 -> 内核开始准备数据
用户进程 <-> 返回失败 -> 准备好数据
用户进程 <-> 返回失败 -> 内核复制数据到用户态
用户进程 <-> 返回失败 -> 内核复制完成
用户进程 <-> 返回成功
用户进程发起read,然后直接返回失败,后续不断轮询请求是否生成
- 优点:用户请求完可以立马去操作别的事情
- 缺点:由于是轮询,完成的时候会比实际慢,cpu频繁轮询,导致不能切换进程
multiplexing 多路复用/事件驱动
非阻塞是当前用户进程不断轮询检查。多路复用是专门交给一个进程的统一处理状态检查。一次可以处理多个IO,一次性返回。
常见的有 select、poll、epoll函数。
- 用户进程先依赖select,等内核数据准备好,select触发可读条件,
- 用户进程再把内核态复制到用户态(这里是阻塞)
select -> 调用内核方法 -> 内核无数据
-> 内核开始准备数据
用户进程 <- 返回可读条件 -> 准备好数据
用户进程 -> 系统调用 -> 内核复制数据到用户态
-> 内核复制完成
用户进程 <-> 返回成功
- 优点:当连接数大的时候,性能就很好
- 缺点:当连接数小的时候,由于另外多调用了select函数效果比非阻塞还要差。
signal dirver 信号驱动
- 用户进程先注册SIGIO和回调方法,等待内核处理好数据发送SIGIO信号,触发注册回调的方法。
- 用户进程再把内核态复制到用户态(这里是阻塞)
用户进程 <-> 注册SIGIO/同时立即返回 -> 内核无数据
-> 内核开始准备数据
信号处理的函数 <- 发送SIGIO信号 -> 准备好数据
用户进程 -> 系统调用 -> 内核复制数据到用户态
-> 内核复制完成
用户进程 <-> 返回成功
asyn 异步
aio_read -> 调用内核方法/立即返回 -> 内核无数据
-> 内核开始准备数据
-> 准备好数据
-> 内核复制数据到用户态
-> 内核复制完成
信号处理 <- 调用aio_read的指定的信号
返回成功&用户进程处理
用户进程发起aio_read,立即返回,继续做其他事情 内核会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,内核最后通过aio_read信号 触发方法,返回成功,用户进程完成调用。