嵌入式系统应用-第四章操作系统(rt_thread)内核kernel 上_rt thread操作系统

第四章 操作系统系统内核应用

本章将对操作系统内核的方面的应用进行介绍,通过若干个实验,来介绍内核资源的使用,方便读者理解并且应用。

4.1 线程

4.1.1 线程介绍

线程,即任务的载体。一般被设计成 while(1) 的循环模式,但在循环中一定要有让出 CPU 使用权的动作。如果是可以执行完毕的线程,则系统会自动将执行完毕的线程进行删除 / 脱离。

4.1.2 线程创建

主要创建入口函数,实现具体功能、分配内存大小、优先级和时间片段。 创建函数主要用到动态创建和静态创建。如果该线程一直都是要运行的MCU里面,建议采用静态创建。如果该线程只是在运行的过程中在特定情况触发,可以采用动态创建。

线程动态创建
创建一个闪烁程序


# include <rtthread.h>
# define THREAD\_PRIORITY 25
# define THREAD\_STACK\_SIZE 512
# define THREAD\_TIMESLICE 5
static rt\_thread\_t tid1 = RT_NULL;

/\* 线 程 1 的 入 口 函 数 \*/
static void thread1\_entry(void \*parameter)
{
 		while(1)  // 灯闪烁程序
	   {
			LED\_Ctr(led0,led_on);
	  		rt\_thread\_mdelay(500);
	 		LED\_Ctr(led0,led_off); 
	  		rt\_thread\_mdelay(500);
	  	}
}

int thread\_sample(void)
{
	/\* 创 建 线 程 1, 名 称 是 thread1, 入 口 是 thread1\_entry\*/
	tid1 = rt\_thread\_create("thread1",
	thread1_entry, RT_NULL,
	THREAD_STACK_SIZE,
	THREAD_PRIORITY, THREAD_TIMESLICE);
	/\* 如 果 获 得 线 程 控 制 块, 启 动 这 个 线 程 \*/
	if (tid1 != RT_NULL)
			rt\_thread\_startup(tid1);
}

静态创建
创建一个闪烁程序

# include <rtthread.h>
# define THREAD\_PRIORITY 25
# define THREAD\_STACK\_SIZE 512
# define THREAD\_TIMESLICE 5

	ALIGN(RT_ALIGN_SIZE)
	static char thread2_stack[1024];
	static struct rt\_thread thread2;
	/\* 线 程 2 入 口 \*/
	static void thread2\_entry(void \*param)
	{
	    while(1)  // 灯闪烁程序
	   {
			LED\_Ctr(led0,led_on);
	  		rt\_thread\_mdelay(500);
	 		LED\_Ctr(led0,led_off); 
	  		rt\_thread\_mdelay(500);
	  	}
	/\* 线 程 2 运 行 结 束 后 也 将 自 动 被 系 统 脱 离 \*/
	}

int thread\_sample(void)
{
	/\* 初 始 化 线 程 2, 名 称 是 thread2, 入 口 是 thread2\_entry \*/	
	rt\_thread\_init(&thread2,"thread2",thread2_entry,RT_NULL,&thread2_stack[0],
	sizeof(thread2_stack),THREAD_PRIORITY - 1, THREAD_TIMESLICE);
	rt\_thread\_startup(&thread2);
	return 0;
}

4.1.3 线程获取和线程睡眠

获取线程

rt\_thread\_t rt\_thread\_self(void);

线程休眠

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);

4.1.4 线程挂起和恢复

不建议使用下面的函数来挂起线程。可以通过delay来延迟挂起资源。否则一定要用,需要手动切换线程。rt_schedule() 函数

 rt\_thread\_suspend (rt\_thread\_t thread);

rt\_err\_t rt\_thread\_resume (rt\_thread\_t thread);

4.1.5 线程控制

当需要堆一个线程进行其他控制时,可以调用如下函数接口:

rt\_err\_t rt\_thread\_control(rt\_thread\_t thread, rt\_uint8\_t cmd, void\* arg);

参数 thread 为线程句柄;参数 cmd 为控制指令;arg 为控制指令参数。

