嵌入式软件开发必知必会

简介: 嵌入式软件开发必知必会

一、数据段、代码段、堆栈段、BSS段的区别

 进程(执行的程序)会占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等。不过进程对这些内存的管理方式因内存用途不一而不尽相同,有些内存是事先静态分配和统一回收的,而有些却是按需要动态分配和回收的。对任何一个普通进程来讲,它都会涉及到5种不同的数据段下面我们来简单归纳一下进程对应的内存空间中所包含的5种不同的数据区都是干什么的。

BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

(stack):栈又称堆栈,是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

是由操作系统分配的,内存的申请与回收都由OS管理。

全局的未初始化变量存在于.bss段中,具体体现为一个占位符;全局的已初始化变量存于.data段中;而函数内的自动变量都在栈上分配空间。.bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);而.data却需要占用,其内容由程序初始化,因此造成了上述情况。

bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小。data(已手动初始化的数据)段则为数据分配空间,数据保存在目标文件中。数据段包含经过初始化的全局变量以及它们的值。BSS段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段后面。当这个内存区进入程序的地址空间后全部清零。包含数据段和BSS段的整个区段此时通常称为数据区。

接下来,我们来看一个例程,将告诉你,程序中上面所说的究竟在什么位置,请看代码:

1#include <stdio.h>
 2#include <stdlib.h>
 3//位于BSS段,存放在程序组未初始化的内存区域
 4int BSS ; 
 5//位于数据段,存放在程序中已经初始化的内存区域
 6int data = 100 ; 
 7//静态区
 8static int y ; 
 9int stack(void) ; 
10
11int main(void)
12{
13    //静态区
14    static int  k  ;    
15//栈区内存自动申请自动释放
16int i , j ;     
17    int *p = NULL ;
18//堆区内存手动申请手动释放
19    p = malloc(1024) ; 
20    free(p);
21    return 0 ;
22}
23int stack(void)
24{
25    //栈区
26    int i ; 
27    return 0 ;
28}

 那么,这些段最终又是怎么被加载的呢?

  我们以ARM嵌入式Linux系统为例,嵌入式系统在编译链接的过程中会通过一个叫链接脚本的东西,告诉链接器,输入的程序文件中的各个段放到输出的文件中区,然后控制各个段在内存中的布局,这样程序在运行时就有地址空间布局了。

我们来看一个简单的连接脚本:test.lds

1//指定程序的输出格式
 2//大端、小端
 3OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm",
 4          "elf32-littlearm")
 5//输出对应平台的体系结构
 6OUTPUT_ARCH(arm)
 7//将SYMBOL的值设置成入口地址,这里一般都是_start
 8ENTRY(_start)
 9//指定共享库
10SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib");
11SECTIONS
12{
13    //表示从0x40008000这个地址开始加载各个段
14    . = 0x40008000 ; 
15    //文本段
16    .text :
17    {
18        test.o(.text);
19        *.o(.text);
20    }
21    //只读数据段
22    .rodata:{*(.rodata)}
23    //设置对齐格式为4个字节
24    align = 4 ; 
25}

二、位,字,字节,字符等基础

