嵌入式C语言一

工具安装

ubuntu 安装vim

apt-get install vim
sudo apt-get install vim

ubuntu 安装gcc

apt-get install gcc
sudo apt-get install gcc

一个简单的C程序

#include <stdio.h>
int main(void){
        printf("hello world\n");
        return 0;
}
qrrk@qrrk-virtual-machine:~/桌面$ vim main.c
qrrk@qrrk-virtual-machine:~/桌面$ gcc -o hello main.c
qrrk@qrrk-virtual-machine:~/桌面$ ./hello
hello world

● -E:只对C源程序进行预处理,不编译。
● -S:只编译到汇编文件,不再汇编。
● -c:只编译生成目标文件,不进行链接。
● -o:指定输出的可执行文件名。
● -g:生成带有调试信息的debug文件。
● -O2:代码编译优化等级,一般选择2。
● -W:在编译中开启警告(warning)信息。
● -I:大写的I,在编译时指定头文件的路径。
● -l:小写的l(like首字母),指定程序使用的函数库。
● -L:大写的L(like首字母),指定函数库的路径。

使用make
Pastedimage20230517222907
在同一级目录下编写makefile
Pastedimage20230517223409
Git代码管理
Git | 且任容枯 (qierenrongku.github.io)

计算机体系结构和CPU工作原理

嵌入式开发很大一部分工作跟底层紧密相关,如系统移植、BSP开发、驱动开发等,和芯片、硬件打交道的地方比较多
BSP开发(BSP,全称Board Support Package,汉语意思即_板级支持包_,BSP工程师,顾名思义就是负责板级支持包的开发、调试和维护工作)

cpu内部结构及工作原理

Pastedimage20230517224041

CPU设计流程

Pastedimage20230517224334

  • 设计芯片规格:设计出芯片基本的框架、功能,进行模块划分
  • HDL代码实现:使用VHDL或Verilog硬件描述语言把要实现的硬件功能描述出来,接着通过EDA工具不断仿真、修改和验证,直到芯片的逻辑功能完全正确。这种仿真我们一般称为前端仿真,简称前仿。前仿只验证芯片的逻辑功能是否正确,不考虑延时等因素。芯片公司内部一般也会设有数字IC验证工程师岗位,招聘工程师专门从事这个工作。加法器
module adder(
	input x, y,
	output carry, out
);
	assign {out, carry} = x + y;
endmodule
  • 逻辑综合:通过EDA工具就可以将HDL代码转换成具体的逻辑门电路
    Pastedimage20230517224926
  • 仿真验证:通过逻辑综合生成的门级电路,已经包含了延时等各种信息,接下来还需要对这些门级电路进行进一步的静态时序分析和验证。为了提高工作效率,除了使用仿真软件,有时候也会借助FPGA平台进行验证。前端仿真发生在逻辑综合之前,专注于验证电路的逻辑功能是否正确;逻辑综合后的仿真,一般称为后端仿真,简称后仿。后端仿真会考虑延时等因素。后端仿真通过后,从HDL代码到生成门级网表电路,整个芯片的前端设计就结束了。
  • 后端设计:对门级网表电路不断完善和优化,将其进一步设计成物理版图,也就是芯片代工厂做掩膜版需要的电路版图
    1、 DFT:Design For Test,可测试性设计。芯片内部一般会自带测试电路,如插入扫描链、引出JTAG调试接口。2、 布局规划:各个IP电路模块的摆放位置、时钟线综合、信号线的布局等。3、物理版图验证:检查设计规则、连线宽度、间距是否符合工艺要求和电气规则

计算机体系结构

Pastedimage20230517225616Pastedimage20230517225728

混合结构:SoC芯片内部的Cache层采用哈弗架构,集成了指令Cache和数据Cache;SoC芯片外部则采用冯·诺依曼架构,工程实现简单;先到这两个Cache中看看要读取的数据是不是已经缓存到这里了,如果没有缓存命中,再到内存中读取
Pastedimage20230517230032

