当前位置:首页>Linux>Linux 网络子系统源码剖析(一):总览与初始化

Linux 网络子系统源码剖析(一):总览与初始化

  • 2026-09-29 13:30:03
Linux 网络子系统源码剖析(一):总览与初始化

从宏观到微观,揭开 Linux 网络子系统的神秘面纱

系列:Linux 网络子系统源码剖析篇号:第 1 篇 内核版本:Linux 5.10 LTS

📋 本篇导读

你将学到

  • Linux 网络子系统的整体架构

  • 核心数据结构及其关系

  • 网络子系统的初始化流程

  • 协议栈的注册机制

  • 网络设备的注册流程

前置知识

  • C 语言基础

  • Linux 内核模块编程基础

  • 基本的网络协议知识(TCP/IP)

阅读时间

约 30-40 分钟


🎯 网络子系统架构总览

分层架构

Linux 网络子系统采用经典的分层设计,与 OSI 七层模型对应:

核心组件


🔑 核心数据结构

1. struct net_device - 网络设备

这是网络子系统中最重要的结构之一,代表一个网络设备(网卡)。

关键字段说明:

2. struct sk_buff - 数据包缓冲区

sk_buff(socket buffer)是网络子系统中最核心的数据结构,代表一个网络数据包。

sk_buff 内存布局:

3. struct socket - 套接字

socket 是用户空间和内核空间的接口,代表一个套接字。

// include/linux/net.h
struct socket {
socket_state state;                // socket 状态
short type;                        // socket 类型(SOCK_STREAM/SOCK_DGRAM)
unsigned long flags;               // socket 标志
struct file *file;                 // 关联的文件对象
struct sock *sk;                   // 关联的传输层 sock
conststruct proto_ops *ops;       // 协议操作函数集
};

4. struct sock - 传输层套接字

sock 是传输层的核心结构,包含协议相关的信息。

数据结构关系图


🚀 网络子系统初始化流程

初始化时序图

1. net_dev_init() - 网络设备子系统初始化

这是网络子系统初始化的第一步,在 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;

#endif

        // 初始化反压机制

        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);

关键点解析:

  1. 协议处理器链表

  1. 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 调用

#endif

};

2. inet_init() - TCP/IP 协议栈初始化

这是 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,
    }
};

3. 协议栈注册机制

协议注册流程

协议查找机制

查找流程图:

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)提供了网络资源的隔离,是容器技术的基础。

命名空间隔离


📊 实战案例

案例 1:查看网络子系统初始化日志

# 查看内核启动日志中的网络初始化信息
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

案例 2:查看已注册的协议

案例 3:查看网络设备信息

案例 4:查看软中断统计

案例 5:编写简单的协议处理器


📚 总结

核心要点回顾

  1. 分层架构

    • Linux 网络子系统采用分层设计

    • 从应用层到物理层,职责清晰

    • 各层通过标准接口通信

  2. 核心数据结构

    • net_device:网络设备

    • sk_buff:数据包缓冲区

    • socket:用户空间接口

    • sock:传输层套接字

  3. 初始化流程

    • net_dev_init():设备子系统初始化

    • inet_init():TCP/IP 协议栈初始化

    • 协议注册:建立协议到处理函数的映射

  4. 设备注册

    • alloc_netdev():分配设备

    • register_netdev():注册设备

    • 设备通过哈希表快速查找

  5. 网络命名空间

    • 提供网络资源隔离

    • 容器技术的基础

    • 每个命名空间有独立的网络栈

常见问题 FAQ

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 内核技术文章!

最新文章

随机文章