Linux网络技术学习(二)—— net_device数据结构解析

文章目录


网络设备:net_device数据结构

net_device数据结构存储着特定网络设备的所有信息。无论是真是设备(如 Ethernet)或虚拟设备(如Bonding 或 VLAN)

net_device数据结构定义文件位置

net_device数据结构定义在include/linux/netdevice.h
Linux提供了一个通用函数,可以对一些参数做初始化,使其值在所有模型之间都一样。除了针对模型设置每个设备驱动程序都会启用此函数。

net_device数据结构分布

net_device结构的字段分为:配置、统计数据、设备状态、列表管理、流量管理、功能专用、通用、函数指针


标识符

net_device结构有三个标识符

int ifindex

独一无二的ID,当设备以dev_new_index注册时分配给每个设备。

unsigned short dev_id

用于区别可由不同OS同时共享的同一种设备的诸多虚拟实例


配置

char name[IFNAMSIZ]

设备的名称(如:eth0)

unsigned long mem_start

unsigned long mem_end

描述设备所用的共享内存,用于设备与内核沟通

unsigned long base_addr

设备自有内存映射到I/O内存的起始地址

unsigned char if_port

此接口所使用的端口类型

接口类型和端口
有些设备有一个以上的连接器(组合BNC+RJ45),允许用户根据需求选择其中之一使用。参数用于设备的端口类型。
当设备驱动程序没有通过配置命令强制选择特定的端口类型时,就会简单地选择默认的端口类型。
驱动程序可以处理不同的接口模型

    case IF_PORT_10BASET: 
        dev->if_port = map->port;
        netif_carrier_off(dev);
        status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
        mdio_write(dev, mii_phy->phy_addr,
               MII_CONTROL, status & ~(MII_CNTL_SPEED |
                MII_CNTL_AUTO));
        break;
    case IF_PORT_100BASET:
    case IF_PORT_100BASETX: 
        dev->if_port = map->port;
        netif_carrier_off(dev);
        status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
        mdio_write(dev, mii_phy->phy_addr,
               MII_CONTROL, (status & ~MII_CNTL_SPEED) |
               MII_CNTL_SPEED);

unsigned char dma

设备所使用的DMA通道。
文件:arch/arm/kernel/dma.c
内存获取DMA:request_dma 内核释放DMA:free_dma
开启DMA:enable_dma 关闭DMA:disable_dma

unsigned short flags

unsigned short gflags

unsigned short priv_flags

flags字段中的某些位代表网络设备的功能(如IFF_MULTICAST),其他位代表状态的改变(IFF_UP或IFF_RUNNING)。所有的完整列表文件在<include/uapi/linux/if.h>
设备驱动程序通常会在初始化期间设置这些功能,有内核管理,以响应外部事件。(如:ifconfig)
在这里插入图片描述
UP LOOPBACK RUNNING相当于IFF_UP IFF_LOOPBACK IFF_RUNNING
priv_flags用于存储用户空间不可见的标识,由虚拟设备(VLAN和Bridge)使用
gflags几乎不用

u64 features

用于存储其他一些设备功能。features可报告适配卡的功能,以便与CPU通信

unsigned int mtu

MTU(Maximum Transmission Unit)代表最大传输单元 ,表示设备能处理的帧的最大尺寸
在这里插入图片描述
Ethernet 帧规范把最大有效载荷尺寸定在1500个字节。
有时发现Ethernet MTU定义为1518或1514:第一个帧最大尺寸包含报头在内;第二个包含报头但不含帧检查序列(4个字节的校验和)

unsigned short type

设备所属的类型(PPP、CAN等)完整列表<include/uapi/linux/if_arp.h>

unsigned short hard_header_len

以字节为单位的设备头的大小。(如Ethernet报头是14字节)完整列表</usr/include/linux/if_ether.h>

unsigned char broadcast[MAX_ADDR_LEN]

链路层广播地址

unsigned char dev_addr[MAX_ADDR_LEN]

unsigned char addr_len

dev_addr是设备链路层地址,地址的字节长度由addr_len指定。addr_len的值依赖于设备的类型。Ethernet地址都是6个字节

int promiscuity

混杂模式
某些网络管理任务会要求一个系统接收在一条共享缆线传播的所有帧,而不是仅限于地址直接指定给系统的帧。
一个设备如果可以接收所有封包,就意味着处于混杂模式
net_device结构包含一个名为promiscuity计数器,表示一个设备处于混杂模式中。采用计数器的模式而不是标识的模式放入原因在于。多个客户程序可能都会要求混杂模式。进入混杂模式时递增计数器,而退出该模式时递减计数器。除非计数器为零,否则该设备不会退出混杂模式
该字段的操作通过dev_set_promiscuity
根据flags字段中的标识而设置不同接收模式

static void set_rx_mode(struct net_device *dev)
{
    struct vortex_private *vp = netdev_priv(dev);
    void __iomem *ioaddr = vp->ioaddr;
    int new_mode;

    if (dev->flags & IFF_PROMISC) {                                      // 当IFF_PROMISC标志位被置位时,就会对new_mode进行初始化
        if (vortex_debug > 3)
            pr_notice("%s: Setting promiscuous mode.\n", dev->name);
        new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm;  
    } else  if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) {
        new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast;
    } else
        new_mode = SetRxFilter | RxStation | RxBroadcast;

    iowrite16(new_mode, ioaddr + EL3_CMD);   // 接收地址指定网卡的数据流RxStation、多播数据流RxMulticast、广播数据流RxBroadcast等
    // EL3_CMD是对ioaddr内存地址的偏移量,代表与设备交互时命令应拷贝到哪里
}