(1)字(Word:ARM体系结构(32位机)中,字的长度是32位,而在8/16位处理器体系结构中,字的长度是16长是衡量计算机性能的一个重要的技术指标。

(2)半字(Half-Word):在ARM体系结构(32位机)中,半字的长度是16位,而在8/16位处理器体系结构中,半字的长度与字的长度一致。

(3)字节(Byte:ARM体系结构(32位机)和8/16位处理器体系结构中,字节的长度均是8位。字节一般用于表示存储容量,例如

1KB = 2^10B = 1024 B   1MB = 2^10KB = 1024KB = 2^20B

1GB = 2^10MB=1024MB=2^30B   1TB=2^10GB=1024GB=2^40B

(4)(bit) :一个字节等于8bit , 1位的二进制的数码一般用01来表示。

(5)汉字:一个汉字由两个字节组成,如,在其它编码情况下,汉字有可能也由三个字节组成。

(6)字母:一个字母由一个字节组成,如“V”

(7)字符:字符指一个字母或一个字或一个标点或一个符号,不一定几个字节,看情况定。

三、进制转换基础

(1)十进制

我们在日常生活中,只要是和钱相关的东西,我们都在使用十进制,包括我们在学习数学课程的时候,经常用到的也是十进制。十进制也就是基数为10,逢101。在十进制中,一共使用10个不同的数字符号,这些符号处于不同位置时,其权值各不相同。

(2)二进制基数为2,逢21。在二进制中,使用01两种符号。

比如:

     1+1=10(这里的10是二进制的2)   1+0=1  11+1=100(这里的100是二进制的4)

如何算的呢?

如下:

640.png

(3)十六进制基数为16,逢161。十六进制使用16种不同的符号,它们与二进制的转换关系为:00000 10001 20010 30011 40100 50101 60110 70111 81000 91001 A1010 B1011 C1100 D1101 E1110 F1111

 以上的计算,我们可以使用Window自带的计算机来进行验证,如何使用?

在程序----->附件中找到计算器,如图3-1-1所示,这个就是我们电脑附件自带的计算器,它的功能很强大。

640.png

3-1-1 window系统自带的计算器

点击查看,选择程序员,如图3-1-2,我们就可以使用这个便捷的工具来计算了。

640.jpg

3-1-2 选择程序员专用的计算器

(4)接下来,我们来看一个例子,如何将字符串中的十进制数转化为十六进制数,或者将十六进制数转化为十进制数的算法。

1#include <stdio.h>
 2typedef char TUINT8 ;
 3typedef int  TUINT32;
 4TUINT32 Read_DecNumber(const TUINT8* str);
 5TUINT32 Read_HexNumber(const TUINT8* str); 
 6int main(void)
 7{
 8    int ret = Read_DecNumber("1000");
 9    int d = Read_HexNumber("A");
10    printf("将字符串中的数字转化为10进制数 :%d\n",ret);
11    printf("将字符串中的16进制数转化为10进制数 :%d\n",d);
12    return 0 ;
13}
14//将字符串中的数字转化为10进制数
15TUINT32 Read_DecNumber(const TUINT8* str){
16    TUINT32 value;
17    if (! str)
18        return 0;
19    value = 0;
20    while ((*str >= '0') && (*str <= '9')){
21        value = value*10 + (*str - '0');
22        str++;
23    }
24    return value;
25}
26//将字符串中的16进制数转化为10进制数
27TUINT32 Read_HexNumber(const TUINT8* str)
28{
29    TUINT32 value;
30    if (! str)
31        return 0;
32    value = 0;
33    while (1)
34{ 
35        if ((*str >= '0') && (*str <= '9'))
36            value = value*16 + (*str - '0');
37        else if ((*str >= 'A') && (*str <= 'F'))
38            value = value*16 + (*str - 'A') + 10;
39        else if ((*str >= 'a') && (*str <= 'f'))
40            value = value*16 + (*str - 'a') + 10;
41        else
42            break;      
43str++;
44    }
45    return value;
46}

编译运行结果,如图3-1-3所示。

640.jpg

       图3-1-3 10进制数与16进制数互转的结果

当然,嵌入式软件的开发基础远不止上述内容,但最最基础的也往往逃不过这些内容。

目录
相关文章
|
区块链 调度
嵌入式软件设计
嵌入式软件设计
97 0
|
7月前
|
算法 NoSQL 网络协议
嵌入式软件开发应该掌握哪些知识?
本文介绍了嵌入式软件及其在汽车、医疗设备等领域的应用。嵌入式软件是运行在嵌入式系统中的程序,负责控制硬件并提供特定功能。要成为嵌入式软件开发者,需掌握C/C++编程语言、数据结构与算法、Linux基础知识,如文件系统管理、命令操作。进阶知识包括文件I/O、线程进程、IPC和网络编程。高阶知识涉及ARM架构、系统移植、Bootloader、内核移植及Linux驱动开发,包括设备驱动编程和调试优化技术。
144 0
|
7月前
|
算法 机器人 程序员
嵌入式软件开发有没有捷径
嵌入式软件开发有没有捷径
46 0
|
编译器 程序员 Linux
嵌入式软件开发第一讲笔记
嵌入式软件开发第一讲笔记
61 0
|
存储 监控 数据可视化
|
安全 测试技术
嵌入式软件测试笔记3 | 嵌入式软件测试开发的多V模型
嵌入式软件测试笔记3 | 嵌入式软件测试开发的多V模型
10174 0
|
数据采集 存储 设计模式
嵌入式软件应用程序开发框架浅见
嵌入式Linux系统上开发,其实和PC上的软件开发很类似,一个好的框架,能保证系统的稳定性,同时也能降低开发难度。
241 0
|
程序员 项目管理 数据库
艾伟也谈项目管理,微型项目实践感悟
1. 什么是微型项目 微型项目是指绝大部分工作由一个人员负责的项目,这个核心成员负责项目的系统分析、构架、及绝大部分的编码工作。项目的持续时间一般不会超过一个月。项目的参与人员除了核心的程序员外还可能一部分辅助人员,包括第二程序员(负责一部分编码工作)、美工(负责界面设计)等。
1541 0
|
算法 Linux 网络协议
嵌入式软件学习方法 嵌入式软件知识
目前嵌入式软件的学习主要主要面向的几个操作系统是,Linux,WINCE、VxWorks等等。其中Linux是开源免费的,而且其源代码是开放的,更加适合我们学习嵌入式软件。下面我们介绍一下嵌入式软件学习的学习方法。
2016 0