现在的位置: 首页 > 综合 > 正文

深入理解Linux网络技术内幕-关键数据结构(五)

2013年04月26日 ⁄ 综合 ⁄ 共 7330字 ⁄ 字号 评论关闭

net_device结构

net_device结构存储特定网络设备的所有信息。不管真实设备如网卡还是虚拟设备如网桥,都有这样一个结构。
    net_device这个结构也非常庞大,包括很多特定功能的参数,以及其他来自不同分层的参数,因此这个结构在Linux版本中的变化也很快。
net_device结构的字段大致分为以下几类:
配置字段
统计数据
设备状态
列表管理
流量管理
功能专用
通用字段
函数指针

标识符
net_device有三个标识符
int ifindex:独一无二的ID,当设备以dev_new_index注册时分判给每个设备的。
int iflink:这个字段主要是由虚拟隧道设备使用,可用于标识隧道另一端的真实设备
unsigned short dev_id:目前在zSeries ISA NIC上由IPv6所用,此字段用于区别可由不同OS同时共享的同一种设备的诸多虚拟实例。

配置字段
有些配置字段由内核根据网络设备的种类给定一个默认值,而其他字段留给驱动程序填写。驱动程序可以改变默认值,而有些字段可以再执行期间通过像ifconfig和ip的命令进行改变。而有些字段如base_addr、if_port、dma和irq在设备模块加载时由用户设置的,虚拟设备不会使用这些参数。

char name[IFNAMSIZ] 设备名称,如eth0

unsigned long mem_start
unsigned long mem_end 这些字段描述设备所用的共享内存,用于设备与内恶化通信。其初始化和访问只会在设备驱动程序内进行

unsigned long base_addr 设备自身内存映射到I/O内存的起始地址

unsigned int irq 设备用于与内核通信的中断编号,此值可由多个设备共享。驱动程序使用request_irq函数分配此值,使用free_irq释放

unsigned char if_port 此接口所使用的端口类型

unsigned char dma 设备所使用的DMA通道。为了从内核获取和释放DMA通道,文件定义了request_dma和free_dma。取得DMA通道后,使用enable_dma和disable_dma开启和关闭dma通道。这些函数由ISA设备使用,PCI设备采用其他方法,因而不需要这些函数。有些总线不使用DMA。

unsigned short flags
unsigned short gflags
unsigned short priv_flags
flags字段中的某些位代表网络设备的功能如IFF_MULTICAST,而有些位表示状态的改变如IFF_UP或IFF_RUNNING,在include/linux/if.h中有这些标识的完整列表。设备驱动程序在初始化期间设置这些功能,而这些状态标识由内核管理。ifconfig命令可以查看网络设备的一些状态。
priv_flags用于存储用户空间不可见的标识,目前此字段主要由VLAN和Bridge虚拟设备使用
gflags几乎已不再使用,只是由于兼容性而存在。
这些标识可以通过dev_change_flags函数进行修改

int features 表示网卡当前激活的功能,便于与CPU通信。如能不能对高端内存做DMA或硬件能不能对所有封包做检验和工作。此参数由设备驱动程序初始化。在net_device数据结构定义猪可以找到NETIF_F_XXX特征功能列表。

unsigned int mtu MTU代表最大传输单元,表示设备能处理的帧的最大尺寸。

unsigned short type 设备所属类型,在include/linux/if_arp.h包含可能类型的列表

unsigned short hard_header_len 以字节为单位的设备头的大小,如Ethernet报头为14字节。每个设备头的长度定义在该设备头的头文件中。如对于Ethernet,ETH_HLEN就定义在include/linux/if_ether.h中

unsigned char broadcast[MAX_ADDR_LEN] 链路层广播地址

unsigned char dev_addr[MAX_ADDR_LEN]

unsigned char addr_len 设备链路层地址和长度。addr_len的值与设备的类型相关,如Ethernet的地址为6字节

int promiscuity 混杂模式


接口类型和端口

有些设备有一个以上的连接器(如BNC+RJ45),允许用户根据需求选择其中之一使用,if_port这个参数用于设备设备的端口类型。当设备没有配置选择特定的端口类型,则会选择默认的端口类型。

混杂模式

一个系统可以接收在一条共享光缆上传播的所有帧,而不仅仅是地址直接指定给该系统的帧,则说明其处于混杂模式。

net_device结构的promiscuity计算器,表示设备处于混杂模式中。采用计数器而不是简单标识,是因为多个客户程序可能都会要求混杂模式。因此,请求进入混杂模式就递增这个计数器,退出则递减。除非计数器为0,否则该设备不会退出混杂模式。通过dev_set_promiscuity函数对此字段进行操作。

当promiscuity为非零时,flags的IFF_PROMISC标志位也会设置,并由此接口的函数进行检查。

static int __dev_set_promiscuity(struct net_device *dev, int inc) 



    unsigned short old_flags = dev->flags; 

    uid_t uid; 

    gid_t gid;

    ASSERT_RTNL();

    dev->flags |= IFF_PROMISC; 

    dev->promiscuity += inc; 

    if (dev->promiscuity == 0) { 

        /* 

         * Avoid overflow. 

         * If inc causes overflow, untouch promisc and return error. 

         */ 

        if (inc < 0) 

            dev->flags &= ~IFF_PROMISC; 

        else { 

            dev->promiscuity -= inc; 

            printk(KERN_WARNING "%s: promiscuity touches roof, " 

                "set promiscuity failed, promiscuity feature " 

                "of device might be broken.\n", dev->name); 

            return -EOVERFLOW; 

        } 

    }

………………

}

 

统计数据

net_device结构中stats参数用于统计网络数据包信息。struct net_device_stats 结构包含了所有网络设备共有的统计数据,定义在include/linux/netdevice.h头文件中,通过get_stats方法获取网卡状态信息。

struct net_device_stats { 

    unsigned long    rx_packets; 

    unsigned long    tx_packets; 

    unsigned long    rx_bytes; 

    unsigned long    tx_bytes; 

    unsigned long    rx_errors; 

    unsigned long    tx_errors; 

    unsigned long    rx_dropped; 

    unsigned long    tx_dropped; 

    unsigned long    multicast; 

    unsigned long    collisions; 

    unsigned long    rx_length_errors; 

    unsigned long    rx_over_errors; 

    unsigned long    rx_crc_errors; 

    unsigned long    rx_frame_errors; 

    unsigned long    rx_fifo_errors; 

    unsigned long    rx_missed_errors; 

    unsigned long    tx_aborted_errors; 

    unsigned long    tx_carrier_errors; 

    unsigned long    tx_fifo_errors; 

    unsigned long    tx_heartbeat_errors; 

    unsigned long    tx_window_errors; 

    unsigned long    rx_compressed; 

    unsigned long    tx_compressed; 

};

无线设备的这些信息通过iw_statistics结构进行保存,通过get_wireless_stats函数获取。

struct    iw_statistics 



    __u16        status;        /* Status 

                     * - device dependent for now */

    struct iw_quality    qual;        /* Quality of the link 

                         * (instant/mean/max) */ 

    struct iw_discarded    discard;    /* Packet discarded counts */ 

    struct iw_missed    miss;        /* Packet missed counts */ 

};

 

设备状态

为了控制与NIC之间的交互,每个设备驱动程序都必须维护一些信息。在SMP系统中,内核也必须确保不同CPU对同一个设备并发访问的正确处理。net_device结构猪的以下几个字段就是专门用于这些类型的信息:

unsigned long        state;由网络队列子系统使用的一组标识。其索引值是enum netdev_state_t中的常数。个别位的设置和清除都使用函数set_bit和clear_bit函数。这两个函数通常都会被一个更明确的函数封装起来使用。如停止一个设备队列时,子系统调用netif_stop_queue函数:

enum netdev_state_t { 

    __LINK_STATE_START, 

    __LINK_STATE_PRESENT, 

    __LINK_STATE_NOCARRIER, 

    __LINK_STATE_LINKWATCH_PENDING, 

    __LINK_STATE_DORMANT, 

};

static inline void netif_stop_queue(struct net_device *dev) 



    netif_tx_stop_queue(netdev_get_tx_queue(dev, 0)); 

}

static inline void netif_tx_stop_queue(struct netdev_queue *dev_queue) 



    if (WARN_ON(!dev_queue)) { 

        pr_info("netif_stop_queue() cannot be called before register_netdev()\n"); 

        return; 

    } 

    set_bit(__QUEUE_STATE_XOFF, &dev_queue->state); 

}

 

/* register/unregister state machine */ 

    enum { NETREG_UNINITIALIZED=0, 

           NETREG_REGISTERED,    /* completed register_netdevice */ 

           NETREG_UNREGISTERING,    /* called unregister_netdevice */ 

           NETREG_UNREGISTERED,    /* completed unregister todo */ 

           NETREG_RELEASED,        /* called free_netdev */ 

           NETREG_DUMMY,        /* dummy device for NAPI poll */ 

    } reg_state:16;

设备的注册状态

/* 

     * trans_start here is expensive for high speed devices on SMP, 

     * please use netdev_queue->trans_start instead. 

     */ 

    unsigned long        trans_start;    /* Time (in jiffies) of last Tx    */

这个表示最近的一个帧传输启动的时间。设备驱动程序会在传输前设置此值。如在一段给定的时间后传输没有完成。这个字段用于检测适配卡的问题。在这种情况下,驱动程序通常复位适配卡。

unsigned long last_rx 最后一个封包到达的时间

struct net_device *master 有些协议运行一组设备群集起来作为单一设备。这些协议包括EQL、Bonding、以及流量控制的TEQL队列规则。群组中的一个设备会被选定为所谓的主设备。这个字段是一个指针,指向主设备的net_device结构。若此接口不是这类群组中的成员之一,则此指针是NULL。

spinlock_t xmit_lock

int xmit_lock_owner

xmit_lock锁使驱动程序函数hard_start_smit的访问串行化。每个CPU一次只能对任何给定的一个设备做一次传输。xmit_lock_owner是持有该锁的CPU的ID,对单系统而言这个值总为0,而在SMP系统中该锁没有被取走时,其值为-1。当设备驱动程序支持时,也可以做无锁的传输。

void *atalk_ptr

void *ip_ptr

void *dn_ptr

void *ip6_ptr

void *ec_ptr

void *ax25_ptr

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

列表管理

net_device数据结构保存在链表和两个hash表中。

/* device name hash chain */ 

struct hlist_node    name_hlist;

/* device index hash chain */ 

struct hlist_node    index_hlist;

 

链路层多播

多播是一种把数据传递给多个接收者的机制。多播可以再网络层和链路层中使用。链路层的多播需要在链路层报头猪使用特殊地址和控制信息。Ethernet本身就支持多播。

利用一个特定位把多播地址和其他范围的地址区分开来。当一个接口被要求加入多个多播群组时,则该接口只简单监听所有多播地址。net_device结构猪的flags之一就是用于表示该设备是否监听所有地址,决定何时设置和清除此标志,由allmulti字段控制。每个设备都会为其监听的每个链路层多播地址保存一个dev_mc_list结构的实例。链路层多播地址可以分别用dev_mc_add和dev_mc_delete函数添加或删除。

struct netdev_hw_addr_list    mc;    /* Multicast mac addresses */指向此设备的dev_mc_list结构列表表头的指针和数目

int allmulti 为非0时,引起此设备监听所有的多播地址。allmulti如promiscuity一样,是一个计数器而不是简单的布尔值。当此变量由0变为非0时,就会调用dev_set_allmulti函数,以指示该端口监听所有的多播地址。当allmulti变为0时,就会发生相反的事情。

流量管理

流量管理的内核配置选项为Device drivers->Networking support->Networking options->QoS and/or fair queueing

net_device结构中的相关字段为:

struct Qdisc        *qdisc; 用于管理入口和出口的封包队列 

unsigned long        tx_queue_len;    /* Max frames per queue allowed */队列允许的最大长度

注意,所有队列长度为0的设备都是虚拟设备,虚拟设备依赖相关联的真实设备区做所有的队列化工作,回环设备除外。

 

通用字段

int            watchdog_timeo; /* used by dev_watchdog() */ 

struct timer_list    watchdog_timer; //看门狗定时器

const struct iw_handler_def *    wireless_handlers;

struct iw_public_data *    wireless_data;

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

/* delayed register/unregister */ 

struct list_head    todo_list;

网络设备的注册和注销是以两个步骤完成的。todo_list用于处理第二个步骤


参考资料:
【上篇】
【下篇】

抱歉!评论已关闭.