分类: LINUX
2014-01-03 14:05:57
原文地址:深入理解Linux网络技术内幕-关键数据结构(五) 作者:visualfan
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用于处理第二个步骤