RTEMS是以线程为基本调度单位的,调度算法基于优先级的抢占式线程调度,支持256个线程优先级。当然RTEMS值hi创建同等优先级线程,相同优先级的线程采用时间片轮转调度。调度器寻找下一个最高优先级就绪线程的时间是o(1),这是实时性得到保障的一个关键机制。
为了增强对用户应用需求的可扩展性,rtems实现了基于线程的任务扩展机制,可在创建任务、上下文切换或者删除任务等系统事件发生时插入用户指定的函数(称为钩子函数,hook函数),以完成用户设定的特殊功能。
对于RTEMS这样的基于抢占式和优先级策略调度的os而言,为保证硬实时特性,需要选择一个合适的调度算法,静态调度算法-速率单调调度RMS是为周期性任务解决多任务调度的一种很好的算法。
本文会先介绍核心层的thread handler组件与调度实现,然后对RMS算法在RTEMS上的实现进行分析。
RTEMS的调度相对于通用操作系统而言较为简单,主要是基于线程的优先级进行的,除了对于优先级反转问题需要在运行时改变线程的优先级外,其他情况下不会对线程的优先级修改。
thread handler组件提供了thread_control结构体和以此配套的一组基本线程管理数据结构和管理函数,涉及线程初始化,线程启动,线程暂停,线程恢复,线程切换等。不同的高层API接口实现只是对这些结构和函数进行封装,这样线程管理就可以分为两层,方便API接口进行拓展。
thread control block是thread handler组件对线程的静态进本情况和动态运行变化过程的描述,其包含了与线程运行管理所需要的所有信息集合,是线程存在的唯一标志。线程的创建主要体现在为该线程生成一个TCB,而线程终止则体现在回收其TCB。
TCB定义在cpukit/score/include/rtems/score/thread.h中,如下:
/**
* This structure defines the Thread Control Block (TCB).
*/
struct Thread_Control_struct {
/** This field is the object management structure for each thread. */
Objects_Control Object;
/** This field is used to enqueue the thread on RBTrees. */
RBTree_Node RBNode;
/** This field is the current execution state of this thread. */
States_Control current_state;
/** This field is the current priority state of this thread. */
Priority_Control current_priority;
/** This field is the base priority of this thread. */
Priority_Control real_priority;
/**
* @brief Thread priority control.
*/
Thread_Priority_control Priority;
/** This field is the number of mutexes currently held by this thread. */
uint32_t resource_count;
/** This field is the blocking information for this thread. */
Thread_Wait_information Wait;
/** This field is the Watchdog used to manage thread delays and timeouts. */
Watchdog_Control Timer;
#if defined(RTEMS_MULTIPROCESSING)
/** This field is the received response packet in an MP system. */
MP_packet_Prefix *receive_packet;
#endif
/*================= end of common block =================*/
#if defined(RTEMS_SMP)
/**
* @brief Thread lock control.
*/
Thread_Lock_control Lock;
#endif
#ifdef __RTEMS_STRICT_ORDER_MUTEX__
/** This field is the head of queue of priority inheritance mutex
* held by the thread.
*/
Chain_Control lock_mutex;
#endif
#if defined(RTEMS_SMP)
/**
* @brief Resource node to build a dependency tree in case this thread owns
* resources or depends on a resource.
*/
Resource_Node Resource_node;
#endif
#if defined(RTEMS_MULTIPROCESSING)
/** This field is true if the thread is offered globally */
bool is_global;
#endif
/** This field is true if the thread is preemptible. */
bool is_preemptible;
/**
* @brief Scheduler related control.
*/
Thread_Scheduler_control Scheduler;
#if __RTEMS_ADA__
/** This field is the GNAT self context pointer. */
void *rtems_ada_self;
#endif
/** This field is the length of the time quantum that this thread is
* allowed to consume. The algorithm used to manage limits on CPU usage
* is specified by budget_algorithm.
*/
uint32_t cpu_time_budget;
/** This field is the algorithm used to manage this thread's time
* quantum. The algorithm may be specified as none which case,
* no limit is in place.
*/
Thread_CPU_budget_algorithms budget_algorithm;
/** This field is the method invoked with the budgeted time is consumed. */
Thread_CPU_budget_algorithm_callout budget_callout;
/** This field is the amount of CPU time consumed by this thread
* since it was created.
*/
Thread_CPU_usage_t cpu_time_used;
/** This field contains information about the starting state of
* this thread.
*/
Thread_Start_information Start;
Thread_Action_control Post_switch_actions;
/** This field contains the context of this thread. */
Context_Control Registers;
#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
/** This field points to the floating point context for this thread.
* If NULL, the thread is integer only.
*/
Context_Control_fp *fp_context;
#endif
/** This field points to the newlib reentrancy structure for this thread. */
struct _reent *libc_reent;
/** This array contains the API extension area pointers. */
void *API_Extensions[ THREAD_API_LAST + 1 ];
#if !defined(RTEMS_SMP)
/** This field points to the set of per task variables. */
rtems_task_variable_t *task_variables;
#endif
/**
* This is the thread key value chain's control, which is used
* to track all key value for specific thread, and when thread
* exits, we can remove all key value for specific thread by
* iterating this chain, or we have to search a whole rbtree,
* which is inefficient.
*/
Chain_Control Key_Chain;
/**
* @brief Thread life-cycle control.
*
* Control state changes triggered by thread restart and delete requests.
*/
Thread_Life_control Life;
Thread_Capture_control Capture;
/**
* @brief Variable length array of user extension pointers.
*
* The length is defined by the application via <rtems/confdefs.h>.
*/
void *extensions[ RTEMS_ZERO_LENGTH_ARRAY ];
};
其中解释几个重要的成员变量:
object是线程作为对象而存在的对象管理结构,也就是说TCB结构是作为一个对象。
real_priority和current_priority是线程的基础优先级和当前运行的优先级。
resource_count是代表线程获取到的资源(互斥量)数量,是为了实现优先级继承算法和优先级天花板算法而设计的。
cpu_time_budget是分配给线程的时间片,而budget_algorithm是用来管理该时间片的算法。RTEMS支持如下四种时间片算法配置:
a. THREAD_CPU_BUDGET_ALGORITHM_NONE //不使用budget算法
b. THREAD_CPU_BUDGET_ALGORITHM_RESET_TIMESLICE //线程切换时重新设置时间片
c. THREAD_CPU_BUDGET_ALGORITHM_EXHAUST_TIMESLICE //线程时用完时间片才重置
d. THREAD_CPU_BUDGET_ALGORITHM_CALLOUT //时间片用完时调用回调函数
运行的线程变为就绪或者阻塞时,都需要保存当前进程的上下文。线程的上下文是和硬件的体系结构密切相关,RTEMS中,ARM的上下文定义如下:
typedef struct {
#if defined(ARM_MULTILIB_ARCH_V4)
uint32_t register_cpsr;
uint32_t register_r4;
uint32_t register_r5;
uint32_t register_r6;
uint32_t register_r7;
uint32_t register_r8;
uint32_t register_r9;
uint32_t register_r10;
uint32_t register_fp;
uint32_t register_sp;
uint32_t register_lr;
#elif defined(ARM_MULTILIB_ARCH_V6M) || defined(ARM_MULTILIB_ARCH_V7M)
uint32_t register_r4;
uint32_t register_r5;
uint32_t register_r6;
uint32_t register_r7;
uint32_t register_r8;
uint32_t register_r9;
uint32_t register_r10;
uint32_t register_r11;
void *register_lr;
void *register_sp;
uint32_t isr_nest_level;
#else
void *register_sp;
#endif
#ifdef ARM_MULTILIB_HAS_THREAD_ID_REGISTER
uint32_t thread_id;
#endif
#ifdef ARM_MULTILIB_VFP
uint64_t register_d8;
uint64_t register_d9;
uint64_t register_d10;
uint64_t register_d11;
uint64_t register_d12;
uint64_t register_d13;
uint64_t register_d14;
uint64_t register_d15;
#endif
#ifdef RTEMS_SMP
volatile bool is_executing;
#endif
} Context_Control;
TCB中的Registers就是上下文的变量。
每个优先级的就绪的线程都是一个FIFO链表,TCB中会包含该链表,便于线程调度时的操作。_thread_ready_chain就是由各个优先级的FIFO构成的数组,这是一个全局变量。
chain_control结构是对FIFO链表进行管理的数据结构,包含了链表的头和尾,这样可以很快确定每个优先级的第一个线程和最后一个。但问题在于,不容易确定下一个优先级最高的线程,如果切换出来的线程所在的优先级只有一个就绪线程,那么就需要在_thread_ready_chain数组中找到优先级最高的就绪线程,这是一个耗时的操作,因此RTEMS采用位图的方法解决了这个问题。它把256个优先级分为16*16,这样的话,就相当于两个16位整数就可以表示了,也就是两个16位长的二进制数组。这样提高了搜索速度。这样一个priority优先级可以分为major(priority/16)和minor(priority%16)两部分。major*16+minor.
a. 所有任务都是周期任务
b. 任务的相对截止时间等于任务周期
c.任务在每个周期的计算时间都相等
d.任务之间不进行通信,也不需要同步,是独立的
e.任何可以在计算的任何位置被抢占
任务的优先级和任务的周期表吓你为单调函数关系,任务周期越短,任务优先级越高。静态是指任务的优先级是事前确定的,在任务运行过程中不会改变。
typedef struct {
/** This field is the object management portion of a Period instance. */
Objects_Control Object;
/**
* @brief Protects the rate monotonic period state.
*/
ISR_LOCK_MEMBER( Lock )
/** This is the timer used to provide the unblocking mechanism. */
Watchdog_Control Timer;
/** This field indicates the current state of the period. */
rtems_rate_monotonic_period_states state;
/**
* @brief A priority node for use by the scheduler job release and cancel
* operations.
*/
Priority_Node Priority;
/**
* This field contains the length of the next period to be
* executed.
*/
uint32_t next_length;
/**
* This field contains a pointer to the TCB for the thread
* which owns and uses this period instance.
*/
Thread_Control *owner;
/**
* This field contains the cpu usage value of the owning thread when
* the period was initiated. It is used to compute the period's
* statistics.
*/
Timestamp_Control cpu_usage_period_initiated;
/**
* This field contains the wall time value when the period
* was initiated. It is used to compute the period's statistics.
*/
Timestamp_Control time_period_initiated;
/**
* This field contains the statistics maintained for the period.
*/
Rate_monotonic_Statistics Statistics;
/**
* This field contains the number of postponed jobs.
* When the watchdog timeout, this variable will be increased immediately.
*/
uint32_t postponed_jobs;
/**
* This field contains the tick of the latest deadline decided by the period
* watchdog.
*/
uint64_t latest_deadline;
} Rate_monotonic_Control;
Watchdog_Control 是和这个结构体关联的定时器
rtems_rate_monotonic_period_states RMS的状态
对于RMS,RTEMS提供了以下API:
rtems_rate_monotonic_create 创建一个RMS
rtems_rate_monotonic_cancel 取消一个RMS
rtems_rate_monotonic_delete 删除一个RMS
rtems_rate_monotonic_get_status 获得RMS状态
rtems_rate_monotonic_period 核心函数,用于启动一个周期,在这里发生RMS状态转换
程序调用rtems_rate_monotonic_period ,此时系统认为线程开始一项周期任务,且周期长度为length,记这一时刻time_at_period。当线程的执行流到达下一个rtems_rate_monotonic_period语句时(在一般程序中,是回到之前调用的那句rtems_rate_monotonic_period ),系统会判断从time_at_period开始到现在是否经过了length个tick,如果超过了,重新开始一个周期。通常是不会超过的,此时这个线程会被阻塞,等待下一周期的开始,知道系统时刻确实到达time_at_period+length。线程会被唤醒。
RTMES/testsuites/sptests/sp32中包含了对RMS的测试用例。其功能是让任务运行五个确定的周期,在每个周期的开始和结束记录时间,然后相减,看是否与指定的周期长度相等。