I/O的概念
操做系统的分为socket的I/O还有用户界面的输入输出,通常一个输入操做分为两个不一样的阶段,1)等待数据准备好;2)从内核向进程复制数据
从理论上来说,阻塞I/O、非阻塞I/O、复用I/O、信号驱动I/O都是同步IO模型,异步I/O就是异步IO,同步I/O向应用程序通知的是I/O就绪事件,异步I/O向应用程序通知的是I/O完成事件。
同步和异步:都是针对应用程序和操做系统之间交互而言的。同步的话,若是上层应用不主动询问操做系统的话,操做系统是不会主动通知应用程序的。异步的话,操做系统会主动通知应用程序。
阻塞:若是条件不知足的话,操做系统将应用程序挂起或者休眠,此时应用程序处于阻塞状态,只有当数据准备好以后或者数据已经从内核空间拷贝到用户空间,操做系统通知线程,此时唤醒线程。
非阻塞:若是条件不知足的话,该方法不会阻塞当前线程,而是当即返回一个标志信息(数据尚未准备好),或者timeou以后返回,此时应用线程还能够进行其余的操做。
在linux中并不存在真正的异步I/O,而是经过多路复用I/O的方式去模拟异步IO
注意:同步并不等同于阻塞,同步调用的时候,即使调用不返回,可是应用线程还处于运行状态,能够进行其余的操做,而阻塞调用时,应用线程处于阻塞状态。
操做系统5种通讯模型
解释:应用程序会调用一个IO函数,致使应用程序阻塞,程序会一直停留在此处,若是数据准备好了,内核会将数据拷贝至应用程序
伪代码:
{ read(socket, buffer); process(buffer); }
特色:当处理socket数量较少的时候,使用阻塞式I/O比较合适,当socket数量较大,为每个socket都分配一个读线程、处理线程以及同步的事件,那么对操做系统的开销会变得很大,而且不支持大数据量的socket连接
解释:非阻塞IO就是经过反复调用IO函数,若是没有数据,那么会当即返回一个错误的接口,不断去调用,不断去轮询,查看是否能够获取数据,一般来讲,这种比较耗费大量的cpu时间,在数据拷贝阶段,仍是阻塞的,原理就是应用程序在socket非阻塞的时候,告诉操做系统,不要在没有数据的时候把我挂起或者休眠,而是返回一个错误的信号
伪代码:
{ while(read(socket, buffer) != SUCCESS) ; process(buffer); }
特色:不容易使用,须要编写更多的代码,若是非阻塞IO在控制创建多个链接,在数据的收发量不均,时间不定的状况下,更具优点
解释:主要就是select、poll和epoll,有了I/O复用,咱们可使用select或者poll或者epoll,两次调用,两次返回,相对于阻塞I/O没有什么优越性,关键是能实现同时对多个IO端口进行监听,会使进程阻塞,能够同时阻塞多个I/O操做。
多路复用I/O模型是目前使用比较多的模型,所谓多路复用,能够理解为在一个应用线程里面能够处理多个socket链接,或者同一个端口接受来自多个客户端的请求(链接、读、写),多路复用I/O与传统的非阻塞IO的区别在于,在传统的非阻塞I/O中,是由用户去轮询socket状态,而在多路复用I/O中,由内核去轮询向select注册的socket的状态,因此多路复用IO须要操做系统的支持。
最大的优点是,在同一个线程中,能够同时处理多个socket的IO请求,在同步阻塞模型中,必须经过多线程的方式才能够达到这个目的。while循环以前,将socket添加到select监视中,而后while循环一直调用select获取被激活的socket,一旦socket可读,便调用read函数将socket中的数据读取出来。可是每一个IO请求的过程仍是阻塞的(在select函数上阻塞),平均时间甚至比同步阻塞IO模型还要长。
伪代码:
{ select(socket); while(true){ sockets = select(); for(socket in sockets){ if(can_read(socket)){ read(socket, buffer); process(buffer); } } } }
特色:使用场景:
1)当客户处理多个描述符(一般是交互式输入和socket)时,必须使用I/O复用
2)一个客户同时处理多个socket是可能的
3)若是一个TCP服务器既要处理监听socket,又要处理已经链接的socket,通常就要使用I/O复用
4)若是一个服务器既要处理TCP,又要处理UDP,通常就要使用I/O复用
解释:首先咱们容许socket进行信号驱动I/O,而且安装一个信号处理函数,进行继续运行不阻塞,当数据准备好以后,进程收到一个SIGIO信号,能够在信号处理函数中调用I/O操做函数处理数据
特色:这种模型的优点在于等待数据包到达期间,进程不会被阻塞,只须要等待信号处理函数的通知,既能够是数据准备好被处理,也能够是数据包已经准备好被读取
解释:先异步调用,可是不会当即返回数据,而是等待被调用部件完成后,经过状态、通知和回调来通知调用者的输入输出操做,这个与信号驱动I/O的区别在于:1)信号驱动I/O是由内核通知咱们如何启动一个I/O操做;2)而异步I/O模型是由内核通知咱们I/O操做什么时候完成,异步I/O模型才是最理想的I/O模型,在异步I/O模型中,当用户线程发起read操做以后,马上就能够开始去作其余的事情。而另一方面,从内核的角度,当它收到asynchronous read以后,它会马上返回,说明read请求已经成功发起,所以不会对用户线程产生任何block。而后内核会等待数据准备完成,而后将数据拷贝到用户线程,当这一切都完成以后,内核就会给用户线程发一个信号,告诉它read操做完成了。也就说用户线程彻底不须要实际的整个I/O操做是如何进行的,只须要先发起一个请求,当接受内核返回成功的信号时表示I/O操做已经完成,能够直接去使用数据了,异步I/O模型中,由操做系统内核去等待数据就绪,copy数据到用户空间,用户线程始终没有阻塞。
特色:咱们调用aio_read函数,给内核传递描述符、缓冲区指针,缓冲区大小和文件偏移,并告诉内核当整个操做完成的时候如何通知咱们。该系统调用后就当即返回,并且在等待I/O完成期间,咱们的进程不被阻塞
参考资料: