linux dev alloc name,深入理解Linux网络技术内幕-设备注册和初始化(二)

NIC注册和注销的通用架构

Linux系统中NIC网络设备驱动程序利用网络代码进行注册和注销有其通用的架构,这里以PCI Ethernet NIC为例,其他设备类型只是所以函数名称和调用方式不同,主要依据于设备总线提供的接口。

6655815e36072d815b9909839e95f955.png

其中(a)为设备注册的大致流程图,而(b)为设备注销的流程图。

在PCI Ethernet NIC设备驱动程序的探测函数(热插拔设备)或模块初始化函数中,首先要为设备分配一个net_device数据结构,并对其中的成员进行必要的初始化,对其中与设备类型密切相关的特殊成员利用驱动程序自己实现的setup函数进行初始化;Ethernet NIC设备驱动程序还需要调用netdev_boot_setup_check检查是否在系统启动参数中对网络设备进行了设置;然后调用register_netdev完成设备的注册。

在分配net_device数据结构时,驱动程序一般不直接调用alloc_netdev函数,而是调用为其类型封装后的函数,如Ethernet NIC设备直接调用alloc_etherdev函数,使用更加方便简单。

而Ethernet NIC设备的注销则是相反的过程,首先调用unregister_netdev在系统中注销设备,然后将分配的net_device数据结构释放。

在释放net_device数据结构时,设备也可能不直接调用free_netdev函数中,而是调用net_device数据结构中的成员函数:

/* Called from unregister, can be used to call free_netdev */

void (*destructor)(struct net_device *dev);

虚拟设备驱动程序一般采用这种方式,实现自己的destructor函数来释放net_device数据结构。

网络设备注册过程

网络设备在系统中注册后,内核在处理数据包时才能调用设备接口实现的处理函数。网络设备的注册是通过register_netdev函数完成的:

/**

*    register_netdev    - register a network device

*    @dev: device to register

*

*    Take a completed network device structure and add it to the kernel

*    interfaces. A %NETDEV_REGISTER message is sent to the netdev notifier

*    chain. 0 is returned on success. A negative errno code is returned

*    on a failure to set up the device, or if the name is a duplicate.

*

*    This is a wrapper around register_netdevice that takes the rtnl semaphore

*    and expands the device name if you passed a format string to

*    alloc_netdev.

*/

int register_netdev(struct net_device *dev)

{

int err;

rtnl_lock();

/*

* If the name is a format string the caller wants us to do a

* name allocation.

*/

if (strchr(dev->name, '%')) {

err = dev_alloc_name(dev, dev->name);

if (err < 0)

goto out;

}

err = register_netdevice(dev);

out:

rtnl_unlock();

return err;

}

EXPORT_SYMBOL(register_netdev);

其中rtnl_lock是内核保护运行时的net_device数据结构的互斥手段,一般在修改net_device中flag字段,表示有事件发生需要改变设备的状态;或者用户通过ifconfig、route等命令修改接口的配置时,通过ioctl和netlink接口告诉内核操作设备的net_device结构,都需要调用这个锁来进行互斥。

dev_alloc_name(dev, dev->name)函数会在系统中找到这种类型的网络设备中第一个没有使用的序列号来替换设备名称中的%d,生成如eth2的设备名称。

int dev_alloc_name(struct net_device *dev, const char *name)

{

char buf[IFNAMSIZ];

struct net *net;

int ret;

BUG_ON(!dev_net(dev));

net = dev_net(dev);

ret = __dev_alloc_name(net, name, buf);

if (ret >= 0)

strlcpy(dev->name, buf, IFNAMSIZ); //将返回的设备名称复制到net_device的name字段

return ret;

}

static int __dev_alloc_name(struct net *net, const char *name, char *buf)

