线程管理
本文档根据《嘉楠K230开发手册》V1.0(2024-11-30)整理。正文、表格、示例代码与插图均来自原始手册。
在本章中,会涉及如下内容:
① RT-Thread如何给每个线程分配CPU时间
② 如何选择某个线程来运行
③ 线程优先级如何起作用
④ 线程有哪些状态
⑤ 如何实现线程
⑥ 如何使用线程参数
⑦ 怎么修改线程优先级
⑧ 怎么删除线程
⑨ 怎么实现周期性的线程
⑩ 如何使用空闲线程
基本概念
在操作系统概念中,进程是资源分配的实体,而线程是执行的实体。同一个进程的所有线程共享相同的资源,而每个进程至少需要拥有一个线程,线程在进程的地址空间运行,完成内核或用户规定的任务。
RT-Thread Smart 的线程可以分为两类:
① 内核线程:运行于内核地址空间,没有对应的用户态进程,因此不能访问用户地址空间。它们用来完成特定于内核的任务,或者兼容 所有原 RT-Thread 的应用代码。
② 用户线程:所有的用户线程都属于某一个用户进程,它们共享进程拥有的资源,比如进程的用户态和内核态地址空间。不同进程间的线程不能直接相互访问。
内核线程和用户线程如下图所示:

所有的进程都只能在用户态创建,通过系统调用访问内核的资源,下文如无特别说明,所述进程都是指用户态进程。而内核线程只运行在内核地址空间,一般不会访问用户地址空间的数据。
无论是用户线程、内核线程,它们都可以使用同一套接口函数来创建。
线程创建与删除
线程的组成
在RT-Thread中,线程是 RT-Thread 中最基本的调度单位,使用rt_thread结构体表示线程。
rt_thread描述了一个线程执行的运行环境,也描述了这个线程所处的优先等级。
系统中总共存在两类线程,分别是系统线程和用户线程
① 系统线程由RT-Thread内核创建
② 用户线程由用户应用程序创建
这两类线程都会从内核对象容器中分配线程对象,如下图所示。
每个线程由三部分组成:线程控制块(rt_thread结构体)、线程栈和入口函数。

