**
摘 要
**
本论文旨在通过研究hello.c在linux系统下的整个生命周期。通过对hello.c的概述;hello.c的预处理;编译;汇编;链接;一直到hello.c的进程;存储;IO管理。了解hello的一生。通过使用linux操作系统,对hello.c进行执行,用gcc等工具进行实验。对深入理解计算机系统这本书进行总结与贯通。让程序员了解整个程序的周期,使学生对于课程的理解以及知识的升华有很大帮助。
关键词:hello;编译;汇编;链接;存储;进程;预处理;ELF;进程
等
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
**
目 录
**
第1章 概述… - 4 -
1.1 Hello简介… - 4 -
1.2 环境与工具… - 4 -
1.3 中间结果… - 4 -
1.4 本章小结… - 4 -
第2章 预处理… - 5 -
2.1 预处理的概念与作用… - 5 -
2.2在Ubuntu下预处理的命令… - 5 -
2.3 Hello的预处理结果解析… - 5 -
2.4 本章小结… - 5 -
第3章 编译… - 6 -
3.1 编译的概念与作用… - 6 -
3.2 在Ubuntu下编译的命令… - 6 -
3.3 Hello的编译结果解析… - 6 -
3.4 本章小结… - 6 -
第4章 汇编… - 7 -
4.1 汇编的概念与作用… - 7 -
4.2 在Ubuntu下汇编的命令… - 7 -
4.3 可重定位目标elf格式… - 7 -
4.4 Hello.o的结果解析… - 7 -
4.5 本章小结… - 7 -
第5章 链接… - 8 -
5.1 链接的概念与作用… - 8 -
5.2 在Ubuntu下链接的命令… - 8 -
5.3 可执行目标文件hello的格式… - 8 -
5.4 hello的虚拟地址空间… - 8 -
5.5 链接的重定位过程分析… - 8 -
5.6 hello的执行流程… - 8 -
5.7 Hello的动态链接分析… - 8 -
5.8 本章小结… - 9 -
第6章 hello进程管理… - 10 -
6.1 进程的概念与作用… - 10 -
6.2 简述壳Shell-bash的作用与处理流程… - 10 -
6.3 Hello的fork进程创建过程… - 10 -
6.4 Hello的execve过程… - 10 -
6.5 Hello的进程执行… - 10 -
6.6 hello的异常与信号处理… - 10 -
6.7本章小结… - 10 -
第7章 hello的存储管理… - 11 -
7.1 hello的存储器地址空间… - 11 -
7.2 Intel逻辑地址到线性地址的变换-段式管理… - 11 -
7.3 Hello的线性地址到物理地址的变换-页式管理… - 11 -
7.4 TLB与四级页表支持下的VA到PA的变换… - 11 -
7.5 三级Cache支持下的物理内存访问… - 11 -
7.6 hello进程fork时的内存映射… - 11 -
7.7 hello进程execve时的内存映射… - 11 -
7.8 缺页故障与缺页中断处理… - 11 -
7.9动态存储分配管理… - 11 -
7.10本章小结… - 12 -
第8章 hello的IO管理… - 13 -
8.1 Linux的IO设备管理方法… - 13 -
8.2 简述Unix IO接口及其函数… - 13 -
8.3 printf的实现分析… - 13 -
8.4 getchar的实现分析… - 13 -
8.5本章小结… - 13 -
结论… - 14 -
附件… - 15 -
参考文献… - 16 -
**
第1章 概述
**
1.1 Hello简介
根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
1.2 环境与工具
硬件环境:Intel Core i7 ,x64CPU,
8G RAM, 256G SSD
软件环境:VMware Workstation Ubuntu18.04.1 LTS
开发调试工具:vim,gcc,as,ld,edb,readelf,hexedit
1.3 中间结果
1.4 本章小结
本章主要简单介绍了 hello 的 p2p,020 过程,列出了本次实验信息:环境、中间结果。
(第1章0.5分)
**
第2章 预处理
**
2.1 预处理的概念与作用
程序设计领域中,预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。典型地,由预处理器(preprocessor) 对程序源代码文本进行处理,得到的结果再由编译器核心进一步编译。这个过程并不对程序的源代码进行解析,但它把源代码分割或处理成为特定的单位——(用C/C++的术语来说是)预处理记号(preprocessing token)用来支持语言特性(如C/C++的宏调用)。
最常见的预处理是C语言和C++语言。ISO
C和ISO C++都规定程序由源代码被翻译分为若干有序的阶段(phase),通常前几个阶段由预处理器实现。预处理中会展开以#起始的行,试图解释为预处理指令(preprocessing directive) ,其中ISO C/C++要求支持的包括#if/
#ifdef/ #ifndef/ #else/ #elif/ #endif(条件编译)、#define(宏定义)、#include(源文件包含)、#line(行控制)、#error(错误指令)、#pragma(和实现相关的杂注)以及单独的#(空指令)。预处理指令一般被用来使源代码在不同的执行环境中被方便的修改或者编译。
2.2在Ubuntu下预处理的命令
命令:cpp hello.c > hello.i
2.3 Hello的预处理结果解析
Cpp命令处理后得到hello.i文件,可以看到hello.i也是一个文本文件,打开之后看到原先23行的代码被扩展到3042行增加了许多内容。
但是程序之前的#include<stdlib.h>、<unistd.h>、<stdio.h>不见了。那些在文件
在这之前出现的是 stdio.h unistd.h stdlib.h 的依次展开,以 stdio.h 的展开为例, cpp 到默认的环境变量下寻找 stdio.h
打开/usr/include/stdio.h 发现其中依然使用了
#define 语句,cpp 对此递归展开,所以最终.i 程序中是没有#define
的。而且发现其中使用了大量的#ifdef #ifndef 的语句,cpp 会对条件值进行判断来决定是否执行包含其中的逻辑。其他类似。
2.4 本章小结
本章主要介绍了预处理的概念与作用,每次在写main函数之前在文件头添加的头文件和宏定义其实都需要经过比较复杂的处理。
(第2章0.5分)
**
第3章 编译
**
3.1 编译的概念与作用
利用编译程序从源语言编写的源程序产生目标程序的过程。编译器将我们用高级语言C写的程序按照一定的语法规则分方法翻译成汇编代码。
编译一般分为三个步骤:
1、 词法分析:编译器将程序中的字符串分离出来,转化成内部标准的表示结构。
2、 语法分析:将先前词法分析达到的token生成一个抽象语法树。
3、 优化和目标代码生成;编译器的后端会负责对代码进行优化,比如公共子式提取,循环优化,删除无用代码,得到目标代码。
3.2 在Ubuntu下编译的命令
命令:gcc -S hello.i -o hello.s
3.3 Hello的编译结果解析
3.3.1 汇编指令
编译后的得到的hello.s文件也是文本文件
.file 声明源文件
.text 以下是代码段
.section .rodata 以下是rodata节
.globl 声明一个全局变量
.type 用来指定是函数类型或是对象类型
.size 声明大小
.long、.string 声明一个long、string类型
.align 声明对指令或者数据的存放地址进行对齐的方式
3.3.2 数据
hello.c文件中涉及到的c数据类型有整型、数组和字符串。
1)整型
程序中设计的整型数有四处:
编译处理时,在.data段声明该变量,.data节存放已经初始化的全局和静态c变量。
局部变量int i :编译器将局部变量存放在栈和寄存器中,hello.s程序中i存放在-4(%rbp),占据4字节。
int argv :记录传入参数个数
立即数:C程序中有许多直接给出的比如3,0,10等,在汇编中直接以立即数形式给出。
2)字符串
程序中的字符串分别是:
“用法: Hello 学号 姓名 秒数!\n”,printf传人的格式化参数。字符串使用UTF-8的格式编码的。两个字符串都存放在.rodata段,作为全局变量。
“Hello %s %s\n”,仍然是有printf函数传入的格式话参数。
3)数组
hello.c中涉及数组只有一个char
argv[],函数执行的命令行参数。argv是一个字符串数组,作为第二个参数传入。Linux中所有指针类型包括char都为8字节大小,栈中一般为char*[]分配了一段连续的空间来存放相关内容,并且数组一般都是从栈底指针开始分配。函数传参的时候按逆序将参数压入栈中,这样取参数就是正序(只需要将rsp指针上移即可)