RT-Thread 线程管理(一)

线程管理

在日常生活中,要完成一个大任务,一般会将它分解成多个简单、容易解决的小问题,小问题逐个被解决,大问题也就随之解决了。

在多线程操作系统中,也同样需要开发人员把一个复杂的应用分解成多个小的、可调度的、序列化的程序单元,当合理地划分任务并正确地执行时,这种设计能够让系统满足实时系统的性能及时间的要求。

例如让嵌入式系统执行这样的任务,系统通过传感器采集数据,并通过显示屏将数据显示出来,在多线程实时系统中,可以将这个任务分解成两个子任务,如下图所示,一个子任务不间断地读取传感器数据,并将数据写到共享内存中,另外一个子任务周期性的从共享内存中读取数据,并将传感器数据输出到显示屏上。

在这里插入图片描述
在 RT-Thread 中,与上述子任务对应的程序实体就是线程,线程是实现任务的载体,它是 RT-Thread 中最基本的调度单位,它描述了一个任务执行的运行环境,也描述了这个任务所处的优先等级,重要的任务可设置相对较高的优先级,非重要的任务可以设置较低的优先级,不同的任务还可以设置相同的优先级,轮流运行。

当线程运行时,它会认为自己是以独占 CPU 的方式在运行,线程执行时的运行环境称为上下文,具体来说就是各个变量和数据,包括所有的寄存器变量、堆栈、内存信息等。

本章将分成 5 节内容对 RT-Thread 线程管理进行介绍,读完本章,读者会对 RT-Thread 的线程管理机制有比较深入的了解,如:线程有哪些状态、如何创建一个线程、为什么会存在空闲线程等问题,心中也会有一个明确的答案了。

线程管理的功能特点

RT-Thread线程管理的主要功能是对线程进行管理和调度,系统中共存在两类线程,分别是系统线程和用户线程,系统线程由RT-Thread内核创建,用户现场是由应用程序创建,这两类线程都会从内核对象容器中分配线程对象,当线程被删除时,也会被从对象容器中删除,如下图所示,每个线程都有重要的属性,如线程控制块、线程栈、入口函数等。

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

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

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

当调度器调度线程切换时,先将当前线程上下文保存起来,当再切回到这个线程时,线程调度器将该线程的上下文信息恢复。

线程控制块

线程控制块由结构体strcut rt_thread表示,线程控制块是操作系统用于管理线程的一个数据结构,它会存放线程的一些信息,例如优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表结构,线程等待事件集合等。

/* 线程控制块 */
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_uint32_t number_mask;......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;                      /* 用户数据 */
};

其中init_priority是线程创建时指定的线程优先级,在线程运行过程当中不会被改变(除非用户执行线程控制函数进行手动调整)。cleanup会在线程退出时,被空闲线程回调一次以执行用户设置的清理现场等工作。user_data可由用户挂载一些数据信息到线程控制块中,以提供一种类似线程私有数据的实现方式。

线程栈

线程具有独立的栈,当进行线程切换时,会将当前线程的上下文存在栈中,当线程要恢复运行时,再从栈中读取上下文信息,进行恢复。

线程栈还用来存放函数中的局部变量:函数中的局部变量从线程栈空间中申请;函数中局部变量初始时从寄存器中分配(ARM架构),当这个函数再调用另一个函数时,这些局部变量将放入栈中。

对于线程第一次运行,可以以手工的方式构造这个上下文来设置一些初始的环境:入口函数(PC寄存器)、入口参数(R0寄存器)、返回位置(LR寄存器)、当前机器运行状态(CPSR寄存器)。

线程栈的增长方向是芯片架构密切相关的,对于ARM Cortex-M架构,线程栈可构造如下图所示。
在这里插入图片描述
线程栈大小可以这样设定:对于资源相对较大的MCU,可以适当设计较大的线程栈;也可以在初始时设置较大的栈,例如指定大小为1K或2K字节,然后在FinSH中用list_thread命令查看线程运行的过程中线程所使用的栈的大小,通过此命令,能够看到从线程启动运行时,到当前时刻点,线程使用的最大栈深度,而后加上适当的余量形成最终的线程栈大小,最后对栈空间大小加以修改。

线程状态

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

  1. 初始状态:当线程刚开始创建还没开始运行时就处于初始状态。初始状态下,线程不参与调度。此状态定义为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 个节拍的时长,如下图。

在这里插入图片描述

线程的入口函数

线程控制块中的entry是线程的入口函数,它是线程实现预期功能的函数。线程的入口函数由用户设计实现,一般有以下两种代码形式:

无限循环模式
在实时系统中,线程通常时被动式的,实时系统总是等待外界事件的发生,而后进行相应的服务。

void thread_entry(void* paramenter)
{while (1){/* 等待事件的发生 *//* 对事件进行服务、进行处理 */}
}

线程看似没有什么限制程序执行的因素,似乎所有的操作都可以执行。但是作为一个实时系统,一个优先级明确的实时系统,如果一个线程中的程序陷入了死循环操作,那么比它优先级低的线程都将不能够得到执行。

