
本篇定位:Linux 网络栈是其最复杂的子系统之一。裸机网络靠 lwIP/自己写,Linux 有完整 TCP/IP 栈 socket 抽象 NAPI 收包 netfilter 钩子。本篇讲清数据通路(收发路径)、sk_buff、net_device、NAPI、netfilter。读完能理解ping/curl的内核路径、能看懂网卡驱动收发框架。网络栈是 Linux 最大数据通路,你裸机用 lwIP 是简化版FreeRTOS 用 lwIP:socket-like API,单核,轮询/中断收包。Linux 网络栈:socket 抽象 完整 TCP/IP NAPI(中断轮询混合) netfilter 钩子 多核并行。增量是 NAPI(防中断风暴)、netfilter(防火墙钩子)、sk_buff(统一缓冲)。你 lwIP 经验能迁移,新增的是规模和优化。目录一、网络栈分层核心抽象二、socket:用户接口2.1 socket 是什么2.2 socket 在内核2.3 socket 一切皆文件三、sk_buff:数据包⭐3.1 sk_buff 是什么3.2 sk_buff 的缓冲区布局3.3 关键 API3.4 收发包时 skb 的变化四、net_device:网卡抽象4.1 net_device 是什么4.2 注册网卡4.3 网卡驱动核心:ndo_start_xmit五、发包路径(TX)5.1 完整发包5.2 拥塞控制(TCP)六、收包路径(RX)与 NAPI⭐6.1 老方式(纯中断)6.2 NAPI(New API)6.3 NAPI 的好处6.4 网卡驱动 NAPI 实现七、netfilter:防火墙钩子⭐7.1 netfilter 是什么7.2 五个钩子点7.3 注册钩子7.4 iptables/nftables八、路由8.1 路由表8.2 路由查找8.3 路由策略(policy routing)九、网络性能优化9.1 关键优化9.2 多队列(RSS)9.3 XDP(高性能路径)十、网络监控10.1 基础10.2 性能10.3 调路由/防火墙十一、对照总表十二、本篇小结速查表一、网络栈分层用户态: socket() / send() / recv() │ ▼ syscall VFS 层: socketfs(socket 是文件) │ ▼ 协议栈: TCP / UDP / ICMP / IP │ ▼ netfilter: 钩子(防火墙/NAT/过滤) │ ▼ 链路层: net_device(网卡驱动) │ ▼ 硬件: 网卡(MAC/PHY)核心抽象抽象作用socket用户态接口(fd)sk_buff数据包(贯穿全栈)net_device网卡抽象NAPI收包机制(中断轮询)netfilter钩子点(改包/丢包/NAT)二、socket:用户接口2.1 socket 是什么网络通信的端点,fd 表示类型:stream(TCP)/dgram(UDP)/rawintfdsocket(AF_INET,SOCK_STREAM,0);// TCPbind(fd,...);listen(fd,...);accept(fd,...);connect(fd,...);send(fd,buf,n,0);recv(fd,buf,n,0);close(fd);2.2 socket 在内核structsocket{shorttype;// SOCK_STREAM/DGRAMconststructproto_ops*ops;// 操作(inet_stream_ops 等)structfile*file;// 关联 file(socket 也是文件)structsock*sk;// 协议层 sock...};structsock{structsock_common__sk_common;intsk_protocol;// TCP/UDPstructsk_buff_headsk_receive_queue;// 接收队列structsk_buff_headsk_write_queue;// 发送队列...};struct socet:VFS 层(用户接口)struct sock:协议层(状态/队列)socket fd 经 VFS(12 篇),file-f_op 是 socket_file_ops2.3 socket 一切皆文件socket 也是文件,有 fdread/write/recv/send 都经 VFSfile-private_data 指向 socketsocket 体现一切皆文件裸机网络自己管 socket 结构。Linux socket 是 fd,read/write 统一。VFS 抽象让网络和文件用同一套 syscall(send 是 write 的变种)。这是 Linux 设计哲学的体现。三、sk_buff:数据包⭐3.1 sk_buff 是什么网络数据包的载体,贯穿全栈含包数据 元数据(协议头指针/长度/设备)每层协议不拷贝数据,只改 sk_buff 的指针(零拷贝思想)structsk_buff{structsk_buff*next,*prev;// 链表structnet_device*dev;// 收/发设备charcb[48];// 控制缓冲(私有)unsignedintlen;// 总长度unsignedchar*head,*data;// 缓冲区起/数据起unsignedchar*tail,*end;// 数据尾/缓冲尾__u16 protocol;// L2 协议(ETH_P_IP)__u32 priority;.../* 协议头指针(各层) */unsignedchar*transport_header;// L4(TCP/UDP)unsignedchar*network_header;// L3(IP)unsignedchar*mac_header;// L2(以太网)};3.2 sk_buff 的缓冲区布局head data tail end │ │ │ │ ▼ ▼ ▼ ▼ ┌─────预留(可加L2头)────┬────实际数据────┬──预留(可加尾)──┐ │ │ IP/TCP/负载 │ │ └─────────────────────────┴────────────────┴─────────────────┘head/data/tail/end 四指针加协议头:skb_push(数据指针前移,腾出头部空间)去协议头:skb_pull(数据指针后移)3.3 关键 APIAPI作用alloc_skb(size, GFP)分配 skbkfree_skb(skb)释放skb_put(skb, len)尾部追加数据(tail 后移)skb_push(skb, len)头部加协议头(data 前移)skb_pull(skb, len)去掉头部(data 后移)skb_reserve(skb, len)预留头部空间3.4 收发包时 skb 的变化发包(下行): 应用数据 → skb(只含 payload) → TCP 加头:skb_push(TCP 头) → IP 加头:skb_push(IP 头) → 以太网加头:skb_push(以太网头) → 网卡发 收包(上行): 网卡收 → skb(含以太网头) → 去以太网头:skb_pull → 去 IP 头:skb_pull → 去 TCP 头:skb_pull → 应用数据sk_buff 是网络栈的灵魂裸机网络可能用固定缓冲区 memcpy 加头。Linux sk_buff 用指针操作,加头只是前移 data 指针,不拷贝数据。这是零拷贝思想,网络栈高性能的关键。你写网卡驱动要熟练操作 skb。四、net_device:网卡抽象4.1 net_device 是什么每个网卡一个 net_device注册后出现 eth0/wlan0 接口structnet_device{charname[IFNAMSIZ];// eth0unsignedlongmem_end,mem_start;unsignedlongbase_addr;// I/O 基址intirq;// 中断conststructnet_device_ops*netdev_ops;// 操作intmtu;// 最大传输单元unsignedchardev_addr[6];// MAC 地址structnapi_structnapi;// NAPI...};structnet_device_ops{int(*ndo_open)(structnet_device*dev);// ifconfig upint(*ndo_stop)(structnet_device*dev);// ifconfig downint(*ndo_start_xmit)(structsk_buff*skb,// 发包structnet_device*dev);...};4.2 注册网卡structnet_device*devalloc_netdev(sizeof(structmy_priv),eth%d,NET_NAME_UNKNOWN,ether_setup);dev-netdev_opsmy_ops;register_netdev(dev);// 注册,出现 eth04.3 网卡驱动核心:ndo_start_xmitstaticintmy_start_xmit(structsk_buff*skb,structnet_device*dev){// 把 skb 数据发到硬件(DMA 或 PIO)// 发完 free skbdev_kfree_skb(skb);returnNETDEV_TX_OK;}协议栈发包最终调驱动的 ndo_start_xmit驱动把 skb 数据 DMA 到网卡,网卡发发完释放 skb五、发包路径(TX)5.1 完整发包用户: send(fd, buf, n) → syscall → sock_sendmsg → TCP/UDP 处理(加 L4 头,分段,拥塞控制) → IP 处理(加 L3 头,路由) → netfilter NF_INET_LOCAL_OUT 钩子 → 链路层(加 L2 头,ARP 解析 MAC) → netfilter NF_INET_POST_ROUTING 钩子(NAT) → dev_queue_xmit → 驱动 ndo_start_xmit → 网卡硬件发送5.2 拥塞控制(TCP)TCP 发包受拥塞窗口控制慢启动/拥塞避免/快重传/快恢复拥塞丢包则窗口减半六、收包路径(RX)与 NAPI⭐6.1 老方式(纯中断)每个包来一个中断高流量下中断风暴,CPU 全在中断6.2 NAPI(New API)中断 轮询混合第一个包来中断 → 关中断,切换到轮询轮询批量取包(从网卡 ring buffer)取完或配额到 → 重开中断1. 网卡收包 → 中断 2. 驱动 ISR:关中断,napi_schedule 3. softirq(NET_RX_SOFTIRQ)调度 4. napi_poll 批量取包(一次取 budget 个) 5. 取完 → 重开中断 6. 没取完 → 留 softirq 下次继续6.3 NAPI 的好处高流量时轮询,无中断风暴低流量时中断,及时响应批量处理,缓存友好6.4 网卡驱动 NAPI 实现staticintmy_napi_poll(structnapi_struct*napi,intbudget){intrx_count0;structmy_dev*devcontainer_of(napi,structmy_dev,napi);// 批量从网卡 ring buffer 取包while(rx_countbudget){structsk_buff*skbmy_rx(dev);if(!skb)break;netif_receive_skb(skb);// 上交协议栈rx_count;}if(rx_countbudget){// 取完了,重开中断napi_complete_done(napi,rx_count);my_enable_irq(dev);}returnrx_count;}staticirqreturn_tmy_rx_irq(intirq,void*dev_id){structmy_dev*devdev_id;my_disable_irq(dev);// 关中断napi_schedule(dev-napi);// 调度轮询returnIRQ_HANDLED;}NAPI 是现代网卡标配裸机网络可能纯中断(每包中断)。Linux NAPI中断轮询混合,高流量不中断风暴。你写网卡驱动(以太网 MAC)必须用 NAPI。这是 05 篇 softirq 在网络的应用(NET_RX_SOFTIRQ)。七、netfilter:防火墙钩子⭐7.1 netfilter 是什么内核网络栈的钩子框架在包路径关键点插入回调,可改包/丢包/NATiptables/nftables 基于 netfilter7.2 五个钩子点包路径: 收包 → PREROUTING → 路由判断 ─┬─→ 本机 → INPUT → 协议栈 → 应用 │ └─→ 转发 → FORWARD → POSTROUTING → 发出 应用 → OUTPUT → POSTROUTING → 发出 钩子点: NF_INET_PRE_ROUTING 收包早期(路由前) NF_INET_LOCAL_IN 进本机 NF_INET_FORWARD 转发 NF_INET_LOCAL_OUT 本机发出 NF_INET_POST_ROUTING 发出最后7.3 注册钩子staticstructnf_hook_opsmy_hook{.hookmy_hook_fn,.pfNFPROTO_IPV4,.hooknumNF_INET_LOCAL_IN,.priorityNF_IP_PRI_FIRST,};nf_register_net_hook(init_net,my_hook);unsignedintmy_hook_fn(void*priv,structsk_buff*skb,conststructnf_hook_state*state){// 检查包,返回:// NF_ACCEPT: 接受,继续// NF_DROP: 丢弃// NF_STOLEN: 拿走(自己处理)returnNF_ACCEPT;}7.4 iptables/nftables用户态工具,规则转成 netfilter 钩子表(filter/nat/mangle/raw) 链(INPUT/OUTPUT/FORWARD…)iptables-AINPUT-ptcp--dport22-jACCEPT# 允许 SSHiptables-AINPUT-jDROP# 其余丢弃嵌入式视角:netfilter 在嵌入式做过滤/NAT嵌入式 Linux 做网关/路由器,用 iptables 配 NAT(masquerade)/端口过滤。netfilter 是底层钩子,iptables 是前端。你 BSP 配网络策略,懂 netfilter 钩子点能精确定位包在哪被处理。八、路由8.1 路由表iproute# 看路由表# default via 192.168.1.1 dev eth0# 192.168.1.0/24 dev eth0 proto kernel8.2 路由查找每个发包查路由表,决定下一跳 出接口FIB(Forwarding Information Base)表路由缓存(已废弃,现在用 LC-Trie)8.3 路由策略(policy routing)多表路由(按源地址/ToS 选表)ip rule管理规则九、网络性能优化9.1 关键优化优化作用NAPI收包中断轮询,防风暴sk_buff 零拷贝加头不 memcpyTSO/GSOTCP 分段卸载到网卡(发大包,网卡分)LRO/GRO收包合并(网卡合并小包成大包)checksum offload校验和硬件算RSS多队列,每核收包(配 blk-mq 思想)XDPeXpress Data Path,ebpf 在网卡层处理(超快)9.2 多队列(RSS)现代网卡多 RX/TX 队列,每队列一中断,绑不同核并行收发,性能线性扩展/proc/interrupts看每队列中断分布9.3 XDP(高性能路径)eBPF 程序在网卡驱动收包最早点运行包不过协议栈,直接处理(丢/改/转发)DDoS 防护/负载均衡利器十、网络监控10.1 基础ifconfig/ip-slink# 接口统计cat/proc/net/dev# 每接口收发字节数/包数/错误ss-tlnp# 看 socket(替代 netstat)netstat-i# 接口统计10.2 性能ethtool-Seth0# 网卡详细统计ethtool-geth0# ring buffer 大小ethtool-leth0# 多队列tcpdump-ieth0# 抓包(16 篇)iperf3# 带宽测10.3 调路由/防火墙iproute# 路由iprule# 策略路由iptables-L-n-v# 防火墙规则计数conntrack-L# 连接跟踪十一、对照总表概念lwIP/裸机Linux 网络栈socketsocket-likesocket fd(一切皆文件)数据包pbufsk_buff(指针加头)网卡抽象自己结构net_device收包中断NAPI(中断轮询)防火墙无netfilter 钩子路由自己表FIB 多表拥塞控制简化完整 TCP 拥塞多核单核RSS 多队列并行卸载无TSO/GSO/checksum抓包自己写tcpdump十二、本篇小结网络栈分层:socket(VFS)→ TCP/IP → netfilter → net_device → 硬件socket:网络端点,fd 表示,也是文件(VFS 抽象);struct socket(VFS层) struct sock(协议层)sk_buff:数据包,指针操作加头(push)/去头(pull),零拷贝思想,网络栈灵魂net_device:网卡抽象,ndo_start_xmit 发包;alloc_netdev register_netdevNAPI:中断轮询混合收包,第一个包中断→关中断轮询批量取→取完开中断,防中断风暴,网卡驱动必备netfilter:五钩子点(PRE_ROUTING/LOCAL_IN/FORWARD/LOCAL_OUT/POST_ROUTING),iptables/nftables 前端发包:send → TCP/IP → netfilter → dev_queue_xmit → ndo_start_xmit → 网卡收包:网卡中断 → NAPI 轮询 → netif_receive_skb → 协议栈 → socket 队列优化:NAPI/sk_buff 零拷贝/TSO/GSO/checksum offload/RSS 多队列/XDP监控:ifconfig/ip/ss/ethtool/tcpdump/iperf3速查表想干啥用什么看接口ip link / ifconfig看接口统计ip -s link / cat /proc/net/dev看 socketss -tlnp看路由ip route看防火墙iptables -L -n -v看网卡统计ethtool -S eth0看 ring bufferethtool -g eth0抓包tcpdump -i eth0带宽测iperf3配 IPip addr add 192.168.1.10/24 dev eth0配默认网关ip route add default via 192.168.1.1改 MACip link set dev eth0 address xx:xx注册网卡驱动alloc_netdev register_netdev收包 NAPInapi_schedule napi_poll发包ndo_start_xmit(skb, dev)netfilter 钩子nf_register_net_hook技术之路漫漫分享是为了更好地交流。如果本文的内容对你有启发希望能得到你的点赞 和收藏 ⭐。如果你在调试过程中遇到了其他问题欢迎在评论区 留言我们一起探讨。也欢迎关注 我一起交流底层开发的那些事儿。