# Phase 0 · Day 2: 内核中断 + 网络收包路径 --- ## 一、你的系统数据 ``` IRQ 56 (ens160-rxtx-0): CPU0:1329万 CPU1:191万 CPU2:112万 CPU3:676万 IRQ 57 (ens160-rxtx-1): CPU0:11万 CPU1:33万 CPU2:36万 CPU3:38万 IRQ 58 (ens160-rxtx-2): CPU0:19万 CPU1:37万 CPU2:43万 CPU3:42万 IRQ 59 (ens160-rxtx-3): CPU0:30万 CPU1:39万 CPU2:52万 CPU3:52万 IRQ 60 (ens160-event): 全 0 (事件队列, 未使用) NET_RX 软中断: CPU0:1339万 CPU1:436万 CPU2:381万 CPU3:791万 ksoftirqd: 每个 CPU 一个 (PID 16/24/30/36) ``` - 4 个 MSI-X 向量 → 4 个 RX 队列 → 4 个 CPU 并行收包 - IRQ 56 在 CPU0 上触发了 1329 万次(主流量),CPU1-3 只触发了少量——IRQ affinity 配置不均 ## 二、收包 6 道关卡 ``` 关卡1: 硬件中断 网卡 DMA 写 RX ring → MSI-X 中断 → APIC → CPU → IDT → irq_handler 此时在硬中断上下文,所有中断被屏蔽 关卡2: NAPI 调度 irq_handler → napi_schedule → 关网卡中断 + 触发 NET_RX_SOFTIRQ → 返回 源码: net/core/dev.c ____napi_schedule (line 4551) 关卡3: ksoftirqd ksoftirqd 被唤醒 → __do_softirq → net_rx_action → 遍历 poll_list 源码: kernel/softirq.c + net/core/dev.c 关卡4: 驱动 poll → netif_receive_skb 从 RX descriptor ring 取包 → alloc_skb → memcpy → netif_receive_skb(skb) 源码: net/core/dev.c netif_receive_skb (line 5899) 关卡5: 协议分发 (L2→L3) __netif_receive_skb_core → skb->protocol → 查 ptype_base 哈希表 → ip_rcv() 源码: net/core/dev.c __netif_receive_skb_core (line 5434) net/ipv4/ip_input.c ip_rcv (line 560) 协议注册: net/ipv4/af_inet.c dev_add_pack (line 2065) 关卡6: TCP 收包 (L3→L4→socket) ip_rcv → NF_HOOK → ip_local_deliver → tcp_v4_rcv → __inet_lookup_established → tcp_rcv_established (fast path!) → tcp_queue_rcv → sk_data_ready → wake_up → epoll 返回 源码: net/ipv4/tcp_ipv4.c (line 1999) net/ipv4/tcp_input.c tcp_rcv_established ``` ## 三、DPDK 为什么快 | 内核步骤 | 耗时 | DPDK | |---------|------|------| | MSI-X → IDT → handler | ~3us | PMD 轮询, 无中断 | | napi_schedule + ksoftirqd 调度 | ~5us | PMD 线程永不睡眠 | | alloc_skb (slab 锁) | ~1us | mbuf 从 per-lcore cache 弹出 | | memcpy DMA→skb | ~2us | DMA 直接写 mbuf, 零拷贝 | | ptype_base 查找 | ~0.3us | 应用自己解析协议 | | Netfilter HOOK | ~5us | 不需要 | | ehash 查 socket | ~1us | rte_hash_lookup | | copy_to_user | ~3us | 零拷贝 | | **总计** | **~20us** | **~1us** | ## 四、实战踩坑 ### 坑 1: 中断全绑 CPU0 - **现象**: top 看 CPU0 si=99%, 其他 CPU 空闲 - **根因**: smp_affinity 默认只开 CPU0 - **定位**: `cat /proc/irq/56/smp_affinity` - **修复**: `echo ff > /proc/irq/56/smp_affinity` ### 坑 2: NAPI budget 耗尽 - **现象**: 100G 线速达不到,`ethtool -S` 看 rx_missed 增长 - **根因**: net_rx_action 默认一次最多 300 包,收不够就返回 - **定位**: `cat /proc/net/softnet_stat`, 第三列 = "times queue full" - **修复**: 增大 net.core.netdev_budget,或换 DPDK ### 坑 3: ksoftirqd 被抢占 - **现象**: 收包延迟 P99 抖动严重 - **根因**: ksoftirqd 不是实时线程,被磁盘 I/O 抢占 - **定位**: `perf sched latency` - **修复**: `chrt -f 1 $(pidof ksoftirqd/0)` 或换 DPDK PMD ### 坑 4: Netfilter 静默丢包 - **现象**: tcpdump 能看到包,应用收不到 - **根因**: Docker/k8s 自动注入 iptables drop 规则 - **定位**: `iptables -L -v -n`, 看每条规则的计数器 ### 坑 5: TCP backlog 满 - **现象**: 客户端 connect 超时 - **根因**: accept queue=128, 第 129 个 SYN 被内核静默丢弃 - **定位**: `netstat -s | grep overflow` - **修复**: `echo 4096 > /proc/sys/net/core/somaxconn` ## 五、检验标准 - [ ] 能画中断→NAPI→ksoftirqd→poll→netif_receive_skb→ip_rcv→tcp_v4_rcv 完整调用链 - [ ] 能解释 NAPI 为什么要关中断、用软中断 - [ ] 能说 ptype_base 的作用 - [ ] 能列 DPDK 比内核快 ≥5 个原因 - [ ] 能在 /proc/interrupts 和 /proc/softirqs 找到你的网卡