1. 嵌入式Linux网络基础:TCP/IP协议栈回顾、Linux网络设备模型、网络驱动架构概览
各位同学,咱们今天正式开课。做嵌入式Linux网络开发,很多人一上来就撸驱动代码,结果遇到问题抓瞎。我个人的习惯是,先花点时间把地基打牢。这一章,我们就来聊聊网络基础——TCP/IP协议栈、Linux网络设备模型,还有驱动架构的全貌。
说白了,你写的网络驱动,最终是要跟协议栈对话的。你不了解协议栈的脾气,驱动写得再花哨,也是白搭。
1.1 TCP/IP协议栈回顾
先问大家一个问题:一个数据包从你的网卡发出去,到对端应用程序收到,中间经历了什么?
嗯,这就是TCP/IP协议栈干的事。它分四层,每一层各司其职。
| 层级 | 核心协议 | 职责 | 嵌入式中的典型体现 |
|---|---|---|---|
| 应用层 | HTTP, MQTT, CoAP | 为用户提供网络服务 | 你的业务逻辑代码 |
| 传输层 | TCP, UDP | 端到端连接、流量控制 | Socket编程接口 |
| 网络层 | IP, ICMP, ARP | 路由、寻址、分片 | net/ipv4 目录下的代码 |
| 链路层 | 以太网, MAC | 帧封装、介质访问 | 你的网卡驱动 |
我在项目中遇到过一个问题:一个IoT设备,用UDP发送数据,偶尔丢包。查了半天,发现是应用层发的包太大,IP层分片后,某个分片丢了,整个UDP报文就废了。你看,不懂网络层,你连问题都定位不了。
关键点:在嵌入式Linux中,协议栈是内核的一部分。驱动开发者最常打交道的是链路层和网络层。你写的 hard_start_xmit 函数,就是链路层把帧交给硬件的最后一步。
1.2 Linux网络设备模型
Linux内核怎么管理网络设备?它有一套统一的模型。你想想看,一个USB网卡和一个PCIe网卡,驱动完全不同,但内核怎么把它们统一管理起来的?
答案就是 struct net_device。这是所有网络设备的抽象。
// 一个简化的 net_device 结构体视角
struct net_device {
char name[IFNAMSIZ]; // 设备名,比如 eth0
unsigned long mem_end; // 内存区域
unsigned long mem_start;
unsigned long base_addr; // I/O 基地址
unsigned int irq; // 中断号
unsigned char dev_addr[MAX_ADDR_LEN]; // MAC 地址
const struct net_device_ops *netdev_ops; // 驱动操作函数集
// ... 还有几百个字段
};
这个结构体很大,但别怕。你只需要关注几个核心字段:
- name:设备名,用户空间看到的那个。
- netdev_ops:驱动要实现的函数指针表,比如打开、关闭、发送、设置MAC等。
- dev_addr:MAC地址,驱动初始化时要填好。
我曾经接手过一个项目,板子上有两个网卡,一个有线一个无线。结果系统启动后,两个设备名随机分配,导致应用层配置脚本总是配错。后来我查了内核文档,才知道可以通过 devicetree 的 aliases 节点固定设备名。嗯,这里要注意,设备模型不仅仅是数据结构,还涉及用户空间的udev规则和设备树。
我的建议:调试网络设备时,先 cat /proc/net/dev 看看设备列表和统计信息。这比看代码快多了。
1.3 网络驱动架构概览
好,现在我们把目光聚焦到驱动本身。一个典型的网络驱动,从上到下分三层:
- 内核协议栈接口层:通过
net_device_ops与协议栈交互。 - 核心逻辑层:处理硬件寄存器、DMA描述符、中断处理。
- 硬件接口层:直接操作MAC/PHY芯片。
我画了一张图,帮你理清这个关系。
这张图很直观。数据发送时,协议栈调用驱动的 ndo_start_xmit,驱动把数据塞进DMA描述符,硬件自动发出去。数据接收时,硬件产生中断,驱动在中断处理里把数据收上来,通过 netif_rx 或 NAPI 送给协议栈。
避坑指南:我曾经在中断处理里直接调用 netif_rx,结果在高负载下系统卡死。后来改用NAPI,问题解决。记住:中断上下文里别做耗时操作,能用NAPI就别用纯中断。
1.4 关键数据结构速览
写驱动,离不开几个核心数据结构。我列个表,你心里有个数。
| 数据结构 | 所在头文件 | 作用 |
|---|---|---|
struct net_device |
linux/netdevice.h |
网络设备抽象,驱动的主结构体 |
struct net_device_ops |
linux/netdevice.h |
驱动函数表,协议栈通过它调用驱动 |
struct sk_buff |
linux/skbuff.h |
套接字缓冲区,数据包在内核中的载体 |
struct napi_struct |
linux/netdevice.h |
NAPI轮询结构体,用于高效收包 |
struct ethtool_ops |
linux/ethtool.h |
用户空间 ethtool 工具的操作接口 |
这里我要特别提一下 sk_buff。这玩意儿是网络数据包在内核里的“快递箱”。你驱动收包时,要分配一个 sk_buff,把硬件收到的数据拷进去,然后发给协议栈。发包时,协议栈给你一个 sk_buff,你把数据取出来,塞给硬件。
核心原则:驱动不负责解析协议,只负责搬运数据。协议栈才是干脑力活的。
1.5 本章小结
这一章我们回顾了TCP/IP协议栈的四层模型,重点看了链路层和网络层。然后深入了Linux网络设备模型的核心——net_device。最后,用一张架构图理清了网络驱动的三层结构。
说白了,网络驱动就是协议栈和硬件之间的翻译官。你理解了协议栈的脾气,掌握了设备模型的套路,写驱动就是填函数表、配寄存器的事。