CPU性能提升:流水线

Pastedimage20230517230446

总线和地址

地址:CPU管脚发出的信号,也就是存储单元对应的编号,即地址(CPU管脚发出的一组地址控制信号,在经过译码器译码)
总线:总线其实就是各种数字信号的集合,包括地址信号、数据信号、控制信号等

指令集和微架构

图灵原型机的基本思想是:任何复杂的运算都可以分解为有限个基本指令的组合来完成。我们的CPU在设计的时候就是这么干的,只支持有限个基本的运算指令,如加、减、乘、与、或、非、移位、跳转等。这些指令通过不同的组合,可以构成不同的指令序列(程序),实现不同的逻辑功能。
不同架构的处理器支持的指令类型是不同的。ARM架构的处理器只支持ARM指令,X86架构的处理器只支持X86指令。如果你在ARM架构的处理器上运行X86指令,就无法运行,报未定义指令的错误,因为ARM架构的处理器只支持ARM指令集中定义的指令。CPU支持的有限个指令的集合,我们称之为指令集。
微架构,对应的英文是Microarchitecture,也就是处理器架构。集成电路工程师在设计处理器时,会按照指令集规定的指令,设计具体的译码和运算电路来支持这些指令的运行;指令集在CPU处理器内部的具体硬件电路的实现,我们就称为微架构。一套相同的指令集,可以由不同形式的电路实现,可以有不同的微架构。

ARM体系结构与汇编语言

计算机的指令集一般可分为4种:复杂指令集(CISC)、精简指令集(RISC)、显式并行指令集(EPIC)和超长指令字指令集(VLIW)

ARM寻址方式

  • 寄存器寻址:通过寄存器名就可以直接对寄存器中的数据进行读写
  • 立即数寻址:在立即数寻址中,ARM指令中的操作数为一个常数。立即数以#为前缀,0x前缀表示该立即数为十六进制,不加前缀默认是十进制。
  • 寄存器偏移寻址:
  • 寄存器间接寻址:
  • 基址寻址:
  • 多寄存器寻址:
  • 相对寻址:

c和汇编混合编程

下载交叉编译链 gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

wget https://releases.linaro.org/components/toolchain/binaries/4.9-2017.01/arm-linux-gnueabihf/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

创建一个文件夹并把编译链解压到该文件夹

sudo mkdir /usr/local/arm
sudo tar -vxf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz -C /usr/local/arm

配置环境变量

1、打开编辑~/.bashrc 文件
sudo vim ~/.bashrc
2、在最底部添加以下内容
export PATH=$PATH:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin
3、使环境变量立即生效
source ~/.bashrc

安装其他库

sudo apt-get install lsb-core lib32stdc++6

验证编译器是否安装成功(查看版本号命令)

arm-linux-gnueabihf-gcc -v

程序的编译、链接、安装和运行

readelf -h a.out 
ELF 头:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF32
  数据:                              2 补码,小端序 (little endian)
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              EXEC (可执行文件)
  系统架构:                          ARM
  版本:                              0x1
  入口点地址:               0x10328
  程序头起点:          52 (bytes into file)
  Start of section headers:          68216 (bytes into file)
  标志:             0x5000200, Version5 EABI, soft-float ABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         29
  Section header string table index: 28
readelf -S a.out 
There are 29 section headers, starting at offset 0x10a78:

节头:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        00010154 000154 000013 00   A  0   0  1
  [ 2] .note.gnu.bu[...] NOTE            00010168 000168 000024 00   A  0   0  4
  [ 3] .note.ABI-tag     NOTE            0001018c 00018c 000020 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        000101ac 0001ac 00002c 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          000101d8 0001d8 000050 10   A  6   1  4
  [ 6] .dynstr           STRTAB          00010228 000228 00004e 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          00010276 000276 00000a 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         00010280 000280 000030 00   A  6   1  4
  [ 9] .rel.dyn          REL             000102b0 0002b0 000008 08   A  5   0  4
  [10] .rel.plt          REL             000102b8 0002b8 000020 08  AI  5  21  4
  [11] .init             PROGBITS        000102d8 0002d8 00000c 00  AX  0   0  4
  [12] .plt              PROGBITS        000102e4 0002e4 000044 04  AX  0   0  4
  [13] .text             PROGBITS        00010328 000328 0001b4 00  AX  0   0  4
  [14] .fini             PROGBITS        000104dc 0004dc 000008 00  AX  0   0  4
  [15] .rodata           PROGBITS        000104e4 0004e4 000134 00   A  0   0  4
  [16] .ARM.exidx        ARM_EXIDX       00010618 000618 000008 00  AL 13   0  4
  [17] .eh_frame         PROGBITS        00010620 000620 000004 00   A  0   0  4
  [18] .init_array       INIT_ARRAY      0002ff10 00ff10 000004 04  WA  0   0  4
  [19] .fini_array       FINI_ARRAY      0002ff14 00ff14 000004 04  WA  0   0  4
  [20] .dynamic          DYNAMIC         0002ff18 00ff18 0000e8 08  WA  6   0  4
  [21] .got              PROGBITS        00030000 010000 000024 04  WA  0   0  4
  [22] .data             PROGBITS        00030024 010024 000010 00  WA  0   0  4
  [23] .bss              NOBITS          00030034 010034 00000c 00  WA  0   0  4
  [24] .comment          PROGBITS        00000000 010034 000025 01  MS  0   0  1
  [25] .ARM.attributes   ARM_ATTRIBUTES  00000000 010059 00002a 00      0   0  1
  [26] .symtab           SYMTAB          00000000 010084 0006d0 10     27  85  4
  [27] .strtab           STRTAB          00000000 010754 00021d 00      0   0  1
  [28] .shstrtab         STRTAB          00000000 010971 000105 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), y (purecode), p (processor specific)

一个可执行文件通常由不同的段(section)构成:代码段、数据段、BSS段、只读数据段等。每个section用一个section header来描述,包括段名、段的类型、段的起始地址、段的偏移和段的大小等。一个可执行文件中的每一个section都有一个section header,将这些section headers集中放到一起,就是section header table,翻译成中文就是节头表。我们可以使用readelf-S命令来查看一个可执行文件的节头表。

一个可执行文件的基本构成:一个可执行文件由一系列section组成,section header table自身也是以一个section的形式存储在可执行文件中的。section header table里的各个section header用来描述各个section的名称、类型、起始地址、大小等信息。除此之外,可执行文件还会有一个文件头ELF header,用来描述文件类型、要运行的处理器平台、入口地址等信息。当程序运行时,加载器会根据此文件头来获取可执行文件的一些信息。

Pastedimage20230522230824

函数翻译成二进制指令放在代码段中,初始化的全局变量和静态局部变量放在数据段中,未初始化的全局变量和静态变量会放置在BSS段
程序中定义的一些字符串、printf函数打印的字符串常量则放置在只读数据段.rodata

预处理

通过#pragma预处理命令可以设定编译器的状态,指示编译器完成一些特定的动作。
● #pragma pack([n]):指示结构体和联合成员的对齐方式。
● #pragma message("string"):在编译信息输出窗口打印自己的文本信息。
● #pragma warning:有选择地改变编译器的警告信息行为。
● #pragma once:在头文件中添加这条指令,可以防止头文件多次编译。

符号表与重定位

 readelf -s a.out 

Symbol table '.dynsym' contains 5 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.4 (3)
     3: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.4 (3)
     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND _[...]@GLIBC_2.34 (2)

