首先说明我对这些概念也不是很清楚,以下内容是我做的一些理事。
同步和异步、阻塞和非阻塞这是两组概念,说的是不同的事情,同步和阻塞没有必然的联系,异步和非阻塞也没有必然的联系。同步和异步是只跟IO操作过程中进程的状态变化有关。阻塞和非阻塞就是进程的两种状态。比如你去银行,排除的话就是一种同步的方式,叫号的话就是异步的方式。排队必须自己看着什么时候轮到自己,而叫号则不必,轮到你的时候会触发一个事件,或者说你会收到一个信号,别人会叫你。不管是排除还是叫号,如果你在等待的过程中不能做其他事情,那就是阻塞模式,否则就是非阻塞模式。同步的时候可以有阻塞和非阻塞,异步的时候也可以有阻塞和非阻塞。
阻塞 I/O
Linux下的I/O操作默认是阻塞I/O,即open和socket创建的I/O都是阻塞I/O。当读写操作没有完成时,函数就不会返回,进程一直阻塞在那里。
非阻塞I/O
非阻塞模式的使用并不普遍,因为非阻塞模式会浪费大量的CPU资源。
网络编程时listen,recvfrom,connect都会引起阻塞。非阻塞IO通常应用于网络编程中,IO请求时加上O_NONBLOCK一类的标志位,函数立刻返回,IO没有就绪会返回错误,需要请求进程主动轮询不断发IO请求直到返回正确
比如调用recvfrom时,如果系统还没有接收到网络数据,内核马上返回一个 EWOULDBLOCK的错误。
当一个应用程序使用了非阻塞模式的套接字,它需要使用一个循环来不停地测试是否一个文件描述符有数据可读(称做 polling(轮询))。应用程序不停的 polling 内核来检查是否 I/O操作已经就绪。这将是一个极浪费 CPU资源的操作。这种模式使用中不是很普遍。
I/O多路复用
针对批量IP操作时,使用I/O多路复用,非常有好。
在使用 I/O 多路技术的时候,我们调用 select()函数和 poll()函数或epoll函数(2.6内核开始支持),在调用它们的时候阻塞。
当我们调用 select函数阻塞的时候,select 函数等待数据报套接字进入读就绪状态。当select函数返回的时候, 也就是套接字可以读取数据的时候。 这时候我们就可以调用 recvfrom函数来将数据拷贝到我们的程序缓冲区中。
IO复用同非阻塞IO本质一样,不过利用了新的select系统调用,由内核来负责本来是请求进程该做的轮询操作。看似比非阻塞IO还多了一个系统调用开销,不过因为可以支持多路IO,才算提高了效率。
多路复用的高级之处在于:
它能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。
异步I/O
当我们运行在异步 I/O 模式下时,我们如果想进行 I/O 操作,只需要告诉内核我们要进行 I/O 操作,然后内核会马上返回。具体的 I/O 和数据的拷贝全部由内核来完成,我们的程序可以继续向下执行。当内核完成所有的 I/O 操作和数据拷贝后,内核将通知我们的程序。