返回 RT_EOK,表示执行成功。返回 -RT_ERROR,表示执行失败。

指示控制命令 cmd 当前支持的命令如下:
RT_THREAD_CTRL_CHANGE_PRIORITY,动态更改线程优先级。
RT_THREAD_CTRL_STARTUP,开始运行一个线程。
RT_THREAD_CTRL_CLOSE,关闭一个线程。

可以运行线程2开始关闭线程1,运行5次后关闭线程2;彼此一次关闭:

/\* 线 程 2 入 口 \*/
static void thread2\_entry(void \*param)
{		
	 u32 cnt=0;
	 rt\_thread\_control(tid1,RT_THREAD_CTRL_CLOSE,RT_NULL);	
	 while(1)  // 灯闪烁程序
	 {
			LED\_Ctr(led0,led_on);
			rt\_thread\_mdelay(500);
			LED\_Ctr(led0,led_off); 
			rt\_thread\_mdelay(500);
		  if(cnt<5)
			{
				 cnt++;
			}
			else
			{
				  rt\_thread\_control(&thread2,RT_THREAD_CTRL_CLOSE,RT_NULL);
				  rt\_thread\_control(tid1,RT_THREAD_CTRL_STARTUP,RT_NULL);          				
				  cnt=0;
				
			}
		}
}


/\* 线 程 1 的 入 口 函 数 \*/
static void thread1\_entry(void \*parameter)
{
	  u32 cnt=0;
	  rt\_thread\_control(&thread2,RT_THREAD_CTRL_CLOSE,RT_NULL);
 		while(1)  // 灯闪烁程序
	  {
			LED\_Ctr(led1,led_on);
	  		rt\_thread\_mdelay(500);
			LED\_Ctr(led1,led_off); 
	  		rt\_thread\_mdelay(500);
			if(cnt<5)
				cnt++;
			else
           {
					  rt\_thread\_control(tid1,RT_THREAD_CTRL_CLOSE,RT_NULL);
					  rt\_thread\_control(&thread2,RT_THREAD_CTRL_STARTUP,RT_NULL);
                      cnt=0;
          				
			}  
	 }
}

4.1.6 线程总结

线程运行的过程中,同一时间内只允许一个线程在处理器中运行,从运行的过程上划分,线程有多种
不同的运行状态,如初始状态、挂起状态、就绪状态等。在RT-Thread 中,线程包含五种状态,操作系统
会自动根据它运行的情况来动态调整它的状态。RT-Thread 中线程的五种状态,如下表所示:

1. 初始状态
当线程刚开始创建还没开始运行时就处于初始状态;在初始状态下,线程不参与调度。此状 态在RT-Thread
中的宏定义为RT_THREAD_INIT
2. 就绪状态
在就绪状态下,线程按照优先级排队,等待被执行;一旦当前线程运行完毕让出处理器,操
作系统会马上寻找最高优先级的就绪态线程运行。此状态在RT-Thread 中的宏定义为 RT_THREAD_READY
3 运行状态
线程当前正在运行。在单核系统中,只有rt_thread_self() 函数返回的线程处于运行状态;
在多核系统中,可能就不止这一个线程处于运行状态。此状态在RT-Thread 中的宏定义为 RT_THREAD_RUNNING
4 挂起状态
也称阻塞态。它可能因为资源不可用而挂起等待,或线程主动延时一段时间而挂起。在挂起 状态下,线程不参与调度。此状态在RT-Thread中的宏定义为RT_THREAD_SUSPEND
5关闭状态
当线程运行结束时将处于关闭状态。关闭状态的线程不参与线程的调度。此状态在RT-Thread 中的宏定义为RT_THREAD_CLOSE

优先级
RT-Thread 线程的优先级是表示线程被调度的优先程度。每个线程都具有优先级,线程越重要,赋予的优先级就应越高,- 线程被调度的可能才会越大。
RT-Thread 最大支持256 个线程优先级(0~255),数值越小的优先级越高,0 为最高优先级。在一些资源比较紧张的系统中,可以根据实际情况选择只支持8 个或32 个优先级的系统配置;对于ARM Cortex-M系列,普遍采用32 个优先级。最低优先级默认分配给空闲线程使用,用户一般不使用。在系统中,当有比
当前线程优先级更高的线程就绪时,当前线程将立刻被换出,高优先级线程抢占处理器运行。

时间片
每个线程都有时间片这个参数,但时间片仅对优先级相同的就绪态线程有效。系统对优先级相同的就绪态线程采用时间片轮转的调度方式进行调度时,时间片起到约束线程单次运行时长的作用,其单位是一个系统节拍(OS Tick),详见第五章。假设有2 个优先级相同的就绪态线程A 与B,A 线程的时间片设置为10,B 线程的时间片设置为5,那么当系统中不存在比A 优先级高的就绪态线程时,系统会在A、B 线程间来回切换执行,并且每次对A 线程执行10 个节拍的时长,对B 线程执行5 个节拍的时长,如下图。

线程

4.2 线程同步

4.2.1 信号量

信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的。
信号量工作示意图如下图所示,每个信号量对象都有一个信号量值和一个线程等待队列,信号量的值对应了信号量对象的实例数目、资源数目,假如信号量值为 5,则表示共有 5 个信号量实例(资源)可以被使用,当信号量实例数目为零时,再申请该信号量的线程就会被挂起在该信号量的等待队列上,等待可用的信号量实例(资源)。
在这里插入图片描述

信号量申明

#include <rtthread.h>
#define THREAD\_PRIORITY 6
#define THREAD\_STACK\_SIZE 512
#define THREAD\_TIMESLICE 5

信号量申明
struct rt\_semaphore sem_lock;
信号量线程获取
rt\_sem\_take(&sem_lock, RT_WAITING_FOREVER);
信号量释放
rt\_sem\_release(&sem_lock);

信号量静态和动态创建

	rt\_sem\_init(&sem_KeyOn, "key\_on", 1, RT_IPC_FLAG_FIFO);

信号量释放

	rt\_sem\_detach(&sem_lock);

这里我们创建两个线程,线程1扫描按键,发送信号量,线程2 获取信号量,点亮灯。

#include "led.h"
#include "rtthread.h"
#include "sem\_key.h"
#include "key.h"

// 按键获取
Board_Key \*pKey;

// 申明信号量
struct rt\_semaphore sem_KeyOn;

#define THREAD\_PRIORITY 25
#define THREAD\_STACK\_SIZE 512
#define THREAD\_TIMESLICE 5
static rt\_thread\_t tid1 = RT_NULL;

ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt\_thread thread2;
/\* 线 程 2 入 口 \*/
static void thread2\_entry(void \*param)
{		

	 
	 while(1)  // 点亮
	 {
		  rt\_sem\_take(&sem_KeyOn,RT_WAITING_FOREVER);  // 获取信号量
			LED\_Ctr(led0,led_on);			
	 }
}


/\* 线 程 1 的 入 口 函 数 \*/
static void thread1\_entry(void \*parameter)
{

	  Board_Key \*pk=parameter;
 		while(1)  // 灯闪烁程序
	  {
				Key\_Scan();
				if(pk->key==key0 && pk->sta==press)
			     rt\_sem\_release(&sem_KeyOn);  // 释放信号量
			  rt\_thread\_mdelay(10);
	  }
}


int Sem\_Sample(void)
{	  
	  /\* 按键初始化 \*/	
		Key\_Init(&pKey);
		/\* 静态创建信号量 \*/
	  rt\_sem\_init(&sem_KeyOn, "keyOn", 0, RT_IPC_FLAG_FIFO);

   /\* 动态创建信号量 \*/
 
	
		/\* 初 始 化 线 程 2, 名 称 是 thread2, 入 口 是 thread2\_entry \*/	
		rt\_thread\_init(&thread2,"thread2",thread2_entry,RT_NULL,&thread2_stack[0],
		sizeof(thread2_stack),THREAD_PRIORITY , THREAD_TIMESLICE);
		rt\_thread\_startup(&thread2);
	
		/\* 创 建 线 程 1, 名 称 是 thread1, 入 口 是 thread1\_entry\*/
		tid1 = rt\_thread\_create("thread1",thread1_entry, pKey,THREAD_STACK_SIZE,
		THREAD_PRIORITY, THREAD_TIMESLICE);
		/\* 如 果 获 得 线 程 控 制 块, 启 动 这 个 线 程 \*/
		if (tid1 != RT_NULL)
				rt\_thread\_startup(tid1);
		
		

		return 0;
}