{

int i = 0;

const char *p;

const int max_netdevices = 8*PAGE_SIZE;

unsigned long *inuse;

struct net_device *d;

/*检查设备名称中是否有%d,或其他不合法字符*/

p = strnchr(name, IFNAMSIZ-1, '%');

if (p) {

if (p[1] != 'd' || strchr(p + 2, '%'))

return -EINVAL;

/*分配一个物理页面作为位图,来对系统中该类型设备已用序列号进行标记*/

inuse = (unsigned long *) get_zeroed_page(GFP_ATOMIC);

if (!inuse)

return -ENOMEM;

/*变量网络命名空间中的所有设备,即net_device结构*/

for_each_netdev(net, d) {

if (!sscanf(d->name, name, &i)) //获取同类型网络设备的其序列号,这里极为巧妙

continue;

if (i < 0 || i >= max_netdevices) //判断序列号的范围

continue;

snprintf(buf, IFNAMSIZ, name, i);

if (!strncmp(buf, d->name, IFNAMSIZ)) /*验证解析的序列号是否正确*/

set_bit(i, inuse); //在位图中将该位标记

}

i = find_first_zero_bit(inuse, max_netdevices); //找到第一个为0的序列号

free_page((unsigned long) inuse);

}

if (buf != name)

snprintf(buf, IFNAMSIZ, name, i); //根据找到的序列号,输出完整的设备名

if (!__dev_get_by_name(net, buf)) //在name_list链表中查找是否有同名的设备

return i;

/* It is possible to run out of possible slots

* when the name is long and there isn't enough space left

* for the digits, or if all bits are used.

*/

return -ENFILE;

}

在这里就为设备完成了完整设备名的组合,内核在这里位图的使用非常巧妙,以后可以在处理位图时,可以直接使用内核实现的set_bit和find_first_zero_bit、clear_bit等函数。

register_netdevice才是网络设备注册的最重要步骤:

int register_netdevice(struct net_device *dev)

{

int ret;

struct net *net = dev_net(dev);  //设备的网络空间

BUG_ON(dev_boot_phase);

ASSERT_RTNL();

might_sleep();

/* When net_device's are persistent, this will be fatal. */

BUG_ON(dev->reg_state != NETREG_UNINITIALIZED); //alloc_netdev时不需要设置这个成员,因为其为0

BUG_ON(!net);

/*初始化net_device中的一些成员锁*/

spin_lock_init(&dev->addr_list_lock);

netdev_set_addr_lockdep_class(dev);

dev->iflink = -1;

/* Init, if this function is available */

if (dev->netdev_ops->ndo_init) { //调用设备驱动程序操作中实现的初始化函数

ret = dev->netdev_ops->ndo_init(dev);

if (ret) {

if (ret > 0)

ret = -EIO;

goto out;

}

}

ret = dev_get_valid_name(dev, dev->name, 0); //检查设备名称的有效性

if (ret)

goto err_uninit;

dev->ifindex = dev_new_index(net); //为设备分配一个唯一的索引号

if (dev->iflink == -1)

dev->iflink = dev->ifindex;

/* Transfer changeable features to wanted_features and enable

* software offloads (GSO and GRO).

*/

/*设置设备的一些特性*/

dev->hw_features |= NETIF_F_SOFT_FEATURES;

dev->features |= NETIF_F_SOFT_FEATURES;

dev->wanted_features = dev->features & dev->hw_features;

/* Enable GRO and NETIF_F_HIGHDMA for vlans by default,

* vlan_dev_init() will do the dev->features check, so these features

* are enabled only if supported by underlying device.

*/

dev->vlan_features |= (NETIF_F_GRO | NETIF_F_HIGHDMA);

ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev); //调用通知链,发出事件通知

ret = notifier_to_errno(ret);

if (ret)

goto err_uninit;

ret = netdev_register_kobject(dev); //设备注册的核心函数,主要是调用device_add函数,将设备添加到内核的设备管理器中

if (ret)

goto err_uninit;

dev->reg_state = NETREG_REGISTERED;  //设置net_device的状态

netdev_update_features(dev);

/*

*    Default initial state at registry is that the

*    device is present.

*/

set_bit(__LINK_STATE_PRESENT, &dev->state);

dev_init_scheduler(dev); //在这里会设置设备的看门狗定时器

dev_hold(dev); //增加设备的引用计数

list_netdevice(dev);  //将设备加入系统的indexlist、namelist和devlist中

/* Notify protocols, that a new device appeared. */

ret = call_netdevice_notifiers(NETDEV_REGISTER, dev); //通过通知链发出设备注册通知

ret = notifier_to_errno(ret);

if (ret) {

rollback_registered(dev);

dev->reg_state = NETREG_UNREGISTERED;

}

/*

*    Prevent userspace races by waiting until the network

*    device is fully setup before sending notifications.

*/

if (!dev->rtnl_link_ops ||

dev->rtnl_link_state == RTNL_LINK_INITIALIZED)

rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);

out:

return ret;

err_uninit:

if (dev->netdev_ops->ndo_uninit)

