RTEMS是如何运行第一个任务的?又是怎么样新建任务并进行切换的?
这要从RTEMS运行到boot_card开始说起,boot_card之前的初始化过程可参考之前的博文:
http://blog.youkuaiyun.com/hahachenchen789/article/details/52929513
boot_card函数位于c/src/lib/libbsp/shared/boot_card.c文件中,函数在调用完一些驱动和hooking之后,最后调用rtems_initialize_start_multitasking函数来进入初始化task。
void boot_card(
const char *cmdline
)
{
.......
/*
* Complete initialization of RTEMS and switch to the first task.
* Global C++ constructors will be executed in the context of that task.
*/
rtems_initialize_start_multitasking();
/***************************************************************
***************************************************************
* APPLICATION RUNS NOW!!! We will not return to here!!! *
***************************************************************
***************************************************************/
}
rtems_initialize_start_multitasking函数位于rtems/cpukit/sapi/exinit.c中:
void rtems_initialize_start_multitasking(void)
{
_System_state_Set( SYSTEM_STATE_UP );
_SMP_Request_start_multitasking();
_Thread_Start_multitasking();
/*******************************************************************
*******************************************************************
*******************************************************************
****** APPLICATION RUNS HERE ******
****** THE FUNCTION NEVER RETURNS ******
*******************************************************************
*******************************************************************
*******************************************************************/
}
那么通常第一个调用的task就是Init,这也是RTEMS的testsuites中大部分例子的第一个task。也就是用户的初始化任务,那么为了保证在其他应用任务启动前初始化任务能先启动,必须给它分配一个较高的优先级。
比如在rtems/testsuites/samples/base_sp中init.c中的函数init就是典型的例子:
rtems_task Init(
rtems_task_argument argument
)
{
rtems_name task_name;
rtems_id tid;
rtems_status_code status;
TEST_BEGIN();
printf( "Creating and starting an application task\n" );
task_name = rtems_build_name( 'T', 'A', '1', ' ' );
status = rtems_task_create( task_name, 1, RTEMS_MINIMUM_STACK_SIZE,
RTEMS_INTERRUPT_LEVEL(0), RTEMS_DEFAULT_ATTRIBUTES, &tid );
directive_failed( status, "create" );
status = rtems_task_start( tid, Application_task, ARGUMENT );
directive_failed( status, "start" );
status = rtems_task_delete( RTEMS_SELF );
directive_failed( status, "delete" );
}
首先用户初始化任务会创建并启动一个新的任务,然后把自身删除。然后就是运行这个新任务了。
那么用户的初始化任务,也就是init是如何开始运行的?
在cpukit/rtems/include/rtems/rtems/tasks.h中定义了系统的初始化任务表这样一个数据结构,包含了初始化任务的名字,栈的大小,初始的优先级,属性,入口点等参数。
typedef struct {
/** This is the Initialization Task's name. */
rtems_name name;
/** This is the Initialization Task's stack size. */
size_t stack_size;
/** This is the Initialization Task's priority. */
rtems_task_priority initial_priority;
/** This is the Initialization Task's attributes. */
rtems_attribute attribute_set;
/** This is the Initialization Task's entry point. */
rtems_task_entry entry_point;
/** This is the Initialization Task's initial mode. */
rtems_mode mode_set;
/** This is the Initialization Task's argument. */
rtems_task_argument argument;
} rtems_initialization_tasks_table;
在cpukit/sapi/include/confdefs.h中定义哥系统的初始化任务表,只包含一个初始化任务:
rtems_initialization_tasks_table Initialization_tasks[] = {
{ CONFIGURE_INIT_TASK_NAME,
CONFIGURE_INIT_TASK_STACK_SIZE,
CONFIGURE_INIT_TASK_PRIORITY,
CONFIGURE_INIT_TASK_ATTRIBUTES,
CONFIGURE_INIT_TASK_ENTRY_POINT,
CONFIGURE_INIT_TASK_INITIAL_MODES,
CONFIGURE_INIT_TASK_ARGUMENTS
}
};
其中CONFIGURE_INIT_TASK_ENTRY_POINT的定义如下:
#ifndef CONFIGURE_INIT_TASK_ENTRY_POINT
#ifdef __cplusplus
extern "C" {
#endif
rtems_task Init (rtems_task_argument );
#ifdef __cplusplus
}
#endif
#define CONFIGURE_INIT_TASK_ENTRY_POINT Init
extern const char* bsp_boot_cmdline;
#define CONFIGURE_INIT_TASK_ARGUMENTS ((rtems_task_argument) &bsp_boot_cmdline)
#endif
也就是说如果用户没有定义
CONFIGURE_INIT_TASK_ENTRY_POINT,则系统会将init作为默认的初始化任务的入口点。也就是转到了init函数。而且CONFIGURE_INIT_TASK_PRIORITY的值为1,优先级非常高,仅次于0,因此能够保证其优先执行。
ps:RTEMS共256个优先级(0-255).数字越小,优先级越高,要注意的是RTEMS与POSIX标准正好相反,所以使用RTEMS的标准时,会转化为POSIX的优先级,转化方法是Prtems=255-Pposix。
rtems_status_code rtems_task_create(
rtems_name name,
rtems_task_priority initial_priority,
size_t stack_size,
rtems_mode initial_modes,
rtems_attribute attribute_set,
rtems_id *id
);
可以利用该函数实现多线程。
注意:该函数从功能上类似于Unix中的fork和exec,但是不同的在于,Unix具有内存管理功能,fork和exec函数之后的父进程和子进程之间的地址空间是隔离的。但是RTEMS并不具有这种功能,因此更为准确的描述是该函数与POSIX 的thread功能相同。