一个Linux程序如何跑起来_zhangbaqing的博客-程序员秘密

技术标签: Linux  一个程序如何运行起来  

1. 一个Linux可执行程序如何产生?

 /*
 * filename: 01_helloWorld.c
 * */
#include <stdio.h>

int main(void)
{
    
    printf("hello world\n");

    return 0;
}
  • 预处理–预处理用于处理预处理命令,头文件的扩展、宏替换、注释的删除
    #include是一条预处理命令,它的作用是将头文件的内容包含到本文件中
    “包含”是指将头文件中的所有代码都会在#include处展开

    gcc -E 01_helloWorld.c > 01_helloWorld.i

    在预处理之后自动停止后面的操作,并把预处理的结果重定向到01_helloWorld.i这个文件中。

    为什么不能在头文件中定义全局变量?

    因为定义全局变量的代码会存在于所有以#include包含该头文件的文件中。也就是说,所有的这些文件,都会定义一个同样的全局变量,这样就不可避免的造成了冲突;

  • 编译环节----对源代码进行语法分析,并优化产生对应的汇编代码

    gcc -S 01_helloWorld.c -o 01_helloWorld.o

    这里是产生汇编文件,不是最终的二进制文本文件。

  • 汇编过程–将源代码翻译成可执行的指令,并生成目标文件

    gcc -c 01_helloWorld.c -o 01_helloWorld.o

  • 链接过程–将各个目标文件,包括库文件,链接成一个可执行程序
    地址和空间的分配
    符号解析
    重定向

    Linux环境下,该工作是由GNU的链接器ld完成的

    gcc -g -Wall -v 01_helloWorld.c -o 01_helloWorld

    通过-v选项可以查看完整和详细的gcc编译过程

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) 
COLLECT_GCC_OPTIONS='-g' '-Wall' '-v' '-o' '01_helloWorld' '-mtune=generic' '-march=x86-64'
 /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/cc1 -quiet -v 01_helloWorld.c -quiet -dumpbase 01_helloWorld.c -mtune=generic -march=x86-64 -auxbase 01_helloWorld -g -Wall -version -o /tmp/ccjd04QA.s
GNU C (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-36) (x86_64-redhat-linux)
	compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-36), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include
 /usr/local/include
 /usr/include
End of search list.
GNU C (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-36) (x86_64-redhat-linux)
	compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-36), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 592abcad67b46aec035d56e51f71d007
COLLECT_GCC_OPTIONS='-g' '-Wall' '-v' '-o' '01_helloWorld' '-mtune=generic' '-march=x86-64'
 as -v --64 -o /tmp/ccFbz7iT.o /tmp/ccjd04QA.s
GNU assembler version 2.27 (x86_64-redhat-linux) using BFD version version 2.27-34.base.el7
COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-g' '-Wall' '-v' '-o' '01_helloWorld' '-mtune=generic' '-march=x86-64'
 /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o 01_helloWorld /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. /tmp/ccFbz7iT.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o

2. 程序的构成

Linux下二进制可执行程序的一般格式为ELF格式,可以通过readelf命令查看可执行程序的ELF格式:

readelf -h 01_helloWorld # 查看ELF Header

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x400430
  Start of program headers:          64 (bytes into file)
  Start of section headers:          7168 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         36
  Section header string table index: 35

