net_device剖析

       网络设备接口层为了千变万化的设备定义了统一、抽象的数据结构net_device结构体来实现多种硬件在软件层次上的统一。net_device本身是一个巨型结构体,包含网络设备的属性描述和操作接口。

(1)全局信息

char name[IFNAMESIZ];

name是网络设备的名称。

int (*init)(struct net_device *dev);

init 为设备初始化函数指针,如果这个指针被设置了,则网络设备被注册时调用该函数完成对net_device结构体的初始化。但是,设备驱动程序可以不实现这个函数并将其赋值为NULL。

(2)硬件信息

unsigned long mem_end;
unsigned long mem_start;

mem_start和mem_end 分别定义了设备所使用的共享内存的起始和结束地址


unsigned long base_addr;
unsigned char irq;
unsigned char if_port;
unsigned char dma;

base_addr 为网络设备I/O 基地址。
irq为设备使用的中断号。
if_port指定多端口设备使用哪一个端口,该字段仅针对多端口设备。例如,如果设备同时支持IF_PORT_10BASE2(同轴电缆)和IF_PORT_10BASET(双绞线),则可使用该字段。
dma 指定分配给设备的DMA通道。

(3)接口信息


unsigned short hard_header_len;

hard_header_len 是网络设备的硬件头长度,在以太网设备的初始化函数中,该成员被赋为ETH_HLEN,即14。

</pre><pre name="code" class="cpp">unsigned short type;

type是接口的硬件类型。


unsigned mtu;

mtu指最大传输单元(MTU)。


unsigned char dev_addr[MAX_ADDR_LEN];
unsigned char broadcast[MAX_ADDR_LEN];

dev_addr[ ]、broadcast[ ]无符号字符数组,分别用于存放设备的硬件地址和广播地址。对于以太网而言,这两个地址的长度都为6 个字节。以太网设备的广播地址为6个0xFF,而MAC地址需由驱动程序从硬件上读出并填充到dev_addr[ ]中。


unsigned short flags;
flags 指网络接口标志,以IFF_(interface flags)开头,部分标志由内核来管理,其他的在接口初始化时被设置以说明设备接口的能力和特性。接口标志包括IFF_UP(当设备被激活并可以开始发送数据包时,内核设置该标志)、IFF_AUTOMEDIA(设备可在多种媒介间切换)、IFF_BROADCAST(允许广播)、IFF_DEBUG(调试模式,可用于控制printk调用的详细程度)、IFF_LOOPBACK(回环)、IFF_MULTICAST(允许组播)、IFF_NOARP(接口不能执行ARP)、IFF_POINTOPOINT(接口连接到点到点链路)等。

(4)设备操作函数

int (*open)(struct net_device *dev);
int (*stop)(struct net_device *dev);

open()函数的作用是打开网络接口设备,获得设备需要的I/O 地址、IRQ、DMA通道等。stop()函数的作用是停止网络接口设备,与open()函数的作用相反。

int (*hard_start_xmit) (struct sk_buff *skb,struct net_device *dev);

hard_start_xmit() 函数会启动数据包的发送,当系统调用驱动程序的hard_start_xmit()函数时,需要向其传入一个sk_buff 结构体指针,以使得驱动程序能获取从上层传递下来的数据包。

void (*tx_timeout)(struct net_device *dev);

当数据包的发送超时时,tx_timeout ()函数会被调用,该函数需采取重新启动数据包发送过程或重新启动硬件等策略来恢复网络设备到正常状态。

int (*hard_header) (struct sk_buff *skb,
struct net_device *dev,
unsigned short type,
void *daddr,
 *saddr,
unsigned len);

hard_header()函数完成硬件帧头填充,返回填充的字节数。传入该函数的参数包括sk_buff 指针、设备指针、协议类型、目的地址、源地址以及数据长度。对于以太网设备而言,将内核提供的eth_header()函数赋值给hard_header 指针即可。

struct net_device_stats* (*get_stats)(struct net_device *dev);

get_stats()函数用于获得网络设备的状态信息,它返回一个net_device_stats 结构体。net_device_stats结构体保存了网络设备详细的流量统计信息,如发送和接收到的数据包数、字节数等。

