基础概念理解#
RT-Thread 内核相关#
作用:#
RT-Thread 内核是操作系统的**“⼼脏”**,负责管理系统中的各个线程(相当于我们⽇常⽣活中的“任 务”),每个线程负责⼀个任务,并确保它们可以有条不紊地完成。可以理解为一位⾼效的“经理”,它协调公司⾥的各个部⻔(线程),确保每个部⻔按时完成任务,并合理分配资源
线程调度:#
可以理解为“任务”,持多达 256 个优先级(当然可以通过配置改成 32 或 8 个),0 是最⾼优先级。
线程的组成:#
线程控制块:它类似⾝份证,记录了⼈的名字、任务、优先级和⼯作状态等信息。
线程栈:它是⼯⼈⽤来保存临时数据的地⽅,当线程执⾏任务时,临时数据都保存在栈中。 .
⼊⼝函数:线程的任务内容,告诉线程具体要做什么。
线程的状态:#
初始状态:线程刚创建,未开始⼯作。 .
就绪状态:线程准备好了,随时可以开始⼯作,但还没有拿到资源。
运⾏状态:线程正在执⾏任务,即正在使⽤ CPU。
挂起状态:线程暂时停⽌⼯作,可能是因为在等资源,⽐如等数据到来或者等待定时器。 .
关闭状态:线程的任务完成了,线程结束,不再使⽤
时间⽚:#
给线程分配的固定的⼯作时间,即“时间⽚”。
举例:线程 A 和线程 B 优先级相同, A 的 时间⽚是 10,B 的时间⽚是 5,那么 A 会⼯作 10 个时间单位,B 只⼯作 5 个时间单位,接着再轮 到 A。
时钟管理:#
时钟管理基于“时钟节拍”(tick)类似秒表,每个节拍是系统中最小的时间单位,系统默认为1ms
系统提供两类定时器: 单次触发定时器:只触发⼀次,然后停⽌。 周期触发定时器:不断触发,直到你⼿动停⽌它。就像洗⾐机的定时模式,洗完⼀遍后可以⾃动重复
线程间同步:#
在多线程系统中,多个线程之间需要“协调”。RT-Thread 提供了信号量、互斥量和事件集来帮助线程间进⾏同步。
信号量(Semaphore):就像⻋站发⻋信号,只有收到信号的线程才能开始执⾏任务。
互斥量(Mutex):确保多个线程不会同时访问同⼀资源,避免混乱。⽐如,打印机只能同时被⼀个⼈使⽤。
事件集:允许线程在多个事件触发时进⾏操作。就像等着多个快递到达,可以选择全部到⻬再处理,或者⼀个到达就处理⼀个。
线程间通信 :#
线程之间需要交换信息,RT-Thread 提供了邮箱和消息队列来实现。邮箱就像是⼀个固定容量的收件箱,⽽消息队列是⼀个可伸缩的队列,消息可以有不同的⻓度。
RT-Thread 启动流程 :#
RT-Thread 的启动流程相当于系统开机的步骤。系统⾸先执⾏启动⽂件的代码,完成基本的硬件初始化。然后调⽤ rtthread_startup() 函数,执⾏系统的初始化⼯作,最后进⼊ main()函数,正式开始执⾏⽤⼾代码。
可以把这个过程想象成开⻋的步骤:启动引擎(初始化硬件),检查⻋况(初始化系统),最后开始驾驶(执⾏⽤⼾程序)。
rt_thread_create函数原型rt_thread_t rt_thread_create(const char *name, void (*entry)(void *parameter), void *parameter, rt_uint32_t stack_size, rt_uint8_t priority, rt_uint32_t tick);参数详细说明及通俗比喻
name(厨师名字)
解释:每个厨师(线程)都有一个独特的名字,可以用来标识和管理该厨师。这就像给每个厨师发一张名牌,方便在厨房中知道谁在做什么。
通俗例子:名字
"Chef1"、"Chef2"、"Chef3"等。代码示例:
"led_ctrl",表示该线程用于控制 LED 灯。
entry(厨师的工作内容)
解释:表示每个厨师在厨房中具体要做什么(线程的主任务)。每个厨师都有自己擅长的菜品和任务,所以你要告诉厨师他们的工作内容。这就是线程的入口函数(每个线程的具体执行逻辑)。
通俗例子:比如,
Chef1的工作内容是炒菜、Chef2的工作内容是烤肉。代码示例:定义一个线程入口函数,如
void led_thread_entry(void *parameter),表示这个线程要做的任务是“闪烁 LED 灯”。
3. **`parameter`(厨师的配料)** - **解释**:给厨师的配料或工作材料(传递给线程的参数)。有些厨师需要不同的配料(不同类型的参数)来完成任务,比如炒菜时给盐或酱油等。 - **通俗例子**:`Chef1` 需要辣椒、`Chef2` 需要酱油,而 `Chef3` 只需要盐和蒜。这些就是不同的参数。 - **代码示例**:`RT_NULL` 表示不传递任何配料(参数)。例如:`led_thread_entry` 中不需要额外的参数时,就可以设为 `RT_NULL`。
4. **`stack_size`(厨师的工作台大小)** - **解释**:每个厨师需要不同大小的工作台来放材料和工具(线程栈大小)。如果工作台太小,厨师无法放下所有工具和材料,工作会受到限制甚至失败。 - **通俗例子**:做大菜的厨师需要更大的工作台,而只需要切点水果的厨师可能只需要一个小桌子就够了。 - **确定方法**:如果线程任务简单(比如控制 LED),可以设置较小的栈空间(如 512 字节);如果线程需要处理复杂数据(如图像处理),则需要较大的栈空间(如 1024 字节)。 - **代码示例**:`512` 表示该线程分配了 512 字节的栈空间,用于存储局部变量和函数调用等数据。
5. **`priority`(厨师的做菜顺序)** - **解释**:指定每个厨师(线程)的做菜顺序(优先级)。在餐厅中,主厨(优先级高的线程)要优先准备高级菜品,普通厨师(优先级低的线程)则可以稍后再做。 - **通俗例子**:`Chef1` 优先级最高,专门负责烤牛排;`Chef2` 做普通菜,优先级稍低;`Chef3` 负责清理厨房,优先级最低。 - **确定方法**:优先级范围从 0(最高)到 `RT_THREAD_PRIORITY_MAX-1`(最低)。紧急任务(如数据采集)可以设为 5,普通任务(如 LED 控制)可以设为 25。 - **代码示例**:`25` 表示该线程优先级较低,用于非紧急的周期性任务。
6. **`tick`(厨师每次做菜的时间片)** - **解释**:表示同一优先级的多个厨师轮流使用厨房的时间长度。时间片越长,厨师可以连续做菜的时间越多(一次性执行时间越长)。 - **通俗例子**:`Chef1` 每次可以在厨房待 10 分钟,`Chef2` 每次可以待 5 分钟。 - **确定方法**:对于需要频繁切换的线程(如信号采集),可以设置较小的时间片;对于稳定执行的线程(如数据处理),可设定较大的时间片。 - **代码示例**:`5` 表示线程每次获得 5 个时钟周期的执行时间。
在 RT-Thread 操作系统中,rt_thread_mdelay 和普通的 delay 函数(如常见的 delay_ms)有本质上的区别,主要体现在系统调度、实时性和功耗管理等方面。下面我将从工作原理、使用场景、优缺点等多个方面进行详细解释,并以实际案例帮助理解。
1. 工作原理上的区别
rt_thread_mdelay:
rt_thread_mdelay是 RT-Thread 中提供的一个基于系统节拍(tick)实现的延时函数。它是线程级别的延时函数,会让调用该函数的线程进入 休眠状态,并释放 CPU 资源给其他线程继续运行。在延时结束后,系统会根据线程调度规则恢复该线程的运行。工作机制:
当
rt_thread_mdelay被调用时,当前线程会进入RT_THREAD_SUSPEND状态(挂起状态)。调度器开始运行其他优先级相同或更高的线程。
延时时间结束后,线程从
挂起状态转变为就绪状态,等待系统再次调度该线程。
普通
delay(如delay_ms):
普通的
delay函数一般是基于忙等待(busy-waiting)机制实现的,即通过不断循环来耗费时间,从而达到延时的效果。普通延时函数在延时期间不会释放 CPU 资源,因此 CPU 处于忙碌状态,无法执行其他任务。工作机制:
CPU 执行一个空循环来持续占用时间。
在整个延时过程中,CPU 资源不会被释放。
只有当延时结束时,才能继续执行下一个任务。
2. 使用场景上的区别
rt_thread_mdelay适用场景:
适用于需要在多线程环境中释放 CPU 资源的延时操作。
当需要在延时过程中让出 CPU 资源给其他线程使用时,如:多线程间的通信、任务调度等场景。
适合实时操作系统环境,在多任务环境下能有效管理 CPU 资源。
普通
delay适用场景:
适用于单线程、裸机环境或不涉及多任务调度的简单延时需求。
当延时过程中不希望其他任务打断(如一些简单的硬件初始化延时)时。
适用于对 CPU 资源利用率和功耗管理要求不高的场合。
3. 优缺点对比
区别点
rt_thread_mdelay普通
delay是否释放 CPU
是,延时过程中释放 CPU 资源
否,延时期间 CPU 一直被占用
系统调度
支持多线程调度和任务切换
无法调度其他任务(除非使用中断机制)
功耗管理
可以进入低功耗模式,节约能量
无法进入低功耗模式,CPU 一直忙等待
实时性
适合实时操作系统,延时更精确
仅适合简单场合,实时性较差
复杂度
需要在多线程环境中使用
简单、易用
4. 通俗比喻
假设你在一个咖啡店排队点单:
普通
delay就像你在柜台前排队时干等着,什么都不做。在排队的这段时间内,你不能去做其他事情,只能原地等待。这段时间对于你来说是“浪费的时间”。
rt_thread_mdelay就像你在排队时先去做别的事情,比如去取咖啡杯或看看店里的装饰。你没有白白等待,而是去做了别的事情。等到时间到了,你再回来继续排队。这样就不会浪费时间,系统(CPU)可以去处理其他任务(线程)。