Symbol table '.symtab' contains 109 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00010154     0 SECTION LOCAL  DEFAULT    1 .interp
     2: 00010168     0 SECTION LOCAL  DEFAULT    2 .note.gnu.build-id
     3: 0001018c     0 SECTION LOCAL  DEFAULT    3 .note.ABI-tag
     4: 000101ac     0 SECTION LOCAL  DEFAULT    4 .gnu.hash
     5: 000101d8     0 SECTION LOCAL  DEFAULT    5 .dynsym
     6: 00010228     0 SECTION LOCAL  DEFAULT    6 .dynstr
     7: 00010276     0 SECTION LOCAL  DEFAULT    7 .gnu.version
     8: 00010280     0 SECTION LOCAL  DEFAULT    8 .gnu.version_r
     9: 000102b0     0 SECTION LOCAL  DEFAULT    9 .rel.dyn
    10: 000102b8     0 SECTION LOCAL  DEFAULT   10 .rel.plt
    11: 000102d8     0 SECTION LOCAL  DEFAULT   11 .init
    12: 000102e4     0 SECTION LOCAL  DEFAULT   12 .plt
    13: 00010328     0 SECTION LOCAL  DEFAULT   13 .text
    14: 000104dc     0 SECTION LOCAL  DEFAULT   14 .fini
    15: 000104e4     0 SECTION LOCAL  DEFAULT   15 .rodata
    16: 00010618     0 SECTION LOCAL  DEFAULT   16 .ARM.exidx
    17: 00010620     0 SECTION LOCAL  DEFAULT   17 .eh_frame
    18: 0002ff10     0 SECTION LOCAL  DEFAULT   18 .init_array
    19: 0002ff14     0 SECTION LOCAL  DEFAULT   19 .fini_array
    20: 0002ff18     0 SECTION LOCAL  DEFAULT   20 .dynamic
    21: 00030000     0 SECTION LOCAL  DEFAULT   21 .got
    22: 00030024     0 SECTION LOCAL  DEFAULT   22 .data
    23: 00030034     0 SECTION LOCAL  DEFAULT   23 .bss
    24: 00000000     0 SECTION LOCAL  DEFAULT   24 .comment
    25: 00000000     0 SECTION LOCAL  DEFAULT   25 .ARM.attributes
    26: 00000000     0 FILE    LOCAL  DEFAULT  ABS crt1.o
    27: 0001018c     0 NOTYPE  LOCAL  DEFAULT    3 $d
    28: 0001018c    32 OBJECT  LOCAL  DEFAULT    3 __abi_tag
    29: 00010328     0 NOTYPE  LOCAL  DEFAULT   13 $a
    30: 00010364     0 NOTYPE  LOCAL  DEFAULT   13 $d
    31: 00010618     0 NOTYPE  LOCAL  DEFAULT   16 $d
    32: 000104e4     0 NOTYPE  LOCAL  DEFAULT   15 $d
    33: 00030024     0 NOTYPE  LOCAL  DEFAULT   22 $d
    34: 00000000     0 FILE    LOCAL  DEFAULT  ABS crti.o
    35: 0001036c     0 NOTYPE  LOCAL  DEFAULT   13 $a
    36: 0001036c     0 FUNC    LOCAL  DEFAULT   13 call_weak_fn
    37: 00010388     0 NOTYPE  LOCAL  DEFAULT   13 $d
    38: 000102d8     0 NOTYPE  LOCAL  DEFAULT   11 $a
    39: 000104dc     0 NOTYPE  LOCAL  DEFAULT   14 $a
    40: 00000000     0 FILE    LOCAL  DEFAULT  ABS crtn.o
    41: 000102e0     0 NOTYPE  LOCAL  DEFAULT   11 $a
    42: 000104e0     0 NOTYPE  LOCAL  DEFAULT   14 $a
    43: 00000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    44: 000104e8     0 NOTYPE  LOCAL  DEFAULT   15 $d
    45: 000104e8     0 OBJECT  LOCAL  DEFAULT   15 all_implied_fbits
    46: 00010390     0 NOTYPE  LOCAL  DEFAULT   13 $a
    47: 00010390     0 FUNC    LOCAL  DEFAULT   13 deregister_tm_clones
    48: 000103b0     0 NOTYPE  LOCAL  DEFAULT   13 $d
    49: 000103bc     0 NOTYPE  LOCAL  DEFAULT   13 $a
    50: 000103bc     0 FUNC    LOCAL  DEFAULT   13 register_tm_clones
    51: 000103e8     0 NOTYPE  LOCAL  DEFAULT   13 $d
    52: 00030028     0 NOTYPE  LOCAL  DEFAULT   22 $d
    53: 000103f4     0 NOTYPE  LOCAL  DEFAULT   13 $a
    54: 000103f4     0 FUNC    LOCAL  DEFAULT   13 __do_global_dtors_aux
    55: 00010418     0 NOTYPE  LOCAL  DEFAULT   13 $d
    56: 00030034     1 OBJECT  LOCAL  DEFAULT   23 completed.0
    57: 0002ff14     0 NOTYPE  LOCAL  DEFAULT   19 $d
    58: 0002ff14     0 OBJECT  LOCAL  DEFAULT   19 __do_global_dtor[...]
    59: 0001041c     0 NOTYPE  LOCAL  DEFAULT   13 $a
    60: 0001041c     0 FUNC    LOCAL  DEFAULT   13 frame_dummy
    61: 0002ff10     0 NOTYPE  LOCAL  DEFAULT   18 $d
    62: 0002ff10     0 OBJECT  LOCAL  DEFAULT   18 __frame_dummy_in[...]
    63: 00030034     0 NOTYPE  LOCAL  DEFAULT   23 $d
    64: 00000000     0 FILE    LOCAL  DEFAULT  ABS main.c
    65: 0003002c     0 NOTYPE  LOCAL  DEFAULT   22 $d
    66: 00030038     0 NOTYPE  LOCAL  DEFAULT   23 $d
    67: 00010578     0 NOTYPE  LOCAL  DEFAULT   15 $d
    68: 00010420     0 NOTYPE  LOCAL  DEFAULT   13 $a
    69: 00010474     0 NOTYPE  LOCAL  DEFAULT   13 $d
    70: 0003003c     4 OBJECT  LOCAL  DEFAULT   23 uninit_local_val.1
    71: 00030030     4 OBJECT  LOCAL  DEFAULT   22 local_val.0
    72: 00000000     0 FILE    LOCAL  DEFAULT  ABS sub.c
    73: 0001047c     0 NOTYPE  LOCAL  DEFAULT   13 $a
    74: 00000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    75: 00010588     0 NOTYPE  LOCAL  DEFAULT   15 $d
    76: 00010588     0 OBJECT  LOCAL  DEFAULT   15 all_implied_fbits
    77: 00010620     0 NOTYPE  LOCAL  DEFAULT   17 $d
    78: 00010620     0 OBJECT  LOCAL  DEFAULT   17 __FRAME_END__
    79: 00000000     0 FILE    LOCAL  DEFAULT  ABS 
    80: 0002ff18     0 OBJECT  LOCAL  DEFAULT   20 _DYNAMIC
    81: 00030000     0 OBJECT  LOCAL  DEFAULT   21 _GLOBAL_OFFSET_TABLE_
    82: 000102e4     0 NOTYPE  LOCAL  DEFAULT   12 $a
    83: 000102f4     0 NOTYPE  LOCAL  DEFAULT   12 $d
    84: 000102f8     0 NOTYPE  LOCAL  DEFAULT   12 $a
    85: 00030038     4 OBJECT  GLOBAL DEFAULT   23 uninit_val
    86: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_mai[...]
    87: 00030024     0 NOTYPE  WEAK   DEFAULT   22 data_start
    88: 0001047c    48 FUNC    GLOBAL DEFAULT   13 add
    89: 00000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.4
    90: 00030034     0 NOTYPE  GLOBAL DEFAULT   23 __bss_start__
    91: 00030040     0 NOTYPE  GLOBAL DEFAULT   23 _bss_end__
    92: 00030034     0 NOTYPE  GLOBAL DEFAULT   22 _edata
    93: 000104dc     0 FUNC    GLOBAL HIDDEN    14 _fini
    94: 00030040     0 NOTYPE  GLOBAL DEFAULT   23 __bss_end__
    95: 0003002c     4 OBJECT  GLOBAL DEFAULT   22 global_val
    96: 00030024     0 NOTYPE  GLOBAL DEFAULT   22 __data_start
    97: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    98: 00030028     0 OBJECT  GLOBAL HIDDEN    22 __dso_handle
    99: 000104e4     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
   100: 00030040     0 NOTYPE  GLOBAL DEFAULT   23 _end
   101: 00010328     0 FUNC    GLOBAL DEFAULT   13 _start
   102: 00030040     0 NOTYPE  GLOBAL DEFAULT   23 __end__
   103: 00030034     0 NOTYPE  GLOBAL DEFAULT   23 __bss_start
   104: 00010420    92 FUNC    GLOBAL DEFAULT   13 main
   105: 00030034     0 OBJECT  GLOBAL HIDDEN    22 __TMC_END__
   106: 000104ac    48 FUNC    GLOBAL DEFAULT   13 sub
   107: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.4
   108: 000102d8     0 FUNC    GLOBAL HIDDEN    11 _init