也可以动态创建信号量

   /\* 动态创建信号量 \*/
    sem_KeyOff=rt\_sem\_create("keyOff",0,RT_IPC_FLAG_FIFO);
    rt\_sem\_t sem_KeyOff;

创建线程,执行灯灭

ALIGN(RT_ALIGN_SIZE)
static char thread3_stack[1024];
static struct rt\_thread thread3;
/\* 线 程 3 入 口 \*/
static void thread3\_entry(void \*param)
{			 
	 while(1)  // 点亮
	 {
		  rt\_sem\_take(sem_KeyOff,RT_WAITING_FOREVER);  // 获取信号量
			LED\_Ctr(led0,led_off);			  // 关闭灯
	 }
}

/\* 线 程 1 的 入 口 函 数 \*/
static void thread1\_entry(void \*parameter)
{
 		while(1)  // 灯闪烁程序
	  {
## 最后

**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**

**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

![img](https://img-blog.csdnimg.cn/img_convert/5714bce660568dd9c4cfde4a8cdbf83a.png)

![img](https://img-blog.csdnimg.cn/img_convert/9cc710191fa7cc3fea3adca50c39ab37.jpeg)

![img](https://img-blog.csdnimg.cn/img_convert/b58e5cbbc94b2e75bffbd45c4fa9bf10.png)

 ![img](https://img-blog.csdnimg.cn/img_convert/d626a5da3e407ac13a73f4c4d0d8ef76.png)

![img](https://img-blog.csdnimg.cn/img_convert/015c4e8590eb425198ccdda7f9782dfc.png)

![img](https://img-blog.csdnimg.cn/img_convert/efbbd25b1263faf8bedbadf713472e1d.png)

![](https://img-blog.csdnimg.cn/img_convert/cfe8db8b8e46ab78fd8f2768251edc75.png)

 

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

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


5Mo6XGz-1715715660522)]

 [外链图片转存中...(img-E9uhFulz-1715715660523)]

[外链图片转存中...(img-Dm8spS7g-1715715660523)]

[外链图片转存中...(img-Oz6ClgL5-1715715660524)]

[外链图片转存中...(img-SYQerPmU-1715715660524)]

 

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

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RT-Thread是一个开源的实时操作系统,它的设计目标是提供一个简单、高效、可靠的实时操作系统内核。下面是RT-Thread的原理分析: 1. 内核对象的设计:RT-Thread内核对象包括线程、信号量、邮箱、消息队列、内存池等。每个内核对象都有一个控制块,用于描述该对象的状态和属性。内核对象的设计使得RT-Thread内核具有高度的可扩展性和灵活性。 2. 线程调度的机制:RT-Thread采用抢占式的优先级调度算法,支持多级优先级和时间片轮转调度。线程的优先级越高,被调度的机会就越大。当多个线程的优先级相同时,采用时间片轮转调度算法。 3. 中断处理机制:RT-Thread支持中断嵌套和中断优先级控制。当一个中断处理程序正在执行时,如果发生了更高优先级的中断,RT-Thread会挂起当前中断处理程序,转而执行更高优先级的中断处理程序。 4. 内存管理机制:RT-Thread采用动态内存管理机制,支持内存池和动态内存分配。内存池可以提高内存分配的效率,动态内存分配可以更灵活地管理内存。 5. 设备驱动机制:RT-Thread采用设备驱动框架,支持字符设备、块设备、网络设备等。设备驱动程序可以通过注册设备驱动的方式来实现。 6. 文件系统机制:RT-Thread支持多种文件系统,包括FAT、YAFFS、ROMFS等。文件系统可以通过挂载的方式来使用。 7. 网络协议栈:RT-Thread支持TCP/IP协议栈,包括TCP、UDP、IP、ICMP、ARP等协议。网络协议栈可以通过配置的方式来启用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值