# 基础概念理解

##  RT-Thread 内核相关

### 作用：

RT-Thread 内核是操作系统的**“⼼脏”**，负责管理系统中的各个线程（相当于我们⽇常⽣活中的“任 务”），每个线程负责⼀个任务，并确保它们可以有条不紊地完成。可以理解为一位⾼效的“经理”，它协调公司⾥的各个部⻔（线程），确保每个部⻔按时完成任务，并合理分配资源

### 线程调度：

可以理解为“任务”，持多达 256 个优先级（当然可以通过配置改成 32 或 8 个），0 是最⾼优先级。

### 线程的组成：

 线程控制块：它类似⾝份证，记录了⼈的名字、任务、优先级和⼯作状态等信息。 

 线程栈：它是⼯⼈⽤来保存临时数据的地⽅，当线程执⾏任务时，临时数据都保存在栈中。 .

⼊⼝函数：线程的任务内容，告诉线程具体要做什么。

### 线程的状态：

1. 初始状态：线程刚创建，未开始⼯作。 .
2. 就绪状态：线程准备好了，随时可以开始⼯作，但还没有拿到资源。 
3. 运⾏状态：线程正在执⾏任务，即正在使⽤ CPU。 
4. 挂起状态：线程暂时停⽌⼯作，可能是因为在等资源，⽐如等数据到来或者等待定时器。 .
5. 关闭状态：线程的任务完成了，线程结束，不再使⽤

### 时间⽚：

- 给线程分配的固定的⼯作时间，即“时间⽚”。
- 举例：线程 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);
> ```
> 参数详细说明及通俗比喻
> 1. **`name`（厨师名字）**
>     - **解释**：每个厨师（线程）都有一个独特的名字，可以用来标识和管理该厨师。这就像给每个厨师发一张名牌，方便在厨房中知道谁在做什么。
>     - **通俗例子**：名字 `"Chef1"`、 `"Chef2"`、 `"Chef3"` 等。
>     - **代码示例**：`"led_ctrl"`，表示该线程用于控制 LED 灯。
> 
>     2. **`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 资源给其他线程继续运行。在延时结束后，系统会根据线程调度规则恢复该线程的运行。
>   - 工作机制：
>     1. 当 `rt_thread_mdelay` 被调用时，当前线程会进入 `RT_THREAD_SUSPEND` 状态（挂起状态）。
>     2. 调度器开始运行其他优先级相同或更高的线程。
>     3. 延时时间结束后，线程从 `挂起状态` 转变为 `就绪状态`，等待系统再次调度该线程。

> - **普通 `delay`（如 `delay_ms`）**：
>   - 普通的 `delay` 函数一般是基于**忙等待（busy-waiting）**机制实现的，即通过不断循环来耗费时间，从而达到延时的效果。普通延时函数在延时期间不会释放 CPU 资源，因此 CPU 处于**忙碌状态**，无法执行其他任务。
>   - 工作机制：
>     1. CPU 执行一个空循环来持续占用时间。
>     2. 在整个延时过程中，CPU 资源不会被释放。
>     3. 只有当延时结束时，才能继续执行下一个任务。

> ### 2. **使用场景上的区别**
> - **`rt_thread_mdelay` 适用场景**：
>   - 适用于需要在多线程环境中释放 CPU 资源的延时操作。
>   - 当需要在延时过程中让出 CPU 资源给其他线程使用时，如：多线程间的通信、任务调度等场景。
>   - 适合实时操作系统环境，在多任务环境下能有效管理 CPU 资源。

> - **普通 `delay` 适用场景**：
>   - 适用于单线程、裸机环境或不涉及多任务调度的简单延时需求。
>   - 当延时过程中不希望其他任务打断（如一些简单的硬件初始化延时）时。
>   - 适用于对 CPU 资源利用率和功耗管理要求不高的场合。

> ### 3. **优缺点对比**
> |      区别点      |      `rt_thread_mdelay`      |             普通 `delay`             |
> | :--------------: | :--------------------------: | :----------------------------------: |
> | **是否释放 CPU** | 是，延时过程中释放 CPU 资源  |     否，延时期间 CPU 一直被占用      |
> |   **系统调度**   |   支持多线程调度和任务切换   | 无法调度其他任务（除非使用中断机制） |
> |   **功耗管理**   | 可以进入低功耗模式，节约能量 |  无法进入低功耗模式，CPU 一直忙等待  |
> |    **实时性**    | 适合实时操作系统，延时更精确 |      仅适合简单场合，实时性较差      |
> |    **复杂度**    |    需要在多线程环境中使用    |              简单、易用              |

> ### 4. **通俗比喻**
>
> 假设你在一个咖啡店排队点单：

> - **普通 `delay` 就像你在柜台前排队时干等着，什么都不做**。在排队的这段时间内，你不能去做其他事情,只能原地等待。这段时间对于你来说是“浪费的时间"。

> - **`rt_thread_mdelay` 就像你在排队时先去做别的事情，比如去取咖啡杯或看看店里的装饰**。你没有白白等待，而是去做了别的事情。等到时间到了，你再回来继续排队。这样就不会浪费时间，系统（CPU）可以去处理其他任务（线程）。

