Linux中内核基础设置函数do_basic_setup的实现
执行内核基础设置的函数do_basic_setup
void init_workqueues(void)
{hotcpu_notifier(workqueue_cpu_callback, 0);keventd_wq = create_workqueue("events");BUG_ON(!keventd_wq);
}
void __init usermodehelper_init(void)
{khelper_wq = create_singlethread_workqueue("khelper");BUG_ON(!khelper_wq);
}static void __init do_basic_setup(void)
{/* drivers will send hotplug events */init_workqueues();usermodehelper_init();key_init();driver_init();#ifdef CONFIG_SYSCTLsysctl_init();
#endif/* Networking initialization needs a process context */ sock_init();do_initcalls();
}
函数功能概述
init_workqueues: 初始化工作队列机制usermodehelper_init: 初始化用户模式助手机制do_basic_setup: 执行内核基础设置的总入口函数
init_workqueues 函数详解
void init_workqueues(void)
{hotcpu_notifier(workqueue_cpu_callback, 0);keventd_wq = create_workqueue("events");BUG_ON(!keventd_wq);
}
CPU热插拔通知注册
hotcpu_notifier(workqueue_cpu_callback, 0);
CPU热插拔支持:
hotcpu_notifier(): 注册CPU热插拔事件的通知回调workqueue_cpu_callback: 回调函数,当CPU被热添加或热移除时调用- 参数0: 优先级,0表示默认优先级
- 作用: 确保工作队列在CPU热插拔时能正确调整,将工作重新分配到可用的CPU上
创建事件工作队列
keventd_wq = create_workqueue("events");
创建工作队列:
create_workqueue("events"): 创建名为"events"的工作队列keventd_wq: 全局变量,存储创建的工作队列指针- 工作队列作用: 提供内核中延迟执行任务的机制,允许函数在进程上下文中异步执行
错误检查
BUG_ON(!keventd_wq);
严重错误检查:
BUG_ON(!keventd_wq): 如果工作队列创建失败(返回NULL),触发内核严重错误- BUG_ON: 内核宏,条件为真时导致系统panic
- 必要性: 事件工作队列是内核核心基础设施,创建失败系统无法正常运行
usermodehelper_init 函数详解
void __init usermodehelper_init(void)
{khelper_wq = create_singlethread_workqueue("khelper");BUG_ON(!khelper_wq);
}
函数声明
void __init usermodehelper_init(void)
初始化函数:
__init: 标记此函数只在初始化阶段使用,完成后内存可被释放usermodehelper_init: 用户模式助手初始化
创建单线程工作队列
khelper_wq = create_singlethread_workqueue("khelper");
单线程工作队列:
create_singlethread_workqueue("khelper"): 创建名为"khelper"的单线程工作队列- 与普通工作队列的区别:
- 普通工作队列: 在每个CPU上都有工作线程
- 单线程工作队列: 只有一个工作线程,保证任务串行执行
- 用途: 用户模式助手需要串行执行用户空间程序,避免竞争条件
错误检查
BUG_ON(!khelper_wq);
关键设施检查:
- 确保khelper工作队列创建成功
- 失败会导致内核panic,因为用户模式助手是模块加载等功能的依赖
do_basic_setup 函数详解
static void __init do_basic_setup(void)
{/* drivers will send hotplug events */init_workqueues();usermodehelper_init();key_init();driver_init();#ifdef CONFIG_SYSCTLsysctl_init();
#endif/* Networking initialization needs a process context */ sock_init();do_initcalls();
}
函数声明
static void __init do_basic_setup(void)
基础设置函数:
static: 限制函数作用域在当前文件__init: 初始化阶段函数- 这是内核启动过程中执行各种基础初始化的总入口
工作队列初始化
/* drivers will send hotplug events */init_workqueues();usermodehelper_init();
- 依次调用两个工作队列初始化函数
- 为后续的驱动初始化提供异步执行环境
密钥和驱动初始化
key_init();driver_init();
密钥管理系统:
key_init(): 初始化内核密钥管理子系统- 功能: 为内核提供密钥存储和管理能力,用于加密文件系统、网络安全等
驱动模型初始化:
driver_init(): 初始化设备驱动模型核心- 功能:
- 初始化总线类型列表
- 初始化设备模型基础设施
- 准备设备驱动注册机制
系统控制初始化
#ifdef CONFIG_SYSCTLsysctl_init();
#endif
条件编译的系统控制:
#ifdef CONFIG_SYSCTL: 只在配置了sysctl支持时编译sysctl_init(): 初始化sysctl接口- sysctl作用: 提供/proc/sys文件系统,允许运行时调整内核参数
网络子系统初始化
/* Networking initialization needs a process context */ sock_init();
- 网络初始化需要进程上下文
sock_init(): 初始化网络套接字层- 功能:
- 初始化协议家族
- 注册网络文件系统类型
- 准备网络设备基础设施
初始化调用执行
do_initcalls();
执行所有初始化调用:
do_initcalls(): 执行所有通过__initcall宏注册的初始化函数- 机制: 链接器将不同优先级的初始化函数放在特定段中,这里按顺序执行
- 包含的内容:
- 设备驱动初始化
- 文件系统初始化
- 网络协议初始化
- 各种子系统初始化
设备驱动模型初始化driver_init
int __init devices_init(void)
{return subsystem_register(&devices_subsys);
}
int __init buses_init(void)
{return subsystem_register(&bus_subsys);
}
int __init classes_init(void)
{int retval;retval = subsystem_register(&class_subsys);if (retval)return retval;/* ick, this is ugly, the things we go through to keep from showing up* in sysfs... */subsystem_init(&class_obj_subsys);if (!class_obj_subsys.kset.subsys)class_obj_subsys.kset.subsys = &class_obj_subsys;return 0;
}
int __init firmware_init(void)
{return subsystem_register(&firmware_subsys);
}
int __init platform_bus_init(void)
{device_register(&platform_bus);return bus_register(&platform_bus_type);
}
int __init system_bus_init(void)
{system_subsys.kset.kobj.parent = &devices_subsys.kset.kobj;return subsystem_register(&system_subsys);
}
int __init cpu_dev_init(void)
{return sysdev_class_register(&cpu_sysdev_class);
}void __init driver_init(void)
{/* These are the core pieces */devices_init();buses_init();classes_init();firmware_init();/* These are also core pieces, but must come after the* core core pieces.*/platform_bus_init();system_bus_init();cpu_dev_init();
}
函数功能概述
这些函数共同初始化Linux设备驱动模型的核心组件,包括设备、总线、类别、固件等子系统,构建了Linux设备管理的完整框架
各个初始化函数详解
devices_init 函数
int __init devices_init(void)
{return subsystem_register(&devices_subsys);
}
设备子系统初始化:
subsystem_register(&devices_subsys): 注册设备子系统- devices_subsys: 全局设备子系统结构体,管理系统中所有设备
- 作用: 创建/sys/devices目录,为所有设备提供统一的sysfs接口
buses_init 函数
int __init buses_init(void)
{return subsystem_register(&bus_subsys);
}
总线子系统初始化:
subsystem_register(&bus_subsys): 注册总线子系统- bus_subsys: 全局总线子系统结构体
- 作用: 创建/sys/bus目录,管理各种总线类型(PCI、USB等)
classes_init 函数
int __init classes_init(void)
{int retval;retval = subsystem_register(&class_subsys);if (retval)return retval;
subsystem_register(&class_subsys): 注册类别子系统- class_subsys: 设备类别子系统,如input、block、graphics等
- 作用: 创建/sys/class目录,按功能分类设备
/* ick, this is ugly, the things we go through to keep from showing up* in sysfs... */subsystem_init(&class_obj_subsys);if (!class_obj_subsys.kset.subsys)class_obj_subsys.kset.subsys = &class_obj_subsys;return 0;
}
类别子系统初始化 - 第二部分:
subsystem_init(&class_obj_subsys): 初始化类别对象子系统- 条件检查确保kset的subsys指针正确指向自身
- class_obj_subsys: 内部使用的类别对象管理
firmware_init 函数
int __init firmware_init(void)
{return subsystem_register(&firmware_subsys);
}
固件子系统初始化:
subsystem_register(&firmware_subsys): 注册固件子系统- firmware_subsys: 固件管理子系统
- 作用: 创建/sys/firmware目录,处理设备固件加载和管理
platform_bus_init 函数
int __init platform_bus_init(void)
{device_register(&platform_bus);return bus_register(&platform_bus_type);
}
平台总线初始化:
device_register(&platform_bus): 注册平台总线设备- 创建平台总线在sysfs中的设备节点
bus_register(&platform_bus_type): 注册平台总线类型- 创建/sys/bus/platform目录
- 平台总线: 用于不通过标准总线连接的SoC内置设备
system_bus_init 函数
int __init system_bus_init(void)
{system_subsys.kset.kobj.parent = &devices_subsys.kset.kobj;return subsystem_register(&system_subsys);
}
系统总线初始化:
system_subsys.kset.kobj.parent = &devices_subsys.kset.kobj: 设置系统总线的父对象为设备子系统- 作用: 在sysfs中建立层次关系,系统总线作为设备的子目录
subsystem_register(&system_subsys): 注册系统总线子系统
cpu_dev_init 函数
int __init cpu_dev_init(void)
{return sysdev_class_register(&cpu_sysdev_class);
}
CPU设备初始化:
sysdev_class_register(&cpu_sysdev_class): 注册CPU系统设备类别- cpu_sysdev_class: CPU相关的系统设备管理
- 作用: 创建/sys/devices/system/cpu目录,管理CPU相关信息
driver_init 主入口函数
void __init driver_init(void)
{/* These are the core pieces */devices_init();buses_init();classes_init();firmware_init();
核心组件初始化:
- 按顺序初始化四个核心子系统:
- 设备子系统
- 总线子系统
- 类别子系统
- 固件子系统
/* These are also core pieces, but must come after the* core core pieces.*/platform_bus_init();system_bus_init();cpu_dev_init();
}
依赖核心的组件初始化:
- 这些也是核心部件,但必须在核心的核心部件之后
- 初始化依赖于前面核心子系统的组件:
- 平台总线(依赖于设备子系统)
- 系统总线(明确设置父对象为设备子系统)
- CPU设备(依赖于系统设备框架)
函数功能总结
各个初始化函数功能
| 函数 | 功能 | sysfs目录 | 作用 |
|---|---|---|---|
devices_init() | 设备管理 | /sys/devices | 所有设备的物理层次结构 |
buses_init() | 总线管理 | /sys/bus | 按总线类型组织设备 |
classes_init() | 类别管理 | /sys/class | 按功能分类设备 |
firmware_init() | 固件管理 | /sys/firmware | 设备固件加载接口 |
platform_bus_init() | 平台总线 | /sys/bus/platform | SoC内置设备管理 |
system_bus_init() | 系统总线 | /sys/devices/system | 系统级设备管理 |
cpu_dev_init() | CPU设备 | /sys/devices/system/cpu | CPU信息和控制 |
sysctl初始化函数sysctl_init
static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
{struct proc_dir_entry *de;int len;mode_t mode;for (; table->ctl_name; table++) {/* Can't do anything without a proc name. */if (!table->procname)continue;/* Maybe we can't do anything with it... */if (!table->proc_handler && !table->child) {printk(KERN_WARNING "SYSCTL: Can't register %s\n",table->procname);continue;}len = strlen(table->procname);mode = table->mode;de = NULL;if (table->proc_handler)mode |= S_IFREG;else {mode |= S_IFDIR;for (de = root->subdir; de; de = de->next) {if (proc_match(len, table->procname, de))break;}/* If the subdir exists already, de is non-NULL */}if (!de) {de = create_proc_entry(table->procname, mode, root);if (!de)continue;de->data = (void *) table;if (table->proc_handler)de->proc_fops = &proc_sys_file_operations;}table->de = de;if (de->mode & S_IFDIR)register_proc_table(table->child, de);}
}void __init sysctl_init(void)
{
#ifdef CONFIG_PROC_FSregister_proc_table(root_table, proc_sys_root);init_irq_proc();
#endif
}
函数功能概述
这些函数共同初始化Linux系统的sysctl接口,通过proc文件系统提供内核参数的运行时配置能力
register_proc_table 函数详解
函数声明和变量定义
static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
{struct proc_dir_entry *de;int len;mode_t mode;
函数声明:
static void: 文件内静态函数,无返回值ctl_table * table: sysctl表指针,包含要注册的所有条目struct proc_dir_entry *root: proc目录条目,表示父目录
局部变量:
de: 临时proc目录条目指针len: 文件名长度mode: 文件模式
主循环开始
for (; table->ctl_name; table++) {
遍历sysctl表:
- 循环条件
table->ctl_name: 遍历直到遇到ctl_name为0的终止条目 table++: 移动到下一个表条目
基本有效性检查
/* Can't do anything without a proc name. */if (!table->procname)continue;
检查proc名称:
if (!table->procname): 如果procname为空,跳过该条目continue: 继续处理下一个表条目
功能有效性检查
/* Maybe we can't do anything with it... */if (!table->proc_handler && !table->child) {printk(KERN_WARNING "SYSCTL: Can't register %s\n",table->procname);continue;}
检查处理程序或子表:
- 条件:既没有
proc_handler也没有child子表 printk(KERN_WARNING ...): 输出警告信息,指出无法注册的条目continue: 跳过这个无效条目
名称长度和模式获取
len = strlen(table->procname);mode = table->mode;
准备注册参数:
len = strlen(table->procname): 计算proc名称的长度mode = table->mode: 获取文件模式(权限位)
目录条目初始化和类型判断
de = NULL;if (table->proc_handler)mode |= S_IFREG;else {mode |= S_IFDIR;
确定条目类型:
de = NULL: 初始化目录条目指针if (table->proc_handler): 如果有处理程序,这是常规文件mode |= S_IFREG: 添加常规文件类型标志else: 否则这是目录mode |= S_IFDIR: 添加目录类型标志
现有目录查找
for (de = root->subdir; de; de = de->next) {if (proc_match(len, table->procname, de))break;}/* If the subdir exists already, de is non-NULL */}
查找已存在的目录:
for (de = root->subdir; de; de = de->next): 遍历父目录的所有子目录proc_match(len, table->procname, de): 比较名称和长度,查找匹配的目录break: 找到匹配目录时退出循环- 如果子目录已存在,de为非NULL
创建新proc条目
if (!de) {de = create_proc_entry(table->procname, mode, root);if (!de)continue;
创建新条目:
if (!de): 如果目录条目不存在create_proc_entry(table->procname, mode, root): 创建新的proc条目if (!de) continue: 如果创建失败,跳过后续处理
设置条目属性
de->data = (void *) table;if (table->proc_handler)de->proc_fops = &proc_sys_file_operations;}
配置条目属性:
de->data = (void *) table: 将sysctl表指针存储到proc条目数据中if (table->proc_handler): 如果是常规文件de->proc_fops = &proc_sys_file_operations: 设置文件操作结构体
保存条目指针和递归处理
table->de = de;if (de->mode & S_IFDIR)register_proc_table(table->child, de);}
}
完成注册:
table->de = de: 将proc条目指针保存回sysctl表if (de->mode & S_IFDIR): 如果是目录类型register_proc_table(table->child, de): 递归注册子表- 循环结束,函数结束
sysctl_init 函数详解
void __init sysctl_init(void)
{
#ifdef CONFIG_PROC_FSregister_proc_table(root_table, proc_sys_root);init_irq_proc();
#endif
}
条件编译和主注册
#ifdef CONFIG_PROC_FSregister_proc_table(root_table, proc_sys_root);
#ifdef CONFIG_PROC_FS: 只在配置了proc文件系统支持时编译register_proc_table(root_table, proc_sys_root): 注册根sysctl表root_table: 全局根sysctl表proc_sys_root: proc sysctl根目录
IRQ proc初始化
init_irq_proc();
#endif
}
IRQ相关初始化:
init_irq_proc(): 初始化IRQ相关的proc接口#endif: 结束条件编译块- 函数结束
Sysctl目录结构示例
/proc/sys/
├── kernel/
│ ├── shmmax
│ ├── shmall
│ └── ...
├── vm/
│ ├── swappiness
│ ├── dirty_ratio
│ └── ...
├── net/
│ ├── ipv4/
│ │ ├── ip_forward
│ │ └── ...
│ └── ...
└── irq/└── prof_cpu_mask
在/proc中创建IRQ的目录和文件init_irq_proc
void init_irq_proc(void)
{int i;/* create /proc/irq */root_irq_dir = proc_mkdir("irq", NULL);if (!root_irq_dir)return;/** Create entries for all existing IRQs.*/for (i = 0; i < NR_IRQS; i++)register_irq_proc(i);
}
void register_irq_proc(unsigned int irq)
{char name [MAX_NAMELEN];if (!root_irq_dir ||(irq_desc[irq].handler == &no_irq_type) ||irq_dir[irq])return;memset(name, 0, MAX_NAMELEN);sprintf(name, "%d", irq);/* create /proc/irq/1234 */irq_dir[irq] = proc_mkdir(name, root_irq_dir);#ifdef CONFIG_SMP{struct proc_dir_entry *entry;/* create /proc/irq/<irq>/smp_affinity */entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]);if (entry) {entry->nlink = 1;entry->data = (void *)(long)irq;entry->read_proc = irq_affinity_read_proc;entry->write_proc = irq_affinity_write_proc;}smp_affinity_entry[irq] = entry;}
#endif
}
函数功能概述
这两个函数用于在/proc文件系统中创建IRQ(中断请求)的相关信息目录和文件,方便用户空间查看和配置中断信息
代码逐段解析
init_irq_proc函数
void init_irq_proc(void)
{int i;
- 函数定义,无参数无返回值
- 声明循环计数器
i
/* create /proc/irq */root_irq_dir = proc_mkdir("irq", NULL);if (!root_irq_dir)return;
proc_mkdir("irq", NULL): 在/proc根目录下创建名为"irq"的目录root_irq_dir: 全局变量,保存创建的/proc/irq目录指针if (!root_irq_dir) return;: 如果创建失败则直接返回(内存不足等情况)
/** Create entries for all existing IRQs.*/for (i = 0; i < NR_IRQS; i++)register_irq_proc(i);
}
- 为所有存在的IRQ创建条目
for (i = 0; i < NR_IRQS; i++): 遍历所有可能的中断号(NR_IRQS是系统支持的最大IRQ数)register_irq_proc(i): 为每个IRQ号调用注册函数
register_irq_proc函数
void register_irq_proc(unsigned int irq)
{char name [MAX_NAMELEN];
- 函数参数
irq: 要注册的中断号 - 声明字符数组
name用于存储目录名,大小为MAX_NAMELEN
if (!root_irq_dir ||(irq_desc[irq].handler == &no_irq_type) ||irq_dir[irq])return;
三个条件检查,任一条件满足则返回:
!root_irq_dir: /proc/irq根目录不存在(初始化失败)irq_desc[irq].handler == &no_irq_type: 该IRQ的描述符handler指向no_irq_type,表示该IRQ未使用或无效irq_dir[irq]: 该IRQ的目录已经存在(避免重复创建)
memset(name, 0, MAX_NAMELEN);sprintf(name, "%d", irq);
memset(name, 0, MAX_NAMELEN): 清空name缓冲区sprintf(name, "%d", irq): 将IRQ号格式化为字符串存入name(如"1234")
/* create /proc/irq/1234 */irq_dir[irq] = proc_mkdir(name, root_irq_dir);
- 创建/proc/irq/1234这样的目录
proc_mkdir(name, root_irq_dir): 在/proc/irq下创建以IRQ号命名的子目录irq_dir[irq]: 全局数组,保存每个IRQ对应的proc目录指针
#ifdef CONFIG_SMP{struct proc_dir_entry *entry;/* create /proc/irq/<irq>/smp_affinity */entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]);
#ifdef CONFIG_SMP: 只在SMP(对称多处理)配置下编译- 声明
entry指针指向proc目录条目 - 创建/proc/irq//smp_affinity文件
create_proc_entry("smp_affinity", 0600, irq_dir[irq]):- 创建名为"smp_affinity"的文件
- 权限0600(用户读写)
- 位于该IRQ的目录下
if (entry) {entry->nlink = 1;entry->data = (void *)(long)irq;entry->read_proc = irq_affinity_read_proc;entry->write_proc = irq_affinity_write_proc;}smp_affinity_entry[irq] = entry;}
#endif
如果文件创建成功:
entry->nlink = 1: 设置链接数为1entry->data = (void *)(long)irq: 将IRQ号转换为指针存储在data字段中(供读写函数使用)entry->read_proc = irq_affinity_read_proc: 设置读回调函数entry->write_proc = irq_affinity_write_proc: 设置写回调函数smp_affinity_entry[irq] = entry: 全局数组保存smp_affinity文件条目指针
创建的proc文件结构
/proc/irq/
├── 0/
│ └── smp_affinity
├── 1/
│ └── smp_affinity
├── 2/
│ └── smp_affinity
└── .../└── smp_affinity
功能详细说明
1. 信息展示
- 为每个有效的中断创建独立的目录
- 用户可以通过
cat /proc/interrupts查看中断统计信息
2. SMP亲和性配置
smp_affinity文件用于设置IRQ在多个CPU核心之间的分配- 读操作:查看当前IRQ绑定到哪些CPU核心
- 写操作:配置IRQ在哪些CPU核心上处理
- 权限0600:只有root用户可以修改
执行所有注册初始化函数do_initcalls
static void __init do_initcalls(void)
{initcall_t *call;int count = preempt_count();for (call = &__initcall_start; call < &__initcall_end; call++) {char *msg;if (initcall_debug) {printk(KERN_DEBUG "Calling initcall 0x%p", *call);print_fn_descriptor_symbol(": %s()", (unsigned long) *call);printk("\n");}(*call)();msg = NULL;if (preempt_count() != count) {msg = "preemption imbalance";preempt_count() = count;}if (irqs_disabled()) {msg = "disabled interrupts";local_irq_enable();}if (msg) {printk("error in initcall at 0x%p: ""returned with %s\n", *call, msg);}}/* Make sure there is no pending stuff from the initcall sequence */flush_scheduled_work();
}
函数功能概述
do_initcalls 函数负责按顺序执行所有通过__initcall宏注册的初始化函数,这是Linux内核模块化初始化的核心机制
代码详细解析
函数声明和变量定义
static void __init do_initcalls(void)
{initcall_t *call;int count = preempt_count();
static void __init: 静态初始化函数,完成后内存可被释放do_initcalls(void): 执行所有初始化调用的主函数
局部变量:
initcall_t *call: 初始化函数指针,用于遍历初始化调用表int count = preempt_count(): 保存当前抢占计数preempt_count(): 获取当前任务的抢占计数- 作用: 用于后续检查抢占状态是否被错误修改
主循环开始
for (call = &__initcall_start; call < &__initcall_end; call++) {char *msg;
初始化调用遍历循环:
call = &__initcall_start: 从初始化调用表的开始位置开始call < &__initcall_end: 循环直到初始化调用表的结束位置call++: 移动到下一个初始化函数指针char *msg: 错误消息指针,用于记录检查结果
关键符号说明:
__initcall_start: 链接器定义的符号,指向初始化调用表的开始__initcall_end: 链接器定义的符号,指向初始化调用表的结束
调试信息输出
if (initcall_debug) {printk(KERN_DEBUG "Calling initcall 0x%p", *call);print_fn_descriptor_symbol(": %s()", (unsigned long) *call);printk("\n");}
if (initcall_debug): 检查是否启用了初始化调用调试printk(KERN_DEBUG "Calling initcall 0x%p", *call): 输出初始化函数地址print_fn_descriptor_symbol(": %s()", (unsigned long) *call): 输出函数名符号printk("\n"): 换行- 作用: 在调试模式下显示每个初始化函数的地址和名称
执行初始化函数
(*call)();
核心执行语句:
(*call)(): 解引用函数指针并调用该初始化函数- 这是整个函数的核心:实际执行注册的初始化函数
- 这些初始化函数包括设备驱动、文件系统、网络协议等各个子系统的初始化
状态检查初始化
msg = NULL;
重置错误消息:
- 在每次检查前将错误消息指针重置为NULL
- 如果后续检查发现问题,会设置相应的错误消息
抢占状态检查
if (preempt_count() != count) {msg = "preemption imbalance";preempt_count() = count;}
抢占平衡检查:
if (preempt_count() != count): 检查抢占计数是否发生变化msg = "preemption imbalance": 如果变化,设置抢占不平衡错误消息preempt_count() = count: 恢复原来的抢占计数- 抢占计数: 跟踪内核抢占状态的计数器,初始化函数不应该改变它
中断状态检查
if (irqs_disabled()) {msg = "disabled interrupts";local_irq_enable();}
中断使能检查:
if (irqs_disabled()): 检查中断是否被禁用msg = "disabled interrupts": 如果中断被禁用,设置错误消息local_irq_enable(): 重新启用本地CPU中断- 重要性: 初始化函数完成后必须重新启用中断,否则会影响系统响应
错误报告
if (msg) {printk("error in initcall at 0x%p: ""returned with %s\n", *call, msg);}}
错误信息输出:
if (msg): 如果有错误消息(抢占或中断状态异常)printk("error in initcall at 0x%p: returned with %s\n", *call, msg): 输出详细的错误信息- 显示有问题的初始化函数地址
- 显示具体的错误原因
- 循环结束
工作队列清理
/* Make sure there is no pending stuff from the initcall sequence */flush_scheduled_work();
}
延迟任务清理:
- 确保初始化调用序列中没有挂起的内容
flush_scheduled_work(): 刷新所有已调度的工作- 作用: 等待所有在初始化过程中调度的工作队列任务完成
- 必要性: 有些初始化函数可能提交了延迟执行的工作,需要确保它们完成
关键机制详解
初始化调用表构建
// 通过链接器脚本构建初始化调用表
#define __define_initcall(level,fn) \static initcall_t __initcall_##fn __used \__attribute__((__section__(".initcall" level ".init"))) = fn// 不同优先级的初始化调用
#define core_initcall(fn) __define_initcall("1",fn)
#define postcore_initcall(fn) __define_initcall("2",fn)
#define arch_initcall(fn) __define_initcall("3",fn)
#define subsys_initcall(fn) __define_initcall("4",fn)
#define fs_initcall(fn) __define_initcall("5",fn)
#define device_initcall(fn) __define_initcall("6",fn)
#define late_initcall(fn) __define_initcall("7",fn)
初始化调用表内存布局
内存布局:
__initcall_start↓
.initcall1.init → core_initcall 函数
.initcall2.init → postcore_initcall 函数
.initcall3.init → arch_initcall 函数
.initcall4.init → subsys_initcall 函数
.initcall5.init → fs_initcall 函数
.initcall6.init → device_initcall 函数
.initcall7.init → late_initcall 函数↓
__initcall_end
状态检查的重要性
抢占计数检查:
- 防止初始化函数错误地修改抢占状态
- 确保初始化完成后系统处于一致的抢占状态
中断状态检查:
- 防止初始化函数忘记重新启用中断
- 避免系统因中断被禁用而失去响应能力
