Linux网络协议栈(三)——网络设备(1)

网络设备(network device)是内核对网络适配器(硬件)的抽象与封装,并为各个协议实例提供统一的接口,它是硬件与内核的接口,它有两个特征:
(1)    做为基于硬件的网络适配器与基于软件的协议之间的接口;
(2)    内核协议栈异步输入输出点。
记住:网络设备软件对硬件的抽象
网络设备与协议和网络适配器的关系以下:

一、    net_device接口(net_device Interface)
     网络设备是内核中除了字符设备、块设备以外第三类主要设备,它的主要特征之一就是在设备文件系统/dev/没有相应的表示,即不存在/dev/eth0等,这就意味着不能经过简单的读写操做来访问它们。
    net_device结构保存与网络设备相关的全部信息。每个网络设备都对应一个这样的结构,包括真实设备(例如以太网卡)和虚拟设备(好比 bonding 或 VLAN)。
全部设备的 net_device 结构都放在一个全局链表中,链表的头指针是 dev_base。net_device结构的定义在include/linux/netdevice.h中。与 sk_buff 相似,net_device 结构比较大,并且包含了不少特性相关的参数,这些参数在不一样的协议层中使用。出于这个缘由,net_device 结构的组织会有一些改变,用于优化协议栈的性能。 网络设备能够分为不一样的类型,好比以太网卡和令牌环网卡。net_device 结构中的某些变量对同一类型的设备来讲, 取值是相同的; 而某些变量在同一设备的不一样工做模式下,取值必须不一样。所以,对几乎全部类型的设备,linux内核提供了一个通用的函数用于初始化那些在全部模式下取值相同的变量。每个设备驱动在调用这个函数的同时,还初始化那些在当前模式下取值不一样的变量。设备驱动一样能够覆盖那些由内核初始化的变量(例如,在优化设备性能时)。
  net_device的定义:node

Code

    net_device结构主要分为如下几部分:
1.一、    通用字段
name:
网络适配器的名称,好比eth0。在注册网络设备时能够为设备分配一个名称,便必须惟一。
next:
全部的网络设备组成一个由dev_base开头的链表。
int ifindex :
全局惟一的设备ID。在每一个设备注册时,调用dev_new_index 生成。
int iflink:
这个变量主要被(虚拟)隧道设备使用,用于标识隧道的真实设备。
state:
它包含一组被网络队列子系统使用的标记。这些标记的值是枚举类型netdev_state_t中的索引值,这个类型的定义在 include/linux/netdevice.h 中,每一个标记都是诸如__LINK_STATE_XOFF 这样的常量。每一位均可以经过函数 set_bit 和 clear_bit 设置或清除,但一般状况下,都会有一个包装函数来隐藏标记位的信息。例如,在网络队列子系统中止一个设备队列时,它调用函数 netif_stop_queue,这个函数的定义以下: 
  static inline void netif_stop_queue(struct net_device *dev) 

        ... 
        set_bit(_ _LINK_STATE_XOFF, &dev->state); 
}
trans_start:
最后一个帧开始发送的时间(用jiffies度量)。设备驱动在发送以前设置这个变量。这个变量用来检测网卡是否在给定的时间内把帧发送了出去。 太长的发送时间意味
着有错误发生,在这种状况下,设备驱动会重置网卡。
last_rx :
接收到最后一个包的时间(用jiffies度量)。
xmit_lock 和xmit_lock_owner :
xmit_lock 用来序列化对设备驱动函数hard_start_xmit的调用。这意味着,每一个cpu每次只能调用设备完成一次发送。xmit_lock_owner 是拥有锁的 CPU 的 ID。在单cpu 系统上,这个值是 0;在多 cpu 系统中,若是锁没有被占用,这个值是-1。内核一样容许不加锁的发送,前提条件是设备驱动必须支持这个功能。
struct hlist_node name_hlist 
struct hlist_node index_hlist 
  把net_device结构连接到两个哈希表中。
1.二、    硬件相关
unsigned int irq 
  设备中断号。它能够被多个设备共享。设备驱动调用request_irq来分配这个值,并
调用free_irq来释放它。 
unsigned char if_port 
  接口的端口类型。有些设备能够支持多种接口(最多见的组合是 BNC+RJ45),用户能够根据须要来选择使用哪一种接口。这个变量用来设置设备的接口类型。若是配置命令没有指定设备的接口类型,设备驱动就使用缺省的类型。在某些状况下,一个设备驱动能够处理多种接口类型;在这种状况下,设备驱动能够按必定的顺序来测试每一个接口的类型。下面的代码片段展现了一个设备驱动如何根据配置来设置接口的类型: 
  switch (dev->if_port) { 
                case    IF_PORT_10BASE2: 
                       writeb((readb(addr) & 0xf8) | 1, addr); 
                        break; 
                case    IF_PORT_10BASET: 
                       writeb((readb(addr) & 0xf8), addr); 
                        break; 
                } 
unsigned char dma 
  设备所使用的 DMA 通道。为获取和释放一个 DMA 通道,内核在 kernel/dma.c 中定义了两个函数request_dma和free_dma。为了在获取dma通道后,启用或者中止dma通道,内核定义了两个函数enable_dma和disable_dma。这两个函数的实现与
