一个Linux程序如何跑起来
文章目录
- 1. 一个`Linux`可执行程序如何产生?
- 2. 程序的构成
- 3. 程序是如何"跑"的
- 4. C库函数和系统调用
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: ELF64Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: EXEC (Executable file)Machine: Advanced Micro Devices X86-64Version: 0x1Entry point address: 0x400430Start of program headers: 64 (bytes into file)Start of section headers: 7168 (bytes into file)Flags: 0x0Size of this header: 64 (bytes)Size of program headers: 56 (bytes)Number of program headers: 9Size of section headers: 64 (bytes)Number of section headers: 36Section header string table index: 35
readelf -S 01_helloWorld
# 查看Section Headers
Section Headers:[Nr] Name Type Address OffsetSize EntSize Flags Link Info Align[ 0] NULL 0000000000000000 000000000000000000000000 0000000000000000 0 0 0[ 1] .interp PROGBITS 0000000000400238 00000238000000000000001c 0000000000000000 A 0 0 1[ 2] .note.ABI-tag NOTE 0000000000400254 000002540000000000000020 0000000000000000 A 0 0 4[ 3] .note.gnu.build-i NOTE 0000000000400274 000002740000000000000024 0000000000000000 A 0 0 4[ 4] .gnu.hash GNU_HASH 0000000000400298 00000298000000000000001c 0000000000000000 A 5 0 8[ 5] .dynsym DYNSYM 00000000004002b8 000002b80000000000000060 0000000000000018 A 6 1 8[ 6] .dynstr STRTAB 0000000000400318 00000318000000000000003d 0000000000000000 A 0 0 1[ 7] .gnu.version VERSYM 0000000000400356 000003560000000000000008 0000000000000002 A 5 0 2[ 8] .gnu.version_r VERNEED 0000000000400360 000003600000000000000020 0000000000000000 A 6 1 8[ 9] .rela.dyn RELA 0000000000400380 000003800000000000000018 0000000000000018 A 5 0 8[10] .rela.plt RELA 0000000000400398 000003980000000000000030 0000000000000018 AI 5 24 8[11] .init PROGBITS 00000000004003c8 000003c8000000000000001a 0000000000000000 AX 0 0 4[12] .plt PROGBITS 00000000004003f0 000003f00000000000000030 0000000000000010 AX 0 0 16[13] .plt.got PROGBITS 0000000000400420 000004200000000000000008 0000000000000000 AX 0 0 8[14] .text PROGBITS 0000000000400430 000004300000000000000182 0000000000000000 AX 0 0 16[15] .fini PROGBITS 00000000004005b4 000005b40000000000000009 0000000000000000 AX 0 0 4[16] .rodata PROGBITS 00000000004005c0 000005c0000000000000001c 0000000000000000 A 0 0 8[17] .eh_frame_hdr PROGBITS 00000000004005dc 000005dc0000000000000034 0000000000000000 A 0 0 4[18] .eh_frame PROGBITS 0000000000400610 0000061000000000000000f4 0000000000000000 A 0 0 8[19] .init_array INIT_ARRAY 0000000000600e10 00000e100000000000000008 0000000000000008 WA 0 0 8[20] .fini_array FINI_ARRAY 0000000000600e18 00000e180000000000000008 0000000000000008 WA 0 0 8[21] .jcr PROGBITS 0000000000600e20 00000e200000000000000008 0000000000000000 WA 0 0 8[22] .dynamic DYNAMIC 0000000000600e28 00000e2800000000000001d0 0000000000000010 WA 6 0 8[23] .got PROGBITS 0000000000600ff8 00000ff80000000000000008 0000000000000008 WA 0 0 8[24] .got.plt PROGBITS 0000000000601000 000010000000000000000028 0000000000000008 WA 0 0 8[25] .data PROGBITS 0000000000601028 000010280000000000000004 0000000000000000 WA 0 0 1[26] .bss NOBITS 000000000060102c 0000102c0000000000000004 0000000000000000 WA 0 0 1[27] .comment PROGBITS 0000000000000000 0000102c000000000000002d 0000000000000001 MS 0 0 1[28] .debug_aranges PROGBITS 0000000000000000 000010590000000000000030 0000000000000000 0 0 1[29] .debug_info PROGBITS 0000000000000000 000010890000000000000091 0000000000000000 0 0 1[30] .debug_abbrev PROGBITS 0000000000000000 0000111a0000000000000044 0000000000000000 0 0 1[31] .debug_line PROGBITS 0000000000000000 0000115e0000000000000044 0000000000000000 0 0 1[32] .debug_str PROGBITS 0000000000000000 000011a200000000000000c1 0000000000000001 MS 0 0 1[33] .symtab SYMTAB 0000000000000000 000012680000000000000678 0000000000000018 34 52 8[34] .strtab STRTAB 0000000000000000 000018e000000000000001d2 0000000000000000 0 0 1[35] .shstrtab STRTAB 0000000000000000 00001ab2000000000000014c 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
文件的主要内容是由各个section
及symbol
表组成
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.nohwcap
和ld.so.preload
。其中,如果ld.so.nohwcap
存在,则ld
会加载其中未优化版本的库。如果ld.so.preload
存在,则ld
会加载其中的库,之后利用mmap
将ld.so.cache
映射到内存中,ld.so.cache
中保存了库的路径,这样就完成了所有的准备工作。接着ld
加载c
库–libc.so.6
,利用mmap
及mprotect
设置程序的各个内存区域,到这里,程序运行环境已经完成。后面的write
会向文件描述符1
(标准输出)输出hello world\n
,返回值为12,它表示write
成功的字符个数。最后调用exit_group
退出程序,此时参数为0,表示程序退出状态。
4. C库函数和系统调用
Linux
环境下,使用的C库一般都是libc
,它封装了几乎所有的系统调用,代码中使用的“系统调用”实际上就是调用C库中的库函数。C库函数同样位于用户态,所以编译器可以统一处理所有的函数调用,而不是区分该函数到底是不是系统调用。
一个Linux程序如何跑起来相关推荐
- 【历史上的今天】8 月 17 日:Oracle 创始人出生;第一个 COBOL 程序成功跑通!...
透过「历史上的今天」,从过去看未来,从现在亦可以改变未来. 今天是 2021 年 8 月 17 日,在科技历史上,都有哪些关键事件发生呢? 1944 年 8 月 17 日:Oracle(甲骨文)创始人 ...
- 【快速入门】创建你的第一个linux程序(详细教程)
介于有些同学想学习linux,但是又不知道如何着手,希望笔者能这篇博客给大家简单的参考,运行环境为centos+xshell. 一.快速入门 1.打开Linux命令行,输入以下命令,创建出名叫hell ...
- linux 易语言窗口程序_易语言开发Linux程序
令人兴奋的是易语言可以开发Linux程序,易语言是一个跨平台的开发工具,支持Windows及Linux.使用易语言开发Linux程序,您可以在Windows环境下编写基本于Windows的程序,及编写 ...
- 一个LINUX狂人的语录[转]
我已经半年没有使用 Windows 的方式工作了.Linux 高效的完成了我所有的工作. GNU/Linux 不是每个人都想用的.如果你只需要处理一般的事务,打游戏,那么你不需要了解下面这些了. 我不 ...
- 一个LINUX狂人的语录
我已经半年没有使用 Windows 的方式工作了.Linux 高效的完成了我所有的工作. GNU/Linux 不是每个人都想用的.如果你只需要处理一般的事务,打游戏,那么你不需要了解下面这些了. 我不 ...
- 一个Linux狂人的语录 (这篇文章值得推广)
一个Linux狂人的语录 分类: LINUX 我已经半年没有使用 Windows 的方式工作了.Linux 高效的完成了我所有的工作. GNU/Linux 不是每个人都想用的.如果你只需要处理一般的 ...
- 一个LINUX狂人的语录 1
一个LINUX狂人的语录 我已经半年没有使用 Windows 的方式工作了.Linux 高效的完成了我所有的工作. GNU/Linux 不是每个人都想用的.如果你只需要处理一般的事务,打游戏,那么你不 ...
- 一个LINUX狂人的语录(个人认为很精辟)
http://blog.chinaunix.net/uid-57160-id-2734431.html?page=2 我已经半年没有使用 Windows 的方式工作了.Linux 高效的完成了我所有的 ...
- 一个 Linux 狂人的语录
转载自:http://blog.csdn.net/bat603/article/details/1408283 我已经半年没有使用 Windows 的方式工作了.Linux 高效的完成了我所有的工作. ...
最新文章
- Js+Dhtml:WEB程序员简易开发工具包(预先体验版)
- 植物根际微生物组也有昼夜节律
- Perl通过WIN32 OLE来操作EXCEL
- linux主机电影源码,求::totem电影播放机源代码!!!!
- 【Python】关于jupyter几个不得不知道的tips
- SVN客户端--TortoiseSVN使用说明
- WebClient与WebRequest差异
- Window7无法访问 Window server 2008 R2文件服务器的共享
- python模仿声音_5秒钟让python克隆别人的声音
- React 16.7.0-alpha hooks 之规则
- 联想ibm服务器修改uefi,解决联想笔记本bios设置uefi变灰色的方法
- java。用类描述计算机中CPU的速度和硬盘的容量。要求Java应用程序有4个类,名字分别是PC、CPU、HardDisk和Test,其中Test是主类。
- 第25章 串行FLASH文件系统FatFs
- python数据挖掘课后题答案_中国大学MOOC《数据挖掘与python实践》章节答案
- spring中AutoWired/Quafifier/Primary及相关注解知识
- css3软键盘不盖住输入框的方法
- 连通图 P3387 缩点 模板
- Visual Studio Code介绍
- 2022-2028年中国汽车儿童安全座椅行业发展前景分析及市场需求预测报告
- 什么是黑苹果(Hackintosh)
热门文章
- php搭建文章类网站教程,PHPstudy搭建wordpress本地网站教程 | 自媒体培训教程-君墨...
- 创建数据库并指定字符集
- python 开发APP教程
- 针对运营商行业的虚拟化应用性能监测管理解决方案
- 图片管理系统空间 php,自建图片网站 27款开源图片相册管理系统
- windows10计算机无法启动不了,win10无法启动
- Docker - 容器内应用和外部非容器应用互相访问方法
- 腾讯35亿收下搜狗,BAT新一轮大战一触即发
- Android设计中如何切图.9.png(点9图)
- 发布个人项目jar包到maven中央仓库详解