Linux 网络栈简介
Linux 网络栈是 Linux 操作系统中一个强大而模块化的框架,它是高效网络通信的核心组成部分。从 BSD 网络栈演化而来,它提供了清晰定义的接口,从协议无关层到特定协议实现,应有尽有。本文深入探讨 Linux 网络栈的架构、关键组件和运行流程,专为 IT 专业人士和开发者量身打造,帮助他们全面理解这一系统。
互联网模型:四层结构
Linux 网络栈不像传统的七层 OSI 模型那样复杂,它采用简洁的四层互联网模型:
- 链路层:通过设备驱动程序与物理介质(如以太网或串行连接)交互,负责帧的发送和接收。
- 网络层:管理主机间的数据包路由,主要依赖互联网协议(IP)。
- 传输层:使用 TCP 和 UDP 等协议实现端到端通信,处理主机内的连接。
- 应用层:解释数据语义,让应用程序能够有意义地处理网络数据。
这种结构确保了模块化和灵活性,能够适应各种网络协议和硬件。
Linux 网络栈的核心架构
Linux 网络栈在用户空间、内核空间和物理硬件之间运行,数据通过套接字缓冲区(sk_buff)流动。下面是其架构概述:
系统调用接口
系统调用接口是用户空间应用程序与内核网络子系统之间的桥梁,提供标准化方式来访问网络资源。
- 关键系统调用:
- socket():创建用于网络通信的套接字,指定地址族(如 AF_INET)、类型(如 SOCK_STREAM)和协议。
- bind():将套接字绑定到本地地址。
- listen():准备套接字接受传入连接。
- accept():接受传入连接,创建新套接字。
- connect():向远程地址发起连接。
网络套接字还支持标准文件操作(如 read 和 write),将套接字视为文件描述符,与 Linux 的基于文件的 I/O 模型无缝集成。socket_file_ops 结构定义了这些操作,确保与文件语义兼容。
协议无关接口
套接字层提供协议无关接口,抽象底层协议细节,支持 TCP、UDP、SCTP 和原始以太网等多种协议。
- 套接字结构(struct sock): 定义在 linux/include/net/sock.h 中,该结构封装套接字状态,包括协议特定数据和操作。它包含如 __sk_common 用于共享属性,以及 sk_prot 用于协议特定函数的字段。
- 协议操作(struct proto): 每个协议定义一个 proto 结构,指定操作如套接字创建、连接建立和关闭。例如:c
struct proto tcp_prot = { .name = "TCP", .close = tcp_close, .connect = tcp_v4_connect, .accept = inet_csk_accept, ... };该结构确保协议特定逻辑被封装,同时保持统一接口。
网络协议
Linux 支持核心协议如 TCP、UDP 和 IP,这些协议在 inet_init 函数(linux/net/ipv4/af_inet.c)中初始化。主要方面包括:
- 协议注册: 使用 proto_register 注册协议,将其添加到活动协议列表,并可选分配 slab 缓存以提高效率。
- 协议开关(inetsw_array): inetsw_array 将协议映射到其操作,定义如下:c
static struct inet_protosw inetsw_array[] = { { .type = SOCK_STREAM, .protocol = IPPROTO_TCP, .prot = &tcp_prot, .ops = &inet_stream_ops, .flags = INET_PROTOSW_PERMANENT | INET_PROTOSW_ICSK, }, ... };该数组在套接字创建时(如 socket(AF_INET, SOCK_STREAM, 0) 用于 TCP)启用动态协议映射。
- 协议初始化: inet_init 函数初始化 ARP、ICMP、IP、TCP 和 UDP 等模块,确保所有组件就绪。
套接字缓冲区(sk_buff)
sk_buff 结构定义在 linux/include/linux/skbuff.h 中,是网络栈数据移动的核心。它封装跨层的包数据和元数据。
- 关键特性:
- 包数据:存储实际数据和协议头(如 th 用于传输层、iph 用于 IP、mac 用于 MAC 头)。
- 链式连接:单个连接可链接多个 sk_buff 实例。
- 设备关联:引用网络设备(net_device *dev)用于发送或接收。
- 内存管理:确保头部的连续内存,并提供内核函数用于创建、克隆和队列管理。
设备无关接口
设备无关层抽象硬件特定细节,让协议通过统一接口与各种网络设备交互。
- 设备注册: 驱动程序使用 register_netdevice 注册,填充 net_device 结构,包括设备特定例程(如 hard_start_xmit 用于传输)。
- 数据传输: dev_queue_xmit 函数将 sk_buff 包排队发送到硬件,使用设备的 hard_start_xmit 方法。
- 数据接收: netif_rx 函数处理传入包,将其排队到上层处理。新 API(NAPI)通过减少中断提升高负载性能,尽管大多数驱动仍使用传统 netif_rx 接口。
设备驱动程序
网络栈底部是管理物理网络硬件的设备驱动程序(如以太网或 SLIP 接口)。
- 初始化: 驱动分配 net_device 结构,定义例程如 hard_start_xmit 用于包传输。
- 包处理:
- 传输:驱动将 sk_buff 数据移动到硬件队列或环。
- 接收:传入包封装在 sk_buff 中,并传递给 netif_rx 或 NAPI 的 netif_receive_skb。
- 驱动位置: 网络特定驱动位于 linux/drivers/net 中,针对特定硬件需求定制。
运行流程:从套接字到硬件
网络栈的操作可总结如下:
- 套接字创建: 用户空间应用程序创建套接字(如 socket(AF_INET, SOCK_STREAM, 0)),通过 inetsw_array 映射到 TCP。
- 协议交互: 系统调用如 connect 调用 proto_ops(如 inet_stream_ops),委托给协议特定函数(如 tcp_v4_connect)。
- 数据移动: 数据封装在 sk_buff 中,从应用层到设备层遍历栈,在各层添加或移除头。
- 传输/接收: 包通过 dev_queue_xmit 排队传输,或通过 netif_rx 处理接收,与硬件通过驱动交互。
关键结构总结
| 结构 | 位置 | 用途 |
|---|---|---|
| struct sock | linux/include/net/sock.h | 管理套接字状态和协议特定操作。 |
| struct proto | linux/include/net/sock.h | 定义协议特定操作(如 TCP、UDP)。 |
| struct sk_buff | linux/include/linux/skbuff.h | 封装网络通信的包数据和元数据。 |
| struct net_device | linux/include/linux/netdevice.h | 表示网络设备,定义驱动特定例程。 |
结语
Linux 网络栈是一个精心设计的系统,平衡了模块化、性能和灵活性。从系统调用到设备驱动的层级架构,确保了跨多种协议和硬件的稳健网络通信。通过利用 sk_buff 和 proto 等结构,Linux 为现代网络需求提供了可扩展框架,成为开发者和系统管理员的基石。