阻塞非阻塞+同步异步
只针对网络IO而言
典型的一次IO的两个阶段:
- 数据就绪:根据系统IO操作的就绪状态
- 阻塞
- 非阻塞
- 数据读写:根据应用程序和内核的交互方式
- 同步
- 异步
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
//recv---内存缓冲区地址,将所接收数据存放的数组,长度,标记:一般置0
阻塞:调用IO方法的线程进入阻塞状态,不占用CPU
非阻塞:不会改变线程的状态,通过返回值去判断当前线程状态
对recv()而言:
- -1是出错了,有些是设置了特殊错误号
- 0是读到末尾了,对方连接关闭
- 大于0:读取的数据大小
数据就绪阶段一般发生在操作系统的内存缓冲区;数据读写阶段发生在应用程序
同步:调用网络IO进行数据读写的时候,应用程序主动向os的缓冲区读数据
因为数组是自己定义的,所以是我们自己主动去OS读数据
异步:OS提供api然后由操作系统读数据,是OS把数据存到提供的存放数组地址
效率高,但是编程复杂
API:
重要参数:sockfd,buf(存放数据),通知方式(用信号)
在处理 IO 的时候,阻塞和非阻塞都是同步 IO,只有使用了特殊的 API 才是异步 IO
int aio_read(struct aiocb *aiocbp);
int aio_write(struct aiocb *aiocbp);
//异步IO只和非阻塞连用,因为要保证程序执行其他任务,阻塞状态无意义
阻塞非阻塞和同步异步的总结
一个网络IO接口的调用会有两个阶段:数据就绪和数据读写
- 数据就绪分为阻塞和非阻塞:
-
阻塞就是当前线程挂起,但不影响CPU调度
-
非阻塞就是直接返回结果,当前进程状态不会改变
- 数据读写分为同步和异步
- 同步就是:应用程序主动从缓冲区读取数据
- 异步就是:给OS系统一个存取地址和通知方式,数据读写是os自己完成的,os完成后通知线程
IO多路复用:select,poll,epoll都是同步的数据读写方式,除了一些特殊网络接口,不管是阻塞还是非阻塞的数据就绪状态都是同步的。
Linux五种IO模型
1.阻塞 blocking
调用者调用了某个函数,阻塞地等待函数的返回,这期间什么都不做,只有函数返回后这个线程才能进行下一部动作。
2.非阻塞 non-blocking NIO
非阻塞IO执行系统调用总是立刻返回,如果没发生就返回-1,设置错误号EAGAIN;发生了就进行操作.
进程需要每隔一段时间就去检测一下IO事件是否就绪
3.IO复用 IO multiplexing
一次检测多个文件描述符,如果有数据可读可写,系统就直接对这个文件描述符进行操作
不管是阻塞还是非阻塞,系统一次只能进行一次IO操作,Linux通过select,poll,epoll实现IO复用,使得可以同时阻塞多个IO,可以进行多个读写IO函数的检测。
直到数据准备好后,有数据可读可写才真正执行IO操作函数。
4.信号驱动
安装一个信号处理函数,进程继续运行并不阻塞,当IO事件就绪,进程收到SIGIO 信号,然后处理 IO 事件。
注册一个信号处理函数,IO时间就绪,内核通过发送信号给进程,让进程处理IO读写操作
5.异步 asynchronous
Linux中,可以调用 aio_read 函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。
内核完成数据就绪和数据读写过程,最后只要通知进程完成了操作即可。