在第五章和第六章,讨论了内核如何验证NIC以及内核所做的初始化,使得NIC得以和设备驱动程序对话。本章将讨论初始化的其他步骤。
设备注册和除名:
注册时机:
一,加载NIC设备驱动程序:若驱动内建在内核,则则在引导期间初始化,若以模块加载,则在允许期间初始化。初始化完成后,该驱动所控制的所有NIC都会被注册。
二,插入可热插拔设备。可热插拔设备被插入时,内核会通知其驱动(假设相关驱动已加载),驱动再注册设备。
除名时机:
卸载NIC设备驱动,删除可热插拔设备。
分配net_device结构:
调用alloc_netdev(net/core/dev.c)来返回一个指向net_device结构的指针。该函数需要三个参数:
私有数据的大小:net_device结构可以驱动扩充,即加速一个私有数据区。
设备名称
设置函数:用于初始化net_device的部分字段。
内核提供一组包裹函数来提供正确的参数给alloc_netdev。例如,alloc_etherdev用于Ethernet设备。
![](https://i-blog.csdnimg.cn/blog_migrate/511c3577d38a0f1ea409a8f80dd5b6a6.png)
设备注册时,驱动程序调用适当的内含alloc_netdev的包裹函数来获取net_device数据结构。有些驱动还会调用netdev_boot_setup_check来检查加载内核时用户是否提供了任何引导期间参数,见第七章使用引导选项配置网络设备一节。最后,register_netdevice会将新的net_device实例插入设备数据库中。
![](https://i-blog.csdnimg.cn/blog_migrate/1e01175da6775235cad9dbaf68189143.png)
register_netdevice负责一部分的注册,然后net_set_todo把新的net_device实例添加到一个列表,最后netdev_run_todo会浏览该列表完成net_device实例。
![](https://i-blog.csdnimg.cn/blog_migrate/546f4d9622db959839fe03a15e692257.png)
设备注册状态是提供两种方式传递的:
netdev_chain:
Netlink的RTMGRP_LINL多播群组:
设备初始化:
设备初始化可以理解为就是初始化net_device结构中的字段,这些字段会一批一批的由不同函数初始化。主要分为三部分:
设备驱动程序初始化:由设被驱动程序所初始化的字段通常由第六章提到的xxx_probe函数负责,例如IRQ,I/O内存,I/O端口。
设备类型初始化:对常见的网络设备,xxx_setup函数可以对net_device结构中同一系列设备的通用字段初始化。
可选的初始化和特殊情况。
设备除名的主要步骤:
以dev_close关闭设备。
释放所有已分配的资源,如IRQ,I/O内存等。
从全局列表dev_base和两个hash表中把net_device结构删除。
一旦net_device的所有引用都释放了,就释放net_device结构。
删除添加到/proc和/sys的文件。
![](https://i-blog.csdnimg.cn/blog_migrate/3d4ef904c91c0f4c46023e5dd3c0b211.png)
开启和关闭网络设备:
设备注册后就可以使用了,但是需要用于明确开启,否则还是无法传输和接收数据。dev_open函数负责开启设备(net/core/dev.c)。设备开启主要做以下一些工作:
调用net_device结构中的open函数(如果定义了的话)。
设置net_device结构中的state字段为_ _LINK_STATE_START标识设备为开启和运行状态。
设置net_device结构中的flags字段为IFF_UP标识设备为开启。
调用dev_activate初始化由流量控制使用的出口队列规则,然后启动看门狗定时器。
发送设备开启的通知信息给netdev_chain通知链。
关闭设备的流程和开启相反。
net_device结构中的state字段如何处理电源管理和链路变更:
电源管理:
当内核支持电源管理时,只要系统挂起或重新继续,NIC设备驱动程序就会收到通知。在第六章提到,pci_driver结构中有两个函数指针suspend和resume。当系统挂起时,就会执行设备驱动提供的suspend函数,并清除state字段中的_ _LINK_STATE_PRESENT,若设备已开启,还会关闭出口队列。当系统重新继续时,调用resume函数,然后设置
_ _LINK_STATE_PRESENT,若设备挂起前已开启,则打开其出口队列且重启流量控制所使用的看门狗定时器。
链路状态变更侦测:
可能导致链接状态变更的一些常见情况:
一,电缆插入NIC,或者从NIC拔出。
二,电缆线另一端的设备电源关闭了。这类设备有Hub,路由器,桥接器,以及PC NIC等
当设备驱动侦测到设备上有载波而调用netif_carrier_on时,此函数会:
清除state中的
_ _LINK_STATE_NOCARRIER标识。
产生一个链路状态变更事件,并交给linkwatch_fire_event处理。
若设备已开启,启动看门狗定时器,以侦测传输是否失败。
当设备驱动侦测到设上遗失载波而调用netif_carrier_off时,此函数会:
设置state中的
_ _LINK_STATE_NOCARRIER标识。
产生一个链路状态变更事件,并交给linkwatch_fire_event处理。
链路变更事件用lw_event结构定义。此结构只有两个字段,一个指向相关net__device的指针和一个用来构成全局lweventlist列表的指针。全局链表包含未决的链路变更事件。任何设备都只有一个lw_event实例在链表中。当设备已有一个未决链接状态变更事件时,新事件就不需要入队了,该情况可以用state中的
_ _LINK_STATE_LINKWATCH_PENDING标识判断。对lw_event实例的处理只包括三个方面:
清除
state中的
_ _LINK_STATE_LINKWATCH_PENDING标识。
发送相关通知信息到netdev_chain通知链。
发送RTM_NEWLINK通知信息到RTMGRP_LINK RTnetlink群组。
![](https://i-blog.csdnimg.cn/blog_migrate/181af2772d7a2ac8a0ecad6f1335c6f4.png)
从用户空间配置设备:
配置网络设备的一些工具:
ifconfig和mii-tool,来自net-tools套件。
ethtool,来自ethtool套件。
ip link,来自IPROUTE2套件。
![](https://i-blog.csdnimg.cn/blog_migrate/cd6dffbc9e4edf7839d52eb0d26ed6d5.png)
虚拟设备:
Bonding:允许你绑定一组接口使其看起来像是一个接口,流量可以通过各种算法分散在这些接口之间,当eth0宕掉时,bond0在真实设备间分配流量时必须知道此事,把这件事考虑进来,若eth1也宕掉了,bond0就得关闭。
VLAN:Linux支持802.1Q协议,允许定义VLAN接口。如下图,当eth0宕掉时,所有虚拟接口也必须宕掉。
![](https://i-blog.csdnimg.cn/blog_migrate/277c72972984142f82e752a6f2862ab1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d1c186f9489ddfb466d71725c76d23d6.png)