对于任何一种操作系统,都有某种类型的启动机制。确切的工作方式因一个系统而异。通常说操作系统将“启动”。这是“bootstrap”的缩写,它描述了 CPU 如何从内存空缺,特别是稳定的程序执行状态。传统上,一小块软件被加载到内存中;它可以简单地保存在 ROM 中。在过去的几年里,它可能是通过计算机前面板上的开关键入的。这个“引导加载程序”会读入更复杂的引导程序,然后加载并启动操作系统。这是今天台式计算机启动的过程;BIOS 中的代码寻找可引导设备(硬盘驱动器或 CD-ROM),从中加载引导程序,从而加载操作系统。
嵌入式系统的操作系统也可以通过这种方式进行初始化。事实上,源自桌面操作系统的嵌入式操作系统正是这样做的。但对于大多数“经典”实时操作系统,使用了更简单(因此更快)的过程。
操作系统只是一个软件。如果该软件已经在内存中(例如以某种形式的 ROM),那么只需安排 CPU 的复位序列以执行操作系统的初始化代码结束即可。这是大多数 RTOS 的工作方式,Nucleus SE 也不例外。
大多数嵌入式软件开发工具包都包含必要的启动代码来处理 CPU 复位并到达main() 函数的入口点。Nucleus SE 分发代码与此过程无关,因为它旨在尽可能地可移植。相反,它提供了一个main() 函数,该函数控制 CPU 并初始化和启动操作系统;稍后将对此进行详细描述。
内存初始化
Nucleus SE 代码中所有静态变量的声明都以ROM 或RAM 为前缀,以指示它们可能位于何处。这两个#define 符号在nuse_types.h中定义, 应设置以适应正在使用的开发工具包(编译器和链接器)的功能。通常,ROM 可以设置为const 而RAM 留空。
所有ROM 变量都是静态初始化的,这是合乎逻辑的。没有 静态初始化RAM变量(因为这仅适用于某些工具包,这些工具包安排从 ROM 到 RAM 的自动复制);包含显式初始化代码,本文将详细介绍这些代码。
Nucleus SE 不会在 RAM 中保存任何“恒定”数据,这在小型系统中可能供不应求。不是使用复杂的数据结构来描述内核对象,而是采用了一系列表(数组),它们很容易在 ROM 或 RAM 中适当地定位。
main() 函数
以下是 Nucleus SE main() 函数的完整代码:
void main(void)
{
NUSE_Init(); /* 初始化内核数据 /
/ 用户初始化代码在这里 /
NUSE_Scheduler(); / 开始任务 */
}
操作顺序非常简单:
首先调用 NUSE_Init() 函数。这将初始化所有 Nucleus SE 数据结构,并在下面更详细地概述。
接下来,用户有机会插入任何特定于应用程序的初始化代码,这些代码将在任务调度程序启动之前执行。可以在本文后面找到有关此代码可以实现的内容的更多详细信息。
最后,启动 Nucleus SE 调度程序 ( NUSE_Scheduler() )。本文稍后还将对此进行更详细的研究。
NUSE_Init() 函数
此函数初始化所有 Nucleus SE 内核变量和数据结构。这是完整的代码:
void NUSE_Init(void)
{
U8 索引;
/* 全局数据 /
NUSE_Task_Active = 0;
NUSE_Task_State = NUSE_STARTUP_CONTEXT;
#if NUSE_SYSTEM_TIME_SUPPORT
NUSE_Tick_Clock = 0;
#endif
#if NUSE_SCHEDULER_TYPE == NUSE_TIME_SLICE_SCHEDULER
NUSE_Time_Slice_Ticks = NUSE_TIME_SLICE_TICKS;
#endif
/ 任务 /
#if ((NUSE_SCHEDULER_TYPE != NUSE_RUN_TO_COMPLETION_SCHEDULER)
|| NUSE_SIGNAL_SUPPORT || NUSE_TASK_SLEEP
|| NUSE_SUSPEND_ENABLE || NUSE_SCHEDULE_COUNT_SUPPORT)
for (index=0; index {
NUSE_Init_Task(index);
}
#endif
/ 分区池 /
#if NUSE_PARTITION_POOL_NUMBER != 0
for (index=0; index {
NUSE_Init_Partition_Pool(index);
}
#endif
/ 邮箱 /
#if NUSE_MAILBOX_NUMBER != 0
for (index=0; index {
NUSE_Init_Mailbox(index);
}
#endif
/ 队列 /
#if NUSE_QUEUE_NUMBER != 0
for (index=0; index {
NUSE_Init_Queue(索引);
}
#endif
/ 管道 /
#if NUSE_PIPE_NUMBER != 0
for (index=0; index {
NUSE_Init_Pipe(索引);
}
#endif
/ 信号量 /
#if NUSE_SEMAPHORE_NUMBER != 0
for (index=0; index {
NUSE_Init_Semaphore(index);
}
#endif
/ 事件组 /
#if NUSE_EVENT_GROUP_NUMBER != 0
for (index=0; index {
NUSE_Init_Event_Group(index);
}
#endif
/ 定时器 */
#if NUSE_TIMER_NUMBER != 0
for (index=0; index {
NUSE_Init_Timer(index);
}
#endif
}
首先,初始化一些全局变量:
NUSE_Task_Active—— 当前活动任务的索引——设置为零;这可能会在适当的时候由调度程序修改。
NUSE_Task_State 设置为NUSE_STARTUP_CONTEXT,这表明对任何后续应用程序初始化代码的 API 功能有限。
如果启用系统时间支持,NUSE_Tick_Clock 设置为零。
如果已启用时间片调度程序,则NUSE_Time_Slice_Ticks 将设置为配置的时间片值NUSE_TIME_SLICE_TICKS。
然后,调用一系列函数来初始化内核对象:
调用 NUSE_Init_Task() 来初始化每个任务的数据结构。只有在选择 Run to Completion 调度程序并且信号、任务挂起和调度计数都未配置时才省略此调用(因为这种组合将导致没有属于任务的 RAM 数据结构,因此没有初始化要完毕)。
调用 NUSE_Init_Partition_Pool() 来初始化每个分区池对象。如果未配置分区池,则省略调用。
调用 NUSE_Init_Mailbox() 来初始化每个邮箱对象。如果未配置邮箱,则省略呼叫。
调用 NUSE_Init_Queue() 来初始化每个队列对象。如果未配置队列,则省略调用。
调用 NUSE_Init_Pipe() 来初始化每个管道对象。如果没有配置管道,则省略调用。
调用 NUSE_Init_Semaphore() 来初始化每个信号量对象。如果未配置信号量,则省略调用。
调用 NUSE_Init_Event_Group() 来初始化每个事件组对象。如果未配置事件组,则省略调用。
调用 NUSE_Init_Timer() 来初始化每个计时器对象。如果没有配置定时器,则省略调用。
初始化任务
这是NUSE_Init_Task()的完整代码:
void NUSE_Init_Task(NUSE_TASK task)
{
#if NUSE_SCHEDULER_TYPE != NUSE_RUN_TO_COMPLETION_SCHEDULER
NUSE_Task_Context[task][15] = /* SR /
NUSE_STATUS_REGISTER;
NUSE_Task_Context[task][16] = / PC /
NUSE_Task_Start_Address[task];
NUSE_Task_Context[task][17] = / SP */
(U32 *)NUSE_Task_Stack_Base[task] +
NUSE_Task_Stack_Size[task];
#endif
#if NUSE_SIGNAL_SUPPORT || NUSE_INCLUDE_EVERYTHING
NUSE_Task_Signal_Flags[task] = 0;
#endif
#if NUSE_TASK_SLEEP || NUSE_INCLUDE_EVERYTHING
NUSE_Task_Timeout_Counter[任务] = 0;
#endif
#if NUSE_SUSPEND_ENABLE || NUSE_INCLUDE_EVERYTHING
#if NUSE_INITIAL_TASK_STATE_SUPPORT ||
NUSE_INCLUDE_EVERYTHING
NUSE_Task_Status[task] =
NUSE_Task_Initial_State[task];
#else
NUSE_Task_Status[task] = NUSE_READY;
#endif
#endif
#if NUSE_SCHEDULE_COUNT_SUPPORT || NUSE_INCLUDE_EVERYTHING
NUSE_Task_Schedule_Count[task] = 0;
#endif
}
除非已配置 Run to Completion 调度程序,否则任务的上下文块 – NUSE_Task_Context[task][] – 将被初始化。大多数条目没有设置值,因为它们代表通用机器寄存器,在任务启动时假定这些寄存器具有不确定的值。在 Nucleus SE 的示例 (Freescale ColdFire) 实现中(这对于任何处理器都是类似的),最后三个条目是明确设置的:
NUSE_Task_Context[task][15] 保存状态寄存器 ( SR ) 并设置为#define 符号NUSE_STATUS_REGISTER 中的值。
NUSE_Task_Context[task][16] 保存程序计数器(PC)并设置为任务代码入口点的地址:NUSE_Task_Start_Address[task]。
NUSE_Task_Context[task][17] 保存堆栈指针 (SP),它被初始化为通过将任务的堆栈基地址 ( NUSE_Task_Stack_Base[task] ) 添加到任务的堆栈大小 ( NUSE_Task_Stack_Size[task] ) 计算出的值。
如果启用了信号支持,则任务的信号标志 ( NUSE_Task_Signal_Flags[task] ) 将设置为零。
如果启用了任务睡眠(即 API 调用NUSE_Task_Sleep()),则任务的超时计数器(NUSE_Task_Timeout_Counter[task])将设置为零。
如果启用了任务挂起,则任务的状态 ( NUSE_Task_Status[task] ) 将被初始化。如果启用了任务初始任务状态支持,则此初始值是用户指定的(在NUSE_Task_Initial_State[task] 中)。否则状态设置为NUSE_READY。
如果启用了任务计划计数,则任务的计数器 ( NUSE_Task_Schedule_Count[task] ) 将设置为零。
初始化分区池
void NUSE_Init_Partition_Pool(NUSE_PARTITION_POOL pool)
{
NUSE_Partition_Pool_Partition_Used[pool] = 0;
#if NUSE_BLOCKING_ENABLE
NUSE_Partition_Pool_Blocking_Count[pool] = 0;
#endif
}
相关实战:https://www.yunduoketang.com/article/wangxiaozhiboxitong1.html
https://www.yunduoketang.com/article/wangxiaozhiboxitong1.html
https://www.yunduoketang.com/article/wangxiaozhiboxitong1.html
https://www.yunduoketang.com/article/xianshangzhiboruanjian3.html
https://www.yunduoketang.com/article/xianshangzhibodajian1.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/37240.html