readelf -S 01_helloWorld # 查看Section Headers

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       0000000000000060  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400318  00000318
       000000000000003d  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000400356  00000356
       0000000000000008  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400360  00000360
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000400380  00000380
       0000000000000018  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000400398  00000398
       0000000000000030  0000000000000018  AI       5    24     8
  [11] .init             PROGBITS         00000000004003c8  000003c8
       000000000000001a  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         00000000004003f0  000003f0
       0000000000000030  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         0000000000400420  00000420
       0000000000000008  0000000000000000  AX       0     0     8
  [14] .text             PROGBITS         0000000000400430  00000430
       0000000000000182  0000000000000000  AX       0     0     16
  [15] .fini             PROGBITS         00000000004005b4  000005b4
       0000000000000009  0000000000000000  AX       0     0     4
  [16] .rodata           PROGBITS         00000000004005c0  000005c0
       000000000000001c  0000000000000000   A       0     0     8
  [17] .eh_frame_hdr     PROGBITS         00000000004005dc  000005dc
       0000000000000034  0000000000000000   A       0     0     4
  [18] .eh_frame         PROGBITS         0000000000400610  00000610
       00000000000000f4  0000000000000000   A       0     0     8
  [19] .init_array       INIT_ARRAY       0000000000600e10  00000e10
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .fini_array       FINI_ARRAY       0000000000600e18  00000e18
       0000000000000008  0000000000000008  WA       0     0     8
  [21] .jcr              PROGBITS         0000000000600e20  00000e20
       0000000000000008  0000000000000000  WA       0     0     8
  [22] .dynamic          DYNAMIC          0000000000600e28  00000e28
       00000000000001d0  0000000000000010  WA       6     0     8
  [23] .got              PROGBITS         0000000000600ff8  00000ff8
       0000000000000008  0000000000000008  WA       0     0     8
  [24] .got.plt          PROGBITS         0000000000601000  00001000
       0000000000000028  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000601028  00001028
       0000000000000004  0000000000000000  WA       0     0     1
  [26] .bss              NOBITS           000000000060102c  0000102c
       0000000000000004  0000000000000000  WA       0     0     1
  [27] .comment          PROGBITS         0000000000000000  0000102c
       000000000000002d  0000000000000001  MS       0     0     1
  [28] .debug_aranges    PROGBITS         0000000000000000  00001059
       0000000000000030  0000000000000000           0     0     1
  [29] .debug_info       PROGBITS         0000000000000000  00001089
       0000000000000091  0000000000000000           0     0     1
  [30] .debug_abbrev     PROGBITS         0000000000000000  0000111a
       0000000000000044  0000000000000000           0     0     1
  [31] .debug_line       PROGBITS         0000000000000000  0000115e
       0000000000000044  0000000000000000           0     0     1
  [32] .debug_str        PROGBITS         0000000000000000  000011a2
       00000000000000c1  0000000000000001  MS       0     0     1
  [33] .symtab           SYMTAB           0000000000000000  00001268
       0000000000000678  0000000000000018          34    52     8
  [34] .strtab           STRTAB           0000000000000000  000018e0
       00000000000001d2  0000000000000000           0     0     1
  [35] .shstrtab         STRTAB           0000000000000000  00001ab2
       000000000000014c  0000000000000000           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),
  l (large), p (processor specific)

ELF文件的主要内容是由各个sectionsymbol表组成
section列表中,最熟悉的有:

text段:代码段,用于保存可执行指令
data段:数据段,用于保存有非0初始值的全局变量和静态变量
bss段:用于保存没有初始值或者初值为0的全局变量和静态变量

当程序加载时,bss段中的变量会初始化为0

3. 程序是如何"跑"的

Linux环境下,可以使用strace跟踪系统调用,帮助自己研究系统程序加载、运行和退出的过程。
strace ./01_helloWorld

execve("./01_helloWorld", ["./01_helloWorld"], [/* 22 vars */]) = 0
brk(NULL)                               = 0x244a000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff59fe3f000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {
    st_mode=S_IFREG|0644, st_size=44167, ...}) = 0
