第 5 章 SYSINIT框架

This translation may be out of date. To help with the translations please access the FreeBSD translations instance.

SYSINIT是一个通用的调用排序与分别执行机制的框架。 FreeBSD目前使用它来进行内核的动态初始化。 SYSINIT使得FreeBSD的内核各子系统可以在内核或模块动态加载链接时被重整、 添加、删除、替换,这样,内核和模块加载时就不必去修改一个静态的有序初始化 安排表甚至重新编译内核。这个体系也使得内核模块 (现在称为KLD可以与内核不同时编译、链接、 在引导系统时加载,甚至在系统运行时加载。这些操作是通过 "内核链接器"(kernel linker)和"链接器集合" (linker set)完成的。

5.1. 术语

链接器集合(Linker Set)

一种链接方法。这种方法将整个程序源文件中静态申明的数据收集到 一个可邻近寻址的数据单元中。

5.2. SYSINIT操作

SYSINIT要依靠链接器获取遍布整个程序源代码多处申明的静态数据 并把它们组成一个彼此相邻的数据块。这种链接方法被称为 "链接器集合"(linker set)。 SYSINIT使用两个链接器集合以维护两个数据集合, 包含每个数据条目的调用顺序、函数、一个会被提交给该函数的数据指针。

SYSINIT按照两类优先级标识对函数排序以便执行。 第一类优先级的标识是子系统的标识, 给出SYSINIT分别执行子系统的函数的全局顺序, 定义在sys/kernel.h中的枚举 sysinit_sub_id内。第二类优先级标识在子系统中的元素的顺序, 定义在sys/kernel.h中的枚举 sysinit_elem_order内。

有两种时刻需要使用SYSINIT:系统启动或内核模块加载时, 系统析构或内核模块卸载时。内核子系统通常在系统启动时使用SYSINIT 的定义项以初始化数据结构。例如,进程调度子系统使用一个SYSINIT 定义项来初始化运行队列数据结构。设备驱动程序应避免直接使用 SYSINIT(),对于总线结构上的物理真实设备应使用 DRIVER_MODULE()调用的函数先侦测设备的存在, 如果存在,再进行设备的初始化。这一系统过程中, 会做一些专门针对设备的事情,然后调用SYSINIT()本身。 对于非总线结构一部分的虚设备,应改用DEV_MODULE()

5.3. 使用SYSINIT

5.3.1. 接口

5.3.1.1. 头文件

sys/kernel.h

5.3.1.2. 宏

SYSINIT(uniquifier, subsystem, order, func, ident)
SYSUNINIT(uniquifier, subsystem, order, func, ident)

5.3.2. 启动

SYSINIT()在SYSINIT启动数据集合中 建立一个SYSINIT数据项,以便SYSINIT在系统启动或模块加载时排序 并执行其中的函数。SYSINIT()有一个参数uniquifier, SYSINIT用它来标识数据项,随后是子系统顺序号、子系统元素顺序号、 待调用函数、传递给函数的数据。所有的函数必须有一个恒量指针参数。

例 1. SYSINIT()的例子
#include sys/kernel.h

void foo_null(void *unused)
{
        foo_doo();
}
SYSINIT(foo, SI_SUB_FOO, SI_ORDER_FOO, foo_null, NULL);

struct foo foo_voodoo = {
        FOO_VOODOO;
}

void foo_arg(void *vdata)
{
        struct foo *foo = (struct foo *)vdata;
        foo_data(foo);
}
SYSINIT(bar, SI_SUB_FOO, SI_ORDER_FOO, foo_arg, foo_voodoo);

注意,SI_SUB_FOOSI_ORDER_FOO 应当分别在上面提到的枚举sysinit_sub_idsysinit_elem_order之中。既可以使用已有的枚举项, 也可以将自己的枚举项添加到这两个枚举的定义之中。 你可以使用数学表达式微调SYSINIT的执行顺序。 以下的例子示例了一个需要刚好要在内核参数调整的SYSINIT之前执行的SYSINIT。

例 2. 调整SYSINIT()顺序的例子
static void
mptable_register(void *dummy __unused)
{

	apic_register_enumerator(mptable_enumerator);
}

SYSINIT(mptable_register, SI_SUB_TUNABLES - 1, SI_ORDER_FIRST,
    mptable_register, NULL);

5.3.3. 析构

SYSUNINIT()的行为与SYSINIT()的相当, 只是它将数据项填加至SYSINIT的析构数据集合。

例 3. SYSUNINIT()的例子
#include sys/kernel.h

void foo_cleanup(void *unused)
{
        foo_kill();
}
SYSUNINIT(foobar, SI_SUB_FOO, SI_ORDER_FOO, foo_cleanup, NULL);

struct foo_stack foo_stack = {
        FOO_STACK_VOODOO;
}

void foo_flush(void *vdata)
{
}
SYSUNINIT(barfoo, SI_SUB_FOO, SI_ORDER_FOO, foo_flush, foo_stack);

Last modified on: March 9, 2024 by Danilo G. Baio