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初始化和启动的更多相关文章

  1. 使用Nucleus SE实时操作系统

    使用Nucleus SE实时操作系统 Using the Nucleus SE real-time operating system 到目前为止,在本系列文章中,我们详细介绍了Nucleus SE提供 ...

  2. Solr初始化源码分析-Solr初始化与启动

    用solr做项目已经有一年有余,但都是使用层面,只是利用solr现有机制,修改参数,然后监控调优,从没有对solr进行源码级别的研究.但是,最近手头的一个项目,让我感觉必须把solrn内部原理和扩展机 ...

  3. Hadoop源码学习笔记之NameNode启动场景流程四:rpc server初始化及启动

    老规矩,还是分三步走,分别为源码调用分析.伪代码核心梳理.调用关系图解. 一.源码调用分析 根据上篇的梳理,直接从initialize()方法着手.源码如下,部分代码的功能以及说明,已经在注释阐述了. ...

  4. k8s replicaset controller分析(1)-初始化与启动分析

    replicaset controller分析 replicaset controller简介 replicaset controller是kube-controller-manager组件中众多控制 ...

  5. kube-scheduler源码分析(1)-初始化与启动分析

    kube-scheduler源码分析(1)-初始化与启动分析 kube-scheduler简介 kube-scheduler组件是kubernetes中的核心组件之一,主要负责pod资源对象的调度工作 ...

  6. k8s client-go源码分析 informer源码分析(2)-初始化与启动分析

    k8s client-go源码分析 informer源码分析(2)-初始化与启动分析 前面一篇文章对k8s informer做了概要分析,本篇文章将对informer的初始化与启动进行分析. info ...

  7. 第1章 ZigBee协议栈初始化网络启动流程

    作者:宋老师,华清远见嵌入式学院讲师. ZigBee的基本流程:由协调器的组网(创建PAN ID),终端设备和路由设备发现网络以及加入网络. 基本流程:main()->osal_init_sys ...

  8. TaskTracker任务初始化及启动task源码级分析

    在监听器初始化Job.JobTracker相应TaskTracker心跳.调度器分配task源码级分析中我们分析的Tasktracker发送心跳的机制,这一节我们分析TaskTracker接受JobT ...

  9. ucos系统初始化及启动过程

    之前在ucos多任务切换中漏掉了一个变量, OSCtxSwCtr标识系统任务切换次数 主要应该还是用在调试功能中 Ucos系统初始化函数为OSInit(),主要完成以下功能 全局变量初始化 就绪任务表 ...

随机推荐

  1. Aircrack-ng破解无线WIFI密码

    首先,如果kali是装在虚拟机里面的话,是不能用物理机的无线网卡的.所以,如果我们要想进行无线破解,需要外接一个无线网卡设备,并且该设备要支持 monitor 监听模式 iwconfig :系统配置无 ...

  2. UVA10905孩子们的游戏

    题意:       给你n个数字,让你用这n个数组组成一个最大的数字并输出来. 思路:       这个题目看完第一反应就是直接按照字符串排序,然后轻轻松松写完,交上去直接wa了,为什么会wa呢?感觉 ...

  3. 使用同步或异步的方式完成 I/O 访问和操作(Windows核心编程)

    0x01 Windows 中对文件的底层操作 Windows 为了方便开发人员操作 I/O 设备(这些设备包括套接字.管道.文件.串口.目录等),对这些设备的差异进行了隐藏,所以开发人员在使用这些设备 ...

  4. Win64 驱动内核编程-6.内核里操作注册表

    内核里操作注册表 RING0 操作注册表和 RING3 的区别也不大,同样是"获得句柄->执行操作->关闭句柄"的模式,同样也只能使用内核 API 不能使用 WIN32 ...

  5. MS06-040漏洞研究(上)【转载】

    课程简介 我在之前的课程中讨论过W32Dasm这款软件中的漏洞分析与利用的方法,由于使用该软件的人群毕竟是小众群体,因此该漏洞的危害相对来说还是比较小的.但是如果漏洞出现在Windows系统中,那么情 ...

  6. 【翻译】WPF中的数据绑定表达式

    有很多文章讨论绑定的概念,并讲解如何使用StaticResources和DynamicResources绑定属性.这些概念使用WPF提供的数据绑定表达式.在本文中,让我们研究WPF提供的不同类型的数据 ...

  7. MySQL模糊查询,查询语句是对的。但是就是没有查询结果

    问题 解决 当我用其他非中文的值去查询,发现可以查询到结果 所以问题就出现在数据库的配置中 spring.datasource.url=jdbc:mysql://localhost:3306/data ...

  8. GitBash管理代码

    一.Git是什么? Git是目前世界上最先进的分布式版本控制系统. 1.Git和SVN的区别 SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中 ...

  9. 【BUAA软工】Beta阶段事后分析

    设想与目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 解决的问题 总体解决的问题:新手编程者配置编程环境难.本地编写的代码跨设备同步难.本地ide安装使用过程 ...

  10. Pytorch_Part1_简介&张量

    VisualPytorch beta发布了! 功能概述:通过可视化拖拽网络层方式搭建模型,可选择不同数据集.损失函数.优化器生成可运行pytorch代码 扩展功能:1. 模型搭建支持模块的嵌套:2. ...