学校内部网站开发价格,上海推广网站,wordpress 微博分享插件,ui培训设计培训班线程管理
在日常生活中#xff0c;我们要完成一个大任务#xff0c;一般会将它分解成多个简单、容易解决的小问题#xff0c;小问题逐个被解决#xff0c;大问题也就随之解决了。
在多线程操作系统中#xff0c;也同样需要开发人员把一个复杂的应用分解成多个小的、可调…线程管理
在日常生活中我们要完成一个大任务一般会将它分解成多个简单、容易解决的小问题小问题逐个被解决大问题也就随之解决了。
在多线程操作系统中也同样需要开发人员把一个复杂的应用分解成多个小的、可调度的、序列化的程序单元当合理地划分任务并正确地执行时这种设计能够让系统满足实时系统的性能及时间的要求。
例如让嵌入式系统执行这样的任务系统通过传感器采集数据并通过显示屏将数据显示出来在多线程实时系统中可以将这个任务分解成两个子任务。 如图所示一个子任务不间断地读取传感器数据并将数据写到共享内存中另外一个子任务周期性地从共享内存中读取数据并将传感器数据输出到显示屏上。 在RT-Thread中与上述子任务对应的程序实体就是线程线程是实现任务的载体它是RT-Thread中最基本的调度单位它描述了一个任务执行的运行环境也描述了这个任务所处的优先等级重要的任务可设置相对较高的优先级非重要的任务可以设置较低的优先级不同的任务还可以设置相同的优先级轮流运行。
当线程运行时它会认为自己是以独占CPU的方式在运行。 线程执行时的运行环境称为上下文具体来说就是各个变量和数据包括所有的寄存器变量、堆栈、内存信息等。
线程管理的功能特点
RT-Thread线程管理的主要功能是对线程进行管理和调度系统中共存在两类线程分别是系统线程和用户线程。
系统线程是由内核创建的线程用户线程是由应用程序创建的线程
这两类线程都会从内核对象容器中分配线程对象当线程被删除时也会被从对象容器中删除。 如图所示每个线程都有重要的属性如线程控制块线程栈入口函数等。
RT-Thread线程调度器是抢占式的主要的工作就是从就绪线程列表中查找最高优先级线程保证最高优先级的线程能够被运行最高优先级的任务一旦就绪总能得到CPU的使用权。
当一个运行着的线程使一个比它优先级高的线程满足运行条件当前线程的 CPU 使用权就被剥夺了或者说被让出了高优先级的线程立刻得到了 CPU 的使用权。
如果是中断服务程序使一个高优先级的线程满足运行条件中断完成时被中断的线程挂起优先级高的线程开始运行。
当调度器调度线程切换时先将当前线程上下文保存起来当再切回到这个线程时线程调度器将该线程的上下文信息恢复。
线程控制块
线程控制块由结构体struct 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字节也可以在初始时设置较大的栈例如指定大小为1K或2K字节然后在FinSH中用list_thread命令查看线程运行过程中线程所使用的栈的大小通过此命令能够看到从线程启动运行时到当前时刻点线程使用的最大栈深度而后加上适当的余量形成最终的线程栈大小最后对栈空间大小加以修改。
线程状态
线程运行的过程中同一时间只允许一个线程在处理器中运行从运行的过程上划分线程有多种不同的运行状态如初始状态、挂起状态、就绪状态等。
初始状态当线程刚开始创建还没开始运行时就处于初始状态在初始状态下线程不参与调度。就绪状态在就绪状态下线程按照优先级排队等待被执行一旦当前线程运行完毕让出处理器操作系统会马上寻找最高优先级的就绪态线程运行。运行状态线程当前正在运行。在单核系统中只有rt_thread_self()函数返回的线程处于运行状态在多核系统中可能就不止这一个线程处于运行状态。挂起状态也称阻塞态。它可能因为资源不可用而挂起等待或线程主动延时一段时间而挂起。在挂起状态下线程不参与调度。关闭状态当前线程运行结束时处于关闭状态。关闭状态的线程不参与线程的调度。
线程优先级
RT-Thread 线程的优先级是表示线程被调度的优先程度。每个线程都具有优先级线程越重要赋予的优先级就应越高线程被调度的可能才会越大。
RTT最大支持256个线程优先级0~255数值越小的优先级越高0为最高优先级。 在一些资源比较紧张的系统中可以根据实际情况只选择8个或32个优先级的系统配置。 对于ARM Cortex-M系列普遍采用32个优先级。 最低优先级默认分配给空闲线程使用用户一般不使用。
在系统中当有比当前线程优先级更高的线程就绪时当前线程将立刻被换出高优先级线程抢占处理器运行。
时间片
每个线程都有时间片这个参数但时间片仅对优先级相同的就绪态线程有效。 系统对优先级相同的就绪态线程采用时间片轮转的调度方式进行调度时时间片起到约束线程单次运行时长的作用其单位是一个系统节拍 OS Tick。 假设有2个优先级相同的就绪态线程A与BA线程的时间片设置为10B线程的时间片设置为5那么当系统中不存在比 A 优先级高的就绪态线程时系统会在 A、B 线程间来回切换执行并且每次对 A 线程执行 10 个节拍的时长对 B 线程执行 5 个节拍的时长如下图。
线程的入口函数
线程控制块中的entry是线程的入口函数它是线程实现预期功能的函数。线程的入口函数由用户设计实现一般有以下两种代码形式
无限循环模式 在实时系统中线程通常是被动式的这个是由实时系统的特性所决定的实时系统总是等待外界事件的发生而后进行相应的服务
void thread_entry(void *parameter)
{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_create/init()进入到初始状态初始状态的线程通过调用函数startup()进入到就绪状态就绪状态的线程被调度器调度后进入运行状态当处于运行状态的线程调用 rt_thread_delay()rt_sem_take()rt_mutex_take()rt_mb_recv() 等函数或者获取不到资源时将进入到挂起状态处于挂起状态的线程如果等待超时依然未能获得资源或由于其它线程释放了资源那么它将返回到就绪状态。 挂起状态的线程如果调用 rt_thread_delete/detach() 函数将更改为关闭状态RT_THREAD_CLOSE而运行状态的线程如果运行结束就会在线程的最后部分执行 rt_thread_exit() 函数将状态更改为关闭状态。
系统线程
系统线程是指由系统创建的线程用户线程是由用户程序调用线程管理接口创建的线程在 RT-Thread 内核中的系统线程有空闲线程和主线程。
空闲线程
空闲线程idle是系统创建的最低优先级的线程线程状态永远为就绪态。当系统中无其他就绪线程存在时调度器将调度空闲线程它通常是一个死循环且永远不能被挂起。
若某线程运行完毕系统将自动删除线程自动执行rt_thread_exit()函数先将该线程从系统就绪队列中删除再将该线程的状态更改为关闭状态不再参与系统调度然后挂入rt_thread_defunct僵尸队列资源未回收处于关闭状态的线程队列中最后空闲线程会回收被删除线程的资源。
空闲线程也提供了接口来运行用户设置的钩子函数在空闲线程运行时会调用钩子函数适合处理功耗管理、看门狗喂狗等工作。 空闲线程必须有得到执行的机会即其他线程不允许一直while(1)死卡必须调用具有阻塞性质的函数否则例如线程删除、回收等操作将无法得到正确执行。
主线程
在系统启动时系统会创建main线程它的入口函数为main_thread_entry()用户的应用入口函数main()就是从这里真正开始的系统调度器启动后main 线程就开始运行过程如下图用户可以在 main() 函数里添加自己的应用程序初始化代码。