第四章 操作系统系统内核应用
本章将对操作系统内核的方面的应用进行介绍,通过若干个实验,来介绍内核资源的使用,方便读者理解并且应用。
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)
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!