3.5.2.4 device_core_init 函数详解
3.5.2.4 device_core_init 函数详解
📚 本节导读
学习时长: 约 15 分钟
难度级别: ⭐⭐☆☆☆(中级)
前置知识:
- 3.5.2.3 os_hw_board_init 函数详解
- 信号量基础知识
🎯 学习目标
- 理解设备管理框架的作用
- 掌握设备信号量的用途
- 理解为什么这个函数在 LOW 子级别
- 了解线程安全的重要性
一、函数概述
1.1 函数定位
device_core_init 负责初始化设备管理框架的核心组件,创建用于保护设备操作的互斥信号量。
1.2 基本信息
| 属性 | 值 |
|---|---|
| 函数名 | device_core_init |
| 源文件 | drivers/device.c |
| 初始化级别 | OS_INIT_LEVEL_PRE_KERNEL_1 |
| 子级别 | OS_INIT_SUBLEVEL_LOW |
| 地址(来自 project.map) | 0x080227b0 |
1.3 为什么放在 LOW 子级别?
设备框架初始化依赖于堆内存已经可用(因为 os_semaphore_create 内部可能需要动态分配内存),因此放在最后执行,确保所有依赖项已经就绪。
二、函数详解
2.1 完整代码
来源: drivers/device.c (第 1102-1108 行)
static os_err_t device_core_init(void)
{
dev_sem = os_semaphore_create(&dev_sem_dummy, "dev_sem", 1, 1);
return OS_SUCCESS;
}
OS_INIT_CALL(device_core_init, OS_INIT_LEVEL_PRE_KERNEL_1, OS_INIT_SUBLEVEL_LOW);2.2 分步详解
第一步:创建设备互斥信号量
dev_sem = os_semaphore_create(&dev_sem_dummy, "dev_sem", 1, 1);详细解释:
| 参数 | 值 | 说明 |
|---|---|---|
| 第 1 个 | &dev_sem_dummy | 静态分配的信号量控制块,避免在内核早期阶段使用堆分配 |
| 第 2 个 | "dev_sem" | 信号量名称,用于调试 |
| 第 3 个 | 1 | 初始值 = 1 |
| 第 4 个 | 1 | 最大值 = 1(互斥信号量) |
关于 dev_sem_dummy 的来源
dev_sem_dummy 定义在 device.c 第 34 行:
static os_semaphore_dummy_t dev_sem_dummy;其类型 os_semaphore_dummy_t 定义在 kernel/include/os_dummy.h(第 153-170 行):
struct dummy_semaphore
{
os_list_node_t task_list_head_dummy; /* 阻塞任务链表头 */
os_list_node_t resource_node_dummy; /* 资源链表节点 */
uint32_t count_dummy; /* 当前计数值 */
uint32_t max_count_dummy; /* 最大计数值 */
uint8_t object_inited_dummy; /* 是否已初始化 */
uint8_t object_alloc_type_dummy; /* 分配类型:静态/动态 */
uint8_t wake_type_dummy; /* 唤醒类型:优先级/FIFO */
char name_dummy[OS_NAME_MAX + 1]; /* 信号量名称 */
os_spinlock_t lock_dummy; /* SMP 自旋锁 */
};
typedef struct dummy_semaphore os_semaphore_dummy_t;在 os_semaphore_create 内部,通过 OS_TYPE_CONVERT(os_semaphore_t *, semaphore_cb) 将 dev_sem_dummy 类型转换为内核实际使用的 os_semaphore_t,两者内存布局兼容,共享同一块内存。这种设计允许用户在不依赖堆分配的情况下静态定义信号量控制块。
这是一个互斥信号量(初始值 = 最大值 = 1),用于确保设备操作的互斥性。
详细说明
关于 os_semaphore_create 函数的完整解析(包括静态/动态分配、执行流程等),请参考:os_semaphore_create
第二步:返回成功
return OS_SUCCESS;函数直接返回 OS_SUCCESS,表示初始化成功。
三、设备框架设计
3.1 为什么需要设备信号量?
3.1.1 线程安全
设备操作(如注册、打开、读写)可能在多个任务中同时发生,需要保证原子性:
任务 A 任务 B
| |
| os_device_register() |
| ↓ |
| 获取 dev_sem |
| ↓ |
| 操作设备列表 | os_device_open()
| ↓ | ↓
| 释放 dev_sem | 等待 dev_sem
| | ↓
| | 获取 dev_sem
| | ↓
| | 打开设备
| | ↓
| | 释放 dev_sem3.1.2 保护的操作
dev_sem 保护以下操作:
| 操作 | 说明 |
|---|---|
os_device_register() | 注册设备 |
os_device_unregister() | 注销设备 |
os_device_find() | 查找设备 |
os_device_open() | 打开设备 |
os_device_close() | 关闭设备 |
os_device_for_each() | 遍历设备列表 |
注意
os_device_read 和 os_device_write 不使用全局的 dev_sem,而是使用设备实例自身的信号量(dev->sem)。
3.2 信号量工作原理
互斥信号量的使用模式:
// 获取信号量
os_semaphore_wait(dev_sem, OS_WAIT_FOREVER);
// 临界区:操作设备
// ...
// 释放信号量
os_semaphore_post(dev_sem);状态转换:
信号量状态:
1 (可用) → 0 (已占用) → 1 (可用)
↑ ↑
│ │
释放 获取四、在启动流程中的位置
4.1 执行顺序
pre_kernel_1_start()
↓
cortex_m_set_vector()
↓
os_hw_board_init()
↓
device_core_init() ← 这里
↓
__driver_stm32_usart_early_driver_init()4.2 为什么必须最后执行?
后续的设备驱动注册(如串口驱动 __driver_stm32_usart_early_driver_init)需要使用设备框架,因此必须在设备框架初始化之后执行。
💡 本节总结
重点回顾
主要步骤:
- 创建设备互斥信号量(初始值=1,最大值=1)
为什么放在 LOW 子级别:
- 依赖堆内存已初始化(
os_semaphore_create可能需要堆分配) - 为后续设备驱动注册提供基础
- 依赖堆内存已初始化(
关键符号定义:
dev_sem:设备互斥信号量标识符(os_semaphore_id类型)dev_sem_dummy:静态信号量控制块(os_semaphore_dummy_t类型,定义在device.c第 34 行,通过OS_TYPE_CONVERT转换为内核使用的os_semaphore_t)
下一步
接下来请学习: