2019独角兽企业重金招聘Python工程师标准>>>
NIC可用之前,其相关联的net_device数据结构必须先初始化,添加到内核网络设备数据库、配置并开启。不要把注册/除名以及开启/关闭混淆是十分重要的,这是两种不同的概念:
- 如果把加载设备驱动程序模块的动作排除的话,注册和除名是独立于用户之外的,是由内核驱动的。仅仅注册了的设备还不能运转。
- 开启和关闭设备都需要用户参与。一旦设备已由内核注册,用户就可通过用户命令看到该设备,配置并予以开启。
设备注册之时
加载NIC设备驱动程序
如果NIC设备驱动程序内建至内核中,则在引导期间初始化;否则,如果以模块加载,就会在运行期间初始化;否则,如果以模块加载,就会在运行期间初始化。每当发生初始化时,该驱动程序所控制的所有NIC都会被注册。
插入可热插拔网络设备
当用户把可热插拔NIC设备插入进来时,内核会通知其驱动程序,而驱动程序在注册该设备。
设备除名之时
卸载NIC设备驱动程序
这仅是针对那些以模块加载的驱动程序,不适合那些内建至内核的驱动程序。当管理员卸载NIC设备驱动程序时,所有相关联的NIC都必须除名。
删除可热插拔网络设备
当用户从系统(其运行的内核支持可热插拔设备)删除可热插拔NIC时,则网络设备就会被除名。
设备注册状态通知
内核组件和用户空间应用程序可能都想知道何时发生网络设备注册、除名、关闭或者开启之事。这类事件的通知是通过两种通道传送的:
netdev_chain,内核组件可以注册此通知链。设备的注册和除名在各个阶段的进展都是通过netdev_chain通知链报告的。此链定义在net/core/dev.c中,而对此类事件感兴趣的内核组件可以通过register_netdevice_notifier和unregister_netdevice_notifier分别对该链注册或除名。
netdev_chain报告的事件:
NETDEV_UP,NETDEV_GOING_DOWN,NETDEV_DOWN,送出NETDEV_UP以报告设备已开启,而且此事件是由dev_open产生。当设备要关闭时,就会送出NETDEV_GOING_DOWN.当设备已关闭时,就会送出NETDEV_DOWN.这些事件都是由dev_close()产生的。
NETDEV_REGISTER设备已注册,此事件是有register_netdevice产生的。
NETDEV_UNREGISTER,设备已经除名,此事件是由unregister_netdevice产生的。
NETDEV_CHANGEADDR设备的硬件地址已改变。
NETDEV_CHANGENAME设备已改变其名称
NETDEV_CHANGE设备的状态或配置已经改变,此事件会用在上述情况未包括的其他情况下。
注意,向链注册时,register_netdevice_notifier也会(仅对新注册者)重放当前系统已注册设备的所有过去的NETDEV_REGISTER和NETDEV_UP通知信息。这样就能给新注册者有关已注册设备的清晰图像。内核注册该链的子系统:路由,使用此通知信息新增或删除与此设备相关联的所有路由项目;协议代码,当改变一个本地设备的MAC地址时,ARP表也必须据此更新。RTnetlink。
Netlink的RTMGRP_LINK多播群组,用户空间程序可以注册netlink的RTMGRP_LINK多播群组,当设备的状态或配置中有变更时,就会用rtmsg_ifinfo把通知信息传送给Link多播群组RTMGRP_LINK,其中一些通知信息如下:
- 当netdev_chain通知链收到一个通知信息时,RTnetlink会注册前一节所提及的netdev_chain,然后重放其接受到的通知信息。
- 当一个已关闭的设备开启时或者处于相反的过程。
- 当net_device->flags中的一个标识有变动时。
netplugd是守护进程,会监听这些通知信息,然后根据用户配置文件而反应。
函数netdev_wait_allrefs
netdev_wait_allrefs由一个循环组成,只有当dev->refcnt建至零时才会结束。此函数每秒都会发送出一个NETDEV_UNREGISTER通知信息,而没10秒都会在控制台上打印出一条警告信息。剩余时间都在休眠。此函数直到对输入net_device结构的所有引用都已释放为止。有两种常见情况需要传送一个以上的通知信息:
bug,例如有段代码持有对net_device结构的引用,但是因为没有在netdev_chain通知链注册,或因为没有正确处理通知信息,使其无法释放。
未决的定时器,例如,假设当定时器到期时要执行的那个函数必须访问的数据中,包含了对net_device结构的引用。在这种情况下,你必须等待直到该定时器到期,而且其处理函数有望会释放其引用。
1: /*
2: * netdev_wait_allrefs - wait until all references are gone.
3: *
4: * This is called when unregistering network devices.
5: *
6: * Any protocol or device that holds a reference should register
7: * for netdevice notification, and cleanup and put back the
8: * reference if they receive an UNREGISTER event.
9: * We can get stuck here if buggy protocols don't correctly
10: * call dev_put.
11: */
12: static void netdev_wait_allrefs(struct net_device *dev)
13: {
14: unsigned long rebroadcast_time, warning_time;
15: int refcnt;
16:
17: linkwatch_forget_dev(dev);
18:
19: rebroadcast_time = warning_time = jiffies;
20: refcnt = netdev_refcnt_read(dev);
21:
22: while (refcnt != 0) {
23: if (time_after(jiffies, rebroadcast_time + 1 * HZ)) {
24: rtnl_lock();
25:
26: /* Rebroadcast unregister notification */
27: call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
28: /* don't resend NETDEV_UNREGISTER_BATCH, _BATCH users
29: * should have already handle it the first time */
30:
31: if (test_bit(__LINK_STATE_LINKWATCH_PENDING,
32: &dev->state)) {
33: /* We must not have linkwatch events
34: * pending on unregister. If this
35: * happens, we simply run the queue
36: * unscheduled, resulting in a noop
37: * for this device.
38: */
39: linkwatch_run_queue();
40: }
41:
42: __rtnl_unlock();
43:
44: rebroadcast_time = jiffies;
45: }
46:
47: msleep(250);
48:
49: refcnt = netdev_refcnt_read(dev);
50:
51: if (time_after(jiffies, warning_time + 10 * HZ)) {
52: printk(KERN_EMERG "unregister_netdevice: "
53: "waiting for %s to become free. Usage "
54: "count = %d\n",
55: dev->name, refcnt);
56: warning_time = jiffies;
57: }
58: }
59: }
开启和关闭网络设备
设备一旦注册就可用,但是,除非用户明确的开启,否则还是无法传输和接收数据流。开始设备函数为dev_open
- 如果有定义的话,调用dev->open。并非所有设备驱动程序都初始化此函数
- 设置dev->state中的__LINK_STATE_START标识,把设备标识为开启和运行中。
- 设置dev->flags中的IFF_UP标识,把设备标识为开启。
- 调用dev_activate以初始化由流量控制使用的出口队列规则,然后启动看门狗定时器。如果流量控制没有用户配置,就指定默认的FIFO
- 传送NETDEV_UP通知信息给netdev_chain通知链,以通知感兴趣的内核组件,该设备现已开启。
设备关闭
- 传送NETDEV_GOING_DOWN通知信息给netdev_chain通知链,以通知感兴趣的内核组件该设备即将被关闭
- 调用dev_deactivate以关闭出口队列规则,使得该设备再也无法用于传输,然后因为不在需要,停止看门狗定时器
- 清除dev->state中的__LINK_STATE_START标识,把设备标识为关闭
- 如果一个轮询动作被调度,以读取设备上的入口封包,就要等待该动作完成。因为__LINK_STATE_START标识已被清除,该设备上已不能再为其他接收的轮询动作进行调度了,但是在该标志清除前可能有一个轮询动作未决。
- 如果有定义,调用dev->stop。
- 清除dev->flags中的IFF_UP标识,把设备标识为关闭
- 传送NETDEV_DOWN通知链
链接状态变更侦测
可能导致链接状态变更的一些情况:
1,电缆线插入NIC,或者从NIC中拔出。
2. 电缆线另一端的设备电源关掉或关闭了。这类设备有HUB,桥接器,路由器以及PC NIC等