I/O介绍
I/O通常有内存IO、网络I/O、磁盘I/O等,但我们通常说的是网络I/O以及磁盘I/O。网络I/O本质是socket读取。
每次I/O氢气,都会有两个阶段:第一步:等待数据,即数据从磁盘到内存,将数据从磁盘文件先加载到内核内存空间(缓冲区),等待数据准备完成,时间较长。第二步:复制数据,即数据内核内存到进程内存,将数据从内核缓冲复制到用户空间的进程内存中,时间较短。
Web请求处理过程
1、客户端发起情况到服务器网卡
2、服务器网卡接收到请求和转交给内核处理
3、内核根据请求对应的套接字,将请求交给工作在用户空间的web服务进程
4、web服务进程根据用户请求,向内核进行系统调用,申请获取率相应资源(如:客户端获取图片)
5、内核发现web服务器进程请求的是一个存放在本地硬盘上的资源,因此通过驱动程序连接磁盘
6、内核调用磁盘,获取需要的资源
7、内核将资源存放在自己的缓存区,并通知web服务器进程
8、web服务器进程通过系统调用取得资源并将其复制到进程自己的缓存区中
9、web服务器进程形成响应,通过系统调用再次发给内核以响应请求
10、内核将响应发送至网卡
11、网卡发送响应给用户
通过这样一个复杂过程,一次请求就完成了
简单来说如下:
用户请求===》送达用户空间===》系统调用===》内核空间===》内核都怕磁盘上读取图片资源===》返回到用户空间===》响应给用户
上述简单的说明了一下,客户端向web服务器请求过程,在这个过程中,有两个I/O过程:一是客户端请求的网络I/O,二个事web服务器请求图片磁盘I/O.
I/O模型名词介绍
说到I/O模型,都会牵扯到同步、异步、阻塞、非阻塞这几个词,以下介绍这几个词的概念
阻塞和非阻塞
阻塞和非阻塞指的是执行一个操作时等操作结束再返回结果,还是马上返回结果。
阻塞(blocking):指IO操作需要彻底完成后才能返回到用户空间,调用结果返回之前,调用者被挂起(当前线程进入非可执行状态,在这个状态,cpu不会分配时间片,线程暂停运行)只有到结果才能进入活动状态;
阻塞例子:海底捞的服务器为你点菜,当你点完菜后,服务员把消息传到后厨,这时你就在餐桌上等待,直到厨师把和配菜准备好以后送到你桌上,你才能开吃。在上菜的过程中你还不能离开,因为你离开之后服务员
上菜却找不到你人,所以你就是等待,这时候你出入阻塞等待状态,前面说的你是调用者,你被挂起,进入了非可执行状态。
非阻塞(nonblocking):指I/O操作被调用后立即返回给用户一个状态值,无需等到I/O操作彻底完成,最终的调用结果返回之前,调用者并不会被挂起;
非阻塞例子:海底捞的服务器为你点菜,当你点完菜后,服务员把消息传到后厨,过了三分钟,你跑到后厨问,我的锅底或者肥牛卷好了没有?后厨说没好,然后你去处理其它事情,然后又过了五分钟,你又跑到后厨问,我的某个菜好了没有,如果没有,你还是继续做其他事情,然后等会再问一次,这个时候就是在I/O操作的同时,你没有被挂起,可以操作其他事情,但是如果I/O操作完成,你需要立马接受。
同步和异步
同步/异步关注的是消息通信机制
同步(synchronous):调用者等待被调用者返回消息,才能继续执行。同步阻塞例子:去餐馆吃饭,点了一个盖浇饭,然后在餐桌上一直等到
盖浇饭做好,自己端到餐桌就餐。这就是典型的同步阻塞。当厨师给你做饭的时候,你需要一直在那里等着。
同步非阻塞例子:
去餐馆吃饭,点了一个盖浇饭,你点饭之后,过了几分钟时间差不多了,就去问老板饭做好了没有,如果好了就去端,如果没好等一会再去问,实时同步做饭进度,依次循环去问直到饭做好,这就是同步非阻塞。异步(asynchronous):被调用者通过状态、通知或回调机制主动通知被调用者的运行状态.
I/O模型类型
IO模型分为以下五类
1、阻塞型:所有过程全阻塞
2、非阻塞型:如果没有数据buffer,即立即返回EWOULDBLOCK
3、I/O复用型(select和poll):wait和copy阶段分别阻塞
4、信号驱动型I/O(SIGIO):在wait阶段不阻塞,但copy阶段阻塞(信号驱动I/O),即通知
5、异步I/O(AIO):完全阻塞方式,当I/O完成时提供信号
说明:应用程序调用一个IO的recvfrom函数,会导致应用程序阻塞,进入阻塞状态后直到I/O操作结束才会返回;如果系统内核数据没有准备好,那就一直等待数据准备,因为是调用了rexvfrom函数导致了应用程序阻塞,所以一直在等,做不了任何事情,内核数据准备好之后把数据从内核拷贝到用户空间,拷贝结束后,I/O函数返回成功提示。注:其阻塞时在I/O操作阶段。
非阻塞I/O
说明:用户线程发起IO请求时立即返回。但并未读取到任何数据,则返回字段为“EWOULDBLOCK”,用户线程需要不断地发起IO请求,直达数据到达后,才能读取到数据,继续执行。即“轮询”机制。整个IO请求过程,虽然用户线程每次发起IO请求后可以立即返回,但是为了等到数据,然需要不断地轮询、重复请求、消耗了大量的CPU资源;是比较浪费CPU的方式,一般很少用到这种模型,而且在其他模型中使用非阻塞IO这一特性。
I/O复用(select和poll)
说明:I/O复用模型会用到select或者poll函数,在I/O复用模型中,并不是阻塞到I/O操作过程中,而是阻塞到select或者poll函数中,以select为例,进程在select处阻塞,等待几个描述符中的一个变为可操作,如果没有等待到就继续阻塞在第一阶段,如果等到了一个描述符变为可操作,则调用recvform函数将数据拷贝大应用缓冲区。
信号驱动I/O(SIGIO)
说明:首先,我们允许套接接口进行信号驱动I/O,并安装一个信号处理函数SIGIO,并安装一个信号处理函数SIGIO,如果数据没有准备好,则立即返回结果,进程继续工作并不阻塞。当数据准备好时,系统内核会主动发送一个SIGIO信号给应用程序,应用程序接受到信号后,可以在信号处理函数中调用I/O操作函数recvform进行数据处理。信号驱动I/O模型的优点是当数据报到达时,可以不阻塞,主循环可以继续执行,只是等待处理程序的通知,或者数据已经准备好处理,或者数据报已经准备好被读了。
异步I/O(AIO)
说明:
当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态通知和回调通知来告诉调用者的输入输出操作。 用户可以直接对I/O执行读写操作,这些操作告诉内核用户读写缓冲区的位置,以及I/O操作完成之后内核通知应用程序的方式,就是上面讲的通过状态通知或者回调通知来告诉调用者。异步I/O的读写操作总是立即返回,但没有返回结果说是否阻塞,因为异步I/O操作真正的读写操作已由内核接管,内核自己对数据处理完成后生成一个信号,然后通知用户刚才交给自己的事件已经处理完成。
五种I/O模型的总结及比较
从两张图中我们可以看到,越往后,阻塞越少,理论上效率也是最优。其中五种I/O模型中,前三属于同步I/O,后者属于异步I/O.
同步I/O
阻塞I/O 非阻塞I/O I/O复用(select 和poll)
异步I/O
信号驱动I/O(SIGIO) 半异步 异步I/O(AIO) 全异步
异步I/O和信号驱动I/O的区别
信号驱动I/O模式下,内核可以复制的时候通知给我们应用程序发送SIGIO信号,异步I/O模式下,内核在所用的操作由内核操作完成后才会通知我们的应用程序。