摘 要
本论文将CSAPP课程主要通过描述hello小程序地一生,对于我们在课上所学地知识点进行总结、回顾与复习,并在ubuntu环境下回顾相关地操作,利用edb等工具,对于一个程序运行的各个部分重要的功能等进行操作 ,加深对于计算机系统相关知识的理解。
关键词:hello程序;知识总结;Ubuntu实例测试
目 录
第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的预处理结果解析 - 6 -
2.4 本章小结 - 7 -
第3章 编译 - 8 -
3.1 编译的概念与作用 - 8 -
3.2 在UBUNTU下编译的命令 - 8 -
3.3 HELLO的编译结果解析 - 8 -
3.4 本章小结 - 14 -
第4章 汇编 - 15 -
4.1 汇编的概念与作用 - 15 -
4.2 在UBUNTU下汇编的命令 - 15 -
4.3 可重定位目标ELF格式 - 15 -
4.4 HELLO.O的结果解析 - 19 -
4.5 本章小结 - 21 -
第5章 链接 - 22 -
5.1 链接的概念与作用 - 22 -
5.2 在UBUNTU下链接的命令 - 22 -
5.3 可执行目标文件HELLO的格式 - 24 -
5.4 HELLO的虚拟地址空间 - 27 -
5.5 链接的重定位过程分析 - 28 -
5.6 HELLO的执行流程 - 31 -
5.7 HELLO的动态链接分析 - 33 -
5.8 本章小结 - 34 -
第6章 HELLO进程管理 - 36 -
6.1 进程的概念与作用 - 36 -
6.2 简述壳SHELL-BASH的作用与处理流程 - 36 -
6.3 HELLO的FORK进程创建过程 - 37 -
6.4 HELLO的EXECVE过程 - 37 -
6.5 HELLO的进程执行 - 38 -
6.6 HELLO的异常与信号处理 - 40 -
6.7本章小结 - 43 -
第7章 HELLO的存储管理 - 44 -
7.1 HELLO的存储器地址空间 - 44 -
7.2 INTEL逻辑地址到线性地址的变换-段式管理 - 44 -
7.3 HELLO的线性地址到物理地址的变换-页式管理 - 45 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 46 -
7.5 三级CACHE支持下的物理内存访问 - 46 -
7.6 HELLO进程FORK时的内存映射 - 48 -
7.7 HELLO进程EXECVE时的内存映射 - 48 -
7.8 缺页故障与缺页中断处理 - 49 -
7.9动态存储分配管理 - 49 -
7.10本章小结 - 52 -
第8章 HELLO的IO管理 - 53 -
8.1 LINUX的IO设备管理方法 - 53 -
8.2 简述UNIX IO接口及其函数 - 53 -
8.3 PRINTF的实现分析 - 54 -
8.4 GETCHAR的实现分析 - 55 -
8.5本章小结 - 56 -
结论 - 56 -
附件 - 58 -
参考文献 - 59 -
第1章 概述
1.1 Hello简介
P2P:
Hello程序的生命周期是从一个高级C语言程序开始的,运行hello.c程序,需要将每条C语言语句转化为一系列的低级机器语言指令。
020:
在shell中键入启动命令后,shell为其fork产生一个子进程,hello便从程序变成了进程。Shell映射虚拟内存,进入程序之后,开始载入物理内存,随便在main函数中执行目标代码,CPU运行hello分配时间片,执行逻辑控制流。程序结束后,shell回收hello进程,内核删除相关的数据。
1.2 环境与工具
硬件环境:X64 CPU;2GHz;2G RAM;256GHD Disk 以上
软件环境:Windows7 64位以上;VirtualBox/Vmware 11以上;Ubuntu 16.04 LTS 64位/优麒麟 64位
开发工具:gcc,vim,edb,readelf
1.3 中间结果
Hello.i Hello.c预处理之后文本文件
Hello.s Hello.i编译之后形成的汇编文件
Hello.o Hello.s进行汇编之后的可重定位目标文件
Hello 可执行目标文件
Hello.out Hello反汇编之后的可重定位文件
1.4 本章小结
本章概括地介绍了hello的P2P,020过程。给出了本次实验当中所需要的实验环境(随着报告的进行可能会有所补充),程序在各个阶段的文件的存在形式。也给出了在各个阶段我们需要对与各条语句进行的大致处理。
第2章 预处理
2.1 预处理的概念与作用
预处理的概念:
预处理器cpp根据以字符#开头的命令(宏定义、条件编译),修改原始的C程序。比如hello.c的第一行#include<stdio.h>命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入到程序文本中。结果就得到了另一个C程序,通常以.i作为文件的扩展名。
作用:概括的来讲就是宏定义、文件包含、条件编译
使用与处理功能便于程序的修改、阅读、移植和调试也便于实现模块化程序设计。
宏定义:宏定义是用一个标识符来表示一个字符串,这个字符串可以是常量、变量或表达式。在宏调用中将用该字符串代换宏名。宏定义可以带有参数,宏调用时是以实参代换形参。而不是“值传送”。 为了避免宏代换时发生错误,宏定义中的字符串应加括号,字符串中出现的形式参数两边也应加括号。
文件包含:文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件。
条件编译:条件编译允许只编译源程序中满足条件的程序段,使生成的目标程序较短,从而减少了内存的开销并提高了程序的效率。
2.2在Ubuntu下预处理的命令
命令行:gcc hello.c -E -o hello.i(gcc -E hello.c -o hello.i)
图表1 预处理
2.3 Hello的预处理结果解析
- int main(int argc,char *argv[]){
- int i;
- if(argc!=4){
- printf(“用法: Hello 学号 姓名 秒数!\n”);
- exit(1);
- }
- for(i=0;i<8;i++){
- printf(“Hello %s %s\n”,argv[1],argv[2]);
- sleep(atoi(argv[3]));
- }
- getchar();
- return 0;
- }
图表 2 main函数内容
经过预处理我们将hello.c转化为hello.i文件,打开hello.i文件发现,文件内容增加了很多,hello.i文件对于原来文件中的宏进行了宏展开,头文件当中的内容经过预处理被包含进了.i文件当中。例如定义变量、定义结构体、声明函数等内容。对于#define命令有相应的符号进行替换。
2.4 本章小结
本章简要介绍了预处理的概念以及作用,将已经实现的定义进行宏替换等等。进行宏定义、文件包含、条件编译。同时也通过预处理得到.i文件,去查看经过预处理之后.i文件当中的相关内容,观察变化。
第3章 编译
3.1 编译的概念与作用
编译器ccl将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序。汇编语言非常有用,它为不同高级语言的不同编译器提供了通用的输出语言。例如,c翻译器和fortran翻译器产生的输出文件用的都是一样的汇编语言。
编译的主要作用有:将源程序翻译成目标程序。还拥有词法分析、语法分析、语义分析、源代码优化、代码生成、目标代码优化等功能。
词法分析:对输入的字符串进行分析和分割,形成所使用的源程序语言所允许的记号(token),同时标注不规范记号,产生错误提示信息。
语法分析:分析词法分析得到的记号序列,并按一定规则识别并生成中间表示形式,以及符号表。
语义分析:静态语法检查,分析语法分析过程中产生的中间表示形式和符号表,以检查源程序的语义是否与源语言的静态语义属性相符合。
代码优化:功能等价但是运行时间更短或占用资源更少的等价中间代码。
3.2 在Ubuntu下编译的命令
命令:gcc -S hello.i -o hello.s
图表 3 编译命令
3.3 Hello的编译结果解析
3.3.1 汇编指令:
图表 4 查看编译结果
.file:声明源文件
.text:代码节
.section:
.rodata:只读代码段
.align:数据或者指令的地址对齐方式
.string:声明字符串
.global:声明全局变量(main)
.type:声明一个符号是数据类型还是函数类型
3.3.2数据:
1.局部变量:
Main函数int i操作声明了一个局部变量,在进行编译的时候,编译器会将局部变量放入到栈当中。如图中所示,局部变量i放在-4(%rbp)的位置。
图表 5 查看编译结果
2.字符串:
通过.s文件我们可以看到程序当中有两个字符串,两个字符串都在只读数据段当中:
图表 6 查看字符串
图表 7 查看字符串
3.main函数
用户将argc,argv作为参数传给main函数。char *argv[]是main函数当中的第二个参数,是一个指针数组。数组当中的每一个元素都是指向字符类型的指针。起始地址放置在-32(%rbp)的位置。
3.3.3全局函数
图表 9 main函数
在.s当中globl main表明main函数是一个全局函数
图表 10 全局函数
3.3.4赋值
对于数据传送指令大致有如下几种操作:
指令 描述
movb 传送字节
movw 传送字
movl 传送双字
movq 传送四字
movabsq 传送绝对的四字
在程序当中主要用到的数据传送指令是前四个
1)对于第一个赋值操作int sleepsecs = 2.5。sleepsecs是全局变量,进行赋初值操作,因此直接在.data节中将sleepsecs 声明为值为2的long类型数据。
2)对于第二个赋值操作i=0。在hello.s文件中通过汇编语句movl $0, -4(%rbp)将立即数赋值给我们的局部变量int i。而且值得我们注意的是汇编语句用的是movl,局部变量是int型,4个字节,因此使用字母l。
除此之外补充扩展指令(知识点回顾)
指令 指令
movzbw movsbw
movzbl movsbl
movzwl movswl
movzbq movsbq
movzwq movswq
movslq
cltq
3.3.5算数和逻辑
指令 功能
leaq S,D D=&S
INC D D=D+1
DEC D D=D-1
NEG D D=-D
ADD S,D D=D+S
SUB S,D D=D-S
3.3.6关系操作与控制转移指令
首先我们复习一下关系操作的相关内容。
指令 效果 描述
CMP S1,S2 S2-Sl 比较
TEST S1, S2 S1&S2 测试
SETX D —— ——
JX —— ——
值得注意的是:比较和测试指令不修改任何寄存器的值,只是设置条件码。
图表 11 条件转移
在hello.c语言当中有argc和4的比较。在汇编语言当中cmpl行驶比较功能,令$4立即数和-20(%rbp)的内容进行比较,然后通过je进行跳转。如果等于的话同转到.L2的内容当中。
图表 12 条件转移
上图比较是如果寄存器当中存放的内容<=7,那么执行循环
图表 13 条件转移
上图是将i初始化为0然后进行无条件跳转到.L3
图