前言

在掌握了线程的创建与运行后,我们必须面对两个更深层次的问题:

  1. 操作系统的时间是从哪里来的?如何精准控制任务的执行周期?

  2. 当多个线程并发运行时,如何防止它们争抢资源(打架)?如何让它们有序配合(同步)?

本文梳理自对 RT-Thread 内核原理的深度探讨,涵盖了时钟节拍、软硬定时器、以及互斥量、信号量、事件集的底层逻辑。


第一部分:操作系统的心跳 —— 时钟节拍 (Clock Tick)

1. 什么是时钟节拍?

如果把 CPU 比作人的大脑,时钟节拍 (Tick) 就是 RTOS 的心跳

  • 定义:RTOS 最小的时间单位。

  • 数值:通常配置为 1000Hz(即 1 Tick = 1ms)。

  • 作用

    • 决定了 rt_thread_mdelay(1) 到底延时多久。

    • 决定了同优先级线程的时间片轮转(Round-Robin)切换频率。

    • 是所有软件定时器的时间基准。

2. RTT 是如何获取时钟的?

操作系统本身是一段代码,它无法自己产生时间。它依赖底层的硬件定时器

  • 硬件源头:在 Cortex-M 系列(如 STM32)中,使用 SysTick(系统滴答定时器)

  • 工作流程

    1. 硬件层:配置 SysTick 每 1ms 产生一次中断。

    2. 中断服务 (ISR):触发 SysTick_Handler

    3. 内核层:调用 rt_tick_increase()

      • 全局变量 rt_tick 自加 1。

      • 检查当前线程时间片是否用完。

      • 扫描定时器链表,唤醒超时的线程。


第二部分:定时器 (Timer) —— 这里的“闹钟”怎么设?

1. 硬件定时器 vs 软件定时器

  • 硬件定时器:芯片上的物理外设(TIM1, TIM2...)。精度高,但资源有限,通常只用于生成 Tick 或极高精度的波形控制。

  • 软件定时器:操作系统基于 Tick 模拟出的“虚拟定时器”。

    • 优点:数量无限(只要内存够),创建方便。

    • 缺点:精度受限于 Tick(通常是 1ms)。

2. RTT 定时器的两种模式(关键坑点!)

rt_timer_createflag 参数中,有两个极其重要的标志位:

  • HARD_TIMER (硬时钟模式)

    • 定义:回调函数直接在 SysTick 中断服务函数 中执行。

    • 默认性如果你不写标志位,默认就是这个! (RT_TIMER_FLAG_HARD_TIMER = 0x0)

    • 风险:绝对禁止在回调函数中调用延时或挂起函数,也不能运行复杂逻辑,否则会卡死系统中断。

  • SOFT_TIMER (软时钟模式)

    • 定义:回调函数在系统专用的 timer 线程 中执行。

    • 推荐:安全,允许有限度的阻塞,不会影响中断响应。强烈建议显式加上 RT_TIMER_FLAG_SOFT_TIMER

3. 如何传递多个参数?(void *parameter 的奥义)

虽然 API void (*timeout)(void *parameter) 看起来只能传一个参数,但利用 C 语言的 结构体封装 技巧,可以实现万能传递。

  • 技巧:定义一个结构体,包含 GPIO 号、电压值、设备名等。

  • 传递rt_timer_create(..., &my_struct_config, ...) 传递结构体的地址。

  • 接收:在回调函数中将 void* 强转回 struct MyConfig*

  • 注意:传递的结构体必须是全局变量动态分配的内存,严禁传递局部变量地址(栈内存会被销毁)。


第三部分:线程同步 —— 解决“并发打架”问题

当多个线程操作同一个资源,或者需要互相通知时,必须使用同步机制。

1. 互斥量 (Mutex) —— “厕所的门锁”

  • 核心作用保护临界资源(全局变量、串口等),实现独占访问。

  • 生活比喻:单人厕所。进门锁门(Take),出门开门(Release)。

  • 关键特性:优先级继承 (Priority Inheritance)

    • 解决的问题优先级翻转。即中优先级线程(B)抢占了持有锁的低优先级线程(C)的 CPU,导致高优先级线程(A)等不到锁。

    • 机制:当 A 等待 C 的锁时,内核临时将 C 的优先级提升至 A 的水平。防止 B 插队,让 C 尽快干完活释放锁。

  • 使用禁忌

    • 谁锁的必须谁开(所有权)。

    • 不能在中断中使用(会导致挂起)。

Q&A 重点

:为什么低优先级的 C 持有锁时,还需要让出 CPU 给高优先级的 B?

逻辑权限 != 物理权限

C 持有锁(逻辑权限),意味着“只有 C 能操作这个变量”。

但 B 优先级高(物理权限),意味着“B 有资格抢占 CPU”。

如果 B 不需要那个锁,它完全可以把 C 踢下台自己跑。这就是导致优先级翻转的根源,所以才需要 Mutex 的优先级继承机制来保护 C 不被 B 欺负。

2. 信号量 (Semaphore) —— “停车场的空位”

  • 核心作用线程同步(A 通知 B)、资源计数(限流)。

  • 生活比喻

    • 计数用法:停车场剩余车位。满(0)了就排队,有人出来(Release)就进。

    • 同步用法:生产线传送带。B 往上传送包裹(Release),A 从上面取包裹(Take)。

  • 与互斥量的区别

    • 无所有权:B 可以 Release,A 可以 Take。这在 Mutex 里是非法的,但在 Sem 里是标准的“生产者-消费者”模型。

    • 无优先级继承:不建议用来做“锁”。

  • “骚操作”验证

    • 场景:线程 B 一进去就 rt_sem_release,从来不 take

    • 结论完全合法。这是典型的生产者逻辑(如中断采集数据)。信号量不仅是限制,更是“积压工作量”的记录单。

3. 事件集 (Event) —— “逻辑电路 / 龙珠”

  • 核心作用多对多同步,等待多个条件组合。

  • 生活比喻:必须“WiFi 连接成功” “屏幕初始化完毕” “配置下载完成”,主程序才能启动。

  • 核心逻辑

    • 逻辑与 (AND):所有位都为 1 才唤醒(集齐龙珠)。

    • 逻辑或 (OR):任意一位为 1 就唤醒(紧急报警)。

  • 注意:事件是无记忆的(非排队)。如果发送了 5 次事件,接收端可能只处理一次(位被置 1 后不会累加)。


第四部分:一图流总结

机制 比喻 核心特征 典型场景
互斥量 (Mutex) 独占锁 有所有权、优先级继承 保护共享资源(防冲突)
信号量 (Sem) 计数器 无所有权、生产/消费 任务同步(中断通知线程)、资源限流
事件集 (Event) 逻辑门 32位标志、AND/OR 组合 复杂状态等待(多条件同时满足)
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