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

时间: 2024-08-01 10:44:13

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

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

设备注册于设备除名 设备注册与设备除名一般有 register_netdev和unregister_netdev完成.这两个是包裹函数,负责上锁,真正起作用的是其调用的register_netdevice和unregister_netdevice.参见:net/core/dev.c. 下图描述了设备注册过程中的一些状态变化 状态的改变会用到UNINITIALIZED和REGISTERED之间的状态REGISTERING.这些进程有netdev_run_todo进行.参照"切割操作:netdev_

深入理解Linux网络技术内幕——IPv4 报文的传输发送

报文传输,指的是报文离开本机,发往其他系统的过程. 传输可以由L4层协议发起,也可以由报文转发发起. 在深入理解Linux网络技术内幕--IPv4 报文的接收(转发与本地传递)一文中,我们可以看到,报文转发最后会调用dst_output与邻居子系统进行交互,然后传给设备驱动程序. 这里,我们从L4层协议发起的传输,最后也会经历这一过程(调用dst_output).本文讨论的是L4层协议发起的传输,在IPv4协议处理(IP层)中的一些环节. 大蓝图 我们先看下传输环节的大蓝图,以便对传输这一过程有

深入理解Linux网络技术内幕——网络设备初始化

概述 内核的初始化过程过程中,与网络相关的工作如下所示: 内核引导时执行start_kernel,start_kernel结束之前会调用rest_init,rest_init初始化内核线程init(在Linux3-12中为kernel_init). asmlinkage void __init start_kernel(void) { ... parse_early_param();//间接调用parse_args parse_args(...); //处理内核引导程序(boot loader)

深入理解Linux网络技术内幕——帧的接收与传输

帧的接收 NAPI与netif_rx(非NAPI) Linux内核获取网络帧到达通知的方式有两中:中断和轮询.(中断值设备向内核发出中断,轮询指linux内核主动轮询设备) 在早起的linux内核中,网络帧主要以中断的方式通知linux内核帧的到达.这是非NAPI方式. 现在的操作系统中,linux内核使用NAPI方式, 获取帧到达的消息.NAPI混合使用了中断和轮询. netif_rx(非NAPI): 每一个帧接收完毕时,设备向内核发送一个中断.(在低流量负载的情况下,这种方式对比轮询优势明显

深入理解Linux网络技术内幕——协议处理函数

网络帧在进入网络层时,需要区分不同的网络协议进行处理,这就需要涉及协议处理函数. 首先我们从驱动接收到一个数据帧,分析数据帧在协议栈中自下而上的传输流程. 设备驱动程序在接收到一个数据帧时,会将其保存在一个sk_buff缓冲区数据结构,并对其进行初始化. struct sk_buff { ...... __be16 protocol:16; ...... } 在这个缓冲区结构体中,有一个protocol字段,用于标识网络层的协议. 我们知道网络帧在设备驱动程序中处理后,设备驱动程序会调用neti

深入理解Linux网络技术内幕——虚拟设备初始化小结

概述 虚拟设备是建立在一个或者多个真实设备上的抽象.虚拟设备和真实设备的对应关系时多对多的,但是并不是所有的组合都能被定义,或者被内核所支持. 下面列举一些比较常见虚拟设备: Bonding //绑定,虚拟设备绑定物理设备,使其如同单一设备 802.1Q //IEEE标准,用于建立VLAN Bridging //网桥的虚拟代表 Aliasing interfaces //别名接口,用以让一个Ethernet真实接口有多个虚拟接口 True equalizer (TEQL) //普通均衡器,是一个

深入理解Linux网络技术内幕——PCI层和网络接口卡

概述 内核的PCI子系统(即PCI层)提供了不同设备一些通用的功能,以便简化各种设备驱动程序. PCI层重要结构体如下: pci_device_id 设备标识,根据PCI标志定义的ID,而不是Linux本地的. pci_dev 类似于网络设备的net_device.每个PCI会被分配一个net_dev实例. pci_driver PCI层和设备驱动程序之间的接口.主要由一些函数指针组成.如下所示: struct pci_driver { struct list_head node; char *

《深入理解Linux网络技术内幕》阅读笔记 --- 路由

一.Linux内核中路由相关的主要数据结构 struct fib_result:对路由表查找后返回该结构,它的内容并不是简单的包含下一跳信息,而且包含其他特性,例如策略路由所需的更多参数. struct fib_rule:表示由策略路由在路由流量时选择路由表的规则 struct fib_node:一条路由表项.例如,该数据结构用于存储由route add或ip route add命令添加一条路由时生成的信息. struct fn_zone:一个zone表示子网掩码长度相同的一组路由 struct

深入理解Linux网络技术内幕——路由子系统的概念与高级路由

本文讨论IPv4的路由子系统.(IPv6对路由的处理不同). 基本概念 路由子系统工作在三层,用来转发入口流量. 路由子系统主要设计 路由器.路由.路由表等概念. 路由器: 配备多个网络接口卡(NIC),并且能利用自身网络信息进行入口流量转发的设备. 路由: 流量转发,决定目的地的过程 路由表:转发信息库,该库中储存路由需要本地接收还是转发的信息, 以及转发流量时所需要的信息.(即,信息库用来判断,要不要转发,如果要转发,向哪里转发). 我们了解,路由器有多个网卡,但是多个NIC的设备不一定就是