5种IO模型

五种IO模型

5种IO模型

同步和异步仅仅是关于所关注的消息如何通知的机制,而不是处理消息的机制。

也就是说:

同步情况下,是由处理消息者自己去等待消息是否被触发。

异步情况下,是由触发机制来通知处理消息者。

阻塞/非阻塞:线程所访问的数据是否就绪,线程是否需要等待;

同步/异步:访问数据的方式,同步需要主动读写数据,在读写数据的过程中还是会阻塞;异步只需要/O操作完成的通知,并不主动读写数据,由操作系统内核完成数据的读写。

---阻塞IO

5种IO模型

可以看到,当应用进程调用的数据还没有准备好的情况下,应用进程就会一直阻塞在这边。

---非阻塞IO

非阻塞模式的使用会浪费大量的cpu资源。因为要不断的询问。

5种IO模型

当一个应用程序使用了非阻塞模式,他需要使用一个循环来不断的询问数据是否准备好(轮询,polling),应用程序不停的polling来检查数据是否准备完成,这将是一个很浪费CPU资源的操作。实际使用中不是很普遍。

---  多路复用IO

5种IO模型 

select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。当用户进程调用了select,那么整个进程会被block,而同时,内核会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。

---信号驱动IO

5种IO模型

所谓信号驱动,就是利用信号机制,安装信号SIGIO的处理函数(进行IO相关操作),通过监控文件描述符,当其就绪时,通知目标进程进行IO操作(signal handler)。

为了在一个套接字上使用信号驱动 I/O 操作,下面这三步是所必须的。
(1)一个和 SIGIO信号的处理函数必须设定。
(2)套接字的拥有者必须被设定。一般来说是使用 fcntl 函数的 F_SETOWN 参数来
进行设定拥有者。
(3)套接字必须被允许使用异步 I/O。一般是通过调用 fcntl 函数的 F_SETFL 命令,O_ASYNC为参数来实现。

---异步IO

5种IO模型

用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,内核会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,内核会给用户进程发送一个signal,告诉它read操作完成了。

异步 I/O和信号驱动I/O的区别是:
       1、信号驱动 I/O 模式下,内核在操作可以被操作的时候通知给我们的应用程序发送SIGIO 消息。(刚开始)

       2、异步 I/O 模式下,内核在所有的操作都已经被内核操作结束之后才会通知我们的应用程序。(已结束)

5种IO模型

两者的区别就在于同步IO做”IO操作”的时候会将进程阻塞。

按照这个定义,之前所述的阻塞IO,非阻塞IO,IO多路复用、信号驱动都属于同步IO。有人可能会说,非阻塞 IO并没有被block啊。这里有个非常“狡猾”的地方,定义中所指的”IO操作”是指真实的IO操作,就是例子中的recvfrom这个system call。非阻塞 IO在执行recvfrom这个system call的时候,如果内核的数据没有准备好,这时候不会block进程。但是,当内核中数据准备好的时候,recvfrom会将数据从内核拷贝到用户内存中,这个时候进程是被block了,在这段时间内,进程是被block的。而异步IO则不一样,当进程发起IO 操作之后,就直接返回再也不理睬了,直到内核发送一个信号,告诉进程说IO完成。在这整个过程中,进程完全没有被block。

经过上面的介绍,会发现非阻塞IO和异步IO的区别还是很明显的。在非阻塞IO中,虽然进程大部分时间都不会被block,但是它仍然要求进程去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存。而异步IO则完全不同。它就像是用户进程将整个IO操作交给了他人(内核)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。

 

有的地方说IO多路复用、信号驱动两种IO模式属于异步IO,原因是IO多路复用进程是被select这个函数block,而不是被socket IO给block,但是仔细看看,IO多路复用、信号驱动在执行IO操作的时候(定义中所指的”IO操作”是指真实的IO操作,就是例子中的recvfrom这个system call系统调用)请求的时候,都是被阻塞的,因此IO多路复用、信号驱动属于同步IO。

其实异步操作是可以被阻塞住的,只不过通常不是在处理消息时阻塞,而是在等待消息被触发时被阻塞.比如select函数,假如传入的最后一个timeout参数为NULL,那么如果所关注的事件没有一个被触发,程序就会一直阻塞在这个select调用处.而如果使用异步非阻塞的情况,比如aio_*组的操作,当我发起一个aio_read操作时,函数会马上返回不会被阻塞,当所关注的事件被触发时会调用之前注册的回调函数进行处理.如果在银行等待办理业务的人采用的是异步的方式去等待消息被触发,也就是领了一张小纸条,假如在这段时间里他不能离开银行做其它的事情,那么很显然,这个人被阻塞在了这个等待的操作上面;但是呢,这个人突然发觉自己烟瘾犯了,需要出去抽根烟,于是他告诉大堂经理说,排到我这个号码的时候麻烦到外面通知我一***册一个回调函数),那么他就没有被阻塞在这个等待的操作上面,自然这个就是异步+非阻塞的方式了。

参考:https://blog.csdn.net/u010177010/article/details/50969543

参考:https://zhidao.baidu.com/question/1887604005316927828.html

参考:https://blog.csdn.net/lltaoyy/article/details/54861749