dev->netdev_ops->ndo_uninit(dev);

goto out;

}

EXPORT_SYMBOL(register_netdevice);

由上可知注册的主要过程是netdev_register_kobject函数中的device_add过程,和list_netdevice(dev)将设备加入到系统的几个hash链表中,便于系统处理数据包时查找对应的设备。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/560606.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

外贸常用术语_外贸中常用的会计术语及付款方式术语 | 会计英语

点击上面“财经英语”关注公众号&#xff01;点击下面小程序加入: 学习圈财经英语 学习圈 预付现金 Cash advance 凭提货单支付现金 Cash against Bill of Lading (B/L) 凭单据付现款||凭装货单付现款 Cash against Documents 现金结存||现金差额 Cash balance 现收现付制||现金…

c语言中block做函数参数,c语言中的block

//block块(数据类型) 封装的一段具有特定功能的代码段//函数有函数名&#xff0c;block没有名字&#xff0c;也可以叫做匿名函数&#xff1b;函数不能做参数传递&#xff0c;block可以作为参数传递&#xff1b;//形式&#xff1a;/* 返回值类型,(^ block变量名)(参数列表)&…

python 多维list 排序_一行代码的优雅| Python列表生成式

欢迎回来&#xff0c;上一周我们整理了基础课中三大结构有关内容的具体应用及案例。可以通过以下几篇推文进行回溯&#xff1a;Python语言基础50课我的Python - 100天笔记 |D1-D7我的Python - 100天笔记 |D8-D14列表是Python中非常常见的数据结构&#xff0c;在基础课中也占了不…

c语言高斯白序列x,C语言程序设计程设计指导书(晓庄).doc

C语言程序设计程设计指导书(晓庄)C语言程序设计课程设计任务书南京晓庄学院数学与信息技术学院2011-9-20一、C语言程序设计课程设计的目的说明1)复习巩固C语言的基础知识&#xff0c;进一步加深对C语言编程的理解和掌握&#xff1b;2)利用所学知识&#xff0c;理论和实际结合锻…

arduino智能风扇系统

研究方法与思路&#xff1a; 我们小组一开始找的是一个别人做好的项目&#xff0c;按键控制风扇挡位&#xff0c;不同挡位对应不同LED灯。但是实现过程中存在bug。而且仅仅通过按键控制风扇着实有局限性&#xff0c;因此我觉定用串口控制风扇开关的模式&#xff0c;输入1&…

verilog 生成块_如何高效的编写Verilog——终极版

为了高效的编写Verilog&#xff0c;通常有些编辑器插件可以自动生成代码&#xff0c;比如自动端口定义&#xff0c;自动连线&#xff0c;自动实例化等等。公司的环境有很好用的自动化插件&#xff0c;想给自己的电脑也整个怎么做。比如Emacs中有个插件叫verilog-mode。但是博主…

arduino串口输入改变模式模块

最新更新: 当时太嫩&#xff0c;做这个不知道arduino还有中断的方法&#xff0c;利用中断的方法可以比下面这种循环判断的方法更简单。 最近做了不少arduino智能系统&#xff0c;我发现在与用户交互的过程中&#xff0c;经常用到&#xff1a;通过在串口输入不同的值达到手动控…

android悬浮动态权限,Android 获取判断是否有悬浮窗权限的方法

现在很多应用都会用到悬浮窗&#xff0c;很多国产rom把悬浮窗权限加入控制了&#xff0c;你就需要判断是否有悬浮窗权限&#xff0c;然后做对应操作。Android 原生有自带权限管理的&#xff0c;只是被隐藏了。看android源码在android.app下就有个AppOpsManager类。类说明如下&a…

剩余 大小 查看内存_JVM的内存分配策略以及进入分代的条件

JVM的参数和知识点太多啦&#xff0c;记录下来&#xff0c;供自己随时回顾。java对象什么时候进入年轻代&#xff08;新生代&#xff09;&#xff1f;java对象什么时候进入老年代&#xff1f;对象优先在Eden分配大对象直接进入老年代空间分配担保机制java对象什么时候进入年轻代…

Arduino的串口结束符及串口缓冲区

文章目录1.深入理解缓冲区和Serial.available()&#xff1a;2.深入理解串口结束符3.验证结论&#xff1a;1.深入理解缓冲区和Serial.available()&#xff1a; 运行下面的代码&#xff0c;我发现如果不在串口输入任何数字&#xff0c;就会一直显示“no”&#xff0c;输入一个数…

