设备注册于设备除名

    设备注册与设备除名一般有 register_netdev和unregister_netdev完成。这两个是包裹函数,负责上锁,真正起作用的是其调用的register_netdevice和unregister_netdevice。参见:net/core/dev.c。
    下图描述了设备注册过程中的一些状态变化



    状态的改变会用到UNINITIALIZED和REGISTERED之间的状态REGISTERING。这些进程有netdev_run_todo进行。参照“切割操作:netdev_run_todo”
    为设备进行注册和除名时,设备驱动程序可以使用net_device的两个虚拟函数init和uninit进行私有数据的初始化和清理工作。
    设备除名时,只有对net_device的引用计数为0,netdev_wait_allrefs才会返回,除名工作才会顺利完成。
    设备的注册和除名操作都是由netdev_run_todo完成的。

切割操作:netdev_run_todo

    register_netdevice会负责一部分的注册工作,然后再让netdev_run_todo予以完成。
    net_device的改变会通过rtnl_lock和rtnl_unlock收到Routing
Netlink信号量的保护。这也是为什么register_netdev执行时需要请求锁,并且在返回时要释放锁的原因。一旦register_netdevice完成了它自己的工作,它会通过net_set_todo将net_device结构体添加到net_todo_list中。net_todo_list包含一系列有注册(或除名)操作需要被完成的设备列表。这份列表有register_netdev在释放锁时间接予以处理。
    因此rtnl_unlock不仅会释放锁,还会调用 netdev_run_todo。netdev_run_todo会浏览net_todo_list列表,然后完成全部的实例注册。

void rtnl_unlock(void)
{
/* This fellow will unlock it for us. */
netdev_run_todo();
}
EXPORT_SYMBOL(rtnl_unlock);
    netdev_run_todo所进行的工作不需要持有锁。


设备注册状态通知

    内核组件和应用程序都可能想知道设备的注册、除名、开启、关闭。设备装态信息通过两种方式传送通知。
netdev_chain
Netlink的 RTMGRP_LINK多播组

netdev_chain通知链:
    设备的注册和除名阶段各个状态的变化都是通过netdev_chain来进行通告的。对其感兴趣的内核组件可以分别通过register_netdevice_notifier和unregister_netdevice_notifier来针对该链进行注册和除名。
    netdev_chain报告的事件定义在:
//格式:
#define NETDEV_UP 0x0001 /* For now you can't veto a device up/down */
#define NETDEV_DOWN 0x0002
...... //含义:
NETDEV_UP //该报告表明设备被启用,由dev_open产生报告
NETDEV_DOWN //设备已关闭,由dev_close生成报告
NETDEV_REBOOT //因硬件设备,设备已重启
NETDEV_CHANGE //设备的状态或者配置发生改变
NETDEV_REGISTER //设备已经注册
NETDEV_UNREGISTER //设备已经除名
NETDEV_CHANGEMTU //
NETDE //
NETDEV_PRE_UP //
NETDEV_PRE_TYPE_CHANGE //
NETDEV_POST_TYPE_CHANGE //
NETDEV_POST_INIT //
NETDEV_UNREGISTER_FINAL //
NETDEV_RELEASE //
NETDEV_NOTIFY_PEERS //
NETDEV_JOIN //
NETDEV_CHANGEUPPER
V_CHANGEADDR //设备硬件地址(或关联的广播地址)发生改变
NETDEV_GOING_DOWN //
NETDEV_CHANGENAME //设备名称发生改变
NETDEV_FEAT_CHANGE //
NETDEV_BONDING_FAILOVER
//
NETDEV_RESEND_IGMP //

 很多设备都在netdev_chain中注册,如:路由,防火墙
RTnetlink链接通知
    当设备发生一些状态改变(或其它事件),会通过rtmsg_ifinfo把通知传给link多播组。

设备注册:

    设备注册不仅仅只是把net_device嵌入到全局表dev_base和哈希表dev_name_head、dev_index_head中,它还包括初始化net_device部分参数,发送广播通告(提醒其他模块本设备加入)、以及一些其他的工作。