统计数据

net_device结构没有提供一个用来记录统计数据的收集字段(瑞芯微RK包含struct net_device_stats),而是引入一个由驱动程序设置的prive指针,指向一个存储有关接口信息的私有数据结构。私有数据由统计数据组成(如 已收发的封包数目,以及已经发生的错误数目)
几乎不同的Ethernet卡使用不同的私有结构。私有结构的格式取决于设备的类型以及特定的模型。
net_device_stats包含所有网络设备共有的统计数据,可以通过get_stats方式获取
私有数据结构复杂度多少,依赖于适配卡的功能,以及设备驱动程序编写者打算采用多精密的统计数据和复杂的设计以提高性能

设备状态

为了控制与NIC之间的交互,每个设备驱动程序都必须维护一些信息,如表示接口需要哪些行为的时间戳和标识。

unsigned long state

由网络队列子系统多使用的一组标识。其索引值是enum netdev_state_t(<include/linux/netdevice.h>)中的常数。
位的设置和清除都使用通用的函数set_bit和clear_bit

set_bit(__LINK_STATE_START, &dev->state);
clear_bit(__LINK_STATE_START, &dev->state);

enum { … } reg_state

设备的注册状态

unsigned long trans_start

最近的一个帧传输启动的时间(以jiffies测量)。设备驱动程序会在传输前设置此值。如果在一段给定时间后传输没有完成,这个字段用于检测适配卡的问题。传输时间长就意味着有地方出错了,驱动程序通常复位适配卡 。

spinlock xmit_lock

int xmit_lock_owner

xmit_lock锁使驱动程序函数hard_start_xmit的访问串行化。每个CPU一次只能对任何定的一个设备做一次传输。
xmit_lock_owner是持有该锁的CPU的ID

void *atalk_ptr

void *ip_ptr

void *dn_ptr

void *ip6_ptr

void *ec_ptr

这6个字段都是指针,指向特定协议专用的数据结构,每个数据结构都包含一些该协议私有的参数。
如,ip_ptr指向一个类型为in_device的数据结构,包含各种不同的与IPv4相关的参数IP地址列表等


列表管理

net_device数据结构均被插入到一个全局列表和两个hash表中

struct net_device *next

把每个net_device数据结构链接为全局列表中的下一个(next)元素。

struct hlist_node name_hlist

struct hlist_node index_hlist

把net_device结构链接至两个hash表的bucket列表


链路层多播

多播是一种用于数据传递多位接收者的机制。多播可以在L3网络层(IP)以及L2链路层(Ethernet)中使用。
链路层多播的传送,可以通过在链路层报头中使用特殊地址或者控制信息。Ethernet本身就支持多播(Ethernet地址如何划分为单播、多播或广播)
利用一个特定位把多播地址和其他范围的地址区分开来。这意味着可能地址中有50%是多播(如2^48的50%是很大的数),这比维护一份很长的列表更有效率

net_decive数据结构中的flags之一就是用于表示该设备是否监听所有地址。

int allmulti

用于监听所有地址。当此变量从0变为非零时,就会调用dev_set_allmulti函数,以指示该接口监听所有多播地址。

每个设备都会为其监听的每个链路层多播地址保存一个dev_mc_list结构的实例。链路层多播地址可以分别用dev_mc_add和dev_mc_delete函数添加或删除

struct dev_mc_list *mc_list

指向此设备的dev_mc_list结构列表表头的指针

int mc_count

此设备的多播地址数目,也就是mc_list所指的列表长度


流量管理

struct net_device *net_sched

用于软中断之一使用

struct Qdisc *qdisc

struct Qdisc *qdisc_sleeping

struct Qdisc *qdisc_ingress

struct list_head qdisc_list

用于管理入口和出口的封包队列

spinlock_t queue_lock

spinlock_t ingress_lock

流量控制子系统为网络设备定义了一个私有的出口队列
queue_lock用于避免对出口队列的并发访问;ingress_lock用于避免入口队列的并发访问

unsigned long tx_queue_len

设备的传送队列的长度。当内核支持流量控制时,可以不用tx_queue_len。可以通过文件系统控制
不同设备类型的tx_queue_len值
在这里插入图片描述
在这里插入图片描述


功能专用

struct divert_blk *divert

分流器是一种功能,允许你改变输入的封包的源和目的地址。因此,有可能以此配置所指定的特征特征重新发送流量至不同的接口或不同的主机。

struct net_bridge_port *br_port

此设备配置成桥接端口时,就需要额外的信息。

void (*vlan_rx_register)(…)

void (*vlan_rx_add_vid)(…)

void (*vlan_rx_kill_vid)(…)

函数指针由VLAN代码使用


通用

int watchdog_timeo

struct timer_list watchdog_timer

实现看门狗定时器

int (*poll)(…)

struct list_head poll_list

int quota

int weight

由NAPI功能使用

const struct iw_handler_def *wireless_handlers

struct iw_public_data *wireless_data

用于无线设备的参数和指针函数

struct list_head todo_list

网络设备的注册和除名。todo_list用于处理第二步骤


函数指针

net_device数据结构中也有函数指针,这些函数主要用于:传输和接收帧;在缓冲区上添加或解析链路层报头;改变配置的一部分;获取统计数据;与特定功能交互

struct ethtool_ops *ethtool_ops

指向一组函数指针的指针,用于设置或取出不同设备参数的配置

struct net_device_ops *netdev_ops;

里面包含初始化、清理、销毁、开启以及关闭设备,设置mac地址等函数


参考文献

《Understanding Linux Network Internals》

  • 2
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bazinga bingo

您的鼓励就是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值