5种IO模型分别是阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动的IO模型、异步IO模型
IO (Input/Output,输入/输出)即数据的读取(接收)或写入(发送)操作,通常用户进程中的一个完整IO分为两阶段:用户进程空间<–>内核空间、内核空间<–>设备空间(磁盘、网络等)。IO有内存IO、网络IO和磁盘IO三种,通常我们说的IO指的是后两者。网络I/O就是通过网络进行数据的拉取和输出。磁盘I/O主要是对磁盘进行读写工作。
LINUX中进程无法直接操作I/O设备,其必须通过系统调用请求kernel来协助完成I/O动作;内核会为每个I/O设备维护一个缓冲区。
对于一个输入操作来说,进程IO系统调用后,内核会先看缓冲区中有没有相应的缓存数据,没有的话再到设备中读取,因为设备IO一般速度较慢,需要等待;内核缓冲区有数据则直接复制到进程空间。
所以,对于一个网络输入操作通常包括两个不同阶段:
虚拟内存被操作系统划分成两块:内核空间和用户空间。
为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。
内核空间是内核代码运行的地方,
用户空间是用户程序代码运行的地方。
当进程运行在内核空间时就处于内核态,当进程运行在用户空间时就处于用户态
首先同步和异步是对于被调用方来说的,比如说你陪你女朋友去逛商场这个方法,然后看到某家美甲店,她要进去作美甲,这时候你女朋友有两个选择
1.做完美甲在美甲店等你来接她。
2.做完美甲打电话给你让你来接她。
这期间你自己可以选择是在店里等他还是选择去哪里先逛,你女朋友就不管你了,爱干啥干啥去。对于她自己来说,她选择做做完美甲在店里等你,那就是一个同步操作,她的时间和状态停留在了做美甲的前一刻,接下来要和你同步继续逛商场。
选择做完打电话给你就是一个异步操作。
还是上面那个例子,阻塞与非阻塞是对于调用方来说的,也就是你自己的选择。女朋友要做美甲,你也有连个选择
1.在店里等他
2.闲得无聊就先去商场晃晃呗。
你在店里等他就是阻塞住了,啥事也干不了,就等她做美甲了,如果先去商场逛,那就美滋滋了,那就是非阻塞的了。
基于同步,异步,阻塞和非阻塞,还有标准IO的两阶段读取,IO发展出了这么几种模型。
– 阻塞 I/O(blocking IO)
– 非阻塞 I/O(nonblocking IO)
– I/O 多路复用( IO multiplexing)
– 信号驱动 I/O( signal driven IO)
– 异步 I/O(asynchronous IO)
当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除block状态。
在这里插入图片描述
进程发起IO系统调用后,进程被阻塞,转到内核空间处理,整个IO处理完毕后返回进程。操作成功则进程获取到数据
进程发起IO系统调用后,如果内核缓冲区没有数据,需要到IO设备中读取,进程返回一个错误而不会被阻塞;进程发起IO系统调用后,如果内核缓冲区有数据,内核就会把数据返回给进程。
对于上面的阻塞IO模型来说,内核数据没准备好需要进程阻塞的时候,就返回一个错误,以使得进程不被阻塞。
在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。
多个的进程的IO可以注册到一个复用器(select)上,然后用一个进程调用该select,,select会监听所有注册进来的IO。
如果select没有监听的IO在内核缓冲区都没有可读数据,select调用进程会被阻塞;而当任一IO在内核缓冲区中有可数据时,select调用就会返回;而后select调用进程可以自己或通知另外的进程(注册进程)来再次发起读取IO,读取内核中准备好的数据。
典型应用: select、 poll、 epoll三种方案,nginx都可以选择使用这三个方案
select、poll、 epoll在Linux中IO复用 的实现方式主要有
select, poll和epoll
Select:注册IO、阻塞扫描,监听的IO最大连接数不能多于FD_ SIZE(1024);
Poll:原理和Select相似,没有数量限制,但IO数量大扫描线性性能下降;
Epoll :事件驱动不阻塞, mmap实现内核与用户空间的消息传递,数量很大,Linux2.6后内核支持
在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用IO读写操作来进行实际的IO请求操作。这个一般用于UDP中,对TCP套接口几乎是没用的,原因是该信号产生得过于频繁,并且该信号的出现并没有告诉我们发生了什么事情。
当进程发起一个IO操作,会向内核注册一个信号处理函数,然后进程返回不阻塞;当内核数据就绪时会发送一个信号给进程,进程便在信号处理函数中调用IO读取数据
当进程发起一个IO操作,进程返回(不阻塞),但也不能返回结果;内核把整个IO处理完后,会通知进程结果。如果IO操作成功则进程直接获取到数据
注意:
此模型和前面模型最大的区别是:前4个都是阻塞的,因为需要自己把用户准备好的数据,放在我的用户空间,而全异步都帮我们做好了。
用户线程完全不需要关心实际的整个IO操作是如何进行的,只需要先发起一个请求,当接收内核返回的成功信号时表示IO操作已经完成,可以直接去使用数据了。它是最理想的模型。
我觉得可以这样理解,阻塞与非阻塞是针对IO操作,根据IO操作是否立即返回来决定是否是阻塞操作;同步异步是针对进行IO操作的线程,线程是否立即返回来决定是否是同步异步操作。