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


        
        






















  • 相关阅读:
    Leetcode Minimum Path Sum
    Leetcode House Robber II
    洛谷 P5375 【[THUPC2019]组合数据结构问题】题解
    洛谷 P1855 【榨取kkksc03】题解
    CF1173A 【Nauuo and Votes】题解
    [NOIP2018D1T1][NOIP2013D1T1][Luogu P5019]铺设道路 题解
    [BZOJ1083][SCOI2005]繁忙的都市 题解
    统计自己洛谷AC题目爬虫
    CF43A Football 题解
    SP2 PRIME1
  • 原文地址:https://www.cnblogs.com/Windeal/p/4284594.html
Copyright © 2020-2023  润新知