- 线程控制块
线程控制块由结构体“struct rt_thread”表示,线程控制块是操作系统用于管理线程的一个数据结构。
它存放线程的一些信息,例如优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表结构,线程等待事件集合等。
它在_rtdef.h_中定义如下:
/**
* Thread structure
*/
struct rt_thread
{
/* rt 对象 */
char name[RT_NAME_MAX]; /* 线程名字 */
rt_uint8_t type; /* 对象类型 */
rt_uint8_t flags; /* 标注位 */
rt_list_t list; /* 对象列表 */
rt_list_t tlist; /* 线程列表 */
/* 栈指针和入口指针 */
void *sp; /* 栈指针 */
void *entry; /* 入口函数指针*/
void *parameter; /* 参数 */
void *stack_addr; /* 栈地址指针 */
rt_uint32_t stack_size; /* 栈大小*/
/* 错误代码 */
rt_err_t error; /* 线程错误代码 */
rt_uint8_t stat; /* 线程状态 */
/* 优先级 */
rt_uint8_t current_priority; /* 当前优先级 */
rt_uint8_t init_priority; /* 初始优先级 */
…………
rt_ubase_t init_tick; /* 线程初始化计数值 */
rt_ubase_t remaining_tick; /* 线程剩余计数值 */
struct rt_timer thread_timer; /* 内置线程定时器 */
void (*cleanup)(struct rt_thread *tid); /* 线程退出清理函数 */
rt_uint32_t user_data; /* 用户私有数据*/
};
typedef struct rt_thread *rt_thread_t;
- 线程栈
在裸机系统中, 涉及局部变量、子函数调用或中断发生,就需要用到栈。
在RTOS系统中,每个线程运行时,也是普通的函数调用,也涉及局部变量、子函数调用、中断,也要用到栈。
但不同于裸机系统,RTOS存在多个线程,每个线程是独立互不干扰的,因此需要为每个线程都分配独立的栈空间,这就是线程栈。
可以使用两种方法提供线程栈:静态分配、动态分配。栈的大小通常由用户定义,如下使用全局数组提供了一个静态栈,大小为512字节:
rt_uint32_t test_stack[512];
对于资源比较大的MCU,可以适当设置较大的线程栈。
也可以在初始化时设置为较大的栈,比如1K或2K,在进入系统后,通过终端的list_thread命令查看当前线程栈的最大使用率。如果使用率超过70%,将线程栈再设置大一点;如果远低于70%,将线程栈设置小一点。
- 入口函数
入口函数是线程要运行函数,由用户自行设计。
可分为无限循环模式和顺序执行模式。
无限循环模式示例代码:
void thread_entry(void* paramenter){ while (1) { /* 等待事件的发生 */ /* 对事件进行服务、进行处理 */ }}
使用这种模式时,需要注意,一个实时系统,不应该让一个线程一直处于最高优先级占用CPU,让其它线程得不到执行。
因此,在这种模式中,需要调用延时函数或者主动挂起。
这种无限循环模式设计的目的是让这个线程一直循环调度运行,而不结束。
顺序执行模式示例代码:
static void thread_entry(void* parameter){ /* 处理事务 #1 */ … /* 处理事务 #2 */ … /* 处理事务 #3 */}
使用这种模式时,线程不会一直循环,最后一定会执行完毕。
执行完毕后,线程将被系统自动删除。
创建、启动线程
RT-Thread提供两种线程的创建方式:
① 静态线程:使用rt_thread_init()初始化
② 动态线程:使用rt_thread_create()创建
区别:动态线程是系统自动从动态内存堆上分配栈空间与线程句柄,静态线程是由用户分配栈空间与线程句柄
静态线程初始化函数如下:
rt_err_t rt_thread_init(struct rt_thread* thread, //线程句柄 const char* name, //线程名字 void (*entry)(void* parameter), //入口函数 void* parameter, //入口函数参数 void* stack_start, //线程栈起始地址 rt_uint32_t stack_size, //栈大小 rt_uint8_t priority, //线程优先级 rt_uint32_t tick); //线程时间片大小
参数说明:
| 参数 | 描述 |
|---|---|
| thread | 线程句柄,指向线程控制块内存地址,由用户传入 |
| name | 线程名字,由rtconfig.h中定义的RT_NAME_MAX宏指定最大长度 |
| entry | 线程入口函数 |
| parameter | 线程入口函数参数 |
| stack_start | 线程栈起始地址 |
| stack_size | 线程栈大小,单位是字节 |
| priority | 线程优先级,由rtconfig.h中定义的RT_THREAD_PRIORITY_MAX宏指定优先级范围假设支持的是256级优先级,那么范围是从0~255,数值越小优先级越高,0代表最高优先级 |
| tick | 线程的时间片大小,时间片(Tick)是操作系统的时钟节拍当系统中存在相同优先级的线程时,时间片大小指定线程一次调度能够运行的最大时间长度当在时间片运行结束后,运行另外的同优先级线程 |
| 返回值 | 成功:RT_EOK;失败:RT_ERROR |
动态线程创建函数如下:
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 | 线程名字,由rtconfig.h中定义的RT_NAME_MAX宏指定最大长度 |
| entry | 线程入口函数 |
| parameter | 线程入口函数参数 |
| stack_size | 线程栈大小,单位是字节 |
| priority | 线程优先级,由rtconfig.h中定义的RT_THREAD_PRIORITY_MAX宏指定优先级范围假设支持的是256级优先级,那么范围是从0~255,数值越小优先级越高,0代表最高优先级 |
| tick | 线程的时间片大小,时间片(Tick)是操作系统的时钟节拍当系统中存在相同优先级的线程时,时间片大小指定线程一次调度能够运行的最大时间长度当期线程的时间片运行结束后,运行另外的同优先级线程 |
| 返回值 | 成功:thread,线程句柄;失败:RT_NULL |
创建线程后,还需要启动线程,才能让线程运行起来。
启动线程函数如下:
rt_err_t rt_thread_startup(rt_thread_t thread);
参数说明:
| 参数 | 描述 |
|---|---|
| thread | 线程句柄 |
| 返回值 | 成功:RT_EOK;失败:-RT_ERROR |