因此线程中不能陷入死循环操作,必须要有让出CPU使用权的动作,如循环中调用延时函数或者主动挂起。用户设计这种无限循环的线程的目的,就是为了让这个线程一直被系统循环调度运行,永不删除。

顺序执行或有限次循环模式
如简单的顺序语句、do while() 或 for()循环等,此类线程不会循环或不会永久循环,可谓是 “一次性” 线程,一定会被执行完毕。在执行完毕后,线程将被系统自动删除。

static void thread_entry(void* parameter)
{/* 处理事务 #1 *//* 处理事务 #2 *//* 处理事务 #3 */
}

线程错误码

一个线程就是一个执行场景,错误码与执行环境密切相关,所以每个线程配备了一个变量用于保存错误码。

#define RT_EOK           0 /* 无错误     */
#define RT_ERROR         1 /* 普通错误     */
#define RT_ETIMEOUT      2 /* 超时错误     */
#define RT_EFULL         3 /* 资源已满     */
#define RT_EEMPTY        4 /* 无资源     */
#define RT_ENOMEM        5 /* 无内存     */
#define RT_ENOSYS        6 /* 系统不支持     */
#define RT_EBUSY         7 /* 系统忙     */
#define RT_EIO           8 /* IO 错误       */
#define RT_EINTR         9 /* 中断系统调用   */
#define RT_EINVAL       10 /* 非法参数      */

线程状态切换

RT-Thread 提供一系列的操作系统调用接口,使得线程的状态在这五个状态之间来回切换。几种状态间的转换关系如下图所示:
在这里插入图片描述
线程通过调用函数rt_thread_create_init()进入初始状态(RT_THREAD_INIT);初始状态的线程通过调用函数 rt_thread_startup() 进入到就绪状态(RT_THREAD_READY);就绪状态的线程被调度器调度后进入运行状态(RT_THREAD_RUNNING);当处于运行状态的线程调用 rt_thread_delay(),rt_sem_take(),rt_mutex_take(),rt_mb_recv() 等函数或者获取不到资源时,将进入到挂起状态(RT_THREAD_SUSPEND);

处于挂起状态的线程,如果等待超时依然未能获得资源或由于其它线程释放了资源,那么它将返回就绪态。

如果调用rt_thread_delete()/detach()函数,将更改为关闭状态(RT_THREAD_CLOSE);而运行状态的线程,如果运行结束,就会在线程的最后部分执行 rt_thread_exit() 函数,将状态更改为关闭状态。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/61726.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubantu安装mongodb,开启远程访问和认证

