从宏观到微观,揭开 Linux 网络子系统的神秘面纱
Linux 网络子系统的整体架构
核心数据结构及其关系
网络子系统的初始化流程
协议栈的注册机制
网络设备的注册流程
C 语言基础
Linux 内核模块编程基础
基本的网络协议知识(TCP/IP)
约 30-40 分钟
Linux 网络子系统采用经典的分层设计,与 OSI 七层模型对应:
这是网络子系统中最重要的结构之一,代表一个网络设备(网卡)。
关键字段说明:
sk_buff(socket buffer)是网络子系统中最核心的数据结构,代表一个网络数据包。
sk_buff 内存布局:
socket 是用户空间和内核空间的接口,代表一个套接字。
// include/linux/net.hstruct socket {socket_state state; // socket 状态short type; // socket 类型(SOCK_STREAM/SOCK_DGRAM)unsigned long flags; // socket 标志struct file *file; // 关联的文件对象struct sock *sk; // 关联的传输层 sockconststruct proto_ops *ops; // 协议操作函数集};
sock 是传输层的核心结构,包含协议相关的信息。
这是网络子系统初始化的第一步,在 core_initcall 阶段执行。
源码位置:net/core/dev.c
/*
* 网络设备子系统初始化
* 这个函数在内核启动早期被调用
*/
static int __init net_dev_init(void)
{
int i, rc = -ENOMEM;
// ========== 1. 初始化设备索引哈希表 ==========
/*
* 设备索引哈希表用于快速查找网络设备
* 通过设备索引(ifindex)可以快速找到对应的 net_device
*/
if (dev_proc_init())
goto out;
// ========== 2. 初始化 Netlink 接口 ==========
/*
* Netlink 是内核与用户空间通信的机制
* 用于配置网络设备、路由等
*/
if (netdev_kobject_init())
goto out;
// ========== 3. 初始化协议处理器链表 ==========
/*
* 每种协议(IP、ARP 等)都有一个处理器
* 这里初始化协议处理器链表
*/
INIT_LIST_HEAD(&ptype_all);
for (i = 0; i < PTYPE_HASH_SIZE; i++)
INIT_LIST_HEAD(&ptype_base[i]);
// ========== 4. 初始化 NAPI 相关 ==========
/*
* NAPI(New API)是高效的数据包接收机制
* 为每个 CPU 初始化 NAPI 相关的数据结构
*/
for_each_possible_cpu(i) {
struct softnet_data *sd = &per_cpu(softnet_data, i);
// 初始化输入队列
skb_queue_head_init(&sd->input_pkt_queue);
skb_queue_head_init(&sd->process_queue);
// 初始化 NAPI 链表
INIT_LIST_HEAD(&sd->poll_list);
// 初始化输出队列
sd->output_queue = NULL;
sd->output_queue_tailp = &sd->output_queue;
// 初始化 RPS(Receive Packet Steering)
#ifdef CONFIG_RPS
sd->csd.func = rps_trigger_softirq;
sd->csd.info = sd;
sd->cpu = i;
// 初始化反压机制
sd->backlog.poll = process_backlog;
sd->backlog.weight = weight_p;
}
// ========== 5. 注册网络软中断 ==========
/*
* 网络数据包的接收和发送通过软中断处理
* NET_TX_SOFTIRQ: 发送软中断
* NET_RX_SOFTIRQ: 接收软中断
*/
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
// ========== 6. 注册 CPU 热插拔通知 ==========
/*
* 当 CPU 热插拔时,需要迁移网络相关的数据结构
*/
rc = cpuhp_setup_state_nocalls(CPUHP_NET_DEV_DEAD, "net/dev:dead",
NULL, dev_cpu_dead);
WARN_ON(rc < 0);
rc = 0;
out:
return rc;
}
// 使用 subsys_initcall 宏注册初始化函数
// 这确保它在内核启动的 subsys 阶段被调用
subsys_initcall(net_dev_init);
关键点解析:
协议处理器链表
softnet_data - 每 CPU 的网络数据
// 每个 CPU 都有一个 softnet_data 实例
struct softnet_data {
struct sk_buff_head input_pkt_queue; // 输入队列
struct sk_buff_head process_queue; // 处理队列
struct list_head poll_list; // NAPI 轮询链表
struct sk_buff *output_queue; // 输出队列
struct napi_struct backlog; // 反压 NAPI
#ifdef CONFIG_RPS
struct softnet_data *rps_ipi_list; // RPS IPI 链表
struct call_single_data csd; // 跨 CPU 调用
};
这是 TCP/IP 协议栈的初始化函数,在 fs_initcall 阶段执行。
源码位置:net/ipv4/af_inet.c
协议注册详解:
// TCP 协议定义struct prototcp_prot = {.name="TCP",.owner=THIS_MODULE,.close=tcp_close,.connect=tcp_v4_connect,.disconnect=tcp_disconnect,.accept=inet_csk_accept,.ioctl=tcp_ioctl,.init=tcp_v4_init_sock,.destroy=tcp_v4_destroy_sock,.shutdown=tcp_shutdown,.setsockopt=tcp_setsockopt,.getsockopt=tcp_getsockopt,.sendmsg=tcp_sendmsg,.recvmsg=tcp_recvmsg,.backlog_rcv=tcp_v4_do_rcv,.hash=inet_hash,.unhash=inet_unhash,.get_port=inet_csk_get_port,// ... 更多操作函数};// INET 协议族操作static const struct net_proto_family inet_family_ops= {.family=PF_INET,.create=inet_create, // 创建 socket 的函数.owner=THIS_MODULE,};// INET 协议切换表statics truct inet_protos winetsw_array[] = {{.type=SOCK_STREAM,.protocol=IPPROTO_TCP,.prot=&tcp_prot,.ops=&inet_stream_ops,.flags=INET_PROTOSW_PERMANENT|INET_PROTOSW_ICSK,},{.type=SOCK_DGRAM,.protocol=IPPROTO_UDP,.prot=&udp_prot,.ops=&inet_dgram_ops,.flags=INET_PROTOSW_PERMANENT,},{.type=SOCK_RAW,.protocol=IPPROTO_IP,.prot=&raw_prot,.ops=&inet_sockraw_ops,.flags=INET_PROTOSW_REUSE,}};
查找流程图:
socket(AF_INET, SOCK_STREAM, 0)↓查找协议族:net_families[AF_INET]↓inet_family_ops↓调用 inet_create()↓查找协议切换表:inetsw[SOCK_STREAM]↓找到 TCP 协议:• ops = inet_stream_ops• prot = tcp_prot↓分配 sock 并初始化↓返回 socket 文件描述符
设备哈希表结构:
网络命名空间(Network Namespace)提供了网络资源的隔离,是容器技术的基础。
# 查看内核启动日志中的网络初始化信息dmesg | grep-E"net|eth|TCP|UDP"# 输出示例[ 0.123456] NET: Registered protocol family 16[ 0.234567] NET: Registered protocol family 2# AF_INET[ 0.345678] TCP established hash table entries: 32768[ 0.456789] TCP bind hash table entries: 32768[ 0.567890] TCP: Hash tables configured[ 0.678901] UDP hash table entries: 2048[ 1.234567] eth0: registered as netdev
分层架构
Linux 网络子系统采用分层设计
从应用层到物理层,职责清晰
各层通过标准接口通信
核心数据结构
net_device:网络设备
sk_buff:数据包缓冲区
socket:用户空间接口
sock:传输层套接字
初始化流程
net_dev_init():设备子系统初始化
inet_init():TCP/IP 协议栈初始化
协议注册:建立协议到处理函数的映射
设备注册
alloc_netdev():分配设备
register_netdev():注册设备
设备通过哈希表快速查找
网络命名空间
提供网络资源隔离
容器技术的基础
每个命名空间有独立的网络栈
Q1:为什么需要 sk_buff?A:sk_buff 提供了统一的数据包表示,支持高效的内存管理、零拷贝、分片等特性。
Q2:NAPI 是什么?A:NAPI(New API)是一种高效的数据包接收机制,通过轮询减少中断开销。
Q3:如何查看网络设备的驱动?A:使用 ethtool -i <interface> 命令。
Q4:网络命名空间有什么用?A:提供网络资源隔离,是 Docker、Kubernetes 等容器技术的基础。
Q5:如何调试网络子系统?A:可以使用 printk、ftrace、eBPF、tcpdump 等工具。
在下一篇《sk_buff 深度解析》中,我们将深入探讨:
sk_buff 的内存布局和管理
sk_buff 的生命周期
数据操作函数的实现
分片、克隆、线性化等高级特性
性能优化技巧

Linux Networking Documentation
Network Devices
sk_buff
《Understanding Linux Network Internals》by Christian Benvenuti
《Linux Kernel Networking》by Rami Rosen
《TCP/IP Illustrated, Volume 2》by Gary R. Wright
LWN.net - Networking
Kernel Newbies - Networking
Linux Network Stack
作者:肇中系列:Linux 网络子系统源码剖析
网络子系统是 Linux 内核最复杂的子系统之一,但只要掌握了核心概念和数据结构,就能逐步深入理解其实现细节。
如果你有任何问题或建议,欢迎在评论区留言讨论!
下期预告:《sk_buff 深度解析》- 揭秘网络数据包的秘密
关注我,获取更多 Linux 内核技术文章!