网络设备的注册与初始化

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等

你可能感兴趣的:(网络设备的注册与初始化)