加载和启动过程

嵌入式Linux或STM32单片机的程序从上电到执行main函数的过程是一个复杂的过程,涉及到硬件、操作系统和应用程序的交互。下面我将尽可能详细地解释这个过程。

首先,我们来看一下STM32单片机的启动过程:

  • 上电复位:当STM32单片机上电后,会进行复位操作,设置系统的堆栈指针(SP)和程序计数器(PC)。这些信息通常存储在启动文件(startup file)中,这个文件是由芯片制造商提供的,包含了系统启动所需的所有硬件设置。
  • 启动文件:启动文件负责执行微控制器从“复位”到“开始执行main函数”中间这段时间所必须进行的工作。这个文件通常是由开发环境自动提供的,不需要开发人员再行干预启动过程,只需要从main函数开始进行应用程序的设计即可。
  • 向量表:Cortex-M3内核规定,起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在Cortex-M3内核复位后,会自动从起始地址的下一个32位空间取出复位中断入口向量,跳转执行复位中断服务程序。
  • 系统初始化:在执行到启动文件的某个位置后,系统会调用SystemInit函数,这个函数是由芯片厂商提供的,用于初始化系统时钟和其他硬件设置。
  • 全局变量初始化:在SystemInit函数执行完毕后,系统会进行全局变量的初始化。这个过程通常是由编译器自动完成的。
  • 进入main函数:全局变量初始化完成后,系统最后会跳转到用户的main函数,开始执行用户的代码。

对于嵌入式Linux系统,启动过程稍有不同,主要包括以下步骤:

  • 上电启动:当系统上电后,硬件首先会执行一段位于ROM中的引导代码,这段代码负责初始化硬件,并加载引导加载器(bootloader)。
  • 引导加载器(Bootloader):引导加载器的任务是加载操作系统内核。在嵌入式Linux系统中,常见的引导加载器有U-Boot、RedBoot等。引导加载器会设置必要的硬件参数,然后从存储设备中加载Linux内核到RAM,并跳转到内核的启动地址开始执行。
  • Linux内核启动:内核启动后,会进行一系列的初始化操作,包括设置中断、初始化设备驱动、挂载根文件系统等。完成这些操作后,内核会启动一个用户空间的进程,通常是init进程。
  • 用户空间进程:init进程会根据系统的配置,启动其他的用户空间进程,包括各种守护进程、应用程序等。
    以上就是嵌入式Linux和STM32单片机从上电到执行main函数的过程。

理解ELF和BIN文件的加载过程需要了解一些底层的计算机原理。下面我将尽可能详细地解释这两个过程。
首先,我们来看一下ELF文件的加载过程:

  • 读取并检查ELF头部:首先,系统会读取ELF文件的头部信息,包括魔数、文件类型、机器类型、版本等,以验证这是一个有效的ELF文件。
  • 读取程序头表:接着,系统会读取ELF文件的程序头表,这个表描述了如何创建进程的内存映像。
  • 创建进程地址空间:根据程序头表的信息,系统会创建进程的地址空间,包括代码段、数据段、堆、栈等。
  • 加载段:然后,系统会将ELF文件中的各个段(代码段、数据段等)加载到进程的地址空间中。
  • 处理动态链接:如果ELF文件是动态链接的,系统还需要处理动态链接,包括加载动态链接库,解析符号等。
  • 跳转到入口点:最后,系统会将控制权转移到ELF文件的入口点,也就是程序的main函数。

对于BIN文件,其加载过程相对简单:

  • 上电复位:当STM32单片机上电后,会进行复位操作,设置系统的堆栈指针(SP)和程序计数器(PC)。这些信息通常存储在启动文件(startup file)中,这个文件是由芯片制造商提供的,包含了系统启动所需的所有硬件设置。
  • 加载BIN文件:然后,系统会将BIN文件(包含了程序的机器代码和初始化的全局变量)加载到内存中。
  • 跳转到入口点:加载完成后,系统会跳转到程序的入口点,开始执行用户的代码。
    总的来说,ELF文件的加载过程涉及到更多的步骤,包括动态链接等,而BIN文件的加载过程则相对简单,主要是将文件内容加载到内存,然后跳转到程序的入口点。

在嵌入式系统中,程序的 .data 段通常在系统启动时由启动代码(boot code)拷贝到 RAM 中。这个过程通常发生在系统初始化阶段,也就是在主函数(main function)执行之前。

具体来说,当你将 bin 文件烧录到 flash 中后,系统上电启动,启动代码首先会运行。这段启动代码负责设置硬件(例如初始化时钟,配置内存控制器等),然后它会将 flash 中的 .data 段拷贝到 RAM 中。这样,当你的程序开始运行时,它就可以访问已经初始化的数据了。

这个过程是由链接脚本(linker script)控制的,链接脚本会定义 flash 中的 .data 段和 RAM 中的目标位置。

需要注意的是,这个过程可能会根据你的具体硬件和编译工具链有所不同。在某些系统中,可能需要手动编写或修改启动代码或链接脚本来实现这个功能。在使用支持 XIP (执行就地) 的 flash 的系统中,代码可以直接在 flash 中执行,但数据通常仍需要在 RAM 中处理。因此,即使在这些系统中,.data 段通常也会在启动时拷贝到 RAM 中。


启动代码知道要拷贝的 .data 段的数据在 RAM 的哪个地址上,是通过链接脚本(linker script)来实现的。

链接脚本是在编译阶段由编译器使用的一种文件,它定义了程序中各个段(如 .text.data.bss 等)在内存中的布局。链接脚本会为 .data 段在 RAM 中指定一个起始地址和结束地址。这些地址信息会被嵌入到生成的可执行文件中。

当启动代码运行时,它会读取这些嵌入的地址信息,然后将 flash 中的 .data 段的内容拷贝到 RAM 的相应位置。这就是启动代码如何知道在 RAM 中的哪个位置放置 .data 段的内容的。

需要注意的是,这个过程可能会因你的具体硬件和编译工具链的不同而有所不同。在某些系统中,可能需要手动编写或修改启动代码或链接脚本来实现这个功能。


是的,启动代码的执行过程是在 main 函数之前。当你的嵌入式系统上电启动时,CPU 会从一个特定的地址开始执行代码,这个地址通常被称为复位向量(reset vector)。这个地址上的代码就是启动代码,也被称为引导加载器(bootloader)或引导代码(boot code)。

启动代码的主要任务是初始化硬件(例如设置时钟,配置内存控制器等),然后将程序的 .data.bss 段从 Flash 拷贝到 RAM,最后跳转到 main 函数开始执行你的程序。

至于你是否能看到这段代码,这取决于你的开发环境。如果你使用的是开源的操作系统或者裸机(bare metal)环境,那么你应该能够找到这段启动代码。这段代码通常位于项目的启动文件(startup file)中,这个文件通常是汇编语言或者 C 语言编写的。

如果你使用的是商业的嵌入式操作系统,那么启动代码可能是二进制形式的,你可能无法直接查看。但是,你仍然可以通过阅读操作系统的文档或者咨询供应商来了解启动过程的详细信息。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值