现代操作系统的适应性增加了初始化的复杂度。首先,设备驱动程序可以作为模块加载至内核,也可以作为内核的静态组件。此外,设备可以在引导期间就存在,也可以在运行期间插入(或删除):后一种类型的设备称为可热插拔设备,如USB、PCI CardBus、IEEE 1394以及其他。我们将会看到热插拔对内核和用户空间有什么影响。
了解主要的网络相关子系统在何处初始化以及其初始化的方式是很重要的,包括设备驱动程序在内。但是我们这里只关注网络方面的初始化,所以不会讨论一般内核服务下的设备驱动程序(如内存管理)。
下图简要的显示了引导期间一些内核子系统在何处初始化以及以何种顺序初始化(参见init/main.c
)
当内核引导时,会执行start_kernel
对一些子系统做初始化,部分显示于上图。start_kernel终止前会调用init内核线程,由其负责初始化的后续工作。后面涉及的初始化活动多数都在do_basic_setup
内发生。
在各种初始化任务中,我们最感兴趣的有三个:
引导期间选项:
调用两次parse_args
(一次是直接调用,而另一次是通过parse_early_param间接调用)以处理引导加载程序(boot loader,如LILO或GRUB)在引导期间传给内核的配置参数。后面会说明
中断和定时器
硬中断和软中断分别由init_IRQ
和softirq_init
做初始化。中断会在后面博客分析,这里只说明设备驱动程序如何把处理函数注册给IRQ,以及IRQ处理函数在内存中是如何组织的。定时器在引导过程中很早就被初始化,使得后续任务可以使用。
初始化函数
内核子系统及内建的设备驱动程序由do_initcalls
初始化。free_init_mem
会释放一块无用程序所持有的内存。这项优化称为可能要归功于精明的函数标记法。
run_init_process
确定在系统上运行的第一个进程,也就是所有其他进程的父进程;其PID为1,一直运行直到系统做完工作。正常情况下,运行的程序是init(属于SysVinit套件的一部分)。然而,管理员可以通过init = 引导期间选项指定另一个不同程序
。不提供这个选项时,内核就会尝试从一组众所周知的位置去执行init命令,如果都找不到init,就会发生内核panic。用户也可以提供一些引导期间的选项传给init。
一个网络设备可用,就必须被内核认可,并且关联正确的驱动程序。驱动程序把驱动设备所需的所有信息存储在私有数据结构中,然后与其他需要此设备的内核组件交互。注册和初始化任务的一部分由内核负责,而其他部分由设备驱动程序负责。来仔细考察初始化的几个阶段:
在第二章节,我们已经知道net_device
数据结构包含一组函数指针,内核可以用他们与设备驱动程序及特殊内核功能交互。这些函数的初始化部分取决于设备类型,部分取决于厂家和型号。
NIC指的是网络接口控制器
Linux内核中,每个网络设备都由一个net_device
数据结构实例表示。后面会分析该数据结构如何分配以及其字段如何初始化,部分是由设备驱动程序完成,部分是由内核函数完成。这里的重点是设备驱动程序如何分配建立设备/内核通信所需的资源。比如:
request_region
和release_region
注册和释放。几乎所有设备(包括NIC)都采用以下两种方式之一与内核交互:
在后面章节会详细讨论各种NIC驱动设计方式以及中断。你也会看到Linux如何结合使用轮询和中断以提高性能。这里我们只讨论基于中断的情况。
每个中断事件都会运行一个函数,被称为中断处理例程,而中断处理例程必须安装设备的所需进行剪裁,因此由设备驱动程序安装。一般而言,当设备驱动程序注册一个NIC时,会请求并分派一个IRQ。然后,用两个依赖体系结构的函数为给定的IRQ注册或删除处理例程。这两个函数定义在kernel/irq/manage.c
中
当内核接收到中断通知时,会使用IRQ编号找出该驱动程序的处理例程,然后执行该处理例程。内核把IRQ编号和函数处理例程见的相连关系存储在一张全局表中。相连的关系可以是一对一或一对多,因为Linux内核允许几台设备使用相同的IRQ。使用如下两个函数来进行IRQ处理例程的注册和释放
int request_irq(unsigned int irq, void (*handler)(int, void*, struct pt_regs*), unsigned long irqflags, const char* devname, void *dev_id);
void free_irq(unsigned int irq, void *dev_id);
通过中断,NIC能够告知其驱动程序几种不同的事情。其中包括
接收一帧
这是最常见的,标准的情况。
传输失败
这种通知信息只有被称为二进制指数后退功能失败时,才又Ethernet设备产生。注意,驱动程序不会把这种通知信息转送到那些较高层的网络层,这些网络层会通过其他方式获得这种失败(定时器到期,拒绝接收的ACK等等)
DMA传输已完成
给定一个帧传输,当帧上截止NIC的内存准备在此媒介上传输时,驱动程序就会将持有该帧的缓冲区释放掉。
设备有足够内存处理新传输
IRQ线是有限的资源。增加系统能容纳设备数量的简单方式,就是允许几台设备共享同一个IRQ。正常来讲,每个设备会针对该IRQ将其自己的处理例程注册给内核。再由内核启用那些注册同一个共享IRQ的设备的所有处理例程,而不是由内核接收中断通知,寻找正确的设备,再启用其处理例程。接着由处理例程去过滤出错误的启用,如通过读取设备上的注册信息。
IRQ处理例程的映射存储在一个表向量中,每一个IRQ都对应一个处理例程列表,如下图所示
内核内建的组件以及作为模块加载的组件都通过输入参数,使用户调整组件所实现的功能、重写其默认值,或者在系统引导前后有不同的值。
kmod
是内核模块加载程序,允许内核组件请求加载一个模块。内核提供的函数不止一个,但是这里我们只分析request_module。
虚拟设备是建立在一个或多个真实设备之上的抽象。虚拟设备与真实设备之间的关联可以是多对多的,如下。也有可能是在其他虚拟设备之上建立虚拟设备。
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- zicool.com 版权所有 湘ICP备2023022495号-2
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务