最近因为项目原因需要在阿里云服务器上部署MongoDB,操作系统为Ubuntu,网上查阅了一些资料,特此记录一下步骤。 1.运行apt-get install mongodb命令安装MongoDB服务(如果提示找不到该package,说明apt-get的资源库版本比…

android 实现本地一键打包,告别繁琐的studio操作

前言 在实际开发项目中,我们的工程目录往往是多个app在一个工程下的,每次打包都需要手动的用studio点击Build->Generate Signed Bundle or APK->APK 选择app,签名等,甚至有的app签名还不一样,还需要手动的来回切…

iTOP-RK3588开发板Android12 设置系统默认不休眠

修改文件&#xff1a; device/rockchip/rk3588/overlay/frameworks/base/packages/SettingsProvider/res/values/defaults. xml 文件&#xff0c;如下图所示&#xff1a; - <integer name"def_screen_off_timeout">60000</integer> <integer name&q…

设计模式(一)

1、适配器模式 &#xff08;1&#xff09;概述 适配器中有一个适配器包装类Adapter&#xff0c;其包装的对象为适配者Adaptee&#xff0c;适配器作用就是将客户端请求转化为调用适配者中的接口&#xff1b;当调用适配器中的方法时&#xff0c;适配器内部会调用适配者类的方法…

【【萌新的STM32学习25--- USART寄存器的介绍】】

萌新的STM32学习25- USART寄存器的介绍 STM32–USART寄存器介绍&#xff08;F1&#xff09; 控制寄存器1 &#xff08;CR1&#xff09; 位13&#xff1a; 使能USART UE 0&#xff1a; USART分频器和输出被禁止 1&#xff1a; USART模块使能 位12 &#xff1a; 配置8个数据位…

分类算法系列③:模型选择与调优 (Facebook签到位置预测)

目录 模型选择与调优 1、介绍 模型选择&#xff08;Model Selection&#xff09;&#xff1a; 调优&#xff08;Hyperparameter Tuning&#xff09;&#xff1a; 本章重点 2、交叉验证 介绍 为什么需要交叉验证 数据处理 3、⭐超参数搜索-网格搜索(Grid Search) 介绍…

Maven报错 [ERROR] Malformed \uxxxx encoding.

IDEA刷新项目&#xff0c;报错[ERROR] Malformed \uxxxx encoding. 现象 1.控制台报错 [ERROR] Malformed \uxxxx encoding.2.项目代码大部分爆红 3.Pom文件不爆红 4.IDEA未能构建Dependencies 尝试清除IDEA缓存无效&#xff0c;重新克隆项目无效&#xff0c;更换低版本mav…

Vue2里监听localstorage里值的变化

有的时候,我们需要根据本地缓存在localstorage里值的变化做出相应的操作,这就需要我们监听localstorage: 首先,我们在src下的libs文件夹下新建一个stroage.js用于重写setItem事件,当使用setItem的时候,触发,window.dispatchEvent派发事件 const Stroage = {// 重写set…

考生作弊行为分析算法

考生作弊行为分析系统利用pythonyolo系列网络模型算法框架&#xff0c;考生作弊行为分析算法利用图像处理和智能算法对考生的行为进行分析和识别&#xff0c;经过算法服务器的复杂计算和逻辑判断&#xff0c;算法将根据考生行为的特征和规律&#xff0c;判定是否存在作弊行为。…

Git基础教程-常用命令整理:学会Git使用方法和错误解决

目录 一、了解Git的基本概念 二、Git的安装和配置 Git的安装 Git的配置 用户信息 文本编辑器 差异分析工具 查看配置信息 三、Git的基本操作 基本原理 基本操作命令 基本操作示例 场景一&#xff1a;创建新仓库 场景二&#xff1a;拉取并编辑远程仓库 四、常见问…

开源PHP 代挂机源码,可对接QQ、网易云、哔哩哔哩、QQ空间、等级加速等等

本程序运行环境PHP5.6 95dg/config.php修改系统数据库 进入数据库绑定 你搭建的域名即可 部署完成 进入数据库 找到data 输入绑定授权域名即可进行授权打开此网站 网站是无对接接口 需要您自行找对接接口即可 本源码有点乱 有实力的铁铁 可以修改一下哦&#xff01;

Spring MVC介绍

MVC模式是什么 MVC 模式&#xff0c;全称为 Model-View-Controller&#xff08;模型-视图-控制器&#xff09;模式&#xff0c;它是一种软件架构模式&#xff0c;其目标是将软件的用户界面&#xff08;即前台页面&#xff09;和业务逻辑分离&#xff0c;使代码具有更高的可扩展…

造测试数据

对应sql&#xff1a; from openpyxl import Workbook from faker import Faker# 创建一个Workbook对象 workbook Workbook() # 获取默认的活动工作表 sheet workbook.active# 创建一个Faker对象 fake Faker()# 写入表头 header [Name, Address, Email] sheet.append(heade…

ChatGPT 与 Python进行动态可视化分析

Python数据分析目前最为热门的岗位操作。 想使用Python进行可视化分析,但是又不想写代码,测试,验证。可以交给ChatGPT,open AI 来进行操作。 这样的动态图显示,我们只需要给ChatGPT发送一个指令,人工智能就能很快的实现这一操作。 请使用Python与Echarts做一个动态可视…

【LeetCode】3. 无重复字符的最长子串

3. 无重复字符的最长子串&#xff08;中等&#xff09; 方法&#xff1a;滑动窗口 哈希表 思路 这道题主要用到思路是&#xff1a;滑动窗口 什么是滑动窗口&#xff1f; 其实就是一个队列,比如例题中的 abcabcbb&#xff0c;进入这个队列&#xff08;窗口&#xff09;为 ab…

Vue安装过程的困惑解答——nodejs和vue关系、vue的项目结构

文章目录 一、为什么在使用vue前要下载nodejs&#xff1f;二、为什么安装nodejs后就能使用NPM包管理工具&#xff1f;三、为什么是V8引擎并且使用C实现&#xff1f;四、为什么会安装淘宝镜像&#xff1f;五、什么是webpack模板&#xff0c;为什么需要他&#xff1f;六、vue项目…

GIT命令只会抄却不理解?看完原理才能事半功倍!

系列文章目录 手把手教你安装Git&#xff0c;萌新迈向专业的必备一步 GIT命令只会抄却不理解&#xff1f;看完原理才能事半功倍&#xff01; 系列文章目录一、Git 的特征1. 文件系统2. 分布式 二、GIT的术语1. 区域术语2. 名词术语1. 提交对象2. 分支3. HEAD4. 标签&#xff0…

无涯教程-Android - Linear Layout函数

Android LinearLayout是一个视图组&#xff0c;该视图组将垂直或水平的所有子级对齐。 Linear Layout - 属性 以下是LinearLayout特有的重要属性- Sr.NoAttribute & 描述1 android:id 这是唯一标识布局的ID。 2 android:baselineAligned 此值必须是布尔值&#xff0c;为…

chatGPT训练过程

强化学习基础 强化学习是指智能体在不确定环境中最大化其获得的奖励从而达到自主决策的目的。其执行过程为&#xff1a;智能体依据策略决策从而执行动作&#xff0c;然后感知环境获取环境的状态&#xff0c;进而得到奖励(以便下次再到相同状态时能采取更优的动作)&#xff0c;…

(数学) 剑指 Offer 39. 数组中出现次数超过一半的数字 ——【Leetcode每日一题】

❓ 剑指 Offer 39. 数组中出现次数超过一半的数字 难度&#xff1a;简单 数组中有一个数字出现的次数超过数组长度的一半&#xff0c;请找出这个数字。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输…