int register_netdev(struct net_device *dev)
{
int err; rtnl_lock();
err = register_netdevice(dev);
rtnl_unlock();
return err;
}
    register_netdevice开始设备注册工作,并调用net_set_todo,而net_set_todo最终会调用netdev_run_todo完成注册。
    register_netdevice的工作主要包括以下部分:
  • 初始化net_device的部分字段
  • 如果内核支持Divert功能,则用alloc_divert_blk分配该功能所需的数据空间块,并连接至dev->divert
  • 如果设备驱动已经对dev->init进行初始化,则执行此函数。
  • 由dev_new_index分配给设备一个识别码。
  • 把net_device插入到全局表dev_base,以及两张哈希表dev_name_head,dev_index_head。
  • 检查功能标识是否有无效的组合。
  • 设置dev->state中的__LINK_STATE_PRESENT标识,使得设备能为内核所用。
  • 用dev_init_scheduler初始化设备队列规则,以便流量控制用于实现Qos。
  • 通过netdev_chain通知表链通知所有对本设备注册感兴趣的子系统。
    当netdev_run_todo被调用完成注册时,它只更新dev->reg_state,并将设备注册进入sysfs。

设备除名:

    设备除名需要复原设备注册期间所进行的所有工作和一些其他事项:
  • 用dev_close关闭设备
  • 释放所有分配的资源(IRQ、I/O端口...)
  • 从全局表dev_base和两张哈希表中删除设备
  • 当该设备的所有引用都释放后,释放net_device结构的空间、该驱动程序的私有数据结构、以及相链接的内存区域块。
  • 删除添加到/proc 和/sys的所有文件
    net_device中三个函数指针比较有用:   
  
dev->stop       //用以关闭设备,通常包括关闭netif_stop_queue出口,释放硬件资源等
dev->uninit //主要负责引用计数,少用到
dev->destructor //少数虚拟设备使用,通常初始化为: free_netdev 或其包裹函数

设备除名有int unregister_netdevice(struct net_device *dev)进行,其主要工作如下:
  • 如果设备没关闭,使用dev_close关闭
  • 从全局表dev_base和两张哈希表中删除设备
  • 所有与该设备关联的队列规则实例,由dev_shutdown销毁
  • 发送NETDEV_UNREGISTER消息到netdev_chain通知链。
  • 除名消息也必须通知用户空间。
  • 任何链接至net_device的数据块被释放
  • register_netdevice中dev_init的所有操作都要有dev_uninit复原。
    最后调用net_set_todo,使得net_run_todo能完成除名。

引用计数

    只有当设备引用计数都被释放时,net_device结构才能被释放。
     引用计数保存在dev->refcnt中。当引用新增或删除时,使用dev_hold和dev_put更新引用计数。
    当设备以register_netdevice注册时,dev->refcnt初始化为1, 这第一个引用有负责网络设备数据库的内核代码所持有。因此,只有当设备除名时,该值才有可能降为0.
    

网络设备的启用和关闭

    设备一旦注册即可使用,但是如果没有明确的开启其功能,设备仍然无法接收和传输数据流。
    设备的开启请求由dev_open完成:
//net/core/dev.c
int dev_open(struct net_device *dev)
static int __dev_open(struct net_device *dev)
    启用设备包括下列工作:
  • 如果 dev->open初始化了,就调用dev->open。
  • 设置dev_state中的__LINK_STATE_START标识,以表明设备开启或在运行中
  • 设置dev_flags中的IFF_UP,标识设备为开启。
  • 调用dev_activate初始化流量控制所需使用到的出口队列规则,然后启用看门狗定时器。
  • 向netdev_chain发送NETDEV_UP通知
    设备的启用必须显示执行,但设备的关闭则可以通过用户命令显式执行或者通过其他程序隐式执行。
    设备关闭有以下任务:
  • 向netdev_chain发送 NETDEV_GOING_DOWN通知
  • 调用dev_deactivat关闭队列规则
  • 清除dev->state的_ _LINK_STATE_START标志,以表明设备禁用了
  • 如果正在执行读取入口队列数据包的轮询操作,则等待该操作完成。因为dev->state关系,入口队列不会再有入队操作,所以读取当前已经在队列中的即可
  • 如果dev->stop有定义,执行它。
  • 清除dev->flags的IFF_UP操作。
  • 向netdev_chain发送NEtdEV_DOWN通知。
    

更新队列规则状态:


    以后再补充




