很多人一开始盲目的认为同步就是阻塞,异步就是非阻塞。其实这是错误的,首先我们先要区分开同步并非阻塞,异步并非非阻塞。
同步和异步关注的是消息通信机制。
同步
所谓同步就是,当发出一个“调用”地时候,在没有得到结果前,该调用就不会返回,但是一旦返回,就会有返回值。也就是说必须一件一件的做事情,直到当前事件处理完成,才能做下一件事。
异步
而异步的概念与同步刚好相反,即当发出一个“调用”后,调用者不能立即得到结果,即该调用没有返回值。实际上在处理这个调用的部件(被调用方),会通过状态、通知来通知调用方或者使用回调函数。
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
阻塞
阻塞调用是指调用结果返回之前,当前进程将会被挂起(线程则会进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。
有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回,它还会抢占cpu去执行其他逻辑,也会主动检测I/O是否准备好。
非阻塞
非阻塞调用是指在不能立刻得到结果前,该函数不会阻塞当前进程,而是立刻返回。
Linux下的五种I/O模型
- 阻塞I/O(blocking I/O)
- 非阻塞I/O (nonblocking I/O)
- I/O复用(select 和poll) (I/O multiplexing)
- 信号驱动I/O (signal driven I/O (SIGIO))
- 异步I/O (asynchronous I/O (the POSIX aio_functions))
其中前4种都是同步,最后一种才是异步。
阻塞I/O模型:
在调用recv()/recvfrom()函数时,发生在内核中等待数据和复制数据的过程。
当调用recv()函数时,系统首先查是否有准备好的数据。如果数据没有准备好,那么系统就处于等待状态。当数据准备好后,将数据从系统缓冲区复制到用户空间,然后该函数返回。在套接应用程序中,当调用recv()函数时,未必用户空间就已经存在数据,那么此时recv()函数就会处于等待状态。
非阻塞I/O模型
非阻塞式I/O在应用进程发出recv请求时,如果内核数据并没有准备好,那么会直接返回一个error,说明数据未就绪,此时应用进程也不必死等在这里了,可以去做别的事情,但是需要定时的询问内核数据是否就绪。特点就是数据未就绪时不阻塞,但轮询的询问内核数据是否就绪。
I/O复用模型
对一个IO端口,两次调用,两次返回,比阻塞IO并没有什么优越性;关键是能实现同时对多个IO端口进行监听; I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。
信号驱动I/O模型
首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。
异步I/O模型
当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作。