您的位置:首页 > 理论基础 > 计算机网络

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

2014-02-17 23:07 405 查看

Table of Contents

1 简介

2 系统初始化概论

2.1 引导期间选项

2.2 中断和定时器

2.3 初始化函数

3 设备注册和初始化

3.1 硬件初始化

3.2 软件初始化

3.3 功能初始化

4 NIC初始化的基本目标

5 IRQ线

6 I/O端口和内存注册

7 硬件中断

7.1 注册中断

7.2 解除中断

8 模块选项

9 设备处理层初始化

10 动态加载设备/设备驱动

简介

  如果要使一个网络设备可用,它就必须能被内核正确识别并且与正确的设备驱动关联起来。首先,设备驱动既可以做为内核模块动态加载,也可以是内核的一个静态组件。

其次,设备可以在启动时识别,也可以在运行时加载识别(热插拔设备 USB PCI IEEE…)。

系统初始化概论

下图为系统初始化流程



图5-1:内核初始化

引导期间选项

调用两次parse_args(一次是直接调用, 而另外一次是通过parse_early_param间接调用)以处理引导加载程序(bootloader, 如LILO或GRUB)

在引导期间传给内核的配置参数。

中断和定时器

硬中断和软中断分别由init_IRQ和softirq_init做初始化。

初始化函数

内核子系统及内建的设备驱动程序由do_initcall初始化。

设备注册和初始化

注册和初始化的任务的一部分的内核负责,而其他部分由设备驱动程序负责。

硬件初始化

由设备驱动在总线(pci,usb)的协调下完成,主要分配中断号和i/o地址。

软件初始化

在设备可用前需要配置一些参数,如ip地址

功能初始化

与设备相关,如流量控制

NIC初始化的基本目标

IRQ线

NIC必须被分派一个IRQ。

I/O端口和内存注册

I/O端口和内存f分别使用request_region和release_region注册及释放。

硬件中断

注册中断

1:  static inline int __must_check
2:  request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
3:           const char *name, void *dev)
4:  {
5:       return request_threaded_irq(irq, handler, NULL, flags, name, dev);
6:  }


解除中断

1:  extern void free_irq(unsigned int, void *);


模块选项

每个模块都在/sys/modules中分派一个目录。子目录/sys/modules/module/parameters中的每个文件就是

改模块所输出的每个参数。

设备处理层初始化

[注] net/core/dev.c

1:  /*
2:   *   Initialize the DEV module. At boot time this walks the device list and
3:   *   unhooks any devices that fail to initialise (normally hardware not
4:   *   present) and leaves us with a valid list of present and active devices.
5:   *
6:   */
7:
8:  /*
9:   *       This is called single threaded during boot, so no need
10:   *       to take the rtnl semaphore.
11:   */
12:  static int __init net_dev_init(void)
13:  {
14:       int i, rc = -ENOMEM;
15:
16:       BUG_ON(!dev_boot_phase);
17:
18:       if (dev_proc_init())
19:           goto out;
20:
21:       if (netdev_kobject_init())
22:           goto out;
23:
24:       INIT_LIST_HEAD(&ptype_all);
25:       for (i = 0; i < PTYPE_HASH_SIZE; i++)
26:           INIT_LIST_HEAD(&ptype_base[i]);
27:
28:       if (register_pernet_subsys(&netdev_net_ops))
29:           goto out;
30:
31:       /*
32:        *  Initialise the packet receive queues.
33:        */
34:
35:       for_each_possible_cpu(i) {
36:           struct softnet_data *sd = &per_cpu(softnet_data, i);
37:
38:           memset(sd, 0, sizeof(*sd));
39:           skb_queue_head_init(&sd->input_pkt_queue);
40:           skb_queue_head_init(&sd->process_queue);
41:           sd->completion_queue = NULL;
42:           INIT_LIST_HEAD(&sd->poll_list);
43:           sd->output_queue = NULL;
44:           sd->output_queue_tailp = &sd->output_queue;
45:  #ifdef CONFIG_RPS
46:           sd->csd.func = rps_trigger_softirq;
47:           sd->csd.info = sd;
48:           sd->csd.flags = 0;
49:           sd->cpu = i;
50:  #endif
51:
52:           sd->backlog.poll = process_backlog;
53:           sd->backlog.weight = weight_p;
54:           sd->backlog.gro_list = NULL;
55:           sd->backlog.gro_count = 0;
56:       }
57:
58:       dev_boot_phase = 0;
59:
60:       /* The loopback device is special if any other network devices
61:        * is present in a network namespace the loopback device must
62:        * be present. Since we now dynamically allocate and free the
63:        * loopback device ensure this invariant is maintained by
64:        * keeping the loopback device as the first device on the
65:        * list of network devices.  Ensuring the loopback devices
66:        * is the first device that appears and the last network device
67:        * that disappears.
68:        */
69:       if (register_pernet_device(&loopback_net_ops))
70:           goto out;
71:
72:       if (register_pernet_device(&default_device_ops))
73:           goto out;
74:
75:       open_softirq(NET_TX_SOFTIRQ, net_tx_action);
76:       open_softirq(NET_RX_SOFTIRQ, net_rx_action);
77:
78:       hotcpu_notifier(dev_cpu_callback, 0);
79:       dst_init();
80:       dev_mcast_init();
81:       rc = 0;
82:  out:
83:       return rc;
84:  }
85:
86:  subsys_initcall(net_dev_init);