mmap(NULL, 44167, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ff59fe34000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340$\2\0\0\0\0\0"..., 832) = 832
fstat(3, {
    st_mode=S_IFREG|0755, st_size=2151672, ...}) = 0
mmap(NULL, 3981792, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff59f852000
mprotect(0x7ff59fa14000, 2097152, PROT_NONE) = 0
mmap(0x7ff59fc14000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c2000) = 0x7ff59fc14000
mmap(0x7ff59fc1a000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ff59fc1a000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff59fe33000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff59fe31000
arch_prctl(ARCH_SET_FS, 0x7ff59fe31740) = 0
mprotect(0x7ff59fc14000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ)     = 0
mprotect(0x7ff59fe40000, 4096, PROT_READ) = 0
munmap(0x7ff59fe34000, 44167)           = 0
fstat(1, {
    st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff59fe3e000
write(1, "hello world\n", 12hello world
)           = 12
exit_group(0)                           = ?
+++ exited with 0 +++

Linux环境中,执行一个命令时,首先是由shell调用fork,然后再子进程中来真正执行这个命令。

首先是调用execve来加载01_helloWorld,然后ld会分别检查ld.so.nohwcapld.so.preload。其中,如果ld.so.nohwcap存在,则ld会加载其中未优化版本的库。如果ld.so.preload存在,则ld会加载其中的库,之后利用mmapld.so.cache映射到内存中,ld.so.cache中保存了库的路径,这样就完成了所有的准备工作。接着ld加载c库–libc.so.6,利用mmapmprotect设置程序的各个内存区域,到这里,程序运行环境已经完成。后面的write会向文件描述符1(标准输出)输出hello world\n,返回值为12,它表示write成功的字符个数。最后调用exit_group退出程序,此时参数为0,表示程序退出状态。

4. C库函数和系统调用

Linux环境下,使用的C库一般都是libc,它封装了几乎所有的系统调用,代码中使用的“系统调用”实际上就是调用C库中的库函数。C库函数同样位于用户态,所以编译器可以统一处理所有的函数调用,而不是区分该函数到底是不是系统调用。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Eric_qiushui/article/details/89404024

智能推荐

MySQL的DENSE_RANK有关的一道SQL需求_tulinying的博客-程序员秘密

1 需求背景遇到一个需求是这样的,对学生的成绩进行排名,成绩为0的,排名值为null。成绩大于0的,如,3、3、2、1,排序出来为1、1、2、3。如图 这就是一个dense rank的需求,另外比较麻烦的是要处理成绩为0的情况。2 解决方案解决方案如图: 3 相关脚本drop table myt;create table myt as select 3 scoreunion

外资银行_eastspy的博客-程序员秘密

1.负债业务:资金和银行资本的源泉;2.资产业务:银行利润的一个主要来源(利息收入);3.中间业务:新兴的银行业务品种,利润的又一增长点(非利息收入);外资银行在华战略:  入世后的三年,由于国家政策(未完全开放)和外资银行对中国市场的不熟悉,外资银行将主要以合作的形式开展业务;  之后,外资银行将利用自己在外汇业务领域的优势将首先展开外汇业务,在人民币业务上鉴于人民币来源的限制将在一定时期继

【数据分析学习笔记day22】Matplotlib绘图+figure+subplot+直方图hist+ 散点图scatter+柱状图bar+矩阵绘图plt.imshow()+颜色、标记、线型 刻度、标_汪雯琦的博客-程序员秘密

文章目录Matplotlib绘图figuresubplotfig.add_subplot(a, b, c)直方图:hist散点图:scatter柱状图:bar矩阵绘图:plt.imshow()plt.subplots()颜色、标记、线型刻度、标签、图例Matplotlib绘图Matplotlib 是一个 Python 的 2D绘图库,通过 Matplotlib,开发者可以仅需要几行代码,便可...

java中的内部类(嵌套类)_本地类 hero 的修饰符不合法;只允许使用“抽象”或“终态_haming_z的博客-程序员秘密

欢迎使用Markdown编辑器写博客本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦:Markdown和扩展Markdown简洁的语法代码块高亮图片链接和图片上传LaTex数学公式UML序列图和流程图离线写博客导入导出Markdown文件丰富的快捷键快捷键加粗 Ctrl + B 斜体 Ctrl + I 引用 Ctrl

ibm服务器开不了机维修,IBM X3500服务器故障开不了机_国观智库的博客-程序员秘密

服务器型号:IBM X3500系统:windowsserver2008故障:客户IBM X3500服务器正常用着突然关机了,然后就启动不了。前面指示灯有亮黄,两条内存,一个CPU,电源指示灯正常,经排除电容爆了,最好更换主板后正常启动。处理器数量(标配/最大) 1/2二级缓存 2x2MB(双核)或 2x4MB(四核)内存1(标配/最大) 1GB/48GB 全缓冲 667MHz 内存(通过 12个 ...

java基础06-IDEA中Debug的使用和进制转换_魏帅坤的博客-程序员秘密

1.Debug模式1.1 什么是Debug模式是供程序员使用的程序调试工具,它可以用于查看程序的执行流程,也可以用于追踪程序执行过程来调试程序。1.2 Debug介绍与操作流程如何加断点选择要设置断点的代码行,在行号的区域后面单击鼠标左键即可如何运行加了断点的程序在代码区域右键Debug执行看哪里看Debugger窗口看Console窗口点哪里点Step Into (F7)这个箭头,也可以直接按F7如何删除断点选择要删除的断点,单击

随便推点

T4M插件放入unity后怎么找不到_Unity动画系统详解4:如何用代码控制动画?_weixin_39683172的博客-程序员秘密

摘要:通过上一篇咱们知道了播放动画需要使用Animator,那么如何用代码控制动画呢?洪流学堂,让你快人几步。你好,我是跟着大智学Unity的萌新,我叫小新,这几周一起来复(yu)习(xi)动画系统。“智哥,我现在做的吃鸡游戏没有人物,看着好奇怪啊,从哪能搞点人物模型呢?”“号外号外:本人已与百度达成合作,以后有任何问题,只要在百度中输入问题就可瞬间获得答案,方便快捷,特此公告,造福大家。”“智哥...

bootstrap嵌套php,bootstrap模态框嵌套、tabindex属性、去除阴影的方法_蔡子健的博客-程序员秘密

模态框嵌套在开发中,遇到需要通过点击事件触发第一个模态框,触发后通过事件唤起第二个模态框,并且通过事件触发第三个模态框;即模态框嵌套。模态框嵌套需要用一个模态框包裹所涉及嵌套的模态框,从而点击触发模态框不会乱掉。HTML代码如下:tabindex模态框中tabindex属性w3c的解释是:tabindex 属性规定元素的 tab 键控制次序(当 tab 键用于导航时)。几乎所有浏览器均 tabin...

根据经纬度计算球面距离_lp310018931的博客-程序员秘密

在做LBS的应用的时候考虑到了这样一个问题,经纬度后面的小数的位数对于精度影响有多少?通过百度或者Google,可以获得如下信息:在纬度相等的情况下:经度每隔0.00001度,距离相差约1米;经度每隔0.0001度,距离相差约10米;经度每隔0.001度,距离相差约100米;经度每隔0.01度,距离相差约1000米;经度每隔0.1度,距离相差约10000米。在经

JSP include指令报错_平头小强的博客-程序员秘密

错误提示:Multiple annotations found at this line: - Duplicate local variable path - Duplicate local variable   basePath重复变量,因为&amp;lt;%@include%&amp;gt;引进的是代码,把代码包含进来,而新进JSP时,会默认生成&amp;lt;%String path = request.getCo...

关于ios11开发,window层不显示view,项目图片不显示。_ios view.window为空_ancoo_first的博客-程序员秘密

前段时间升级了ios11,发现问题还是很多的,首先,发现原本往window上显示的自定义弹窗都不见了,项目目录下加的的图片,在代码中就是不显示,很苦恼。经过挺长时间的摸索发现ios11神奇的问题。[[UIApplication sharedApplication].windows lastObject]这个不再可以取到window的最外层,取而代之的是[[UIApplication

学习笔记之八_安全(CCNA知识考点)_weixin_34254823的博客-程序员秘密

1、熟记标准的和扩展的IP访问列表号码范围。可以用于配置标准的IP访问列表的号码范围是1~99和1300~1999。扩展的IP访问列表的号码范围100~199和2000~2699。2、理解术语"implicit deny(隐含拒绝)"。每个访问列表的末尾都是一个隐含拒绝语句。它的意思是,如果一个包没有和访问列表中的任何一行匹配,那么将被丢弃。还有,如果在列表中除了deny...

推荐文章

热门文章

相关标签