当前位置: 首页 > news >正文

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();

核心组件初始化:

  • 按顺序初始化四个核心子系统:
    1. 设备子系统
    2. 总线子系统
    3. 类别子系统
    4. 固件子系统
	/* These are also core pieces, but must come after the* core core pieces.*/platform_bus_init();system_bus_init();cpu_dev_init();
}

依赖核心的组件初始化:

  • 这些也是核心部件,但必须在核心的核心部件之后
  • 初始化依赖于前面核心子系统的组件:
    1. 平台总线(依赖于设备子系统)
    2. 系统总线(明确设置父对象为设备子系统)
    3. CPU设备(依赖于系统设备框架)

函数功能总结

各个初始化函数功能

函数功能sysfs目录作用
devices_init()设备管理/sys/devices所有设备的物理层次结构
buses_init()总线管理/sys/bus按总线类型组织设备
classes_init()类别管理/sys/class按功能分类设备
firmware_init()固件管理/sys/firmware设备固件加载接口
platform_bus_init()平台总线/sys/bus/platformSoC内置设备管理
system_bus_init()系统总线/sys/devices/system系统级设备管理
cpu_dev_init()CPU设备/sys/devices/system/cpuCPU信息和控制

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;

三个条件检查,任一条件满足则返回:

  1. !root_irq_dir: /proc/irq根目录不存在(初始化失败)
  2. irq_desc[irq].handler == &no_irq_type: 该IRQ的描述符handler指向no_irq_type,表示该IRQ未使用或无效
  3. 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: 设置链接数为1
  • entry->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

状态检查的重要性

抢占计数检查:

  • 防止初始化函数错误地修改抢占状态
  • 确保初始化完成后系统处于一致的抢占状态

中断状态检查:

  • 防止初始化函数忘记重新启用中断
  • 避免系统因中断被禁用而失去响应能力
http://www.dtcms.com/a/524927.html

相关文章:

  • 乳腺癌良性和恶性图像分类数据集
  • C++通用业务标准库中常用接口函数总结
  • 永久免费的移动建站平台网站备案没通过
  • 汕头建网站国家高新技术企业认定有什么好处
  • 【数论】欧拉定理 扩展欧拉定理
  • 正能量网站入口免费安全想学做网站从哪里入手
  • 南宁网站制作最新招聘信息wordpress文章插件
  • 基于python大数据技术的医疗数据分析与研究
  • 1.Go基础知识入门
  • Linux内核InfiniBand连接管理器(CM)深度解析:架构设计与实现原理
  • 网站网站开发者犯法吗网站负责人
  • Arbess从入门到实战(17) - 使用Arbess+GitPuk+SonarQube实现Java项目代码扫描及自动化部署
  • 开发避坑指南(65):JDK21升级遇NoSuchFieldError:Lombok兼容性修复
  • 周口住房和城乡建设网站大龄工找工作哪个网站好
  • 【DecEx-RAG】
  • 数据结构算法题:list
  • ArkTs-Android 与 ArkTS (HarmonyOS) 存储目录全面对比
  • 网站广告费一般多少钱做网站公司名字应该用图片吗
  • 解决 Word四大烦:消标记、去波浪线、关首字母大写、禁中文引号
  • 统信系统下设置RTC时间
  • 晓羽礼品兑换系统小程序+H5
  • 九一人才网赣州找工作昆明高端seo怎么做
  • KingbaseES:MongoDB 国产化平替的优选方案,从技术适配到政务落地
  • Day 22 复习日——泰坦尼克号人员生还预测
  • Linux串口应用编程
  • 微信连接微网站吗奉化网站关键词优化费用
  • 做网站要多大的画布婚庆网站建设需求分析
  • Java Record 详解
  • UVa 1635 Irrelevant Elements
  • 个人网站怎么做推广网站设计怎么收费