设为首页 加入收藏

TOP

源码解读·RT-Thread操作系统从开机到关机(一)
2019-08-27 07:20:57 】 浏览:95
Tags:源码 解读 RT-Thread 操作系统 开机 关机

本篇内容比较简单,但却很繁琐,篇幅也很长,毕竟是囊括了整个操作系统的生命周期。这篇文章的目的是作为后续设计多任务开发的铺垫,后续会单独再抽出一篇分析任务的相关知识。另外本篇文章以单核MCU为背景,并且以最新的3.1.xLTS版本源码进行分析。主要内容目录如下:

  1. 基于bsp/stm32/stm32f103-mini-system为背景

  2. Cortex-M3的堆栈基础概念

  3. C语言main函数和rt-thread的main

  4. rt-thread操作系统的传统初始化与自动初始化组件

  5. 任务是怎样运行起来的

  6. Idle任务与新的构想

基于bsp/stm32/stm32f103-mini-system的开机介绍

关于体系结构的知识这里不做过多的介绍,因为这些知识要讲清楚的话足以写出一本大部头的书出来。不过会简单介绍一些必要的东西。

Stm32f103单片机是cortex-m3内核,在cortex-m3内核中使用双堆栈psp和msp,模式分为线程模式和handler模式,权限级别分为非特权级别和特权级别(现在只需要知道这么多就行了),handler模式就是当处理发生中断的时候自动进入的模式,其handler模式永远为特权级。

上电开机最开始运行的是MCU内部的ROM部分,这部分代码我们通常看不到,其通常是对芯片进行必要的初始化,比如FLASH和RAM的时钟初始化等,然后跳转到用户flash区域运行用户代码。在STM32中用户flash地址从0x08000000开始。我们写的代码都是从这里开始运行的。其次由于cortexM规定其用户FLASH区域的最前面必须是一张中断向量表。所以也就是说STM32的0x08000000开始是一张中断向量表,这是必须的也是默认的,当然在之后还可以重映射其它地方的向量表。这张向量表中的第一项是一个栈地址,第二项复位向量地址。下面贴一段向量表部分代码(摘录自startup_stm32f103xb.s):

__Vectors       DCD    __initial_sp               ; Topof Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD    NMI_Handler                ; NMIHandler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

另外需要注意的是开机后会自动进入复位异常,通常我们叫上电复位过程,不过意外的是上电复位处理的模式是特权级线程模式。在特权模式下堆栈指针将使用MSP,非特权模式下可以被切换到PSP。RT-Thread操作系统就是这么做的。所以回过头来看,中断向量表第一项指定了MSP的栈起始地址,并被自动加载到MSP,第二项指定了复位向量地址,也被自动加载到PC并运行。这样一来开机后我们能通过debug看到PC指针最先指向复位向量的第一条指令上。我们看一下stm32f103在armcc编译器上的复位向量代码:

; Reset handler
Reset_Handler    PROC
                 EXPORT Reset_Handler             [WEAK]
     IMPORT __main
     IMPORT SystemInit
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP
这是一段汇编代码,其完成两件事,第一件事调用systemInit函数完成一些初始化,第二件事跳转到__main函数。其中systemInit函数我们是可以找到并可以修改的一个C语言实现的函数(暂时不讨论,有兴趣的可以看system_stm32f1xx.c)。而这个__main就牛逼了,这既不是我们自己写的C语言的main也看不到它在哪里实现的。但是现在进入__main后它就是会跑到你最终用C语言写的main。这个__main的来龙去脉稍后会在第三部分分析。

Cortex-M3的堆栈基础概念

在Cortex-M3的处理器内核上堆栈指针分为PSP和MSP。handler模式下总是使用MSP,线程模式可以通过CONTROL寄存器来配置(修改的时候必须处于特权模式才可以)。

 

之所以需要这样设计就是为了将普通软件和系统软件通过权限隔离开,避免普通用户权限操作系统关键资源带来安全风险。当我们使用带有操作系统的环境进行开发时,操作系统就会将关键操作例如任务切换、中断处理等在特权模式操作。而其它的操作都会运行在非特权模式下完成。

操作系统一般都会将必要的操作封装出API接口,以提供给普通软件调用。而这背后的设计思想就是通过触发异常,然后进入特权模式运行异常向量处理程序。而这段异常处理程序早就让操作系统实现了,进而这部分特权操作是操作系统接管处理的。这也就避免用户普通软件去进行不必要的特权操作。例如用户任务想主动放弃CPU从而调用yield,yield将进行任务切换,其中过程大概是“选出另一个任务”->”触发SVC或者Pendsv异常”->进入SVC/Pendsv的handler异常处理程序,此时是特权模式,完成操作后返回到新任务运行。在RT-Thread中进入任务切换是通过触发Pendsv异常。

C语言main函数和RT-Thread的main

前面提到过开机启动最后进入复位向量处运行,最终调用__main就跑到我们外面写的C语言的main函数了。但这并非这么简单,在从__main到我们的main中间还有一系列操作比如初始化堆栈、初始化全局变量区域、初始化C运行时库等,然后再在最后调用用户的main函数。

不过在不同的编译器上这个__main并非是固定的,这里也就armcc是如此,如果是GCC和IAR的话其就不太一样,不过不影响我们分析核心主题。这里仅以借用armcc为例来分析主题中心思想。另外在说明RT-Thread中开启RT_USING_USER_MAIN的时候在ARMCC编译器上还有一个支持挂钩的操作,这种操作一般见于补丁修复的时候。其实现方式是在原有函数的名字前加上$Sub$$前缀就可以将原有函数劫持下来,并通过加上$Super$$前缀再调用原始函数。具体如下:The followingexample shows how to use $Super$$and $Sub$$ to insert a callto the function ExtraFunc() before the call to the legacy functionfoo().

ex
首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇解决SDL/SDL.h: No such file or .. 下一篇初级模拟电路:3-2 BJT的工作原理

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目