1、阻塞IO模型 (同步I/O)
阻塞IO是指进程进行IO操做的时候,由于数据没准备好或者缓冲区里没有空间而没法进行IO操做会进入睡眠,直到数据准备或者缓冲区有空间才回被唤醒的行为。阻塞IO是最通用的IO类型,全部套接字默认状况下都是阻塞的。
输入操做:read、readv、recv、recvfrom和recvmsg,调用这些输入函数之一,若是缓冲区没有数据可读,该进程会投入睡眠,直到有一些数据可达才被唤醒,唤醒以后把相应数据复制到接受缓冲区或者发送错误才返回。
输出操做:write、writev、send、 sendto 和 sendmsg,调用这些输出函数之一,若是发送缓冲区里面没有空间,进程也将进入睡眠,直到有空间为止才被唤醒,唤醒以后把相应数据写到发送缓冲区才会返回,返回值将是内核可以复制到该缓冲区中的字节数。
2、非阻塞IO模型 (同步I/O)
非阻塞IO使咱们进行IO操做时,不会由于这些操做阻塞,若是这个操做不能完成,则调用后马上出错返回。
把套接字设置成非阻塞的代码以下:
int set_nonblock(int fd)
{
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
return old_option;
}
输入操做:若是输入操做不能被知足(对于TCP套接字即至少有一个字节的数据可读,对于UDP套接字即有一个完整的数据报可读),内核中没有给返回相应的数据,那么,调用将当即返回一个-1错误。须要数据的话,咱们必须持续的调用这个函数(也就是所谓的循环接收这种循环接收并非阻塞,从而对CPU形成极大的浪费,也就是忙等,想要等待必定的数据,可是这些数据并无到来,而且还占用着CPU,不太常用这种IO模型)。从而将内核空间的值拷贝到用户空间。一旦拷贝完成了,输入函数就能够返回了,返回的值也就不是-1了。
输出操做:对于一个非阻塞的TCP套接字,若是其发送缓冲区中根本没有空间,输出函数调用将当即返回一个-1错误。若是其发送缓冲区中还有一些空间,返回值将是内核可以复制到该缓冲区中的字节数。
3、IO复用 (同步I/O)
IO复用是一种预先告知内核的能力,使得内核一旦发现指定的描述符集合中有一个或多个触发IO条件,它就会通知进程。这个能力叫作IO复用。
使用IO复用模型,咱们能够调用select或者epoll,阻塞在这两个系统调用(select、epoll_wait)之上,而不是阻塞在真正的I/O系统调用调用上(须要事先把监听的套接字设置成非阻塞)。能够同时对多个读操做,多个写操做的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操做函数。
注意:
当一个服务器 在处理多个客户时,绝对不能阻塞于单个客户相关的系统函数调用。不然致使服务器端程序被挂起,不能为其它客户提供服务。解决方法以下:
(1)使用非阻塞式I/O (2)对每一个客户有单独的控制进程提供服务 (3)对每一个I/O操做设置一个超时。
4、信号驱动式IO (同步I/O)
信号驱动式IO就是指进程预先告知内核,当某个描述符上发送事件时,内核使用信号通知相关进程。信号驱动式IO并无实现真正的异步,由于通知到进程以后,依然是由进程来完成IO操做。
对于TCP套接字:信号驱动式IO不适合处理TCP套接字,由于信号产生的过与频繁,在TCP中,链接请求完成、断开链接发起、断开链接完成、数据到达、数据送走。。。都会产生SIGIO。可是咱们真正只须要它在数据到达或者数据送走的时候才产生信号。
对于UDP套接字:SIGIO信号在数据报到达套接字以及套接字上发生异步错误才会发生。(UDP套接字推荐使用)。
对于一个套接字使用信号驱动式IO,要求进程执行如下三个步骤:
(1)创建SIGIO信号的信号处理函数。
(2)设置该套接字的属主,一般使用fcntl的F_SETOWN命令设置。
(3)开启该套接字的信号驱动式IO,一般使用fcntl的F_SETFL命令打开O_ASYNC标志完成。
5、异步IO
当一个异步过程调用发出后,调用者不能马上获得结果。实际处理这个调用的部件在完成后,经过状态、通知和回调来通知调用者的输入输出操做。
调用aio_read函数(POSIX异步IO函数一aio_或者lio_开头),给内核传递描述符、缓冲区指针、缓冲区大小(和read相同的三个参数)和文件偏移(与lseek相似),告诉内核当整个操做完成时,如何通知咱们。该系统调用马上返回,并且在等待IO完成期间,咱们的进程不被阻塞。
信号驱动式IO和异步IO的区别:
信号驱动式IO是进程事先创建SIGIO的信号处理程序(用sigaction设置IO信号的处理方式),有若是有数据到来,内核会给进程发一个信号,通知进程,进程捕获到这个信号就会去执行处理函数部分,通常在这个函数中执行IO操做(信号发生在IO以前,IO由进程完成)。
异步IO是有进程请求异步读操做,会把套接字描述符,缓冲区指针、缓冲区大小和文件偏移一块儿发给内核,若是有数据到来,内核会把数据拷到相应的位置,而后给进程发一个信号,若是数据没有来,也必须等数据来了,内核把数据拷完以后,才给进程发一个信号,由内核通知咱们IO操做什么时候完成(信号发生在IO以后,IO由内核完成)
阻塞与非阻塞的区别:
阻塞和非阻塞关注的是
进程在等待调用结果(消息、返回值)时的状态
。阻塞是指调用结果返回以前,当前进程会被挂起。调用进程只有在获得结果才会返回。非阻塞调用指不能马上获得结果,该调用不会阻塞当前进程。
同步与异步的区别:
同步与异步关注的是
进程之间的协做方式
,同步是A进程必须获得B进程通知才能去执行某件事(A执行),异步是指A进程通知B进程去执行而后马上获得返回,而后就能够去作本身的事,B完成以后会给A发一个通知(B执行)。
参考:
https://www.zhihu.com/question/19732473
http://blog.163.com/xychenbaihu@yeah/blog/static/13222965520112163171778/
http://blog.csdn.net/jay900323/article/details/18141217/
《UNIX网络编程》
《UNIX环境高级编程》