Nucleus SE RTOS初始化和启动
Nucleus SE RTOS初始化和启动
Nucleus SE RTOS initialization and start-up
对于任何类型的操作系统,都有某种类型的启动机制。具体的工作方式因系统而异。通常说操作系统会“启动”。这是“bootstrap”的缩写,它描述了CPU如何从一个完全没有内存的内存中获得稳定的程序执行状态。传统上,一小片软件被加载到内存中;它可以简单地保存在ROM中。在过去,它可能是通过电脑前面板上的开关输入的。这个“引导加载器”会读入一个更复杂的引导程序,而这个程序又会加载并启动操作系统。这就是桌面计算机今天开始运行的过程;BIOS中的代码寻找可引导的设备(硬盘驱动器或CD-rom),从中可以加载引导程序,从而加载操作系统。
嵌入式系统的操作系统也可以用这种方式初始化。实际上,源于桌面操作系统的嵌入式操作系统正是这样做的。但对于大多数“经典”rtos,使用的是更简单(因此更快)的过程。
操作系统只是一个软件。如果这个软件已经在内存中了——比如说某种形式的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中保存任何“恒定”的数据,在小型系统中,RAM可能供不应求。不使用复杂的数据结构来描述内核对象,而是使用一系列表(数组),这些表(数组)可以很容易地在ROM或RAM中找到。
main()函数
以下是Nucleus SE main()函数的完整代码
void main(void)
{
NUSE_Init(); /* initialize kernel
data */
/* user initialization
code here */
NUSE_Scheduler(); /* start tasks */
}
操作顺序非常简单:
首先调用NUSE_Init()函数。这将初始化所有Nucleus SE数据结构,并在下面详细介绍。
接下来,用户可以插入任何特定于应用程序的初始化代码,这些代码将在任务调度器启动之前执行。关于这段代码可以实现什么的更多细节可以在本文后面找到。
最后,启动Nucleus SE调度程序(NUSE_scheduler())。本文后面还将更详细地研究这一点。
The NUSE_Init() Function
此函数用于初始化所有Nucleus SE内核变量和数据结构。以下是完整代码:
void NUSE_Init(void)
{
U8 index;
/* global data */
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
/* tasks */
#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_task_number; index++)="">
{
NUSE_Init_Task(index);
}
#endif
/* partition pools */
#if NUSE_PARTITION_POOL_NUMBER != 0
for (index=0; index
<nuse_partition_pool_number; index++)="">
{
NUSE_Init_Partition_Pool(index);
}
#endif
/* mailboxes */
#if NUSE_MAILBOX_NUMBER != 0
for (index=0; index
<nuse_mailbox_number; index++)="">
{
NUSE_Init_Mailbox(index);
}
#endif
/* queues */
#if NUSE_QUEUE_NUMBER != 0
for (index=0; index
<nuse_queue_number; index++)="">
{
NUSE_Init_Queue(index);
}
#endif
/* pipes */
#if NUSE_PIPE_NUMBER != 0
for (index=0; index
<nuse_pipe_number; index++)="">
{
NUSE_Init_Pipe(index);
}
#endif
/* semaphores */
#if NUSE_SEMAPHORE_NUMBER != 0
for (index=0; index
<nuse_semaphore_number; index++)="">
{
NUSE_Init_Semaphore(index);
}
#endif
/* event groups */
#if NUSE_EVENT_GROUP_NUMBER != 0
for (index=0; index
<nuse_event_group_number; index++)="">
{
NUSE_Init_Event_Group(index);
}
#endif
/* timers */
#if NUSE_TIMER_NUMBER != 0
for (index=0; index
<nuse_timer_number; index++)="">
{
NUSE_Init_Timer(index);
}
#endif
}
First, some global variables are initialized:
- NUSE_Task_Active – the index of the currently active task – is set to zero;
this may be modified by the scheduler in due course. - NUSE_Task_State is set to NUSE_STARTUP_CONTEXT ,
which indicates the limited API functionality to any following application
initialization code. - If system time support is
enabled, NUSE_Tick_Clock is
set to zero. - If the time slice
scheduler has been enabled, NUSE_Time_Slice_Ticks is
set up to the configured time slice value, NUSE_TIME_SLICE_TICKS .
Then, a series of functions are called to initialize kernel
objects:
- NUSE_Init_Task() is called to initialize data structures for each task.
This call is only omitted if the Run to Completion scheduler is selected and
signals, task suspend, and schedule counting are all not configured (as this
combination would result in there being no RAM data structures appertaining to
tasks and, hence, no initialization to be done). - NUSE_Init_Partition_Pool() is called to initialize each partition pool object. The
calls are omitted if no partition pools have been configured. - NUSE_Init_Mailbox() is called to initialize each mailbox object. The calls are
omitted if no mailboxes have been configured. - NUSE_Init_Queue() is called to initialize each queue object. The calls are
omitted if no queues have been configured. - NUSE_Init_Pipe() is called to initialize each pipe object. The calls are
omitted if no pipes have been configured. - NUSE_Init_Semaphore() is called to initialize each semaphore object. The calls
are omitted if no semaphores have been configured. - NUSE_Init_Event_Group() is called to initialize each event group object. The calls
are omitted if no event groups have been configured. - NUSE_Init_Timer() is called to initialize each timer object. The calls are
omitted if no timers have been configured.
Initializing
Tasks
Here is the complete code for 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[task] = 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
}
除非已配置“运行到完成”计划程序,否则将初始化任务的上下文块–NUSE_Task_context[Task][]。大多数条目没有设置为值,因为它们表示通用机器寄存器,当任务启动时,这些寄存器被假定具有不确定的值。在Nucleus SE的示例(Freescale ColdFire)实现中(这对于任何处理器都是类似的),最后三个条目是显式设置的:
- ·
NUSE_Task_Context[task][15] holds
the status register (SR ) and is
set to the value in the #define symbol NUSE_STATUS_REGISTER . - · NUSE_Task_Context[task][16] holds
the program counter (PC ) and is
set to the address of the entry point of the task’s code: NUSE_Task_Start_Address[task] . - · NUSE_Task_Context[task][17] holds
the stack pointer (SP), which is initialized to a value computed by adding the
address of the task’s stack base (NUSE_Task_Stack_Base[task] )
to the task’s stack size (NUSE_Task_Stack_Size[task] ).
如果启用信号支持,任务的信号标志(NUSE_task_signal_flags[task])设为零。
如果启用了任务休眠(即API调用NUSE_task_sleep()),则任务的超时计数器(NUSE_task_timeout_counter[task])设置为零。
如果任务状态为“已初始化”,则任务状态为“挂起”。如果启用了任务初始任务状态支持,则此初始值由用户指定(在NUSE_Task_initial_State[Task])。否则,状态设置为NUSE_READY。
如果启用任务计划计数,则任务的计数器(NUSE_task_schedule_Count[task])设置为零。
初始化分区池
以下是NUSE_Init_Partition_Pool()的完整代码:
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
}
分区池的“used”计数器(NUSE_partition_pool_partition_used[pool])设置为零。
如果启用了任务阻塞,则分区池的阻塞任务计数器(NUSE_partition_pool_blocking_Count[pool])设置为零。
初始化邮箱
以下是NUSE_Init_Mailbox()的完整代码:
void NUSE_Init_Mailbox(NUSE_MAILBOX mailbox)
{
NUSE_Mailbox_Data[mailbox] = 0;
NUSE_Mailbox_Status[mailbox] =
0;
#if NUSE_BLOCKING_ENABLE
NUSE_Mailbox_Blocking_Count[mailbox] = 0;
#endif
}
邮箱的数据存储(NUSE_mailbox_data[mailbox])设置为零,其状态(NUSE_mailbox_status[mailbox])设置为“未使用”(即零)。
如果启用任务阻止,则邮箱的阻止任务计数器(NUSE_mailbox_blocking_Count[mailbox])设置为零。
初始化队列
以下是NUSE_Init_Queue()的完整代码:
void NUSE_Init_Queue(NUSE_QUEUE queue)
{
NUSE_Queue_Head[queue] = 0;
NUSE_Queue_Tail[queue] = 0;
NUSE_Queue_Items[queue] = 0;
#if NUSE_BLOCKING_ENABLE
NUSE_Queue_Blocking_Count[queue]
= 0;
#endif
}
队列的head和tail指针(实际上,它们是索引–NUSE_queue_head[queue]和NUSE_queue_tail[queue])被设置为指向队列数据区域的开始(即给定值0)。队列的项目计数器(NUSE_queue_Items[queue])也设置为零。
如果启用任务阻塞,队列的阻塞任务计数器(NUSE_queue_blocking_Count[queue])设置为零。
初始化管道
以下是NUSE_Init_Pipe()的完整代码:
void NUSE_Init_Pipe(NUSE_PIPE pipe)
{
NUSE_Pipe_Head[pipe] = 0;
NUSE_Pipe_Tail[pipe] = 0;
NUSE_Pipe_Items[pipe] = 0;
#if NUSE_BLOCKING_ENABLE
NUSE_Pipe_Blocking_Count[pipe] = 0;
#endif
}
管道的头和尾指针(实际上,它们是索引–NUSE_pipe_head[pipe]和NUSE_pipe_tail[pipe])被设置为指向管道数据区域的开始(即给定值0)。管道的项目计数器(NUSE_pipe_Items[pipe])也设置为零。
如果启用任务阻塞,则管道的阻塞任务计数器(NUSE_pipe_blocking_Count[pipe])设置为零。
初始化信号量
以下是NUSE_Init_Semaphore()的完整代码:
void NUSE_Init_Semaphore(NUSE_SEMAPHORE
semaphore)
{
NUSE_Semaphore_Counter[semaphore] =
NUSE_Semaphore_Initial_Value[semaphore];
#if NUSE_BLOCKING_ENABLE
NUSE_Semaphore_Blocking_Count[semaphore] = 0;
#endif
}
信号量的计数器(NUSE_semaphore_counter[semaphore])初始化为用户指定的值(NUSE_semaphore_Initial_value[semaphore])。
如果启用任务阻塞,则信号量的阻塞任务计数器(NUSE_semaphore_blocking_Count[信号量])设置为零。
初始化事件组
以下是NUSE_Init_Event_Group()的完整代码:
void NUSE_Init_Event_Group(NUSE_EVENT_GROUP
group)
{
NUSE_Event_Group_Data[group] =
0;
#if NUSE_BLOCKING_ENABLE
NUSE_Event_Group_Blocking_Count[group] = 0;
#endif
}
事件组的标志被清除;即NUSE_event_group_Data[group]设置为零。
如果启用任务阻止,则事件组的阻止任务计数器(NUSE_event_group_blocking_Count[group])设置为零。
初始化计时器
以下是NUSE_Init_Timer()的完整代码:
void NUSE_Init_Timer(NUSE_TIMER timer)
{
NUSE_Timer_Status[timer] =
FALSE;
NUSE_Timer_Value[timer] =
NUSE_Timer_Initial_Time[timer];
NUSE_Timer_Expirations_Counter[timer] = 0;
}
计时器的状态(NUSE_timer_status[timer])设置为“未使用”;即FALSE。
其倒计时值(NUSE_Timer_value[Timer])初始化为用户指定的值(NUSE_Timer_Initial_Time[Timer])。
其过期计数器(NUSE_Timer_Expirations_counter[Timer])设置为零。
应用程序代码初始化
一旦Nucleus SE数据结构被初始化,就有机会在执行任务之前执行应用程序初始化的代码。此功能有许多可能的用途:
初始化应用程序数据结构。显式赋值比允许静态变量的自动初始化更容易理解和调试。
内核对象分配。假设所有内核对象都是在构建时静态创建的,并由索引值标识,那么分配“所有权”或定义这些对象的用法可能很有用。这可以使用#define符号来完成,但是,如果存在多个任务实例,则最好通过全局数组(按任务的ID编制索引)来分配对象索引。
设备初始化。这可能是安装任何外围设备的好机会。
显然,在执行Nucleus SE初始化之前,很多事情都可以实现,但是在这里定位应用程序初始化代码的好处是现在可以使用内核服务(API调用)。例如,队列或邮箱可能预加载了任务启动时要处理的数据。
允许API调用有一个限制:不能采取通常会导致调用调度程序的操作,例如任务挂起/阻塞。全局变量NUSE_Task_State已设置为NUSE_STARTUP_CONTEXT以反映此限制。
启动计划程序
初始化完成后,只剩下启动调度程序来开始执行应用程序代码-任务。在前面的一篇文章中详细介绍了调度程序的选项和各种类型的调度程序的操作,因此这里只需要简要总结一下。
顺序中的关键点是:
将全局变量NUSE_Task_State设置为NUSE_Task_CONTEXT。
选择要运行的第一个任务的索引。如果启用了对初始任务状态的支持,将对第一个就绪任务执行搜索;否则将使用值0。
调用调度程序–NUSE_scheduler()。
在最后一步中到底发生了什么取决于选择了哪种调度程序类型。对于Run-to-Completion,进入调度循环并按顺序调用任务。对于其他调度程序类型,将加载第一个任务的上下文并将控制权传递给该任务。
Nucleus SE RTOS初始化和启动的更多相关文章
- 使用Nucleus SE实时操作系统
使用Nucleus SE实时操作系统 Using the Nucleus SE real-time operating system 到目前为止,在本系列文章中,我们详细介绍了Nucleus SE提供 ...
- Solr初始化源码分析-Solr初始化与启动
用solr做项目已经有一年有余,但都是使用层面,只是利用solr现有机制,修改参数,然后监控调优,从没有对solr进行源码级别的研究.但是,最近手头的一个项目,让我感觉必须把solrn内部原理和扩展机 ...
- Hadoop源码学习笔记之NameNode启动场景流程四:rpc server初始化及启动
老规矩,还是分三步走,分别为源码调用分析.伪代码核心梳理.调用关系图解. 一.源码调用分析 根据上篇的梳理,直接从initialize()方法着手.源码如下,部分代码的功能以及说明,已经在注释阐述了. ...
- k8s replicaset controller分析(1)-初始化与启动分析
replicaset controller分析 replicaset controller简介 replicaset controller是kube-controller-manager组件中众多控制 ...
- kube-scheduler源码分析(1)-初始化与启动分析
kube-scheduler源码分析(1)-初始化与启动分析 kube-scheduler简介 kube-scheduler组件是kubernetes中的核心组件之一,主要负责pod资源对象的调度工作 ...
- k8s client-go源码分析 informer源码分析(2)-初始化与启动分析
k8s client-go源码分析 informer源码分析(2)-初始化与启动分析 前面一篇文章对k8s informer做了概要分析,本篇文章将对informer的初始化与启动进行分析. info ...
- 第1章 ZigBee协议栈初始化网络启动流程
作者:宋老师,华清远见嵌入式学院讲师. ZigBee的基本流程:由协调器的组网(创建PAN ID),终端设备和路由设备发现网络以及加入网络. 基本流程:main()->osal_init_sys ...
- TaskTracker任务初始化及启动task源码级分析
在监听器初始化Job.JobTracker相应TaskTracker心跳.调度器分配task源码级分析中我们分析的Tasktracker发送心跳的机制,这一节我们分析TaskTracker接受JobT ...
- ucos系统初始化及启动过程
之前在ucos多任务切换中漏掉了一个变量, OSCtxSwCtr标识系统任务切换次数 主要应该还是用在调试功能中 Ucos系统初始化函数为OSInit(),主要完成以下功能 全局变量初始化 就绪任务表 ...
随机推荐
- hdu4287 水题
题意: 水题,就是给你一些单词,和一些按键记录,问打出下面的那些单词,每一个按键记录一共按了多少次. 思路: 直接把每个单词的每一位转换成数字,然后再把每个单词转换的数字 ...
- 一个DDOS病毒的分析(二)
一.基本信息 样本名称:hra33.dll或者lpk.dll 样本大小: 66560 字节 文件类型:Win32的dll文件 病毒名称:Dropped:Generic.ServStart.A3D47B ...
- C++处理char*,char[],string三种类型间的转换
前言 在C和C++中,有一个相当重要的部分,就是字符串的编程描述.在学C的时候,很多人习惯了char[],char*表示法,直到遇见了C++后,出现了第三者:string.这时候,很多初学者就会在这三 ...
- Linux中正则表达式和字符串的查询、替换(tr/diff/wc/find)
目录 正则表达式 基本正则表达式 扩展正则表达式 grep tr diff du wc find 正则表达式 正则表达式,又称正规表示法.常规表示法( Regular Expression,在代码中常 ...
- Asp.NetCore Web应用程序中的请求管道和中间件
你是否会迷惑当我们请求一个ASP.NetWeb应用程序以后,它是怎么处理这些请求的,后台是怎么工作的,今天就讲一下Asp.NetCore Web应用程序中的请求处理过程. 上一节,我们讲到,Start ...
- Govern Service 基于 Redis 的服务治理平台
Govern Service 基于 Redis 的服务治理平台(服务注册/发现 & 配置中心) Govern Service 是一个轻量级.低成本的服务注册.服务发现. 配置服务 SDK,通过 ...
- 熟悉 Bash 快捷键来提高效率
Bash是GNU计划的一部分,是多数Linux发行版提供的默认Shell. Linux的精髓就在于命令行的高效,而学习命令行的第一步便是学习如何快速地输入命令. 其实包括Bash在内的多数Linux ...
- linux下符号链接和硬链接的区别
存在2众不同类型的链接,软链接和硬链接,修改其中一个,硬链接指向的是节点(inode),软链接指向的是路径(path) 软连接文件 软连接文件也叫符号连接,这个文件包含了另一个文件的路径名,类似于wi ...
- Duplicate entry '' for key 'PRIMARY'
今天在在mysql中插入数据 因为直接插入查询出来的表格,insert into 表(student_id,class_id) 直接插入了这两个字段对应的查询出来的表 没有留意到该表的主键没有设置自增 ...
- istio流量管理:非侵入式流量治理
在服务治理中,流量管理是一个广泛的话题,一般情况下,常用的包括: 动态修改服务访问的负载均衡策略,比如根据某个请求特征做会话保持: 同一个服务有多版本管理,将一部分流量切到某个版本上: 对服务进行保护 ...