符号表主要用来保存源程序中各种符号的信息,包括符号的地址、类型、占用空间的大小
符号的类型主要有以下几种:
● OBJECT:对象类型,一般用来表示我们在程序中定义的变量。
● FUNC:关联的是函数名或其他可引用的可执行代码。
● FILE:该符号关联的是当前目标文件的名称。
● SECTION:表明该符号关联的是一个section,主要用来重定位。
● COMMON:表明该符号是一个公用块数据对象,是一个全局弱符号,在当前文件中未分配空间。
● TLS:表明该符号对应的变量存储在线程局部存储中。
● NOTYPE:未指定类型,或者目前还不知道该符号类型。

程序的安装

软件安装的过程其实就是将一个可执行文件安装到ROM的过程
在Linux环境下,我们一般将可执行文件直接复制到系统的官方路径/bin、/sbin、/usr/bin下,程序运行时直接从这些系统默认的路径下去查找可执行文件,将其加载到内存运行。

编写代码

#include <stdio.h>

int main(void){
    printf("hello world\n");
    return 0;
}
gcc main.c -o a.out

制作软件包

Linux操作系统一般可分为两派:Redhat系和Debian系。Redhat系使用RPM包管理机制,而Debian系,像Debian、Ubuntu等操作系统则使用deb包管理机制
一个成熟的发布软件里,除了可执行文件,一般还会有配套的文档说明、图标等,程序开发者将这些文档一起打包发布,提供自动安装的功能,更方便用户下载和安装。在制作deb包时,除了可执行文件,还需要一些控制信息来描述这个安装包,如软件的版本、作者、安装包要安装的路径等,这些控制信息放在一个叫作control的文件里

