# 【NO.133】深入理解异步I/O+epoll+协程

## 1.前言

**同步和异步**的概念描述的是用户线程与内核的交互方式：同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行；而异步是指用户线程发起IO请求后仍继续执行，当内核IO操作完成后会通知用户线程，或者调用用户线程注册的回调函数。
**阻塞和非阻塞**的概念描述的是用户线程调用内核IO操作的方式：阻塞是指IO操作需要彻底完成后才返回到用户空间；而非阻塞是指IO操作被调用后立即返回给用户一个状态值，无需等到IO操作彻底完成。

## 2.异步I/O

在理解异步I/O之前，我们先要知道什么是**同步I/O**
在**阻塞同步I/O**模型下，用户线程向内核发起 recvfrom 系统调用，当数据没有准备好的时候，用户线程阻塞。
此外还有一种**非阻塞同步I/O**，此时用户线程不阻塞于 recvfrom，而是反复向系统查询数据状态。当数据准备好了，就对数据进行后续处理。

![img](https://pic2.zhimg.com/80/v2-b555a5fbc20f5471f20e87f7d34f9e41_720w.webp)

Paste_Image.png

而在**异步I/O**模式下，用户线程在数据还没有准备好的时候**既不阻塞也不反复查询**，而是**继续干自己该干的事情**。内核会开启一个**内核线程**去读取数据，等到数据准备好了，内核给用户线程一个信号，用户线程中断去执行信号处理函数。

![img](https://pic1.zhimg.com/80/v2-369b5305eb19bdb26a628aad2926b72c_720w.webp)

Paste_Image.png

**node.js**中的异步回调也是采用开线程的方式实现的

## 3.epoll

epoll/select 是一种**I/O多路复用模型**
用户线程可以先通过 epoll **注册多个I/O事件**
然后用户线程反复执行调用 epoll/select 查询是否有准备好的事件
如果有准备好的I/O事件则进行处理
关键点是**一个用户线程处理多个I/O事件**

**epoll/select 的区别在于**
select 的底层原理是遍历所有注册的I/O事件，找出准备好的的I/O事件。
而 epoll 则是由内核主动通知哪些I/O事件需要处理，不需要用户线程主动去反复查询，因此大大提高了事件处理的效率。

![img](https://pic3.zhimg.com/80/v2-e071e80242c1d2d5ade493e9cb7ba532_720w.webp)

Paste_Image.png

## 4.协程

协程是一种轻量级的线程
**本质上协程就是用户空间下的线程**
如果把线程/进程当作虚拟“CPU”，协程即跑在这个“CPU”上的线程。

**协程的特点**

1. 占用的资源更少。
2. 所有的切换和调度都发生在用户态。

不管是进程还是线程，每次阻塞、切换都需要陷入系统调用，先让CPU跑操作系统的调度程序，然后再由调度程序决定该跑哪一个线程。而且由于**抢占式调度执行顺序无法确定**的特点，使用线程时需要非常小心地处理同步问题，而协程完全不存在这个问题。
因为**协程可以在用户态显示控制切换**

**例子**
传统的生产者-消费者模型是一个线程写消息，一个线程取消息，通过锁机制控制队列和等待，但一不小心就可能死锁。

如果改用协程，生产者生产消息后，直接通过yield跳转到消费者开始执行，待消费者执行完毕后，切换回生产者继续生产，效率极高：

```
import timedef consumer():    r = ''    while True:        n = yield r        if not n:            return        print('[CONSUMER] Consuming %s...' % n)        time.sleep(1)        r = '200 OK'def produce(c):    c.next()    n = 0    while n < 5:        n = n + 1        print('[PRODUCER] Producing %s...' % n)        r = c.send(n)        print('[PRODUCER] Consumer return: %s' % r)    c.close()if __name__=='__main__':    c = consumer()    produce(c)
```

协程的优点是可以用同步的处理方式实现异步回调的性能

原文链接：https://zhuanlan.zhihu.com/p/254990291

作者：[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10)