RT-Thread记录(三、RT-Thread 线程操作函数及线程管理与FreeRTOS的比较)_rtthread和freertos区别

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

-RT_ERROR 线程脱离失败
*/
rt_err_t rt_thread_detach (rt_thread_t thread);


官方在介绍`rt_thread_detach`有一句话,同样,线程本身不应调用这个接口脱离线程本身。这句话我理解就是不管动态删除还是静态删除,不能在线程函数中自己把自己删除。  
 这里也与FreeRTOS任务后不同,FreeRTOS可以直接在任务中调用函数删除自己。


但是需要特别说明的是,在 RT-Thread 中执行完毕的线程系统会自动将其删除!用户无需多余操作,如何理解呢,看下面的例子:


我们一般线程函数都是死循环,通过延时释放CPU控制权,比如:



static void led1_thread_entry(void *par){
while(1){
//do_something
rt_thread_mdelay(100);
}
}


我们需要删除的线程往往只是为了做某一件事,某一次特殊的事情,比如:



static void this_is_a_need_delete_task(void *par){
//do_one_time_thing
}


其实这个线程是为了某一件特殊事情而创建的,它是需要删除的,我们并不需要做任何特殊处理,因为执行是没有循环的,执行完成以后,RT-Thread 内核会自动把线程删除!!


### 1.5 挂起和恢复线程


线程挂起和恢复,在官方有单独的说明:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/19588193df7446ada4382ceab524cd78.png)既然官方强烈不建议在程序中使用该接口,我们这里就不说明了,因为以应用为主,我们就不去用了。


需要说明的一点是,这里和FreeRTOS也是不同的,FreeRTOS用户可以随意用,最典型的就是使一段代码进入临界区挂起其他任务。


### 1.6 其他线程辅助函数


其他的线程辅助函数,除了线程睡眠函数,其他的在一般的应用中都可以不需要。所以我们简单的过一遍,引用一下官方的介绍。如果后期应用的时候有用到,再来加以详细说明:


#### 1.6.1 获得当前线程


在程序的运行过程中,相同的一段代码可能会被多个线程执行,在执行的时候可以通过下面的函数接口获得当前执行的线程句柄,把下面的函数加在这段代码中的,哪个线程调用就返回哪个线程句柄:



/*
返回值
thread 当前运行的线程句柄
RT_NULL 失败,调度器还未启动
*/
rt_thread_t rt_thread_self(void);


#### 1.6.2 让出处理器资源



rt_err_t rt_thread_yield(void);


调用该函数后,当前线程首先把自己从它所在的就绪优先级线程队列中删除,然后把自己挂到这个优先级队列链表的尾部,然后激活调度器进行线程上下文切换(如果当前优先级只有这一个线程,则这个线程继续执行,不进行上下文切换动作)。


#### 1.6.3 线程睡眠(延时函数)


线程睡眠,直白点说,就是延时函数,只不过RTOS中的延时函数,是会释放CPU使用权的,释放CPU使用权,就等于线程睡眠了。



/*
参数:tick/ms
线程睡眠的时间:sleep/delay 的传入参数 tick 以 1 个 OS Tick 为单位 ;
mdelay 的传入参数 ms 以 1ms 为单位;
返回
RT_EOK 操作成功,一般不需要
*/
rt_err_t rt_thread_sleep(rt_tick_t tick);
rt_err_t rt_thread_delay(rt_tick_t tick);
rt_err_t rt_thread_mdelay(rt_int32_t ms);


#### 1.6.4 线程控制函数



/*
参数说明:
1、thread 线程句柄
2、cmd 指示控制命令
cmd 当前支持的命令包括:
•RT_THREAD_CTRL_CHANGE_PRIORITY:动态更改线程的优先级;
•RT_THREAD_CTRL_STARTUP:开始运行一个线程,等同于 rt_thread_startup() 函数调用;
•RT_THREAD_CTRL_CLOSE:关闭一个线程,
等同于 rt_thread_delete() 或 rt_thread_detach() 函数调

用。
3、arg 控制参数
返回值:
RT_EOK 控制执行正确
-RT_ERROR 失败
*/
rt_err_t rt_thread_control(rt_thread_t thread, rt_uint8_t cmd, void* arg);


#### 1.6.5 设置和删除空闲钩子


空闲钩子函数是空闲线程的钩子函数(不要和调度器钩子函数搞混了),如果设置了空闲钩子函数,就可以在系统执行空闲线程时,自动执行空闲钩子函数来做一些其他事情,比如系统指示灯。设置 / 删除空闲钩子的接口如下:



/*
参数:
hook 设置的钩子函数,在函数中实现一些操作,但是不要有挂起操作
返回值:
RT_EOK 设置成功
-RT_EFULL 设置失败
*/
rt_err_t rt_thread_idle_sethook(void (*hook)(void));
rt_err_t rt_thread_idle_delhook(void (*hook)(void));


官方有一段注意说明如下:![在这里插入图片描述](https://img-blog.csdnimg.cn/b6a414dcf5214876ab5d033b41db2a08.png)


#### 1.6.6 设置调度器钩子


在整个系统的运行时,系统都处于线程运行、中断触发 - 响应中断、切换到其他线程,甚至是线程间的切换过程中,或者说系统的上下文切换是系统中最普遍的事件。有时用户可能会想知道在一个时刻发生了什么样的线程切换,可以通过调用下面的函数接口设置一个相应的钩子函数。在系统线程切换时,这个钩子函数将被调用:



/*
参数:
hook 表示用户定义的钩子函数指针
*/
void rt_scheduler_sethook(void (*hook)(struct rt_thread* from, struct rt_thread* to));
/*
钩子函数 hook() 的声明
参数说明:
1、from 表示系统所要切换出的线程控制块指针
2、to 表示系统所要切换到的线程控制块指针
*/
void hook(struct rt_thread* from, struct rt_thread* to);


注:请仔细编写你的钩子函数,稍有不慎将很可能导致整个系统运行不正常(在这个钩子函数中,基本上不允许调用系统 API,更不应该导致当前运行的上下文挂起)。


## 二、RT-Thread线程创建示例


虽然上面介绍了有一部分的线程操作函数,但是正常需要也就前面几个,记住线程创建,启动,一般的应用就足够了,其他的一些辅助函数在实际中有很多情况是出了问题以后找 bug 的时候才会想起来。


所以我们演示起来也很简单,还记得在 RT-Thread记录 第一篇博文中:


[RT-Thread记录(一、RT-Thread 版本、RT-Thread Studio开发环境 及 配合CubeMX开发快速上手)](https://bbs.csdn.net/topics/618679757)


在上面博文的最后一节:3.3 创建一个跑马灯任务 我上传了一段源码,这里我就不再重复上一边了,我们直接通过截图说明的方式讲解下示例:


### 2.1 静态创建线程示例


![在这里插入图片描述](https://img-blog.csdnimg.cn/3295abb96c62439e91f039c3ff07d5fb.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


### 2.1 动态创建线程示例


![在这里插入图片描述](https://img-blog.csdnimg.cn/7d21eff4217e442c91b14213df0784fa.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


## 三、RT-Thread线程管理简析


经过上面的说明,我们其实能够使用 RT-Thread 对于的函数创建线程进行一般的设计了,但是为了加深对RT-Thread的理解,我们还得聊聊 RT-Thread线程管理。


这一块在官网其实有详细的说明,官方的链接如下:


[RT-Thread官方文档 RT-Thread内核线程管理](https://bbs.csdn.net/topics/618679757)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/af393288cc1541158f7075cb6ae37c01.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


### 3.1 线程调度的基本特点


我这边按照自己的理解认知记录几个重要的点:


1、RT-Thread 的线程调度器是抢占式的,主要的工作就是从就绪线程列表中查找最高优先级线程,保证最高优先级的线程能够被运行,最高优先级的任务一旦就绪,总能得到 CPU 的使用权。


调度器开启以后,就不停的在查询列表,所有的线程根据优先级,状态,在列表中排序,调度器总是找到排序“第一位”的线程执行。RTOS的核心就是链表,这个有时间会单独的介绍。


2、当一个运行着的线程使一个比它优先级高的线程满足运行条件,当前线程的 CPU 使用权就被剥夺了,或者说被让出了,高优先级的线程立刻得到了 CPU 的使用权。


如果是中断服务程序使一个高优先级的线程满足运行条件,中断完成时,被中断的线程挂起,优先级高的线程开始运行。


还是上面说到的调度器的作用,使得高优先级的能够及时执行。


3、当调度器调度线程切换时,先将当前线程上下文保存起来,当再切回到这个线程时,线程调度器将该线程的上下文信息恢复。  
 RT-Thread 线程具有独立的栈,当进行线程切换时,会将当前线程的上下文存在栈中,当线程要恢复运行时,再从栈中读取上下文信息,进行恢复。


要理解上面的话,推荐一篇博文:  
 [FreeRTOS记录(三、FreeRTOS任务调度原理解析\_Systick、PendSV、SVC)](https://bbs.csdn.net/topics/618679757)  
 虽然说的是FreeRTOS的,但是都是基于Cortex-M内核的,原理机制类似。


4、每个线程都有时间片这个参数,但时间片仅对优先级相同的就绪态线程有效。


时间片只有在优先级相同的线程间会根据用户的设置进行对应的分配。


5、线程中不能陷入死循环操作,必须要有让出 CPU 使用权的动作,如循环中调用延时函数或者主动挂起。


使用rtos延时函数,是实际使用最常见的一种方式,切记,delay是需要在while(1){}大括号里面的:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/b7f3edaab6b64e0f808c755c580e91db.png)


### 3.2 线程控制块


在我们上面介绍线程操作函数的时候,经常提到一个词语,线程控制块,线控控制块结构体,RT-Thread 内核对于线程的管理,都是基于这个结构体进行的。这里我们先有个基本的认识,如果真的深入探讨,还是要说到RTOS的链表,需要单独的开篇博文说明。


我们现在要了解的是,内核对于线程的管理是通过这个线程控制块结构体,里面包括 RT-Thread线程所有的“属性”,对这些属性的查看,修改就可以对实现对这个线程的管理控制。


我们来看看控制块结构体(不是直接复制官网的哦!):



/**
* Thread structure
*/
struct rt_thread
{
/* rt object */
char name[RT_NAME_MAX]; /**< the name of thread 线程名称*/
rt_uint8_t type; /**< type of object 对象类型*/
rt_uint8_t flags; /**< thread’s flags 标志位*/

#ifdef RT_USING_MODULE
void *module_id; /**< id of application module */
#endif

rt\_list\_t   list;                     /\*\*< the object list 对象列表\*/
rt\_list\_t   tlist;                    /\*\*< the thread list 线程列表\*/

/\* stack point and entry 栈指针与入口指针\*/
void       \*sp;                       /\*\*< stack point 栈指针\*/
void       \*entry;                    /\*\*< entry 入口函数指针\*/
void       \*parameter;                /\*\*< parameter 参数\*/
void       \*stack_addr;               /\*\*< stack address 栈地址指针 \*/
rt\_uint32\_t stack_size;               /\*\*< stack size 栈大小\*/

/\* error code \*/
rt\_err\_t    error;                    /\*\*< error code 线程错误代码\*/

rt\_uint8\_t  stat;                     /\*\*< thread status 线程状态 \*/

#ifdef RT_USING_SMP /*多核相关支持,我们这里就一个M3内核*/
rt_uint8_t bind_cpu; /**< thread is bind to cpu */
rt_uint8_t oncpu; /**< process on cpu` */

rt\_uint16\_t scheduler_lock_nest;        /\*\*< scheduler lock count \*/
rt\_uint16\_t cpus_lock_nest;             /\*\*< cpus lock count \*/
rt\_uint16\_t critical_lock_nest;         /\*\*< critical lock count \*/

#endif /*RT_USING_SMP*/

/\* priority 优先级\*/
rt\_uint8\_t  current_priority;           /\*\*< current priority 当前优先级 \*/
rt\_uint8\_t  init_priority;              /\*\*< initialized priority 初始优先级 \*/

#if RT_THREAD_PRIORITY_MAX > 32
rt_uint8_t number;
rt_uint8_t high_mask;
#endif
rt_uint32_t number_mask;

#if defined(RT_USING_EVENT) /*使用事件集*/
/* thread event */
rt_uint32_t event_set;
rt_uint8_t event_info;
#endif

#if defined(RT_USING_SIGNALS)
rt_sigset_t sig_pending; /**< the pending signals */
rt_sigset_t sig_mask; /**< the mask bits of signal */

#ifndef RT_USING_SMP /*多核相关支持,我们这里就一个M3内核*/
void *sig_ret; /**< the return stack pointer from signal */
#endif
rt_sighandler_t *sig_vectors; /**< vectors of signal handler */
void *si_list; /**< the signal infor list */
#endif

rt\_ubase\_t  init_tick;              /\*\*< thread's initialized tick 线程初始化计数值\*/
rt\_ubase\_t  remaining_tick;         /\*\*< remaining tick 线程剩余计数值\*/

struct rt\_timer thread_timer;       /\*\*< built-in thread timer 内置线程定时器\*/

 /\*\*< cleanup function when thread exit 

线程退出清除函数
cleanup 函数指针指向的函数,会在线程退出的时候,被idle 线程回调一次,
执行用户的清理现场工作。
*/
void (*cleanup)(struct rt_thread *tid);

/\* light weight process if present \*/

#ifdef RT_USING_LWP
void *lwp;
#endif

rt\_ubase\_t user_data;      /\*\*< private user data beyond this thread 用户数据\*/

};
typedef struct rt_thread *rt_thread_t;


### 3.3 线程状态


线程的状态我们借用官方的几张图,加以说明:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/d9c8a9146a6246afa2d6c6d408c3ea0c.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


来看看 RT-Thread 的任务状态:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/cd549db340a84967ab7d6799a85cfc36.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)  
 在上图中除了今天我们介绍的线程操作函数,还有一些函数还没有介绍过,比如`rt_sem_take(),rt_mutex_take(),rt_mb_recv()` ,这是我们后期会介绍到的关于线程间通信的一些信号量,互斥量相关的函数。


作为对比,再来看看FreeRTOS 的任务状态:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/164e42c463804495bbb76b3052f285b9.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_1,color_FFFFFF,t_70,g_se,x_16)


### 3.4 系统线程


在 RT-Thread 内核中的系统线程有空闲线程和主线程。


**空闲线程 IDLE线程:**


空闲线程是系统创建的最低优先级的线程,线程状态永远为就绪态。当系统中无其他就绪线程存在时,调度器将调度到空闲线程,它通常是一个死循环,且永远不能被挂起。这点其实所有RTOS都是一样的。


但是,空闲线程在 RT-Thread 也有着它的特殊用途:


若某线程运行完毕,系统将自动删除线程:自动执行 rt\_thread\_exit() 函数,先将该线程从系统就绪队列中删除,再将该线程的状态更改为关闭状态,不再参与系统调度,然后挂入 rt\_thread\_defunct 僵尸队列(资源未回收、处于关闭状态的线程队列)中,最后空闲线程会回收被删除线程的资源。


空闲线程也提供了接口来运行用户设置的钩子函数,在空闲线程运行时会调用该钩子函数,适合钩入功耗管理、看门狗喂狗等工作。


**主线程:**


在我们上一篇博文中介绍 RT-Thread 启动流程的时候,说到了系统启动会创建main线程:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/5d14d61399ac4ddca07354a07df459fd.png)  
 FreeRTOS只有空闲线程,并不会创建主线程,所以在FreeRTOS中,一般在main() 之前开启调度,永远不会执行到main()。


## 结语


本文的主要目的是认识 RT-Thread 线程操作函数,同时简单的说明了一下 RT-Thread 线程管理的一些要点,说明了一下 RT-Thread 与 FreeRTOS 在线程操作某些地方的不同,此外还加了一些博主认为的细节的问题, 希望懂的小伙伴可以多多指教,不懂的小伙伴看完还是不明白的可以留言。讲得不好的地方还希望能够指出,博主一定加以修正。


总的来说,本文内容还是比较简单的,小伙伴们可以开动起来,线程创建跑起来玩玩。优先级,任务调度,线程死循环什么的情况都可以试试。更能加加深线程调度的理解。


下一篇 RT-Thread 记录,我会讲一讲 RT-Thread 时钟管理的内容,系统时钟,软件定时器相关。


谢谢!






**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**
![img](https://img-blog.csdnimg.cn/img_convert/aa97bb78e9099069939522047791e8d8.png)
![img](https://img-blog.csdnimg.cn/img_convert/2304b7e30526d34e1f294c1ab82a5aa2.png)

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

理解。


下一篇 RT-Thread 记录,我会讲一讲 RT-Thread 时钟管理的内容,系统时钟,软件定时器相关。


谢谢!






**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**
[外链图片转存中...(img-B7Kqniks-1715903063035)]
[外链图片转存中...(img-BHjCj9Cf-1715903063036)]

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

### 回答1: rt-threadfreertos 都是实时操作系统(RTOS),旨在为嵌入式系统提供可靠的多任务处理能力。 rt-thread 是一款开源的 RTOS,专门设计用于低功耗、小容量的嵌入式系统。rt-thread 采用了轻量级线程实现,支持信号量、互斥锁、消息队列等同步机制,并提供了丰富的驱动支持和组件,如 TCP/IP 协议栈、文件系统、GUI 界面等。rt-thread 的内核代码非常精简,可运行在 RAM 或者 ROM 上。 相比之下,freertos 同样是一款开源的 RTOS,但是功能更为丰富,支持更多的处理器架构,包括 ARM、MIPS、x86 等。freertos 提供了多任务调度、内存管理、IPC 机制、软件定时器等功能,并且还有完善的文档和社区支持。与 rt-thread 相比,freertos 的内核代码更加复杂,但是在可移植性和跨平台性方面表现更好。 综上所述,rt-thread 适合用于资源受限的嵌入式系统,而 freertos 则适用于更为复杂的系统,需要更多的功能和处理器架构支持。 ### 回答2: RT-ThreadFreeRTOS都是开源的实时操作系统(RTOS),主要用于嵌入式系统中。它们都提供了许多标准的功能,如多任务处理、线程同步和通信、定时器和时间管理等。但是,它们在一些方面有所不同,下面将对它们进行详细的比较。 首先,RT-Thread是一个面向对象的RTSOS。在RT-Thread中,所有的线程、设备驱动以及其他的系统对象都是对象。这使得它在处理复杂系统时更加容易。而FreeRTOS则是一个面向任务的RTSOS,这意味着它在调度和处理多个任务时更加强大和灵活,但是处理复杂系统时会更加困难。 其次,RT-Thread内置了很多功能模块,如文件系统、TCP/IP协议栈、USB驱动等,这些模块可以很方便地进行移植和使用。而FreeRTOS则需要额外的组件来实现这些功能。这使得RT-Thread更加适合处理复杂系统,而FreeRTOS则更加适合轻量级和低功耗的系统。 另外,RT-Thread提供了C++接口以及Lua脚本语言支持,可以方便地进行自定义扩展和应用开发。而FreeRTOS则只支持C语言,扩展和应用开发需要更多的工作量。 总的来说,RT-Thread适合处理复杂系统和大型项目,FreeRTOS适合轻量级和低功耗的应用。选择哪一个取决于项目的需求和特点。 ### 回答3: rt-threadfreertos是两种嵌入式实时操作系统,都具有轻量级、开源、定位于小型嵌入式设备等特点。它们的主要区别体现在以下几个方面: 1.架构和设计 rt-thread是一个基于“内核+组件”的架构设计,内核负责任务调度、内存管理线程间通信等核心功能,组件则提供文件系统、网络协议栈等更高层次的服务;而freertos则采用了更为简单的内核设计,将任务抽象为优先级,通过优先级管理和调度任务。rt-thread的组件化设计使得系统功能更为完备,但也导致代码更为复杂,freertos则相对来说更为易于理解和实现。 2.可移植性 rt-thread支持多种平台和开发板,包括ARM Cortex-M、RISC-V、Xtensa等架构,同时也支持uClinux内核。这使得rt-thread具有很高的可移植性,能够适应不同类型的嵌入式设备;而freertos则针对特定芯片或板卡进行了优化,移植性相对来说较差。 3.资源占用情况 rt-thread在处理器内存和rom占用方面相对freertos更优,主要是因为rt-thread采用了更为灵活的组件化设计,能够根据实际需要选择加载不同的组件;而freertos则拥有更为紧凑的内核,能够在小型设备上运行。 4.社区支持和生态 rt-threadfreertos都是活跃的开源项目,拥有庞大的社区和完善的生态。但由于rt-thread的组件化设计和多平台支持,其组件库更丰富,社区支持也更活跃,可以提供更为全面的功能和应用支持。 综上所述,二者在应用场景上有着较为明显的区别rt-thread适用于需求更为复杂、功能更为完备的嵌入式设备,而freertos则更适合资源有限、对实时性要求不高的小型设备。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值