qrrk@qrrk-virtual-machine:~/桌面$ tree helloworld/
helloworld/
├── DEBIAN
│   └── control
└── usr
    └── local
        └── bin
            └── helloworld  # 可执行文件
# control文件
package:helloworld
version:1.0
architecture:i386
maintainer:wit
description: deb package demo
qrrk@qrrk-virtual-machine:~/桌面$ dpkg -b helloworld/ helloworld_1.0_i386.deb
qrrk@qrrk-virtual-machine:~/桌面$ sudo dpkg -i helloworld_1.0_i386.deb 
正在选中未选择的软件包 helloworld:i386。
(正在读取数据库 ... 系统当前共安装有 218803 个文件和目录。)
准备解压 helloworld_1.0_i386.deb  ...
正在解压 helloworld:i386 (1.0) ...
正在设置 helloworld:i386 (1.0) ...
qrrk@qrrk-virtual-machine:~/桌面$ helloworld
hello world
qrrk@qrrk-virtual-machine:~/桌面$ whereis helloworld 
helloworld: /usr/local/bin/helloworld
# dpkg -P helloworld 卸载程序及配置文件
# dpkg -r helloworldr 卸载helloworld程序

main函数分析

编译器在编译一个工程时,默认的程序入口是_start符号,而不是main。符号main是一个约定符号,它用来告诉编译器在一个项目中哪里是程序的入口点。程序员在开发一个项目时,也会遵守这个约定,使用main()函数作为项目的入口函数。其实在main()函数运行之前,已经有“先头部队”代码提前运行了:它们主要完成运行main()函数之前的一些初始化工作,如初始化堆栈指针等
● C语言运行的基本堆栈环境、进程环境。
● 动态库的加载、释放、初始化、清理等工作。
● 向main()函数传参argc、argv,调用main()函数执行。
● 在main()函数退出后,调用exit()函数,结束进程的运行