android 圆角边框边框渐变,支持边框、圆角、渐变色、透明度的GradientButton

最近在项目中发现好多Button背景颜色相同&#xff0c;但圆角大小不等的Button&#xff0c;这样就得写一大堆的shape或者selector&#xff0c;不便于管理及后期维护&#xff0c;于是乎变想能不能写一个支持边框、圆角、渐变色、透明度的万用Button呢。为了能够兼容button自带的属…

(斜率,点和线段)zzuli1196数星星(二)

题目描述 一天&#xff0c;小明坐在院子里数星星&#xff0c;Gardon就出了个难题给他&#xff0c;让他数数天上的星星最多有多少个是在同一条直线上的。天上的星星太多了&#xff0c;小明马上就看花了眼&#xff0c;你能写个程序来帮他计算么&#xff1f; 输入 首先输入一个整…

鸿蒙ide如何运行,深入浅析华为鸿蒙IDE安装与Hello World

一、系统安装1. 到官网下载HUAWEI DevEco Studio2. 安装二、创建项目创建项目目前还没有手机选项&#xff0c;所以我先选择一个电视&#xff1a;IDE的环境看起来和idea差不多&#xff0c;应该比较容易上手。从“关于”里也可以看到&#xff0c;确实是基于IDEA开源版本开发的。我…

springboot 上传文件解析入库_SpringBoot + easyexcel + WebUploader 实现文件上传并解析

1. WebUploader的使用&#xff0c;引入css和js&#xff0c;css其实没什么用&#xff1a;2. 定义上传框&#xff1a;选择文件开始上传3.相关jquery&#xff1a;// 文件上传jQuery(function() {var $ jQuery,$list $(#thelist),$btn $(#ctlBtn),state pending,uploader;uploa…

中快捷搜索_同事用1分钟,我用半小时,原来是因为这8个Word快捷键,秒杀一切办公技巧...

同事用1分钟&#xff0c;我用半小时&#xff0c;这8个Word快捷键&#xff0c;秒杀一切办公技巧​mp.weixin.qq.com文&#xff5c;王羽卒今天给小伙伴们分享几个实用又有效的快捷键&#xff0c;帮助快速完成工作&#xff0c;同事用了半小时&#xff0c;你1分钟就能解决哟&#x…

动态规划理论基础

(采用维特根斯坦的表述方式) 1.达成目的过程可以由不同阶段组成 2.阶段由达成目的的条件确定 (注&#xff1a;规定每一次走一步&#xff0c;第一步就是一个阶段) 3.每个阶段由不同的状态组成 4.状态是阶段中可能面临的所有情况 (注&#xff1a;第一步落脚点可能有多个&am…

荣耀x10max能不能升级为鸿蒙,荣耀终于放出大招!4部荣耀旗舰可升级鸿蒙,网友:终于等到了...

4月24~26日&#xff0c;华为将召开开发者大会&#xff0c;届时手机使用的鸿蒙系统将正式上线&#xff0c;华为和部分荣耀手机将会首先支持。其实&#xff0c;早在2月22日的华为Mate X2的发布会上&#xff0c;余承东就说过4月发布鸿蒙OS。华为并不想推出鸿蒙系统&#xff0c;如果…

android 7.0新增控件,自定义Android控件,封装Arcgis for Android测距测面积工具控件

控件的功能包括&#xff0c;测距、测面积、撤销、恢复、清除、完成六个功能。测距&#xff1a;在地图上绘制线段进行长度测量测面积&#xff1a;在地图上绘制一个面&#xff0c;进行面积测量撤销&#xff1a;撤销到上一步绘制&#xff0c;只能撤销未完成的测量恢复&#xff1a;…

cpython需要另外安装吗_在windows环境下安装和使用Python(CPython)

在windows环境下安装和使用Python(CPython)一、下载1.选择Python版本打开Windows版Python官网下载链接&#xff0c;选择Latest Python 3 Release - Python 3.8.3 。(推荐选择Python3.x&#xff0c;也可以选择其他版本)2.选择需要的Python类型在File中选择Windows x86-64 execut…

python模块:命名空间与重载模块

文章目录模块命名空间&#xff1a;重载模块&#xff1a;#module2.py print(starting to load...) import sys name42 def func():pass class klass:pass print(done loading.) >>> import module2 starting to load... done loading. >>> module2.sys <mo…