从用户空间配置设备相关信息

    许多工具可以用来配置设备相关参数:
  • ipconfig和mii-tool 来自net-tool套件
  • ethtool    来自ethtool套件
  • ip link:来自IP ROUTER2 套件
 










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

  1. 深入理解linux网络技术内幕读书笔记(五)--网络设备初始化

    Table of Contents 1 简介 2 系统初始化概论 2.1 引导期间选项 2.2 中断和定时器 2.3 初始化函数 3 设备注册和初始化 3.1 硬件初始化 3.2 软件初始化 3.3 ...

  2. 深入理解linux网络技术内幕读书笔记(七)--组件初始化的内核基础架构

    Table of Contents 1 引导期间的内核选项 2 注册关键字 3 模块初始化代码 引导期间的内核选项 linux运行用户把内核配置选项传给引导记录,然后引导记录再把选项传给内核. 在引导 ...

  3. 深入理解linux网络技术内幕读书笔记(三)--用户空间与内核的接口

    Table of Contents 1 概论 1.1 procfs (/proc 文件系统) 1.1.1 编程接口 1.2 sysctl (/proc/sys目录) 1.2.1 编程接口 1.3 sy ...

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

    概述    内核的初始化过程过程中,与网络相关的工作如下所示:     内核引导时执行start_kernel,start_kernel结束之前会调用rest_init,rest_init初始化内核线 ...

  5. 深入理解linux网络技术内幕读书笔记(八)--设备注册与初始化

    Table of Contents 1 设备注册之时 2 设备除名之时 3 分配net_device结构 4 NIC注册和除名架构 4.1 注册 4.2 除名 5 设备初始化 6 设备类型初始化: x ...

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

    概述     内核的PCI子系统(即PCI层)提供了不同设备一些通用的功能,以便简化各种设备驱动程序.     PCI层重要结构体如下: pci_device_id     设备标识,根据PCI标志定 ...

  7. 《深入理解Linux网络技术内幕》阅读笔记 --- 路由基本概念

    一.路由的基本概念 1.一条路由就是一组参数,这些参数存储了往一个给定目的地转发流量所需的信息,而一条路由所需的最少的参数集合为:(1)目的网络,(2)出口设备,(3)下一跳网关 2.路由中的相关术语 ...

  8. 深入理解linux网络技术内幕读书笔记(十)--帧的接收

    Table of Contents 1 概述 1.1 帧接收的中断处理 2 设备的开启与关闭 3 队列 4 通知内核帧已接收:NAPI和netif_rx 4.1 NAPI简介 4.1.1 NAPI优点 ...

  9. 深入理解linux网络技术内幕读书笔记(九)--中断与网络驱动程序

    Table of Contents 1 接收到帧时通知驱动程序 1.1 轮询 1.2 中断 2 中断处理程序 3 抢占功能 4 下半部函数 4.1 内核2.4版本以后的下半部函数: 引入软IRQ 5 ...

随机推荐

  1. [BZOJ1122][POI2008]账本BBB 单调队列+后缀和

    Description 一个长度为n的记账单,+表示存¥1,-表示取¥1.现在发现记账单有问题.一开始本来已经存了¥p,并且知道最后账户上还有¥q.你要把记账单修改正确,使得 1:账户永远不会出现负数 ...

  2. spring boot 多数据源分布式事务处理

    有参考文章 ,但是自己没有测试.

  3. Ubuntu 14.04 删除软件附加依赖

    参考:FlowVisor的学习笔记 eg.mininet $ sudo apt-get remove mininet $ sudo apt-get remove --auto-remove minin ...

  4. js循环遍历弹框,先弹出第一个之后逐步弹出第二个。。

    var data = [{ "login_advertTitle": "即使生活琐碎,也要活得优雅", "login_advertCont" ...

  5. Python实现自平衡二叉树AVL

    # -*- coding: utf-8 -*- from enum import Enum #参考http://blog.csdn.net/niteip/article/details/1184069 ...

  6. SPOJ - HIGH Highways(矩阵树定理)

    https://vjudge.net/problem/SPOJ-HIGH 题意: 给n个点m条边,求生成树个数. 思路: 矩阵树裸题. 具体的话可以看一下周冬的论文<生成树的计数及其应用> ...

  7. ThreadPool开启多线程时支持最大连接200个(默认为2个),不加则会超时

    //ThreadPool System.Net.ServicePointManager.DefaultConnectionLimit = 200;

  8. ubuntu16.04上安装Java

    1.下载jdk8 登录网址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 选择 ...

  9. 简单介绍tomcat中maxThreads,acceptCount,connectionTimeout

    <?xml version='1.0' encoding='utf-8'?> <Server port="8005" shutdown="SHUTDOW ...

  10. MongoDB(课时8 模运算)

    3.4.2.3 求模 模运算使用“$mod”来完成,语法: {$mod : [除数,余数]} 范例:求模 db.students.find({"age" : {"$mod ...