第 5 章. SYSINIT 框架

SYSINIT 是一个通用的调用排序和调度机制的框架。FreeBSD 目前使用它来动态初始化内核。SYSINIT 允许在内核链接时重新排序、添加、删除和替换 FreeBSD 的内核子系统,当内核或其模块之一加载时无需编辑静态排序的初始化路由并重新编译内核。此系统还允许内核模块(当前称为KLD)在引导时单独编译、链接和初始化,甚至在系统已运行时稍后加载。这是通过使用“内核链接器”和“链接器集”来实现的。

5.1. 术语

链接器集

一种链接器技术,其中链接器将程序源文件中的静态声明数据收集到一个连续可寻址的数据单元中。

5.2. SYSINIT 操作

SYSINIT 依赖于链接器能够获取程序源代码中多个位置声明的静态数据,并将其作为一个连续的数据块组合在一起的能力。这种链接器技术称为“链接器集”。SYSINIT 使用两个链接器集来维护两个数据集,其中包含每个使用者调用的顺序、函数以及指向传递给该函数的数据的指针。

SYSINIT 在排序函数以供执行时使用两个优先级。第一个优先级是子系统 ID,它为 SYSINIT 调度函数提供了总体顺序。当前预定义的 ID 位于 <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() 采用一个唯一标识符,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 数据添加到 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);

上次修改时间:2024 年 3 月 9 日,作者:Danilo G. Baio