在嵌入式系统裸机环境下,系统上电后要初始化时钟、内存,然后设置堆栈指针,而在普通的操作系统环境下,内存等各种硬件设备已经工作,堆栈环境也已经初始化完毕,不需要做这一部分工作了,保存一些上下文环境后就可以直接跳到第一个C语言入口函数:__libc_start_main
__libc_start_main函数大致流程如下:首先设置程序运行的进程环境,加载共享库,解析用户输入的参数,将参数传递给main()函数,最后调用main()函数运行。main()函数运行结束后,再调用exit函数结束整个进程

链接静态库

test.c

int add(int a, int b){
    return a + b;
}
int sub(int a, int b){
    return a - b;
}

int mul(int a, int b){
    return a * b;
}

int div(int a, int b){
    return a / b;
}

main.c

#include <stdio.h>

int add(int, int);

int main(void){
    int sum = 0;
    sum = add(1,2);
    printf("sum=%d\n", sum);
    return 0;
}
qrrk@qrrk-virtual-machine:~/桌面/study6$ cd ../study7
qrrk@qrrk-virtual-machine:~/桌面/study7$ gcc -c test.c 
qrrk@qrrk-virtual-machine:~/桌面/study7$ ar rcs libtest.a test.o
qrrk@qrrk-virtual-machine:~/桌面/study7$ gcc main.c -L. -ltest
qrrk@qrrk-virtual-machine:~/桌面/study7$ ./a.out 
sum=3

使用ar命令制作静态库时,一些常用的参数介绍如下。
● -c:禁止在创建库时产生的正常消息。
● -r:如果指定的文件已经在库中存在,则替换它。
● -s:无论库是否更新都强制重新生成新的符号表。
● -d:从库中删除指定的文件。
● -o:对压缩文档成员进行排序。
● -q:向库中追加指定文件。
● -t:打印库中的目标文件。
● -x:解压库中的目标文件。

动态链接

静态链接的缺点:生成的可执行文件体积较大,当多个程序引用相同的公共代码时,这些公共代码会多次加载到内存,浪费内存资源。

qrrk@qrrk-virtual-machine:~/桌面/study7$ gcc -fPIC -shared add.c sub.c mul.c div.c -o libtest.so
qrrk@qrrk-virtual-machine:~/桌面/study7$ gcc main.c libtest.so 
qrrk@qrrk-virtual-machine:~/桌面/study7$ ./a.out 
./a.out: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory
qrrk@qrrk-virtual-machine:~/桌面/study7$ sudo cp libtest.so /usr/lib
[sudo] qrrk 的密码: 
qrrk@qrrk-virtual-machine:~/桌面/study7$ ./a.out 
sum=3

可执行文件a.out是采用动态链接生成的,所以在运行a.out之前,libtest.so这个动态链接库要放到/lib、/usr/lib等系统默认的库路径下,否则a.out就会动态链接失败,无法正常运行

在Linux环境下,当我们运行一个程序时,操作系统首先会给程序fork一个子进程,接着动态链接器被加载到内存,操作系统将控制权交给动态链接器,让动态链接器完成动态库的加载和重定位操作,最后跳转到要运行的程序

posted @   且任荣枯  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~
点击右上角即可分享
微信分享提示