1、OneOS概述
OneOS是中国移动针对物联网领域推出的轻量级操作系统,具有可裁剪、跨平台、低功耗、高安全等特点,支持ARM Cortex-M/A、MIPS、RISC-V等主流芯片架构,兼容POSIX、CMSIS等标准接口,支持Javascript、Micropython语言开发,提供图形化开发工具,能够有效提高开发效率并降低开发成本,帮助客户开发稳定可靠、安全易用的物联网应用。OneOS遵循Apache许可证2.0版本,个人、企业客户可以在商业产品中使用,不需要公布源码,没有潜在商业风险。中移物联网有限公司将秉承开放合作的态度,为客户提供适用于各种物联网场景的稳定系统。
2、OneOS源码文件结构
目录或文件名称 |
描述 |
arch(architecture) |
存放和 MCU(或 CPU )架构体系相关的代码。 |
common |
存放一些通用的没有具体业务指向的程序代码,所有模块都可以使用,不通过编译选项控制是否编译,采用默认编译进工程的方式。 |
components |
存放组件代码,可进行裁剪。 |
demos |
存放内核或组件的对外接口如何使用的示例程序。 |
docs |
存放一些文档,如编码规范、编程指南等。 |
drivers |
存放驱动的抽象层代码和具体外设的驱动代码。 |
kernel |
存放内核代码,如任务管理及调度、任务间同步以及通信、内存管理等代码。 |
libc |
Libc 库部分硬件相关接口的底层适配。 |
osal |
OneOS操作系统接口抽象层,支持Posix接口、CMSIS接口、RT-Thread接口等 |
projects |
各种开发板的示例工程 |
scripts |
存放OneOS-Cube工具在编译构造时所需要的脚本文件。 |
templates |
存放为开发者搭建了OneOS当前所支持的MCU的标准工程模板 |
thirdparty |
存放第三方开源社区或第三方厂家的程序,包括组件、工具、协议实现或对接平台的代码等。 |
user |
存放供使用者阅读的文档 |
Kconfig |
Menuconfig配置文件,代码工程(如projects目录下的示例工程)中的Kconfig文件会引用此文件 |
SConscript |
OneOS操作系统使用Scons构建工具时的根编译脚本,该脚本会引用其它目录的SConscript脚本,若在OneOS操作系统根目录增加新的代码目录,需要修改此文件(参见“从零开始构建代码工程”章节)。 |
LICENSE |
License 授权说明。 |
3、启动流程
STM32整个启动过程是指从上电系统启动开始,一直运行到用户main函数之间的这段过程,接下来以stm32l475-atk-pandora工程为例讲解启动的流程。
armcc(keil)启动流程为:
①上电后硬件设置SP、PC
②设置系统时钟
③软件设置SP
④加载.data、.bss,并初始化栈区
⑤跳转到C文件的main函数
从启动文件中的“SystemInit”函数开始,然后再执行“__main”函数,再执行$Sub$$main函数,在这个函数里边会执行“_k_startup()”函数,在“_k_startup()”函数中会执行一系列初始化的函数,其中就会执行到“_k_application_init()”函数,在该函数中会创建一个主程序进程“main”(非用户main),然后该进程开始执行后会进入“_k_main_task_entry()”函数,在该函数中先执行“_k_other_auto_init()”函数,然后才执行用户的main()函数。
stm32l475-atk-pandora工程 |
||
路径 |
文件 |
流程 |
① |
startup_stm32l475xx_arm.s |
SystemInit → __main |
② |
os_startup.c |
$Sub$$main → _k_startup() |
_k_startup() → _k_application_init() |
||
_k_application_init() → _k_main_task_entry() |
||
_k_main_task_entry() → $Super$$main() |
||
$Super$$main() → main() |
路径:
①templatesstm32l475-atk-pandoraboardstartup
②kernelsource
4、“.s”启动文件
STM32 MCU 的startup_xxx.s启动文件的主要功能
主要功能如下:
1. 初始化堆栈指针 SP
2. 初始化程序计数器指针 PC
3. 设置堆、栈的大小
4. 设置中断向量表的入口地址
5. 配置外部 SRAM 作为数据存储器
6. 调用 SystemInit() 函数配置 STM32 的系统时钟
7. 设置 C 库的分支入口 “__main” (最终用来调用 main 函数)
SystemInit() :配置 STM32 的系统时钟,配置向量表
__main :__main函数由编译器生成,并在最后跳转到用户自定义的main()函数。
5、“.map”文件
map文件是通过编译器编译之后,集程序、数据及IO空间的一种映射文件。
在遇到内存越界或溢出的情况,可以借助分析map文件。通过map文件可以知道函数大小、入口地址等一些重要信息。
里边有函数相互调用的关系、还有没有被调用的模块、变量储存的地址、函数的入口地址、代码占用的空间等等信息
打开:
配置
第一部分Section Cross References
指的是各个源文件生成的模块、段(定义的入口)之间相互引用的关系,配置中需勾选上:Cross Reference,例如:
main.o(i.main) refers to os_task.o(i.os_task_create) for os_task_create
表示 main.o文件中的 main 函数调用了 os_task.o 文件中的os_task_create 函数
main.o是main.c源文件生成的目标文件模块,i.main是main函数的入口。
第二部分Removing Unused input sections from the image
移除未使用的模块,配置中需勾选上:Unused Sections Info,就是删除工程代码中,没有被调用的模块。最后面还有个统计信息。
第三部分Image Symbol Table
什么是局部标签与全局标签:
局部标签就是在函数内部用static声明的变量,还有用static声明的函数,基本上都是属于局部标签;全局标签就是不是用static声明的变量和函数,还有就是汇编文件里面的变量如果作用域是本文件的就是局部标签,作用域是全工程的就是全局标签。
第一列是函数名,前缀i.表示隶属于当前文件,出现无前缀的标签代表static标签。
i.前缀表示是在某个文件里面的某个函数,例如这里的drv_sai.o文件里面的HAL_SAI_RxCallback函数;这个 HAL_SAI_RxCallback要注意一下,为什么前面用i.前缀修饰过的函数,后面又出现一次呢?是因为这个函数是用static修饰的一个局部标签函数,用了130个字节的RAM大小,类型属于M3的Thumb指令代码。
映射符号表,配置中需勾选上:Symbols,从中可以看出符号名称,存储地址,存储大小,所在的目标文件
Symbols分为两大类 :Local Symbols局部 和 Global Symbols全局
1.Symbol Name:符号名称
2.Value:存储对应的地址;
大家会发现有0x0800xxxx、0x2000xxxx这样的地址。
0x0800xxxx指存储在FLASH里面的代码、变量等。
0x2000xxxx指存储在内存RAM中的变量Data等。
全局、静态变量等位于0x2000xxxx的内存RAM中。
3.Ov Type:符号对应的类型
符号类型大概有几种:Number、Section、Thumb Code、Data等;
4.Size:存储大小
这个容易理解,我们怀疑内存溢出,可以查看代码存储大小来分析。
5.Object(Section):段目标
这里一般指所在模块(所在源文件)
第四部分Memory Map of the image
内存映射分布,配置中需勾选上:Memory Map。映像文件可以分为加载域(Load Region)和运行域(Execution Region),加载域反映了ARM可执行映像文件的各个段存放在存储器中的位置关系,运行域反映了ARM可执行映像文件各个段真正执行时在存储器中的位置关系。另外,映像中的入口点就是程序开始执行的位置。
加载域就是程序在Flash中的实际存储,而运行域是芯片上电后的运行状态,因为MCU没上电时RAM中没有数据,所以此时所有的东西(包括代码、变量、初始值等)都是存放在flash中的,当上电后又要把变量等复制到RAM中才能正常运行。
Image Entry point : 0x08000401:指映射入口地址。
Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00055334, Max: 0x00080000, ABSOLUTE, COMPRESSED[0x00054cbc])
指加载区域位于LR_IROM1开始地址0x08000000,大小有0x00055334,这块区域最大有0x00080000.
执行区域:
Execution Region ER_IROM1
Execution Region RW_IRAM0
Execution Region RW_IRAM1
这个区域,其实就是对应我们目标配置中的区域,如下图:
第五部分Image component sizes
Code:指代码的大小;
RO-data:指除了内联数据(inline data)之外的常量数据;
RW-data:指可读写(RW)、已初始化的变量数据;
ZI-data:指未初始化或零初始化的数据(ZI)所占字节;
Debug :显示调试数据占用了多少字节;
Object Totals :显示链接到一起以生成映像的对象占用了多少字节。
(incl. Generated):链接器会生成的映像内容,例如,交互操作中间代码。 如果 Object Totals 行包含此类型的数据,则会显示在该行中。本例中共有 1016 字节的 RO 数据,其中32字节是链接器生成的 RO 数据。
(incl. Padding) :链接器根据需要插入填充,以强制字节对齐。
下面的Library Totals显示已提取并作为单个对象添加到映像中的库成员占用了多少字节。
Code、RO-data:位于FLASH中;
RW-data、ZI-data:位于RAM中;
提醒:RW-data已初始化的数据会存储在Flash中,上电会从FLASH搬移至RAM中。
关系如下:
RO Size = Code + RO Data
RW Size = RW Data + ZI Data
ROM Size = Code + RO Data + RW Data
6、静态链接库文件“libxxx.a”
静态链接库文件名命名规则“libxxx.a”,库名前面加“lib”,后缀用“.a”,“xxx”为静态库名。
https://copyfuture.com/blogs-details/202208311102359537
静态链接库用来和所有的目标文件一起组织成可执行文件