net_dev_init中包含如下功能的初始化

初始化cpu相关数据结构,用于网络软中断

调用dev_proc_init,dev_mcast_init在/proc下增加相应的文件

调用netdev_sysfs在/sys下增加相应配置文件

调用net_random_init初始化cpu种子数组,这些数组用于在net_random中生成随机数

调用dst_init初始化dst

初始化网络处理函数数组ptype_base,这些函数用来多路分解接收到的包

在通知链表上注册回调函数用于接收cpu热插拔事件

除了上述初始化,对于网络设备来说更重要的是 初始化它的net_device结构,这个会在第8章详细讲

动态加载设备/设备驱动

讲动态加载之前先介绍2个用户空间程序和1个内核函数

/sbin/modprobe 在内核需要加载某个模块时调用,判断内核传递的模块是不是/etc/modprobe.conf文件中定义的别名

/sbin/hotplug 在内核检测到一个新设备插入或拔出系统时调用,它的任务是根据设备标识加载正确的驱动

内核函数call_usermodehelper 上面两个用户进程统一由这个函数调用,其中参数arg1指示call_usermodehelper调用哪个用户进程,arg2指示call..使用哪个配

置脚本,流程详见下图;

实际上看懂了上面所说的,动态加载的概念应该很清楚了,最后再说说使用场景

以模块方式加载

kmod模块加载器允许内核组件通过调用request_module请求加载某个模块

举个例子;如果系统管理员使用ifconfig配置某个网卡,但这个网卡驱动还没有加载,如eth0,内核就会给/sbin/modprobe发送一个请求,让它加载名称为

eth0的模块。如果/etc/modprobe.conf中包含“alias eth0 xxx”的字符,/sbin/modprobe就会尝试加载xxx.ko模块。

module_param 宏定义在引入sysfs后可以通过文件来访问得到模块参数

模块选项有三项 , 第一项参数名称,第二项参数类型,第三项表示参数作为文件在sys文件系统中所有的权限。

每个模块都会在sys/modules下生成对应的目录,通过目录下的文件可以获取模块参数。

pnp热插拔

hotplug允许内核检测热插拔设备的插入和拔出并通知用户进程(/sbin/hotplug),用户进程根据这些通知来加载相应的驱动

在编译内核时,会在kernel目录下生成modules.pcimap和modules.usbmap两个文件,这两个文件分别包含了内核所支持设备的pci id和usb id,文件中还包

含于每个设备的id相对应的内核模块名称,当用户进程收到内核关于pnp的通知后,会使用这个文件来查找正确的设备驱动

Footnotes:

1 DEFINITION NOT FOUND: 0

2 DEFINITION NOT FOUND: 1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: