摘 要
本文通过分析hello程序从C文件如何转变为可执行文件的全过程,包括预处理、编译、汇编、链接阶段,每一步如何对上一步形成的文件进行操作,形成新文件的过程。hello进程在shell执行的过程,存储管理的过程,I/O处理的过程。以这些过程的分析为例,阐明整个程序的生命周期。
关键词:预处理;编译;汇编;连接;存储管理
目 录
6.2 简述壳Shell-bash的作用与处理流程 - 10 -
7.2 Intel逻辑地址到线性地址的变换-段式管理 - 11 -
7.3 Hello的线性地址到物理地址的变换-页式管理 - 11 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 11 -
7.7 hello进程execve时的内存映射 - 11 -
第1章 概述
1.1 Hello简介
1.什么是Hello程序:
hello程序由用户通过键盘输入,根据高级语言的语法规范形成编译器能够读懂的代码。
- Hello程序是怎么形成的:
完成后的hello.c文件依次经过编译器的预处理对源代码进行转换、编译得到汇编语言代码、汇编再将汇编语言转换为机器语言,最后与库函数进行链接并进行重定位,形成可执行文件。
- Hello的可执行文件:
hello的可执行文件可以通过shell运行并传入命令行参数。shell同样是一个程序,它会通过fork函数为hello创建一个进程,再通过execve执行hello。操作系统的并发机制让hello程序能够与其他程序分片运行。
- 作为一个进程,hello是由操作系统将其的各类信息包括代码、数据等从磁盘加载到内存得以有效的执行。CPU读取其代码并以流水线的方式执行,通过高速缓存高效的读取指令,将程序的各个指令在硬件上实现。
- 当hello对数据进行处理时,其空间在内存上申请。操作系统提供的虚拟内存机制为每个进程维护了自己的空间,从而不会相互干扰。计算机存储结构层层递进,下一级作为上一级的缓存,让hello的数据能够从磁盘传输到CPU寄存器。
- TLB、分级页表等机制又为数据在内存中的高效访问提供了支持,合理的信号机制又让hello程序能够应对执行中的各种情况。操作系统将I/O设备都抽象为了文件,将底层与应用层隔离,将用户态与内核态隔离,通过描述符与接口,让hello程序能够间接调用硬件进行输入输出,例如从键盘读入字符,向屏幕输出内容。hello程序终止后,父进程shell与操作系统一同将其回收,释放其运行过程中占用的内存空间。
- P2P过程:即From program to process,从程序到进程。hello程序编写后,通过预处理、编译、汇编和链接等四个步骤形成一个可执行文件。其中,预处理是指预处理器(Preprocessor)处理以#开始的预编译指令,如宏定义(#define)、头文件引用(#include)、条件编译(#ifdef)等;编译是指使用编译器(Compiler)将C语言,翻译汇编代码;汇编是指使用汇编器(Assembler)将汇编代码翻译成二进制机器语言;而链接是指使用链接器(Linker)将汇编器生成的目标文件外加库链接为一个可执行文件。
- 020过程:
shell首先fork一个子进程,然后通过execve加载并执行hello,映射虚拟内存,进入程序入口后将程序载入物理内存,进入main函数执行目标代码,CPU为运行的hello分配时间片执行逻辑控制流。当程序运行结束后,shell父进程负责回收hello进程,内核删除相关数据结构。即,从0开始,以0结束,为020。
1.2 环境与工具
1.硬件环境:Intel Core i5 X64 CPU;2.5GHz;8G RAM;128G SSD + 1T HDD;
2.软件环境:Windows 10 64位; VMware 14;Ubuntu 16.04 LTS 64位;
3.开发与调试工具:GDB;EDB;OBJDUMP;READELF;CodeBlocks 64位;vim/gedit+gcc;
1.3 中间结果
1.hello.c:hello源文件;
2.hello.i:hello.c预编译的结果,用于研究预编译的作用以及进行编译器的下一步编译操作;
3.hello.s:hello.i编译后的结果,用于研究汇编语言以及编译器的汇编操作,可以与hello.c对应,分析底层的实现;
4.hello.o:hello.s汇编后的结果,可重定位目标程序,没有经过链接,用于链接器或编译器链接生成最终可执行程序;
5.hello.out:hello.o链接后生成的可执行目标文件,可以用来反汇编或者通过EDB、GDB等工具分析链接过程以及程序运行过程,包括进入main函数前后发生的过程;
6.hello:同hello.out,由gcc -m64 -no-pie -fno-PIC hello.c -o
hello命令直接生成;
1.4 本章小结
本章简要介绍了hello程序从代码到编译生成,到执行,再到终止的过程与操作系统发生的事件,分析了其P2P和020的过程,列出了本次任务的硬件、软件环境和调试工具,并且列举了任务过程中出现的中间产物及其作用。
第2章 预处理
2.1 预处理的概念与作用
1.预处理的概念:
(1)预处理是指预处理器(cpp)根据以字符#开头的命令,修改原始的C程序,比如#include <stdio.h>命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入程序文本中。其他常见的预处理指令还有#define(定义宏)、#if、#ifdef、#endif等。
(2)预处理的结果就得到了另外一个C程序,通常是以.i作为文件扩展名。
2.预处理的作用:
预处理的过程中,对于引用一些封装的库或者代码的这些命令来说,预处理器会读取头文件中用到的库的代码,将这段代码直接插入到程序文件中;对于宏定义来说,会完成对宏定义的替换;注释会直接删除掉。最后将处理过后的新的文本保存在hello.i中。预处理阶段的作用是让编译器在随后对文本进行编译的过程中,更加方便,因为访问库函数这类操作在预处理阶段已经完成,减少了编译器的工作。