深入理解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包括一些上锁的手段,以后再补充了。。。