RTEMS是如何运行第一个任务的?又是怎么样新建任务并进行切换的?
这要从RTEMS运行到boot_card开始说起,boot_card之前的初始化过程可参考之前的博文:
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中 新建一个task的函数就是 rtems_task_create: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功能相同。