I/O模型前置概念
I/O模型
Unix 可用的I/O模型主要分为五类:
- 阻塞式I/O
- 非阻塞式I/O
- I/O复用(select / poll)
- 信号驱动I/O
- 异步I/O
一条数据一般会经历两个阶段:
- 等待数据
- 从内核向进程复制数据。
从一个套接字上的输入,一般是等待网络上的数据到达网卡,然后内核从网卡中获取数据并保存在某个缓冲当中,然后将该缓冲的内容复制相应的进程当中。此时进程才可以读取到网络上的数据。
第一阶段网卡将数据保存在内核
- 网卡芯片将模拟信号转为数字信号
- DMA传输:网卡通过DMA(Direct Memory Access)技术,直接将数据写入到内核的Ring Buffer当中。
- 网卡向CPU发起一个硬中断,然后告诉CPU数据到了,来处理。
sequenceDiagram
autonumber
participant nic as 网卡
participant cpu as CPU
participant kenel as 内核
nic ->> nic: 将模拟信号转为数字信号
note left of nic :第一阶段转换数据
nic ->> kenel:DMA传输
note over cpu : 网卡通过DMA技术直接将数据存储到内核缓冲当中即Ring Buffer中
nic ->> cpu:硬中断
note right of nic:网卡向CPU发起硬中断,告诉CPU数据来了
第二阶段内核协议栈处理
CPU收到硬中断之后,会暂停当前任务,执行终端处理程序:
- 发起软中断:为了不占用CPU太久,硬中断处理程序只做简单标记,然后发起一个软中断,让内核线程稍后处理更为复杂的数据包解析。
- 协议栈解析:内核读取数据,剥离头部信息。
- 放入socket接收缓冲区: 如果数据有效,内核会把有效数据放入该socket对应的内核缓冲区。
此时数据依然在内核当中。
sequenceDiagram
autonumber
participant app as 用户进程
participant cpu as CPU
participant kenel as 内核
app ->>+ kenel: 系统调用read
note right of app : 此时进程从用户态转变为内核态
kenel -->>- app:将内核数据复制到用户进程缓冲区中
note over cpu: 此时用户进程还是处于内核态
note over app: 此时数据已全部拿到了用户进程中,并且从内核态->用户态
第三阶段内核到用户空间
- 系统调用: 进程调用系统调用(read, recv等),陷入内核态。
- CPU拷贝【比较耗时的步骤】:CPU必须亲自介入,将数据从内核的socket接收缓冲区,完整的复制到用户进程缓冲区。
- 进程唤醒:拷贝完成后,系统调用返回,进程从内核态返回到用户态,终于取走了数据。
sequenceDiagram
autonumber
participant app as 用户进程
participant cpu as CPU
participant kenel as 内核
app ->>+ kenel: 系统调用read
note right of app : 此时进程从用户态转变为内核态
kenel -->>- app:将内核数据复制到用户进程缓冲区中
note over cpu: 此时用户进程还是处于内核态
note over app: 此时数据已全部拿到了用户进程中,并且从内核态->用户态