计算机系统大作业

摘 要
本文对hello.c在Linux下的生命周期进行了分析。通过一些Linux平台的工具,如gcc,objdump,edb,gdb,readelf对程序代码的预处理,编译,汇编,链接,反汇编的过程进行了分析,对比。通过hello在shell中的动态链接,进程运行,内存管理,I/O管理等过程的研究来深入理解Linux系统的动态链接机制,存储层次结构等内容。对各个阶段皆有所探究,对理解底层程序与操作系统的实现原理又一定促进。
关键词:操作系统;编译;

(摘要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的P2P(From program to program):gcc编译器驱动程序读取hello.c(文本),首先进行预处理(cpp)生成预处理程序hello.i(文本),得到的实际上还是高级语言的原程序文件,不过是对带#指令进行了处理。然后通过编译(cc1)生成汇编语言写的程序hello.s(文本),hello.s是汇编指令构成的程序。然后再对hello.s进行汇编(as)生成hello.o(二进制/机器指令),hello.o是可重定位目标程序。要得到最终机器能执行的程序,还要进行链接,最终才能生成可执行的目标程序。这就是P2P
Hello的O2O(From Zero-0 to Zero -0):Shell 调用fork函数创建一个与父进程shell完全相同的子进程(只读/共享),然后调用execve函数,在当前进程的上下文中加载并运行hello程序。将hello中的.text节,.data节,.bss节等内容加载到当前进程的虚拟地址空间。简单来说就是shell进程生成子进程并调用exceve系统调用启动加载器,以装入hello程序,最后跳转到hello第一条指令执行。通过cache高效的读取指令,将程序的指令在硬件上实现,操作系统提供的虚拟内存机制维护了hello自身的空间,从而不与其他进程互相干扰,TLB,分级页表为数据在内存中的高效访问提供了支持。Hello依靠OS提供的服务(系统调用)简介访问硬件资源。hello执行完成后shell会回收hello进程,然后内核从系统中删除hello的所有痕迹。这就是O2O
1.2 环境与工具
硬件环境:
处理器:Intel Core i7-7700HQ CPU @ 2.80GHZ
磁盘:1T HDD+128 SSD
内存:8G DDR4
1.3 中间结果

  1. hello.c:源代码,原程序文件

  2. hello.i和hello1.i:hello.c通过预处理生成的文本文件

  3. hello.s:hello.i经过编译器编译生成的文本文件

  4. hello.o:hello.s进过汇编生成的二进制文件(可重定位目标文件)

  5. hello.out:hello.o经过链接后生成的二进制文件(可执行目标文件)

  6. asm.txt:hello.o反汇编生成的文本文件(汇编语句)

  7. hello:由要求上的语句直接生成,即gcc -m64 -no -pie -fno -PIC hello.c -o hello 生成

  8. out_asm.txt:由hello反汇编生成的文本文件(汇编语句)

1.4 本章小结
本章十分简要的介绍了hello程序的生命周期。同时,还列出本次实验的基本信息,包括硬件配置,环境等。介绍了研究过程中的中间结果。
(第1章0.5分)

第2章 预处理
2.1 预处理的概念与作用
1.概念
预处理指预处理器根据源文件中以”#”开头的预编译指令,包括:
—删除”#define”并展开所定义的宏
—处理所有条件预编译指令,如”#if”,”#ifdef”,”#endif”等
—插入头文件到”#include”处
—删除所有注释如”//”,”/* */”
—添加行号和文件名标识,以便编译器产生调试用的行号信息
—保留所有#pragma编译指令

2.作用
预处理做了代码文本打的替换工作,提供了很大的灵活性,以适应不同的计算机和操作系统环境的限制。
2.2在Ubuntu下预处理的命令
预处理有两种方式

    • $gcc -E hello.c -o hello.i
    • $cpp hello.c > hello.i

2.3 Hello的预处理结果解析
Hello.i

由于插入了头文件后预处理结果代码非常长,这里只截部分。我们可以看见,跟上文概念处提到的一模一样。在原有代码基础上将头文件stdio.h插入,声明函数,定义结构体,定义宏等等。
我们可以发现预处理是对代码进行了一个初步的处理,对源代码进行了一些替换。

其实这样做的主要意义便是能把用于不同环境的代码放在同一个文件中,再预处理阶段修改代码,使之适应当前的环境。

2.4 本章小结

本章较为简略的介绍了编译器预处理的相关概念。预处理是在编译前对代码进行转换与处理,使得编译得以成功进行。如实现将定义的宏展开,插入头文件内容等等。经过预处理,代码为编译做好了准备。
(第2章0.5分)

第3章 编译
3.1 编译的概念与作用
1.概念
编译过程就是将预处理后得到的hello.i预处理文件进行词法分析、语法分析、语义分析、优化等操作后,生成的汇编代码文件hello.s。而用来进行编译处理的程序称为编译器(Compiler)。
2.作用
汇编代码文件可供编译器进行生成机器码、链接等操作
3.2 在Ubuntu下编译的命令
编译的命令有3种,这里只展示其较为简单的2种

  • $gcc -S hello.i -o hello.s
  • $gcc -S hello.c -o hello.s

3.3 Hello的编译结果解析

首先说明汇编文件的伪指令:

指令 对应内容
.file 声明源文件
.text 声明代码段
.data 声明数据段
.align 声明指令及数据存放地址的对齐方式
.type 指定类型
.size 声明大小
.section .rodata 只读数据
.globl 全局变量

1.字符串
汇编语言中,常量如输出字符串被存储于.rodata中(输出字符串作为全局变量保存)。hello.s中,有两个字符串,我们可以从上图看出
字符串(1):

那么\345\255\246…这些有点莫名其妙的是什么呢,这其实是utf-8编码,中文汉字以’\345’开头,占三个字符,而全角字符’!’占两个。

字符串(2):

2整型数
2.1 hello.c中出现的全局变量sleepsecs
定义如下:

下面来解释一下sleepsecs的定义。.globl代表它是全局变量,对齐方式是.align 4,sleepsecs的size大小为4Byte,类型为.long,但是我们看源文件中是int,这个原因一般是编译器优化,编译器ccl会将int表示为long但对齐方式仍然为整型int的4字节,long类型则为双字quad,这里不再做赘述。

Rsp为栈顶指针,rbp为栈底指针

2.2main函数参数argc,是首个参数,所以保存于%edi,由movl,到-0x20(%rbp),
局部变量i,位于栈空间-0x4(%rbp),占4个字节。

3.数组

argv1,argv2分别是第二个第三个参数,所以argv2位于%rdx,地址为-0x16(%rbp),argv1位于%rsi,地址为-0x2a(%rbp),数组首地址为-0x32(%rbp),所占字节为8

讲完了常量变量我们首先强调一下汇编的数据格式
C声明 Intel数据类型 汇编代码后缀 大小(字节)
char 字节 b 1
short 字 w 2
int 双字 l 4
long 四字 q 8
char* 四字 q 8
float 单精度 s 4
double 双精度 l 8

汇编的各个操作

  1. 赋值(数据传送指令)
    根据大小,分为movb,movw,movl,movq,movabsq。这里不做详细介绍,想要详细了解可以看书。上文也提到了将局部变量i = 0的代码为movl $0x0,-0x4(%rbp)。
  2. 算术操作
    hello程序里的”i++”,其汇编代码为 addl $1,-4(%rbp),即为i的值加1。
    这里列出部分常见的算术和逻辑操作
    leaq S,D 加载有效地址 D←&S
    INC D 加1 D←D+1
    DEC D 减1 D←D-1
    NEG D 取负
    NOT D 取补
    ADD S,D 加 D←D+S
    SUB S,D 减 D←D-S
    IMUL S,D 乘 D←D*S
    XOR S,D 异或 D←D^S
    还有OR或,AND与,SAL左移,SHL右移等
  3. 关系操作
  4. CMP,它是基于S1-S2的,根据不同的数据,分为cmpb比较字节,cmpw比较字等,这里不详细描述。通过CMP比较后,会改变条件码,例如,假如两个操作数相等,那么就会将零标志设置为1。
  5. 条件码
    为了方便讲述后文的条件跳转,我们这里讲解一些基本的条件码
    CF 进位标志。最近的操作使得最高位产生了进位
    ZF 零标志。最近操作得出的结果为0
    SF 符号标志。最近的操作得到的负数
    OF 溢出标志。最近的操作导致一个补码溢出
  6. 条件跳转
    根据CMP指令后条件码而跳转。常见的有je(相等) 跳转条件为ZF,jne(不相等),跳转条件为~ZF。
    hello.s中

就是看argc是否为3,若相等那么就跳转,若不等就不跳转,cmpl后可能会改变条件码
跳转继续向后执行代码

跳转后执行.L2

4.转移控制
call Label过程调用
call *Operand 过程调用
ret 从过程调用中返回
call 函数,相当于先压栈再跳转到调用的子程序
hello.s中
这两处都用到了转移控制,前者是若arg不为3,输出信息并执行exit函数,后者是单纯的调用sleep函数。
还有call getchar,print函数,这里不再描述

5.函数操作
hello程序中的函数操作包括main函数本身参数读取,printf调用,sleep调用,getchar调用。这里我们借用一张书上的图来描绘它们的栈帧结构

main:main函数通过 等操作为函数分配栈空间,若通过exit结束,main不会释放内存,需要通过return正常返回
exit(),printf():printf的参数是根据字符串中的输出位数量来决定的
puts():唯一值得讲解一下的,若printf函数有其他参数时,参数的传递顺序是

参数 1 2 3 4 5 6 其余
地址 %rdi %rsi %rdx %rcx %r8 %r9
getchar():getchar()函数无参数,返回值为int,成功就返回用户输入ASCII码,失败返回-1.

3.4 本章小结
本章介绍了编译器进行编译操作的相关内容。编译器实现将C语言代码转换成汇编代码,从而最终转换为机器代码。生成汇编代码的这个过程需要对数据、操作都进行对应转换,数据包括常量、变量(全局/局部/静态)、表达式、宏等,操作包括算术、逻辑、位、关系、函数等操作。从五个方面对hello.s各部分做出了相应的介绍说明

(第3章2分)

第4章 汇编
4.1 汇编的概念与作用
概念:汇编程序将汇编语言源程序转换为机器指令序列

作用:汇编器(as)将hello.s翻译成机器语言指令,这是一个可重定位文件,其在链接后能被计算机直接执行
4.2 在Ubuntu下汇编的命令

  • $gcc -c hello.s -o hello.o
  • $gcc -c hello.c -o hello.o
  • $as hello.s -o hello.o

4.3 可重定位目标elf格式
ELF头

节头

重定位节

重定位条目被定义为一个结构体
包括8字节的偏移量,4个字节的重定位类型,4个字节的在符号表中的偏移量,8个字节的计算重定位位置的辅助信息。
共有32个不同的重定位类型,而hello.o中出现了2个如上图
R X86_ 64 PC32。 重定位-一个使用32位PC相对地址的引用。一个PC相对地址就是距程序计数器(PC)的当前运行时值的偏移量。当CPU执行一条使用PC相对寻址的指令时,它就将在指令中编码的32位值加上PC的当前运行时值,得到有效地址(如call指令的目标),PC值通常是下一条指令在内存中的地址。

R X86_ 64 _32。 重定位一个使用32位绝对地址的引用。通过绝对寻址,CPU直接使用在指令中编码的32位值作为有效地址,不需要进一步修改。

.symtab节

.sytmab存放着定义和引用函数和全局变量的信息
4.4 Hello.o的结果解析
hello.o反汇编:
首先用 objdump -d -r hello.o > asm.txt

hello.s:

两者异同:hello.s中有清晰的描述,如对全局变量的描述,还有.rodata只读数据段。而asm.txt中只有最基础的描述,记录了文件的格式(elf64-x86-64)和.text代码段。两者都包含main函数完整的代码,但hello.s中是由一段段的伪指令构成的,而asm.txt由汇编指令所构成,包含着完整的跳转逻辑,函数调用等,唯一的空白处是需要链接后才能确定的地址
下面详细说明各方面的差异
函数调用
在asm.txt中,call地址后为4个字节的0(8个0),这是占位符,库函数调用需要链接才能确定地址,所以这指向的是下一条地址的位置,hello.s则是直接用函数名代替
分支转移
很显然,hello.s是通过.L0等助记符表示该段完成跳转。而asm.txt跳转指令中的地址已经为已确定的实际地址。
4.5 本章小结
本章通过对汇编后产生的hello.o的可重定位的ELF格式的考察、对重定位项目的举例分析以及对反汇编文件与hello.s的对比,从原理层次了解了汇编这一过程实现的变化。

第5章 链接
5.1 链接的概念与作用
概念:链接是将多个可重定位目标文件合并以生成可执行目标文件
作用:一个大型程序可以分成很多源文件,可以构建公共函数库,可以分开编译,无需包含共享库所有代码(模块化,效率高)
5.2 在Ubuntu下链接的命令
Linux终端下进入hello.o所在文件夹,输入指令ld -dynamic-linker
/lib64/ld-linux-x86-64.so.2
/usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o
/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o
/usr/lib/x86_64-linux-gnu/crtn.o
hello.o -lc -z relro -o hello

5.3 可执行目标文件hello的格式
ELF格式

ELF头

节头部表:显示hello.out的Section Header Table中的每个Section Header Entry的信息

5.4 hello的虚拟地址空间
首先从程序头部开始看起(截图只举例部分,其余同理)

PHDR:具有读/执行权限。起始位0x400000偏移0x40字节,大小为0x1c0字节

INTERP:具有读权限。起始位0x400000偏移0x200字节,大小为0x1c

并且可以由右边的文字得到程序所用的ELF解析器位置是/lib64/……

LOAD代码段:位于0x400000处,大小为0x838字节。被初始化为可执行目标文件的头0x838字节。
LOAD数据段:有读写权限,不再位于虚拟内存中,位于0x600e10处,总大小0x250字节。其中,有4个字节对应于.bss数据,其余0x24c个字节对应.data数据,
NOTE:位于0x400000偏移0x21c处,大小为0x20字节
5.5 链接的重定位过程分析
hello.o反汇编

hello.out反汇编(部分)

  1. hello中还有其他段,如.init(程序初始化),.fini(程序结束执行),hello.o只有Disassembly of section .text。
  2. c) 数据访问hello.o反汇编文件中,对.rodata中printf的格式串的访问需要通过链接时重定位的绝对引用确定地址,因此在汇编代码相应位置仍为占位符表示,对.data中已初始化的全局变量sleepsecs为0x0&#
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值