int (*do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
int (*set_config)(struct net_device *dev, struct ifmap *map);
int (*set_mac_address)(struct net_device *dev, void *addr);
do_ioctl()函数用于进行设备特定的I/O 控制。
set_config()函数用于配置接口,可用于改变设备的I/O 地址和中断号。

set_mac_address()函数用于设置设备的MAC地址。

int (*poll)( struct net_device *dev,int quota);
对于NAPI(网络中断缓和)兼容的设备驱动,将以轮询方式操作接口,接收数据包。NAPI 是Linux 系统上采用的一种提高网络处理效率的技术,它的核心概念就是不采用中断的方式读取数据包,而是采用首先借助中断唤醒数据包接收的服务程序,然后以轮询方式获取数据包。net_device结构体的上述成员需要在设备初始化时被填充。


(5)辅助成员。

unsigned long trans_start;
unsigned long last_rx;

trans_start记录最后的数据包开始发送时的时间戳,last_rx记录最后一次接收到数据包时的时间戳,这两个时间戳记录的都是jiffies,驱动程序应维护这两个成员。

void *priv;

priv 为设备的私有信息指针,与filp->private_data 的地位相当。设备驱动程序中应该以netdev_priv()函数获得该指针。


spinlock_t xmit_lock;
int xmit_lock_owner;
xmit_lock是避免hard_start_xmit()函数被同时多次调用的自旋锁。xmit_lock_owner则指当前拥有xmit_lock自旋锁的CPU的编号。


<think>我们想通过内核调试接口查看net_device结构体中的字段信息。在Linux内核中,有多种方式可以查看内核数据结构,包括使用调试工具、查看内核日志、通过/proc或/sys文件系统等。由于net_device结构体是内核内部数据结构,普通用户空间工具无法直接访问其原始内容,但我们可以借助以下方法: ### 方法1:使用`/sys/kernel/debug`调试文件系统(需要内核支持) 1. **确保内核启用DEBUG_FS** 在编译内核时需配置`CONFIG_DEBUG_FS=y`,挂载debugfs: ```bash mount -t debugfs none /sys/kernel/debug ``` 2. **查看网络设备调试信息** 部分网络驱动会通过debugfs暴露设备信息,路径通常为: ``` /sys/kernel/debug/<driver_name>/<interface_name>/ ``` 例如Intel网卡驱动(e1000e)可能提供寄存器、统计等信息,但**不一定直接显示net_device结构体**。 ### 方法2:通过内核函数追踪(动态打印) 使用`ftrace`或`perf`动态钩取内核函数,打印结构体字段: 1. **查找操作net_device的函数** 例如钩住`dev_open`函数(当网卡启用时调用): ```bash echo 'dev_open' > /sys/kernel/debug/tracing/set_ftrace_filter echo 1 > /sys/kernel/debug/tracing/events/net/net_dev_open/enable ``` 2. **打印结构体字段** 在`/sys/kernel/debug/tracing/trace_options`中启用`print-struct`选项,然后读取`/sys/kernel/debug/tracing/trace`查看输出。 ### 方法3:使用`crash`工具分析内存转储 若系统发生崩溃,可分析内存转储文件: 1. **安装crash工具** 需安装对应内核的调试符号包(如`kernel-debuginfo`)。 2. **查看指定net_device** ```bash crash /usr/lib/debug/lib/modules/<kernel_version>/vmlinux /var/crash/<dumpfile> crash> struct net_device -x eth0 ``` 此命令将显示`eth0`对应的`net_device`结构体所有字段的十六进制值。 ### 方法4:编写内核模块临时导出信息 编写一个内核模块,直接访问`net_device`并打印其字段(需谨慎操作): ```c #include <linux/netdevice.h> #include <linux/module.h> static int __init debug_init(void) { struct net_device *dev; dev = dev_get_by_name(&init_net, "eth0"); // 获取设备 if (dev) { printk("eth0 state: %lu\n", dev->state); // 示例:打印状态 printk("MTU: %u\n", dev->mtu); // 打印MTU dev_put(dev); } return 0; } module_init(debug_init); ``` 加载模块后通过`dmesg`查看输出。 ### 方法5:使用`drgn`动态内核调试器(推荐) `drgn`是一个可编程的调试工具,可直接读取运行中内核的内存: 1. **安装drgn** ```bash pip3 install drgn ``` 2. **编写drgn脚本** 创建脚本`netdev_dump.py`: ```python import drgn from drgn import container_of from drgn.helpers.linux import for_each_netdev prog = drgn.prog_from_kernel() # 连接到运行中内核 for dev in for_each_netdev(prog, prog['init_net']): if dev.name.string_().decode() == "eth0": print(dev) # 打印具体字段,例如: print("MTU =", dev.mtu) print("Flags =", hex(dev.flags)) break ``` 3. **执行脚本** ```bash sudo drgn netdev_dump.py ``` ### 注意事项 - **内核版本差异**:`net_device`结构体字段随内核版本变化,需参考对应版本的内核源码(如`include/linux/netdevice.h`)。 - **稳定性风险**:动态调试可能影响系统稳定性,建议在测试环境操作。 - **权限要求**:部分操作需要`root`权限。 --- ### 示例:`net_device`关键字段速查 | 字段名 | 含义 | 获取命令(用户空间) | |---------------------|--------------------------|----------------------------| | `dev->name` | 接口名 | `ifconfig eth0` | | `dev->mtu` | 最大传输单元 | `ip link show eth0` | | `dev->flags` | 状态标志(UP/BROADCAST等)| `ifconfig eth0` | | `dev->stats.rx_packets` | 接收包统计 | `ifconfig eth0` 或 `ip -s link` | --- ### 参考源码位置 - **net_device结构体定义**:`linux/include/linux/netdevice.h`[^1] - **设备注册逻辑**:`linux/net/core/dev.c`[^2] §§相关问题§§ 1. 如何通过内核源码查找特定版本的`net_device`结构体定义? 2. 使用`drgn`调试时如何遍历所有网络接口的私有数据? 3. 为什么`net_device`结构体被认为设计过于复杂? 4. 网络设备驱动如何向内核注册一个`net_device`实例? [^1]: Linux内核源码:netdevice.h [^2]: Linux内核源码:dev.c</think>要查看 `net_device` 结构体的字段信息,可以通过以下内核调试方法实现。这些方法需要 root 权限和内核调试支持: --- ### 一、使用 **`/sys/kernel/debug` 调试接口**(推荐) 1. **挂载 debugfs**(若未自动挂载): ```bash mount -t debugfs none /sys/kernel/debug ``` 2. **查看网络设备列表**: ```bash ls /sys/kernel/debug/netdev # 输出示例:eth0 eth1 lo ``` 3. **查看 `eth0` 的 `net_device` 字段**: ```bash cat /sys/kernel/debug/netdev/eth0 ``` **输出示例**: ``` name : eth0 state : UP mtu : 1500 flags : 0x1003 (UP,BROADCAST,RUNNING) tx_queue_len : 1000 ... ``` > **原理**:内核通过 `debugfs` 将 `net_device` 的关键字段导出为可读文本[^1]。 --- ### 二、使用 **`ftrace` 动态跟踪** 1. **启用函数追踪**: ```bash echo function > /sys/kernel/debug/tracing/current_tracer echo 'netif_*' > /sys/kernel/debug/tracing/set_ftrace_filter # 过滤网络相关函数 ``` 2. **打印结构体字段**: ```bash echo 1 > /sys/kernel/debug/tracing/options/print-struct # 启用结构体打印 echo 1 > /sys/kernel/debug/tracing/tracing_on ``` 3. **触发操作**(如重启网卡): ```bash ifconfig eth0 down && ifconfig eth0 up ``` 4. **查看输出**: ```bash cat /sys/kernel/debug/tracing/trace ``` **输出片段**: ``` netif_rx: dev=net_device(name="eth0", state=__LINK_STATE_START, mtu=1500) ``` --- ### 三、通过 **`crash` 工具**(需内核调试符号) 1. **安装调试符号包**: ```bash debuginfo-install kernel # CentOS/RHEL ``` 2. **进入 crash 环境**: ```bash crash /usr/lib/debug/lib/modules/$(uname -r)/vmlinux ``` 3. **查看 `net_device` 结构**: ```bash crash> struct net_device -x eth0 # -x 显示完整内存布局 ``` **输出示例**: ``` struct net_device { name = "eth0\0...", state = 0x1003, mtu = 1500, ... } ``` --- ### 四、编写 **内核模块** 直接读取 ```c #include <linux/netdevice.h> #include <linux/module.h> static int __init debug_init(void) { struct net_device *dev; dev = dev_get_by_name(&init_net, "eth0"); // 获取设备对象 if (dev) { pr_info("eth0 state: %lu\n", dev->state); // 打印状态 pr_info("MTU: %u\n", dev->mtu); // 打印MTU dev_put(dev); } return 0; } module_init(debug_init); ``` **操作步骤**: 1. 编译加载模块后,通过 `dmesg` 查看输出 2. **关键字段**: - `dev->name`:设备名 - `dev->mtu`:最大传输单元 - `dev->stats.rx_packets`:接收包计数 --- ### 五、查看内核源码定义 直接阅读头文件定义(路径随内核版本变化): ```bash grep -A 50 'struct net_device {' /usr/src/linux/include/linux/netdevice.h ``` **关键字段摘要**: ```c struct net_device { char name[IFNAMSIZ]; // 设备名 unsigned long state; // 状态位(UP/DOWN等) unsigned int mtu; // 最大传输单元 struct net_device_stats stats; // 统计信息(RX/TX包计数) // ... 其他100+字段 }; ``` --- ### 注意事项 1. **内核版本差异**:`net_device` 结构在不同内核版本中字段可能变化,需确认当前内核源码 2. **稳定性风险**:直接访问内核结构可能引发崩溃,建议在测试环境操作 3. **字段含义**:参考内核文档 [Documentation/networking/netdevices.txt](https://www.kernel.org/doc/Documentation/networking/netdevices.txt) --- ### 调试输出示例对比 | **字段** | **`/sys/kernel/debug` 输出** | **`ifconfig eth0` 对应项** | |-------------------|-----------------------------|---------------------------| | `name` | `name: eth0` | 接口名 `eth0` | | `state` | `state: UP` | `UP` 状态标志 | | `mtu` | `mtu: 1500` | `MTU:1500` | | `stats.rx_bytes` | `rx_bytes: 123456` | `RX bytes:123456` |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值