赞
踩
RT-Thread的线程可认为是一系列独立线程的集合。每个线程在自己的环境中运行。在任何时刻,只有一个线程得到运行
,RT-Thread调度器决定运行哪个线程。调度器会不断启动、停止每一个线程,宏观看上去所有的线程都在同时在执行。作为线程,不需要对调度器的活动有所了解,在线程切入切出
时保存上下文环境
(寄存器值、堆栈内容)是调度器
主要的职责。为了实现这点,每个RT-Thread线程都需要有自己的堆栈
。当线程切出时,它的执行环境会被保存在该线程的堆栈
中,这样当线程再次运行时,就能从堆栈中正确的恢复
上次的运行环境。
RT-Thread中的线程是抢占式调度机制
,同时支持时间片轮转
调度方式。高优先级的线程可打断低优先级线程,低优先级线程必须在高优先级线程阻塞或结束后才能得到调度。
RT-Thread 的线程调度器是抢占式的,主要的工作就是从就绪线程列表中查找最高优先级线程,保证最高优先级的线程能够被运行,最高优先级的任务一旦就绪,总能得到 CPU 的使用权
。
当一个运行着的线程使一个比它优先级高的线程满足运行条件,当前线程的 CPU 使用权就被剥夺了,或者说被让出了,高优先级的线程立刻得到了 CPU 的使用权。
如果是中断服务
程序使一个高优先级的线程满足运行条件,中断完成时,被中断的线程挂起,优先级高的线程开始运行
。
当调度器调度线程切换时,先将当前线程上下文保存起来,当再切回到这个线程时,线程调度器将该线程的上下文信息恢复。
RT-Thread系统中的每一线程都有多种运行状态。系统初始化完成后,创建的线程就可以在系统中竞争一定的资源,由内核进行调度。
线程状态通常分为以下五种:
创建线程
的时候会将线程的状态设置为初始态。就绪列表
中,就绪的线程已经具备执行的能力
,只等待CPU。正在执行
,此时它占用处理器。等待某个时序或外部中断
,我们就说这个线程处于挂起状态,该线程不在就绪列表中。包含线程被挂起、线程被延时、线程正在等待信号量、读写队列或者等待读写事件等。运行结束
,等待系统回收资源。
状态转化:
线程启动
的时候(调用rt_thread_startup()函数)会将初始态转变为就绪态,表明线程已启动,线程可以进行调度。线程切换
时,就绪列表中最高优先级的线程被执行,从而进入运行态。运行
的线程发生阻塞
(挂起、延时、读信号量等待)时,该线程会从就绪列表中删除,线程状态由运行态变成挂起态,然后发生线程切换,运行就绪列表中最高优先级线程。恢复
后(线程恢复、延时时间超时、读信号量超时或读到信号量等),此时被恢复的线程会被加入就绪列表
,从而由挂起态变成就绪态;此时如果被恢复线程的优先级高于正在运行线程
的优先级,则会发生线程切换
,将该线程由就绪态变成运行态。更高优先级线程创建或者恢复
后,会发生线程调度,此刻就绪列表中最高优先级线程变为运行态,那么原先运行的线程由运行态变为就绪态,依然在就绪列表中。rt_thread_create
// 1. 分配线程结构体
thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread,name);
// 2. 分配栈
stack_start = (void *)RT_KERNEL_MALLOC(stack_size);
// 3. 初始化栈,即构造栈的内容
_rt_thread_init
// 3.1 具体操作
thread->sp = (void *)rt_hw_stack_init
线程挂起可以由多种方法实现:线程调用rt_thread_delay()、rt_thread_suspend()等函数接口可以使得线程主动挂起,放弃CPU使用权,当线程调用rt_sem_take(),rt_mb_recv()等函数时,资源不可使用也会导致调用线程被动挂起。
注:通常不应该使用这个函数来挂起线程本身,如果确实需要采用rt_thread_suspend函数挂起当前线程,需要在调用rt_thread_suspend()函数后立刻调用rt_schedule()函数进行手动的线程上下文切换
。
中断服务函数是一种需要特别注意的上下文环境,它运行在非线程的执行环境下(一般为芯片的一种特殊运行模式(也被称作特权模式)),在这个上下文环境中
不能使用挂起当前线程的操作
,不允许
调用任何会阻塞运行
的API函数接口。另外需要注意的是,中断服务程序最好保持精简短小,快进快出
,一般在中断服务函数中只做标记事件的发生,让对应线程去执行相关处理,因为中断服务函数的优先级高于任何优先级的线程,如果中断处理时间过长,将会导致整个系统的线程无法正常运行。所以在设计的时候必须考虑中断的频率、中断的处理时间等重要因素,以便配合对应中断处理线程的工作。
线程看似没有什么限制程序执行的因素,似乎所有的操作都可以执行。但是做为一个优先级明确的实时系统,
如果一个线程中的程序出现了死循环操作(此处的死循环是指没有不带阻塞机制的线程循环体),那么比这个线程优先级低的线程都将无法执行
,当然也包括了空闲线程,因为死循环的时候,线程不会主动让出CPU,低优先级的线程是不可能得到CPU的使用权的,而高优先级的线程就可以抢占CPU。这个情况在实时操作系统中是必须注意的一点,所以在线程中不允许出现死循环。如果一个线程只有就绪态而无阻塞态,势必会影响到其他低优先级线程的执行
,所以在进行线程设计时,就应该保证线程在不活跃的时候,线程可以进入阻塞态以交出CPU使用权,这就需要我们自己明确知道什么情况下让线程进入阻塞态,保证低优先级线程可以正常运行。在实际设计中,一般会将紧急的处理事件的线程优先级设置得高一些。
空闲线程(idle线程)是RT-Thread系统中没有其他工作进行时自动进入的系统线程。用户可以通过空闲线程钩子方式,在空闲线程上钩入自己的功能函数。通常这个空闲线程钩子能够完成一些额外的特殊功能,例如系统运行状态的指示,系统省电模式等。除了空闲线程钩子,RT-Thread系统还把空闲线程用于一些其他的功能,比如当系统删除一个线程或一个动态线程运行结束时,会先行更改线程状态为非调度状态,然后挂入一个待回收队列中,真正的系统资源回收工作在空闲线程完成,
空闲线程是唯一不允许出现阻塞情况的线程,因为RT-Thread需要保证系统用于都有一个可运行的线程
。
对于空闲线程钩子上挂接的空闲钩子函数,它应该满足以下的条件:
-不会挂起空闲线程;
-不应该陷入死循环,需要留出部分时间用于系统处理系统资源回收。
线程的执行时间一般是指两个方面,一是线程从开始到结束
的时间,二是线程的周期
。
在系统设计的时候这两个时间候我们都需要考虑,例如,对于事件A对应的服务线程Ta,系统要求的实时响应指标是10ms,而Ta的最大运行时间是1ms,那么10ms就是线程Ta的周期了,1ms则是线程的运行时间,简单来说线程Ta在10ms内完成对事件A的响应即可。此时,系统中还存在着以50ms为周期的另一线程Tb,它每次运行的最大时间长度是100us。在这种情况下,即使把线程Tb的优先级抬到比Ta更高的位置,对系统的实时性指标也没什么影响,因为即使在Ta的运行过程中,Tb抢占了Ta的资源,等到Tb执行完毕,消耗的时间也只不过是100us,还是在事件A规定的响应时间内(10ms),Ta能够安全完成对事件A的响应。但是假如系统中还存在线程Tc,其运行时间为20ms,假如将Tc的优先级设置比Ta更高,那么在Ta运行的时候,突然间被Tc打断,等到Tc执行完毕,那Ta已经错过对事件A(10ms)的响应了,这是不允许的。所以在我们设计的时候,必须考虑线程的时间,一般来说处理时间更短的线程优先级应设置更高一些。
详细API可查询RT-Thread官网手册。
本节笔记参考于:野火-《RT-Thread内核实现与应用开发实战》
以及RT-Thread官网:线程管理
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。