阻塞与非阻塞、同步与异步、异步I/O

1. 阻塞与非阻塞

就是是否等待的区别,以 linux的 Socket 通信来说:

  • 阻塞就是调用 read() 读取socket时,如果没有数据,就会阻塞着循环等待,直到有数据时再继续执行读操作
  • 非阻塞就是如果设置了这个socket 非阻塞属性后,没有数据时也会循环地读取,但会直接返回读取的结果(读到的字节数、错误代码)

2. 同步与异步

同步与异步其实是一种思想上的区别,借网上的一个例子来说:

  • 同步就是,当你开了一家快餐只能打包带走的,来一个客人给他现做一份,其他的就在后面排队,阻塞着等待
  • 异步就是,开了一家餐馆或者外卖,客人来了点菜或者发一个订单
应用场景上:
同步需要立刻得到反馈,没有就等着不干别的事情;
而异步就不需要,只需要下订单然后等他完成之后提醒你就行了,这时候可以干其他的事情

3. 异步I/O

这时候可能会有疑问:异步I/O是不是设置Socket非阻塞就行了?有数据就读取,数据没有就立即返回。
不是的。当Socket有数据时,主线程要不断把Socket上的数据搬到到本地的缓冲区内,写的时候要不断地把本地缓冲区的数据搬到Socket上,这其实就相当于阻塞在这里了,程序干不了别的事情。
只有使用特殊API把搬运数据的工作交给内核的才叫异步I/O:
内核搬运完数据,把缓冲区的地址给主线程 ---> 主线程把内容封装成一个请求对象,交给子线程来处理业务逻辑 ---> 子线程解析完,把要写回数据的缓冲区地址告诉主线程 ---> 主线程再把写任务交给内核
这样进程中就没有额外阻塞等待,达到最高效率
  • 只有使用了特殊API的才是异步IO

  • EPOLL/SELECT/POOL都是同步

  • 只有是系统帮我们把数据从内核搬运,通知我们去取的才是异步的

4. Unix/Liunx 5种 I/O 模型

  1. blocking

  2. non-blcoking

    • 通过 setsockopt 改变 cfd 的属性为非阻塞
    • 没有新数据的时候不再阻塞
    • 但是读到空会返回不同的信号:
      • EAGAIN
      • EINTR
      • AGAIN
    • 并且在读取的时候还需要自己从内核把缓冲区的数据搬出来,阻塞在这里
  3. IO multiplexing (IO复用)

    • 通过select/poll/epoll 的系统调用,让内核帮我们监听
    • 当监听的fd变化,返回可读的fd
    • 主要效果是让单线程可以检测多个fd,减少了线程与进程的开销
  4. signal-driven (信号驱动)

    • 大概是注册 SIGACTION 来返回变化的fd
    • 但是在read的时候还是同步的阻塞的
  5. asynchronous (异步)

    • unix中,可以调用aio_read 函数,告诉内核缓冲区指针,和缓冲区大小、文件偏移及通知的方式,然后立刻返回
    • 由内核帮我们进行拷贝数据到缓存区
    • 通过注册的通知方式来提醒
    • 这时我们只需要使用已经装好数据的缓冲区就可以了
    • 大大提升了效率

根据自己的学习笔记与理解来写的,欢迎指正