网络编程之五种I/O模型
一.概述:
在网络编程中,阻塞、非阻塞、同步、异步经常被提到,下面我先谈以下我所理解的在I/O中的阻塞、非阻塞、同步、异步。
五种I/O模型分别是阻塞式I/O,非阻塞式I/O,信号驱动,I/O复用(这四种是同步I/O),异步I/O。
我来举一个例子来解释这五种I/O模型:
有A,B,C,D,E五个人在钓鱼:
A用的是最老式的鱼竿,所以呢,得一直守着,等到鱼上钩了再拉杆。(阻塞式I/O)
B呢,它的鱼竿比较新,不怕鱼上钩后把鱼竿拽走,所以他一边看手机,一边时不时看一下鱼竿是否有鱼上钩,有的话就迅速拉鱼竿。(非阻塞式I/O)
C的鱼竿有个功能,能够显示是否有鱼上钩,所以呢,B就和旁边的MM聊天,隔会再看看有没有鱼上钩,有的话就迅速拉杆。(信号驱动)
D用的鱼竿和C差不多,但他想了一个好办法,就是同时放好几根鱼竿,然后守在旁边,一旦有显示说鱼上钩了,它就将对应的鱼竿拉起来;(I/O复用)
E是个有钱人(有钱人了不起啊),干脆雇了一个人帮他钓鱼,一旦那个人把鱼钓上来了,就给E发个短信。(异步I/O)
下面我先谈以下我所理解的在I/O中的阻塞、非阻塞、同步、异步。
二.I/O操作过程:
首先,进行I/O操作涉及到了硬件,所以就会和操作系统打交道。(从网卡这两个读取/发送数据到网络中是操作系统做的)所以,先解释以下我理解的I/O过程(总的来说是 等数据 和 数据搬迁 这两个过程)
首先,通过系统给我们的API函数调用I/O操作(此时在用户态),这个函数先等待I/O缓存中有数据,操作系统会监测I/O缓存(此时在内核态),当I/O缓存中接收到数据时,操作系统先通知这个函数调用(进入用户态),然后这个函数的实现中会调用一些已经包装好的函数," 这些 "函数会把数据从内核中拷贝到用户缓存区(先到内核态,再到用户态),然后这个函数就会返回。
具体如下图:
三.I/O操作中的阻塞、非阻塞、同步、异步:
(1).同步:同步就是在一个功能调用时,在这个调用没有得到结果之前,这个调用不会返回。(在I/O操作中,如调用read函数,这个函数会被阻塞在read函数调用处,但内核一直在做与read相关的事情,也就是这个函数是激活的(会占用cpu),只是从表面上看函数还没有返回而已)。
(2).阻塞:阻塞调用就是调用一个函数,这个调用结果没有得到结果之前,执行这个函数的线程会被挂起(这个状态下,cpu不会给该函数分配时间片),直到得到结果后才返回。
(3).非阻塞:是指如果一个函数不能立刻得到结果,这个函数不会阻塞该线程而立即返回。
(4).异步:是指在一个功能调用时,这个调用不会立即得到结果,也不会阻塞该线程,这个调用所在的线程会继续执行其它事情,而这个功能是由其它执行部件来完成,当这个部件执行完该功能时,通过状态,通知来通知调用者,或通过回调函数处理这个调用。
执行部件和调用者可以通过三种途径返回结果:
a. 状态、
b. 通知、
c. 回调函数。
使用哪一种依赖于执行部件的实现,除非执行部件提供多种选择,否则不受调用者控制。
a. 如果执行部件用状态来通知,
那么调用者就需要每隔一定时间检查一次,效率就很低
b. 如果是使用通知的方式,
效率则很高,因为执行部件几乎不需要做额外的操作。
c. 至于回调函数,
和通知没太多区别。
四.五种I/O模型:(理解了上面的知识,下面应该比较容易理解了)
(1).阻塞式I/O:
应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。 如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。
如下图:
(2).非阻塞式I/O:
在非阻塞I/O中,一定要把一个I/O操作函数放在一个死循环中,直到这个I/O函数返回一个正确值时才跳出这个循环。
我们把一个SOCKET接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将线程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间。
如下图:
(3).信号驱动:
首先要允许接口进行信号驱动I/O,然后安装一个信号处理函数,线程继续运行并不阻塞。当数据准备好时,线程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。
如下图:
(4).I/O复用:
I/O 复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的,这几个函数可以同时阻塞多个I/O操 作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。(比阻塞式I/O并没有上面优越性,但它实现了对多个I/O端口的监听)
如下图:
(5).异步I/O:
当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作。
如下图:
六.五种I/O模型比较:
七.总结:
理解五种I/O模型应该先理解I/O操作过程,并理解阻塞,非阻塞,同步,异步的一些知识。
I/O操作要和硬件打交道,所以会I/O操作过程会涉及到操作系统。
处理I/O操作分为两个过程,一是等待数据,二是把数据从内核中搬到用户空间。
这五中I/O模型的不同之处是在等待数据的方式上,在第二个过程中是一样的,都是把数据从内核中搬到用户空间,然后进行相关操作。