嵌入式软件的编译知识

文摘   2024-08-30 18:36   广东  

.关注+星号公众号,不容错过精彩

作者:HywelStar

今天主要讨论一下每个程序员都会接触到的程序编译,那么编译的时候到底在干嘛,为什么要这样的过程。

主要针对C/C++语言进行分析。

本章节将解决以下疑问:

  • 编译的过程是怎样的?
  • 为什么需要需要使用到交叉编译?
  • 编译优化相关手段?

1. 引言

编译过程是将源代码转换为计算机能够理解的机器语言代码的关键环节。对于嵌入式系统开发来说,编译过程不仅涉及到生成最终的二进制文件,还包括了对代码的优化和调试支持。

2. 编译的基础知识

编译(compilation)是将高级编程语言(如C、C++)编写的源代码转换为机器码(即二进制代码)的过程,以便嵌入式系统的处理器可以执行。编译过程一般分为以下几个阶段:

  1. 预处理(Preprocessing): 预处理器处理源代码中的宏定义、头文件包含和条件编译指令。生成的输出是一个纯文本文件,展开所有宏和包含的头文件。
  2. 编译(Compilation): 编译器将预处理后的代码转换为中间代码(如汇编代码)。这一步涉及语法分析、语义分析和优化。
  3. 汇编(Assembly): 汇编器将中间代码转换为目标代码(object code),即二进制机器码,但还不能直接执行,因为缺少链接信息。
  4. 链接(Linking): 链接器将多个目标文件和库文件链接在一起,生成一个可执行文件。这一步解决了函数调用和变量引用的地址问题。

对于整个过程,我相信大家都已经非常熟悉,不再做具体的分析了。

3. 常用的编译器工具

在嵌入式开发中,常用的编译工具包括编译器、链接器和构建系统等。以下是一些常用的工具和它们的用途:

GCC(GNU Compiler Collection): GCC是一个功能强大的开源编译器,支持多种编程语言。它是嵌入式开发中最常用的编译器之一,支持多种目标架构(如ARM、AVR、MIPS等)。

  • 编译命令: gcc -o output source.c
  • 选项:
    • -Wall:启用所有常见的警告。
    • -O2:启用优化选项。
    • -g:生成调试信息。

CMake: CMake是一个跨平台的构建系统,可以生成本地构建脚本(如Makefile或Visual Studio项目)。它使得项目的编译配置更加灵活和可维护。

  • CMakeLists.txt 示例:

    cmake_minimum_required(VERSION 3.10)project(MyEmbeddedProject)
    set(CMAKE_C_STANDARD 11)set(SOURCES main.c module1.c module2.c)
    add_executable(my_embedded_app ${SOURCES})

Make: Make是一个自动化构建工具,使用Makefile来定义编译规则和依赖关系。它是Unix和Linux系统上非常常见的构建工具。

  • Makefile 示例:

    CC = gccCFLAGS = -Wall -O2TARGET = my_embedded_appSOURCES = main.c module1.c module2.cOBJECTS = $(SOURCES:.c=.o)
    $(TARGET): $(OBJECTS) $(CC) $(CFLAGS) -o $(TARGET) $(OBJECTS)
    clean: rm -f $(TARGET) $(OBJECTS)

4. 交叉编译工具

在开发过程中,经常用到交叉编译,那为什么要交叉编译?

由于CPU 架构的不一样,使用的指令集也存在差异,编译的目的就是将那些我们看懂的代码编程机器能识别的语言,但由于CPU设计不一样,使用的编译器也就不一样,需要编译出最终能执行的文件。

有一点我们需要注意,编译过程不仅是需要不同的编译器,准确来说应该是一套不同的工具链,包括编译器,汇编器,链接器等。

5. 编译优化

编译优化,对于嵌入式产品来说,特别是量产的产品,程序都是需要加上编译优化,一方面可以提高程序的运行速度,减少内存的使用,提高整体的性能。

大小优化(-Os): 优化代码以减少可执行文件的大小。

速度优化(-O1, -O2, -O3): 优化代码以提高执行速度。

特定架构优化(-march): 针对特定的处理器架构进行优化。

对于前面大小优化和速度优化可能都见过,一般来说调试阶段可能会不优化,但是到了发布都是会有不同等级的优化。

对于特定架构优化:这种一般和特定的处理有关系

比如:i.MX8M 系列:

export CC="aarch64-poky-linux-gcc  -march=armv8-a+crc+crypto -mbranch-protection=standard -fstack-protector-strong  -O2 -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=$SDKTARGETSYSROOT"

-march=armv8-a+crc+crypto:利用特定的硬件特性提升代码执行效率。

-O2,通过多种编译器优化技术生成高效的代码。

6. 调试和测试

编译完成后,调试和测试是确保代码质量的重要步骤。常用的调试工具包括GDB(GNU Debugger)和IDE自带的调试器。

GDB 调试示例:

bash复制代码gdb ./output(gdb) break main(gdb) run(gdb) step(gdb) print variable

还有一些在线调试的IDE工具,通过仿真器进行单步调试。

7. 结语

掌握编译相关的知识和工具是嵌入式软件工程师的重要技能。通过理解编译过程和使用合适的工具,我们可以更高效地开发嵌入式系统,提高代码的性能和可靠性。希望本文对你在嵌入式开发中的编译实践有所帮助。



往期推荐



公众号目录指引

工作中常用的Vim命令

认识Meson的使用

嵌入式开发常用技巧

给代码生成文件头部与函数注释

USB 接口小科普

认识Type-C 与快充相关知识

Type-C 的工作原理

USB PD 协议介绍与交互过程

码思途远
一位码农的日常分享,探索软件技术知识与新闻的数字十字路口。