深入理解Linux网络技术内幕(五)——网络设备初始化


前言

现代操作系统的适应性增加了初始化的复杂度。首先,设备驱动程序可以作为模块加载至内核,也可以作为内核的静态组件。此外,设备可以在引导期间就存在,也可以在运行期间插入(或删除):后一种类型的设备称为可热插拔设备,如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_IRQsoftirq_init做初始化。中断会在后面博客分析,这里只说明设备驱动程序如何把处理函数注册给IRQ,以及IRQ处理函数在内存中是如何组织的。定时器在引导过程中很早就被初始化,使得后续任务可以使用。
初始化函数
内核子系统及内建的设备驱动程序由do_initcalls初始化。free_init_mem会释放一块无用程序所持有的内存。这项优化称为可能要归功于精明的函数标记法。

run_init_process确定在系统上运行的第一个进程,也就是所有其他进程的父进程;其PID为1,一直运行直到系统做完工作。正常情况下,运行的程序是init(属于SysVinit套件的一部分)。然而,管理员可以通过init = 引导期间选项指定另一个不同程序。不提供这个选项时,内核就会尝试从一组众所周知的位置去执行init命令,如果都找不到init,就会发生内核panic。用户也可以提供一些引导期间的选项传给init。

设备注册和初始化

一个网络设备可用,就必须被内核认可,并且关联正确的驱动程序。驱动程序把驱动设备所需的所有信息存储在私有数据结构中,然后与其他需要此设备的内核组件交互。注册和初始化任务的一部分由内核负责,而其他部分由设备驱动程序负责。来仔细考察初始化的几个阶段:

  • 硬件初始化
    由设备驱动程序和通用总线层(例如,PCI或USB)合作完成。驱动程序有时独自而有时通过用户提供的参数协助,把每个设备的这类功能配置成IRQ和I/O地址,使其能与内核交互。与较高层的协议和功能相比,这项活动更接近设备驱动程序,后面将会分析一个例子。
  • 软件初始化
    在设备能够被使用前,依赖于所开启和配置网络协议为何而定,用户需要提供一些配置参数,如IP地址等。
  • 功能初始化
    Linux内核有很多网络选项。因为有些选项需要针对每个设备进行配置,因此,设备初始化引导顺序必须负责这些选项。其中一个例子是流量控制,它可以决定封包加入及退出设备的出口队列的方式。

在第二章节,我们已经知道net_device数据结构包含一组函数指针,内核可以用他们与设备驱动程序及特殊内核功能交互。这些函数的初始化部分取决于设备类型,部分取决于厂家和型号。

NIC初始化的基本目标

NIC指的是网络接口控制器
Linux内核中,每个网络设备都由一个net_device数据结构实例表示。后面会分析该数据结构如何分配以及其字段如何初始化,部分是由设备驱动程序完成,部分是由内核函数完成。这里的重点是设备驱动程序如何分配建立设备/内核通信所需的资源。比如:

  • IRQ线
    在“设备与内核之间的交互”一节中会知道,NIC必须被分配一个IRQ,然后在必要时用它要求内核的注意。然而,虚拟设备不需要分派一个IRQ:回环设备就是一例,因其活动都在内部进行。
    用于请求和释放IRQ线的两个函数会在后面"硬件中断"介绍。
  • I/O端口和内存注册
    一般地,驱动程序将其设备的一个内存区域(例如,其配置寄存器)映射到系统内存,使得驱动程序的读/写操作可以通过系统内存地址直接进行,这样可以简化代码。I/O端口和内存分别使用request_regionrelease_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处理例程的映射存储在一个表向量中,每一个IRQ都对应一个处理例程列表,如下图所示
在这里插入图片描述前面我们已经知道了对处理例程的注册和删除,现在,来看用于存储此映射的数据结构。
映射用irqaction数据结构定义。前面所介绍的request_irq函数其实就是包含setup_irq的包裹函数,用一个irqaction结构为其输入,然后将其插入至一个全局变量irq_desc。irq_desc向量定义在kernel/irq/handler.c中。
处理中断并将其传给驱动程序的内核函数是依赖于体系结构的。在大多数体系结构上称之为handler_IRQ_event
现在,让我们来看看irqaction数据结构的字段中存储了哪些与IRQ处理例程相关的信息:
void (*handler)(int irq, void *dev_id, struct pt_regs *regs)
由设备驱动程序所提供的函数,用以处理中断的通知信息:每当内核接收irq在线的中断事件时,就调用此例程。参数解析如下:
int irq:产生此通知信息的IRQ编号。大多数时候,NIC的设备驱动程序不用此参数就能完成其工作,有设备ID就足够。
void *dev_id:设备标识符。同一个驱动程序可能同时要负责不同的设备,所以需要设备ID来正确处理通知信息。
struct pt_regs *regs:中断事件打断当前进程时,用于存储处理器的寄存器内容的结构。

初始化选项

内核内建的组件以及作为模块加载的组件都通过输入参数,使用户调整组件所实现的功能、重写其默认值,或者在系统引导前后有不同的值。
在这里插入图片描述

模块选项

在这里插入图片描述

设置处理层初始化:net_dev_init

在这里插入图片描述在这里插入图片描述在这里插入图片描述

旧代码

在这里插入图片描述

用户空间辅助程序

在这里插入图片描述

kmod

kmod是内核模块加载程序,允许内核组件请求加载一个模块。内核提供的函数不止一个,但是这里我们只分析request_module。
在这里插入图片描述

热插拔

在这里插入图片描述在这里插入图片描述

虚拟设备

虚拟设备是建立在一个或多个真实设备之上的抽象。虚拟设备与真实设备之间的关联可以是多对多的,如下。也有可能是在其他虚拟设备之上建立虚拟设备。
在这里插入图片描述

虚拟设备范例

在这里插入图片描述在这里插入图片描述

与内核网络协议栈之间的交互

在这里插入图片描述
在这里插入图片描述

通过/proc文件系统调整

在这里插入图片描述在这里插入图片描述

本章涉及的文件和目录

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jacky~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值