内容纲要
前置基础
网卡(NIC)
网卡是一块被设计用来允许计算机在计算机网络上进行通讯的计算机硬件。由于其拥有 MAC 地址,因此属于 OSI 模型的第 2 层。它使得用户可以通过电缆或无线相互连接。每一个网卡都有一个被称为 MAC 地址的独一无二的48位串行号,它被写在卡上的一块 ROM 中。在网络上的每一个计算机都必须拥有一个独一无二的 MAC 地址。没有任何两块被生产出来的网卡拥有同样的地址。这是因为电气电子工程师协会(IEEE)负责为网络接口控制器(网卡)销售商分配唯一的 MAC 地址。
网卡驱动
驱动程序(Device Driver)全称为“设备驱动程序”,是一种可以使计算机中央处理器—— CPU 控制和使用设备的特殊程序,相当于硬件的接口,操作系统通过这个接口,控制硬件设备的工作。所有的硬件都要安装驱动程序,没有驱动程序的硬件是运行不了的,就像一辆有轮胎但是没有传动轴的汽车一样跑不起来,控制不了。假如某设备的驱动程序未能正确安装,便不能正常工作。
「网卡驱动」程序就是 CPU 控制和使用网卡的程序。
网卡驱动处理数据的方式
NAPI(主流)
NAPI 是「中断」和「轮询」的结合体,避开了单轮询的无效做功和单中断的数据量大时频繁中断的消耗。在数据量少是使用中断,数据量多时使用轮询。平时是中断模式,当有数据到达时,会触发中断处理函数,中断处理函数关闭中断开始处理,此时再有数据过来,就不会触发中断,因为中断处理函数中会采用轮询的方式处理数据,直到没有新数据才重新打开中断。
NAPI 的缺陷
- 不能及时处理数据包
- 数据到达速度增加会使内存消耗增加
- 在大数据包,但是低速的情况下,接收中断会急剧增加,造成效率急剧下降
收包流程
- 网卡到内存
- 数据包从网络进入网卡
- 目标地址是本网卡:跳转到 1.2
- 目标地址不是本网卡
- 开启混杂模式:跳转到 1.2
- 未开始混杂魔兽:丢弃该网络包
- 网卡将数据包通过 DMA(Direct Memory Access,直接存储器访问)方式写入指定内存地址(该地址由网卡驱动分配并初始化)
- 网卡通过 IRQ(硬件中断)通知 CPU
- CPU 根据中断表,调用已注册的中断函数(中断函数会调用驱动中相应的函数)
- 驱动先禁用网卡中的中断,再告诉网卡下次接收到数据直接写入内存,不再通知 CPU(避免 CPU 接收大量中断)
- 启动软中断
- 数据包从网络进入网卡
- 内核网络模块
- 内核中的 ksoftirqd 进程负责处理软中断,当收到软中断后,会调用对应的软中断处理函数(1.6 中抛出的软中断,会调用网络模块的处理函数)
- 网络模块中的函数会调用驱动里的轮询函数来一个一个的处理数据包
- 驱动会一个一个的读取网卡写到内存中的数据包
- 驱动程序将内存中的数据包转换成内核网络模块能识别的套接字缓存格式
- 然后调用 GRO 相关函数,最终将数据放入 CPU 的队列中(如果队列满了,会被丢弃)
- CPU 接着处理自己的软中断上下文中的队列里的数据
- 待内存中所有数据被处理完后,启用网卡硬中断
socket 程序运行流程
- 在 socket 程序中,程序执行到 recv 函数时, 进程进入「阻塞」状态(被放入内核等待队列,暂停执行后续代码)
- 待网卡接收到数据
- 将合适的数据写入指定内存
- 触发硬中断并调用驱动里的处理函数,函数禁用网卡硬中断并让网卡下次接收到数据直接写入内存
- 启用软中断
- 内核负责软中断处理的的代码开始执行
- 调用驱动处理内存中的数据
- 数据处理完后启用硬中断
- 数据被送入协议栈
- 内核根据数据中的端口信息等得出了数据属于哪个 socket
- 再根据 socket fd 得出资源所属进程
- 该进程被内核重新放入就「绪队」列等待分配 CPU
- 进程得到 CPU 使用权后,进入「运行」状态,继续处理 recv 后的代码
参考链接
如果这篇文章说不清epoll的本质,那就过来掐死我吧!
DPDK(二):准备知识9 — Linux内核收报流程