深入理解Linux网络技术内幕——设备的注册于初始化(一)

副标题:设备注册相关的基本结构的原理框架

设备注册与删除时间

    设备在下列两种情况下进行注册:
1)加载NIC驱动时
2)插入热插拔设备时
    这里NIC与热插拔设备有些不同。a.对于非热插拔NIC来说,NIC的注册是伴随着其驱动的发生的,而NIC可以内建到内核,也可以作为模块载入,如果内建入内核,则NIC设备和初始化均发生在引导时,如果NIC作为模块加载,则NIC的注册和驱动初始化均发生在模块加载时。b. 对于热插拔NIC设备来说,其驱动已经加载,因此设备的注册发生在插入设备,内核通知关联驱动时。
    设备的注销主要发生在下面两种情况
    1)卸载NIC驱动时(仅适用于以模块形式加载的设备)
    2)拔出热插拔设备时

设备的注册于注销架构

    下图分别描述了网络设备的注册和注销的大致流程,(以PCI Ethernet NIC为例,其它网络类似,主要是名字差异)
技术分享
技术分享
    首先调用alloc_etherdev(即alloc_netdev的包裹函数),alloc_etherdev会为所有Ethernet设备通用的参数做初始化,驱动程序会初始化net_device的另一部分。之后以register_netdev完成注册。

net_device

分配net_device

net_device由alloc_netdev分配空间
//include/linux/netdevice.h
#define alloc_netdev(sizeof_priv, name, setup)     alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1)  
//net/core/dev.c
struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
        void (*setup)(struct net_device *),
        unsigned int txqs, unsigned int rxqs)
alloc_netdev带三个参数:
sizeof_priv: 因为net_device可以由驱动程序拓展私有空间,此参数表示拓展的私有空间的大小
name:设备名字(名字的一部分,可能还有一部分由内核完成)
setup:用于初始化设备的一些参数

此处设备会被分配一个设备名字,改名字一般带有序号,以确保唯一性:如eth0,eth1
技术分享
alloc_netdev一般会有一些包裹函数对其进行包裹:
技术分享

设备(net_device)初始化

net_device结构非常庞大,其成员会有不同函数分批进行初始化。每个函数分别负责一组不同的字段子集。特别是下面三部分:
设备驱动程序:
    如IRQ、IO内存及IO端口,其值取决于硬件配置,有设备驱动程序进行初始化。
设备类型:
    同一设备类型的通用字段进行初始化,由XXX_setup进行。(设备类型初始话属于设备驱动程序初始化一部分,XXX_setup有XXX_probe调用)
各种功能:
    一部分在register_device中进行,有一些在关联模块接到通知时进行。

net_device结构的组织

    alloc_netdev第一个参数知名了私有数据块的大小,因此私有数据块会附加载net_device结构后。即net_device可以分为两部分,一部分是net_device基础的数据,第二部分是私有数据块(如下图所以)。私有数据块的空间分配可以与net_device第一部分数据空间一起完成(如驱动程序A、B),也可以有驱动程序自己分配(如驱动程序C)。
技术分享

技术分享
    从图中可以看出,net_device空间分配是,会有一块对其区域块(alignment padding),因此dev_base(全局表的表头指针)以及net_deivce中的next需要指向结构的开头,而非分配区域块的开头(即跳过填充区域块)。
    net_device插入在一个全局列表(以dev_base开头)和两张哈希表中后面介绍(以dev_name_head和dev_index_head开头)
技术分享
dev_base:
    全局表,内含所有net_device设备,能让内核轻易的浏览所有设备。
dev_name_head:
    hash表,以设备名称为索引,在使用老一代配置工具ioctl接口时极为有用。
dev_index_head:
    hash表,以设备ID:dev->ifindex为索引。使用新一代配置工具ip(来自IPROUTER2套件时),ip工具会通过Netlink与内核交互,这是通常就以设备ID为索引了。
    
查询:
    一般由 dev_get_by_name 和dev_get_by_index通过上面两张Hash表实现。有可能根据设备类型和mac地址通过全局表dev_base列表实现。
    所有查询都由 dev_base_lock锁进行保护
    All lookup routines are defined innet/core/dev.c.

设备状态

net_dev有多个字段用于定义设备当前状态:
flags     //用于各种标识的位域,多数标识代表设备拥有的能力,但IFF_UP特殊,标识设备开启或关闭,参见uapi/linux/if.h(IFF_XXX定义),并参考下文"开启和关闭网络设备“
reg_state //设备注册状态,参见下文注册状态
state     //和其他设备有关的设备状态,参见下文“队列规则状态”

设备注册状态:
NETREG_UNINITIALIZED //定义为0,net_device已分配, 且内容清0
NETREG_REGISTERING    //net_device已添加到“net_device结构的组织”提及的结构内,但内核依然要想/sys添加项目
NETREG_REGISTERED     //设备注册完成
NETREG_UNREGISTERING  //net_device已由“net_device结构的组织”提及的结构内删除
NETREG_UNREGISTERED   //设备完全除名
NETREG_RELEASED       //所有对net_device结构的引用已释放

队列规则状态:
_ _LINK_STATE_START      //设备开启
_ _LINK_STATE_PRESENT    //设备存在
_ _LINK_STATE_NOCARRIER  //没有载波
_ _LINK_STATE_LINKWATCH_EVENT  //设备状态已变更
_ _LINK_STATE_XOFF             //以下三个标识由负责管理流量入口与出口的设备所使用
_ _LINK_STATE_SHED             //
_ _LINK_STATE_RX_SCHED         //

虚拟设备:

      虚拟设备与真实设备有几个地方有差异:
    虚拟设备偶尔会调用register_netdevice和unregister_netdevice,而不是其包裹函数。并且自行上锁、解锁,以便获得更多的持有锁的时间。   
    真实设备不能由用户命令除名,而在卸载时自己除名。虚拟设备可以通过用户命令除名,但除名是否成功依赖于虚拟设备驱动设计。
    虚拟设备本文就不太多介绍了,以后再单独补充一篇虚拟设备与真实设备的差异的博文

上锁:

    net_devices包括一些上锁的手段,以后再补充了。。。


    
    






















郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。