体系结构相关,因此在 include/asm-architecture 下有相关的文件(例如include/asm-i386)。这些函数被 ISA 设备使用;PCI 设备不使用这些函数,它们使
用其余函数。并非全部的设备均可以使用dma,由于有些总线不支持dma。
 unsigned long mem_start 
unsigned long mem_end 
  这两个变量描述设备与内核通讯所用到的内存边界。它们由设备驱动初始化,而且只能被设备驱动访问;高层协议不须要关心这块内存。 
unsigned long base_addr 
  映射到设备内存空间中I/O 内存起始地址。
1.三、    物理层相关
unsigned mtu 
  MTU 的意思是最大传输单元,它表示设备能够处理帧的最大长度。不一样设备的MTU值:

unsigned short type 
    设备类型(以太网,帧中继等)。在include/linux/if_arp.h 中有完整的类型列表。 
unsigned short hard_header_len 
  以字节为单位的帧头部长度。例如,以太网帧的头是 14 字节。某种设备所支持帧的头部长度在相应的设备头文件中定义。对以太网来讲,ETH_HLEN 在
<include/linux/if_ether.h>中定义。 
unsigned char broadcast[MAX_ADDR_LEN] 
  链路层广播地址。 
unsigned char dev_addr[MAX_ADDR_LEN] 
unsigned char addr_len 
  dev_addr是设备的链路层地址,不要把它和IP 地址或者L3 地址混淆了。链路层地址的长度是 addr_len,以字节为单位。addr_len 的大小与设备类型有关。以太网地址的长度是8。 
int promiscuity
promiscuity计数器来标识设备是否工做在混杂模式。之因此使用计数器而不是一个标志位的缘由是:可能有多个用户程序设置设备工做在混杂模式下。所以,每次进入混杂模式,计数器加一;退出混杂模式,计数器减一。只有计数器为0 时,设备才退出混杂模式。这个变量一般调用函数dev_set_promiscuity 来设置。
struct dev_mc_list *mc_list 
  指向dev_mc_list结构 
int mc_count 
  设备多播地址的数量,它一样表示mc_list所指向链表的长度。 
int allmulti 
  若是是非零值,那么设备将监听全部的多播地址。和 promiscuity 同样,这个变量是一个计数器而不只仅是一个布尔值。这是由于多个设备(好比VLAN和bonding
设备)可能独立地要求监听全部地址。若是这个变量的值从0变为非零,内核会调用函数dev_set_allmulti通知设备监听全部的多播地址。若是这个值变为0,则中止监听全部的多播地址。
1.四、    协议相关
void *atalk_ptr 
void *ip_ptr 
void *dn_ptr 
void *ip6_ptr 
void *ec_ptr 
void *ax25_ptr 
这六个变量指向特定协议的数据结构,每一个数据结构都包含协议私有的参数。例如,ip_ptr 指向一个 in_device 类型的结构(尽管 ip_ptr 的类型是 void*),它包含 IPv4相关的参数,其中包括设备的 IP 地址列表等。
1.五、    流量管理
Linux 流量控制子系统的功能已经很是强大,而且已经成为 Linux 内核中的一个重要组件。相关的内核选项是 “Device drivers ->Networking support ->Networking options ->QoS and/or fair queueing”。net_device中的相关变量包括: 
struct net_device *next_sched 
  被内核软中断使用。 
struct Qdisc *qdisc 
struct Qdisc *qdisc_sleeping 
struct Qdisc *qdisc_ingress 
struct list_head qdisc_list 
  这些变量管理设备的接收,发送队列,而且能够被不一样的cpu访问。 
spinlock_t queue_lock 
spinlock_t ingress_lock 
  流量控制子系统为每一个网络设备定义了一个私有的发送队列。 queue_lock用于避免并发的访问(参见第11章)。ingress_lock 用于保护接收队列。 
unsigned long tx_queue_len 
  设备发送队列的长度。若是内核中包含了流量控制子系统,这个变量可能没有什么用(只有几个排队策略会使用它)。常见设备的 tx_queue_len 值(这个值能够经过sysfs文件系统修改(在/sys/class/net/device_name/目录下)):

1.六、    设备驱动程序相关
int (*init)(...) 
void (*uninit)(...) 
void (*destructor)(...) 
int (*open)(...) 
int (*stop)(...) 
用于初始化,清除,销毁,启用和中止一个设备。这些函数并非每一个设备都会用到。
int (*hard_start_xmit)(...) 
 发送一个帧。
int (*hard_header)(...)
 根据源和目标的第2层地址建立第2层报文头。
int (*rebuild_header)(...)
 负责在传送包以前重建第2导报文头。
int (*set_mac_address)(...) 
  修改设备的 MAC 地址。若是设备不提供这个功能(好比网桥设备),能够把这个指针设置为NULL。
int (*change_mtu)(...) 
  修改设备的MTU,修改mtu 不会对设备驱动有任何影响,它只是让协议栈软件能够根据新的mtu 正确地处理分片。linux

相关文章
相关标签/搜索