计算机系统

大作业

题     目 程序人生-Hello’s P2P

专       业 计算机科学与技术

学     号 2021113044

班     级 2103101

学       生 方健   

指 导 教 师 刘宏伟

计算机科学与技术学院

2022年5月

摘  要

本文深入的研究了hello的一生,深入研究它是如何从一个源程序hello.c转化为一个可执行文件的,并在shell里面的运行步骤,详细的分析了每一个过程的具体内容,如编译、汇编、链接、重定位等等。

关键词:汇编、重定位、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简介

P2P: From Program to Process

最开始我们将代码一个个写入hello.c中,它是hello程序的生命的起点,然后调用预处理器cpp将hello.c预处理为hello.i,接下来编译器ccl会将hello.i翻译为hello.s,然后通过汇编器将hello.s翻译为机器指令保存在hello.o中,最后调用链接器形成可执行文件hello,在shell中输入命令./hello,操作系统就会通过fork为其生成子进程,然后通过execve将其加载,不断进行访存、内存申请等操作。最后,在程序结束返回后,由父进程或祖先进程进行回收,程序结束。这样就实现了完整的P2P过程。

020: From Zero-0 to Zero-0

指一个程序从无到拥有自己的内存,地址和时间周期等,然后直到该程序执行结束后,父进程负责将其回收的全部过程。shell在子进程中使用execve执行这个程序时,系统将会把这个程序加载到内存,等待进程结束时,它的父进程将会回收hello,而系统内核将会对内存进行清理,这个过程就叫做020。

1.2 环境与工具

1.2.1 硬件环境:

X64 CPU;2GHz;2G RAM;256GHD Disk

1.2.2 软件环境:

Windows11 64位以上; Vmware 16;Ubuntu 20.04 ;LTS 64位/优麒麟 64位

1.2.3 开发工具:

Visual Studio 2021 64位;CodeBlocks 64位;vi/vim/gedit/gcc;edb

1.3 中间结果

图1-3:中间过程文件

hello.c:源程序

hello.i:经过预处理过后的文件;

hello.s:经过编译生成的汇编文件;

hello.o:汇编生成的可重定位的文件;

hello:通过链接产生的可执行目标文件二进制文件;

elf.txt: 可重定位目标文件hello.o导出的ELF文件;

hello_elf.txt:可执行文件hello导出的ELF文件;

objdump_hello.s:可重定位目标文件hello.o反汇编生成的文件

hello_objdump.s:可执行文件hello反汇编生成的文件

1.4 本章小结

本章简单介绍hello程序的一生,介绍了P2P和020过程,然后又介绍我所使用的硬件环境、软件环境和开发调试工具,最后则简述了过程中间生成的中间文件的名字和作用。

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

预处理概念:

预处理指的是预处理器cpp根据以字符#开头的命令,来修改原始的C程序,最后生成.i文本文件的过程。

预处理作用:

1.将源文件中用#include 形式声明的文件复制到新的程序中;

2..用实际值替换用#define 定义的字符串;

3.根据#if后面的条件决定需要编译的代码;

4.预编译程序可以识别一些特殊符号,预编译程序对于在源程序中出现的这些特殊符号串会用合适值进行替换。

5.预处理还可以帮助程序员节省工作量,提高程序可读性,便于维护。

2.2在Ubuntu下预处理的命令

命令:cpp hello > hello.i

图2-2:Ubuntu下预处理命令展示

2.3 Hello的预处理结果解析

​​​​​                             图2-3:hello.i内容展示

解析:可以看见程序被拓展成了3060行,原始hello.c程序出现在3046行之后。在这之前是头文件stdio.h、unistd.h以及stdlib.h依次展开。预处理器将它们全部都展开直接插入程序文本中,所以最后的.i文件特别的长

2.4 本章小结

本章介绍了预处理的概念和作用,以及预处理的方法,然后展示了.i文件的部分内容,分析了里面内容的由来。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译的概念:

编译程序是通过词法分析和语法分析,在确认所有的指令都符合语法规则后,将其翻译成等价的中间代码或汇编代码来表示。编译器cc1将预处理过的文本文件hello.i 翻译转换成汇编文本文件hello.s。

编译的作用:

1.语法分析:编译程序的语法分析器以单词符号作为输入,分析单词符号串是否形成了符合语法规则的语法单位,方法有自上而下分析法和自下而上分析法两种;

2.中间代码:源程序的一种内部表示,或称之为中间语言。中间代码的作用是使编译程序的结构在逻辑上更为简单明确,特别是使目标代码的优化比较容易实现;

3.代码优化:指对程序进行多种等价变换,使得从变换后的程序出发时能生成更有效的目标代码;

4.目标代码:生成目标代码是编译的最后阶段。目标代码生成器把语法分析或优化后生成的中间代码转换成目标代码。此处目标代码指汇编语言代码,即不同种类的语言提供相同的形式,其指令与处理器的指令集类似,更贴近底层,便于汇编器将其转换为可执行机器语言代码供机器执行。

3.2 在Ubuntu下编译的命令

命令:gcc -S hello.i -o hello.s

图3-2:Ubuntu下编译的命令展示

3.3 Hello的编译结果解析

3.3.1结果展示:

​​​​​                         图3-3-1:hello.s内容展示

3.3.2伪指令

打开我们的hello.s文件我们可以发现汇编代码有一部分是以.作为开头,这些以‘.’开头的行都是指导汇编器和链接器工作的伪指令,通常我们可以忽略这些行,下面给出这些伪指令的含义:如下表

伪指令

含义

.file

声明源文件

.text

声明代码节

.section

文件代码段

.rodata

只读文件

.align

数据指令地址对齐方式

.string

声明字符串

.globl

声明全局变量

.type

声明变量类型

表3-3-2:伪指令的类型、含义

3.3.3数据

(1)字符串常量

​​​​​​          图3-3-3-1:hello.s汇编代码关于字符串常量的部分

解析:.LC0和.LC1作为两个字符串变量被声明对于原程序printf里面的字符串“用法: Hello 学号 姓名 秒数!”和“Hello %s %s”。而在.LC0中出现的\347\224等是因为中文字符没有对应的ASCII码值无法直接显式显示,所以这样的字符方式显示。而且这两个字符串都在.rodata下面,所以是只读数据。

(2)全局变量

图3-3-3-2:hello.s汇编代码关于全局变量main的部分

解析:这里.globl声明了main是一个全局变量,.type main,@function看出main是一个函数类型

(3)变量

​​​​​​​

图3-3-3-3:hello.s汇编代码关于函数参数argc、argv的部分

解析:可以看出来%edi保存的main函数的第一个参数int argc,%rsi保存的是main函数的第二个参数char *argv[],然后在栈中-20(%rbp)的位置保存变量argc占4个字节,在-32(%rbp)的位置保存变量argv[],占八个字节,接下来程序就可以通过-20(%rbp)和-32(%rbp)直接找到函数的参数内容。

​​​​​​​ ​​​​​​​

图3-3-3-3-1:hello.s汇编代码关于局部变量i的部分

解析:可以看出-4(%rbp)保存的是变量i的值,初始为0,每次循环加一,循环的条件是i<=8。

3.3.4赋值

​​​​​​​

图3-3-3-4:hello.s汇编代码关于变量i赋值操作的部分

解析:通过使用mov指令,将立即数0直接赋值给变量i。

3.3.5算术操作

​​​​​​​

图3-3-5-1:hello.s汇编代码关于栈指针%rsp算术操作的部分

解析:通过sub将栈指针%rsp减去立即数32,代表栈申请32个字节的空间。

​​​​​​​

图3-3-5-2:hello.s汇编代码关于栈指针%rsp算术操作的部分

解析:通过add可以将-4(%rbp)里面的值加是立即数1,即i=i+1。

3.3.6关系操作/控制转移

​​​​​​​

​​​​​ ​​​​​​​图3-3-6-1 hello.s汇编代码关于控制转移if else的部分

解析:在-20(%rbp)保存的是第一个参数argc,通过cmpl操作将argc与4做比较,je指令可以根据上一步比较结果设置的条件码状态实现跳转(关系操作!),若argc不等于4,则跳过25行的命令,从26行开始执行if里面的内容;若argc等于4,则跳转到.L2处去执行else部分的内容,从而实现if else的控制转移。

​​​​​​​  ​​​​​​​

​​​​​​​

图3-3-6-2 hello.s汇编代码关于控制转移for循环的部分

解析:首先在.L2里面初始化i=0,然后通过jmp无条件跳转到.L3,在.L3里面进行for循环里面的条件判断,通过cmp将i与8比较,然后通过jle实现条件跳转(关系控制<=),若i<=8,则跳转到.L4里面进行for循环里面的操作,在.L4里面有一个add的操作,使得每次循环i++;若最后i>8了,则在.L3中不会跳转到.L4,结束循环,从而实现了一个完整的for循环的操作。

3.3.7数组/指针/结构操作

​​​​​​​

图3-3-7:hello.s汇编代码关于数组/指针操作argv的部分

解析:在之前谈过,在-32(%rbp)保存着一个数组指针argv,它指向一个字符串数组,其中argv[0]保存着命令行第一个参数,一般是可执行文件的名字这里是./hello,argv[1],argv[2]则保存着我们输入的俩个字符串,这里是我们的学号和姓名,char *占8个字节,将-32(%rbp)加16就是argc[2],-32(%rbp)加8就是argv[1],从而实现利用指针对数组argv[]的调用。

3.3.8函数操作

​​​​​​​

图3-3-8-1:hello.s中关于函数printf的调用部分

解析:printf是库里面的一个函数,在最后链接的时候内容会复制进去,参数传递:call puts时只输入字符串参数首地址,for循环中call printf时输入argv[1]和argc[2]的地址,函数调用:if判断满足条件后在for循环中被调用,调用之后会在屏幕是输出我们想要的结果

​​​​​​​

图3-3-8-2:hello.s关于函数exit的调用

解析:参数传递:输入参数为1后再执行退出命令,exit(1)表示正常退出,exit(0)表示非正常退出;函数调用:if判断条件满足后被调用;在调用之后程序就会结束进程。

​​​​​​​

图3-3-8-3:hello.s关于函数sleep的调用

解析:参数传递:输入参数atoi(argv[3]),函数调用:在for循环中被调用,call sleep;调用之后程序延迟。其中其中函数atoi()是把字符串转换成整型数的一个函数。

​​​​​​​

图3-3-8-4:hello.s关于函数getchar的调用

解析:函数调用:在main中被调用,没有参数传递,getchar()。

​​​​​​​

图3-3-8-5:hello.s中关于main函数的调用。

解析:main是主函数,有俩参数argc和argv先前已经介绍过,在函数里面最后会return 0,来结束main函数。

3.4 本章小结

本章首先介绍了编译的概念与作业,然后介绍了在Ubuntu下编译的命令,展示了hello.s的内容,最后根据hello.s文件,详细介绍了中编译器是怎么处理C语言的各个数据类型以及各类操作的。

(第32分)

第4章 汇编

4.1 汇编的概念与作用

汇编的概念:

驱动程序运行汇编器as将汇编语言(hello.s)翻译成机器语言(hello.o)的过程称为汇编,同时这个机器语言文件是可重定位目标文件。汇编器接受.s文件作为输入,以.o可重定位目标文件作为输出。可重定位目标文件包含二进制代码和数据,在编译时与其他可重定位目标文件能够合并起来,创建成一个可执行目标文件,从而被加载到内存中执行。

汇编的作用:

汇编是将高级语言转化为机器可以直接识别并且执行的代码文件的过程,汇编器将.s文件汇编程序翻译成机器语言指令,并将这些指令打包成为可重定位目标程序的格式,最后将结果保留在.o目标文件中,.o文件是一个包含程序指令编码的二进制文件。

4.2 在Ubuntu下汇编的命令

命令:gcc -c hello.s -o hello.o

​​​​​​​

图4-2:Ubuntu下汇编命令展示

4.3 可重定位目标elf格式

4.3.1导出ELF文件

由于hello.o文件不能直接打开,因此我们需要输入readelf -a hello.o > ./elf.txt将其转换成elf文件后查看,如下图:

​​​​​​​                      图4-3-1:Ubuntu下导出elf文件展示

​​​​

图4-3-1-1:elf文件展示

4.3.2 ELF

                                      图4-3-2:ELF头信息

解析:可以看见ELF以16个字节开头,其中前4个字节称为ELF的魔数,分别与ASCll中的DEF控制符、字符E、字符L、字符F对应,这4个字节是用来确认文件类型的。接下来一个字节是用来表示ELF文件类型的,0x1表示32位,0x2表示64位。第六个字节用来表示字节序,0x1表示小段,0x2表示大端。第7个字节表示ELF的版本号,一般为1。剩下的字节在ELF标准中没有定义用0填充。16个字节下面也给出了文件的类别,字节序,ELF文件类型,版本号,在第11行还给出了这个ELF的起始地址,在15行还给出了ELF头的长度为64个字节,13行给出了节头部表的起始位置是1240,19行说明了节头部表里面有14个表项,18行说明了每一个表项的大小是64个字节。

4.3.3节头部表:

​​​​​​​                                        图4-3-3:节头部表信息

解析:上面分析过里面会有13个表项即13个节,头节目表描述了.o文件中出现的各个节的类型、位置、所占空间大小等必要有效信息。如上图所示,其属性分别有名称、类型、地址(此时暂时未被分配均为0)、偏移量(节相对于文件开始的偏移)、节大小、全体(Entry)大小、旗标(节属性)、链接(与其他节的)、信息(附加节)、对齐(2的Align次方)。以第一个表项为例,它的名称是.test,位置在0x40,大小是146个字节(0x92)。

4.3.4重定位节

                    图4-3-4重定位节信息

解析:当汇编器生成一个目标模块时,它并不知道数据和代码最终将放在内存中的什么位置。它也不知道这个模块引用的任何外部定义的函数或者全局变量的位置。所以,无论何时汇编器遇到最终未知的目标引用,它就会生成一个重定位条目,告诉链接器在将目标文件合并到可执行文件时如何修改新的引用。在重定位节中包含了在代码中使用的一些外部变量等信息,在链接的时候需要根据重定位节的信息对这些变量符号进行修改。本程序需要重定位的信息有:.rodata中的模式串,puts,exit,printf,slepsecs,sleep,getchar这些符号需要与相应的地址进行重定位这些符号的相关信息偏移量如上图。

4.3.5符号表

                                                 图4-3-5:符号表信息

解析:在符号表中存放了所有引用的函数和全局变量的信息。Num为某个符号的编号,value表示符号在对应节中的偏移量,size是所占字节数,type是符号的类型,Bind表示这个符号是本地的还是全局的,Vis在C语言中并未使用可以忽略,Ndx是section的索引值, Name是符号的名称。这里我们发现main这个符号为例,它的Ndx为1,说明它的定义在./test节中(之前节头部表可以看节的索引值),Bind是GLOBAL说明是一个全局变量,Type是FUNC说明main是一个函数,size是146,说明它在./test节中占146个字节,value为0,说明它偏移量为0,就在./test最上面。

4.4 Hello.o的结果解析

4.4.1生成反汇编文件

命令为:objdump -d -r hello.o > objdump_hello.s

​​​​​​​                              图4-4-1-0:Ubuntu下生成反汇编文件展示

​​​​​​​​​​​​                                    图4-4-1-1:hello反汇编生成的objdump_hello.s

4.4.2对比与分析:

(1)操作数变化

hello.s中的操作数一般是十进制表示的,而objdump_hello.s中的操作数一般是十六进制表示的,如下图:

​​​​​​​

图4-4-2-1:hello.s中立即数操作数展示

​​​​​​​

图4-4-2-2:objdump_hello.s中立即数操作数展示

(2)跳转指令变化

hello.s中地址使用段名称如 je .L2,而objdump_hello.s中则使用相对偏移地址,如je  2f <main+0x2f>,如下图:

​​​​​​​

图4-4-2-3:hello.s中跳转指令展示

​​​​​​​

图4-4-2-4:objdump_hello.s中跳转指令展示

(3函数调用

在hello.s中,call的后面紧跟着函数名,而在objdump_hello.s中,call的后面紧跟着下一条指令的地址。这是因为该程序调用的函数是共享库中的函数,最终需要通过动态链接器才能确定函数的运行时执行地址。对于这一类函数调用,call指令中的相对偏移量暂时编码为全0,然后在.rela.text节添加重定位条目,等待链接时的进一步确定。如下图所示:

​​​​​​​                  图4-4-2-5:objdump_hello.s函数调用展示

​​​​​​​                 图4-4-2-6:hello.s函数调用展示

(4)数据访问变化

在hello.s 文件中,使用段名称+%rip访问 rodata(printf 中的字符串),而在反汇编得到的hello.asm中,使用 0+%rip进行访问。其原因与函数调用类似,rodata 中数据地址在运行时才能确定,故访问时也需要重定位。在汇编成为机器语言时,将操作数设置为全 0 并添加相应的重定位条目,如下图所示:

​​​​​​​                         图4-4-2-7:hello.s中数据访问的变化

​​​​​​​                 图4-4-2-8:objdump_hello.s中数据访问的变化

4.4.3机器语言与汇编语言:

发现hello.o的反汇编文件objdump_hello.s与hello.s汇编代码基本是一致的,但是在这反汇编文件的字里行间中,也混杂着一些我们相对陌生的面孔,也就是机器代码。这些机器代码是二进制机器指令的集合,每一条机器代码都对应一条机器指令,到这才是机器真正能识别的语言。每一条汇编语言都可以用机器二进制数据来表示,汇编语言中的操作码和操作数以一种相当于映射的方式和机器语言进行对应,从而让机器能够真正理解代码的含义并且执行相应的功能。总之汇编语言能与机器码建立一一对应的关系。

4.5 本章小结

本章首先介绍了汇编的概念和作用,然后给出了在Ubuntu下启动汇编的指令,以及如何导出ELF文件。然后详细的分析了ELF文件里面的内容,对ELF头、头部表、重定向节、符号表的内容做了详细的解释,然后利用objdump反汇编hello.o将反汇编文件objdump_hello.s与原汇编文件hello.s做比较,分析了俩者的异同点,最后分析了机器语言与汇编语言的关系。

(第41分)

第5章 链接

5.1 链接的概念与作用

链接的概念:

链接是指将多种不同重定位目标文件或静态/动态库的代码和数据部分收集整合起来,修改符号引用,组合并且输出成一个单一可执行目标文件的过程。

链接的作用:

链接能够节省源程序空间,同时对于未编入的常用函数文件能够进行合并从而生成能够正常工作的可执行目标文件。除此之外链接也可以表述为使得一个复杂程序被分解成诸多简明模块来进行编写,并且最终被合并为一个可执行程序。这些作用使得分离编译成为了一种可能。

5.2 在Ubuntu下链接的命令

命令:ld -o hello -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 hello.o /usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o

​​​​​​​                                图5-2:Ubuntu下汇编命令展示

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

5.3.1导出hello的ELF文件

命令: readelf -a hello > ./hello_elf.txt

​​​​​​​                             图5-3-1:导出hello的ELF文件

5.3.2 ELF头

​​​​​​​

图5-3-2:hello的ELF头内容

解析:与之前分析hello.o的过程类似,不过注意到第8行的类型变为了EXEC即可执行文件,同时发现ELF头的地址变了,变为了0x4010f0,这是因为它们需要重定位到最终运行时的内存地址,section也变多了变为了27个,还多了一个program headers程序头部表。

5.3.3节头

​​​​                       图5-3:节头内容展示

解析:节头的内容分析和之前一样,唯一的区别一个就是节的数量多了,由原来的14个变成了现在的27个,多出来的节如下:

主要作用

.interp:

包含了动态链接器在文件系统中的路径;

.note     .ABI-tag:

ELF规范中记录的注释部分,包含一些版本信息;

.gnu     .hash:

符号的哈希表,用于加速查找符号;

.dynamic    .dynsym    .dynstr:

与动态链接符号相关;

.gnu.version   .gnu.version_r:

与版本有关的信息;

.init_array   .fini_array:

存放函数指针,其中的函数分别在main函数

之前之后调用,用于初始化和收尾;

.init    .fini:

存放上述初始化和收尾的代码

.eh_frame_hdr

与异常处理相关。

表5-3-3-1:多出的节主要功能展示

5.3.4程序头

​​​​​​                                                        图5-3-4程序头展示

解析: elf可执行文件易加载到内存,可执行文件连续的片被映射到连续的内存段,程序头部表描述了这一映射关系。程序头部表包括各程序头的偏移量、内存地址、对其要求、目标文件与内存中的段大小及运行时访问权限等信息。

5.3.5段节

​​​​​​​                      图5-3-5:段节展示

解析:如图所示的节段映射,说明了在链接过程中,将多个代码段与数据段分别合并成一个单独的代码段和数据段,并根据段的大小以及偏移量重新设置各个符号的地址。

5.3.6重定位节

​​​​​​                    图5-3-6:重定位节展示

解析:分析过程与之前类似,由于对于可执行目标文件来说,仍然会存在重定位信息,因为有些需要动态链接的块还没有被链接,重定位节中就给出了这些符号的相关信息。

5.3.7符号表

                                               图5-3-7:符号表内容

解析:对于可执行目标文件来说,包含两个符号表,一个符号表的名称为.dynsym, 从名称和符号表中的内容来看应该是还没有动态链接的一些未知符号。另一张符 号表就是熟知的.symtab,里面保存了程序中定义和引用的函数以及全局变量等的 信息,其余分析与之前类似。

5.3.8版本信息

​​​​​​​                         图5-3-8:关于版本信息的节

解析:这部分不重要,只是介绍版本信息而已。

5.4 hello的虚拟地址空间

​​​​​​​                                                   图5-4-1:Date Dump窗口

解析:打开edb,加载程序后,打开edb的Date Dump窗口,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。通过查看edb可以发现虚拟地址开始于0x400000,如上图所示。

​​​​​​                                           图5-4-2:Date Dump窗口

解析:根据查看的信息我们发现虚拟地址结束于0x400ff0

然后根据节头目表,我们可以通过edb查看各个节的信息,比如.text节,虚拟地址起始于0x4010f0,大小为0x145,如下图所示:

​​​​​​​                                图5-4-3:Data Dump寻找./test节部分

同理我们还可以同样找到其他节在虚拟地址空间例的信息

5.5 链接的重定位过程分析

5.5.1生成hello的反汇编文件

命令:objdump -d -r hello > hello_objdump.s   如图:

​​​​​​​                     图5-5-1:生成hello的反汇编文件

5.5.2 hello与hello.o反汇编文件的差异:

(1)代码增多

​​​​​​​ ​​​​​​​

图5-5-2-1:hello.o反汇编内容            图5-5-2-2:hello反汇编内容

解析:可以看出来hello反汇编多了很多代码,接下来我会慢慢介绍为什么会多出这些代码

(2)增加了外部的共享库函数

​​​​​​​              图5-5-2-3:hello反汇编代码关于共享库的部分

解析:在链接之后,链接器会把共享库里面被hello.o里面调用的函数复制进去,所以代码增加了不少。

(3)函数调用地址不同

​​​​​​​              图5-5-2-4:hello.o反汇编文件函数调用部分

​​​​​​​              图5-5-2-5:hello反汇编文件函数调用部分

分析:hello中没有hello.o中的重定位条目,并且跳转和函数调用的地址在hello中都变成了虚拟内存空间地址。对于hello.o的反汇编代码,函数只有在链接之后才能确定运行执行的地址,所以在.rela.text节中为其添加了重定位条目。所以在hello.o文件中,函数的调用时直接用当前的下一个地址占位,而在hello文件中是跳转到一个【函数名@plt】的函数的地址处。

(4).text段的内容增多了

​​​​​​​ ​​​​​​​

图5-5-2-6:hello.o反汇编./test部分        图5-5-2-7:hello反汇编./test部分

分析:发现左方的.text段只有main函数,右方的.text段不仅包含.main,更包含main函数的很多入口函数,如_start之类,所以./test变长了很多。

5.5.3 链接的过程:

通过分析hello和hello.o的不同之处,得到链接的主要过程就是链接就是链接器ld将各个目标文件(即各种.o文件)整合组装在一起时其各个目标文件中的各个库函数段会按照一定的规则累积在一起,需要进行符号解析、重定位及计算符号引用地址三个步骤。

5.5.4重定位:

重定位将合并输入模块。并为每个符号分配运行地址。重定位由两个步骤组成:重定位节与符号定义、重定位节中的符号引用。

1.定位节与符号定义,链接器将相同类型的节合并为同一类型的新的聚合节,此后链接器将运行时内存地址赋值新的聚合节、输入模块定义的每个节,还有输入模块定义的每个符号。

2.重定位节中的符号引用,链接器修改代码节与数据节中对每个符号的引用,使他们指向正确的运行地址,这一步依赖hello.o中的重定位条目。

5.6 hello的执行流程

​​​​​​​                 图5-6:Ubuntu下edb执行

通过edb的调试记录下每列函数调用call命令进入的函数如下所示:

这其中的子函数名和地址(后6位)如下所示:

<_init>      401000

<.plt>       401020

<puts@plt>  401090

<printf@plt> 4010a0

<getchar@plt>4010b0

<atoi@plt>  4010c0

<exit@plt>  4010d0

<sleep@plt> 4010e0

<_start>     4010f0

<_dl_relocate_static_pie> 401120

<main>               401125

<__libc_csu_init>       4011c0

<__libc_csu_fini>       401230

<_fini>                401238

5.7 Hello的动态链接分析

​​​​​​​                    图5-7-1:hello的节头部表

分析:在elf文件中我们可以找到got pit开头地址为0x404000,在edb中找到它

​​​​​​​                   图5-7-2:dl_init之前窗口

dl_init之后

​​​​​​​                    图5-7-3:dl_init之后窗口

分析:找到的地址已经由0变成了相应的偏移量。

5.8 本章小结

在本章中先介绍了链接的概念和作用,还介绍了然后在Ubuntu下利用ld命令实现链接,然后导出hello的ELF文件,分析该ELF文件里面的具体内容,并与hello.o的ELF做对比,分析异同,然后分析了hello的虚拟地址空间、重定位过程、执行流程、以及动态链接过程。

(第51分)

第6章 hello进程管理

6.1 进程的概念与作用

进程的概念:

经典定义是一个执行中程序的实例,提供给我们一种错觉:我们的程序好像是系统中当前运行的唯一程序,我们的程序独占使用处理器和内存,处理器好像是无间断的执行我们程序中的指令,我们程序的代码和数据好像是系统中内存唯一的对象。

进程的作用:

进程提供给了应用程序两个关键抽象

一个独立的逻辑控制流,它提供一个假象,好像我们的程序独占地使用处理器。

一个私有的地址空间,它提供一个假象,好像我们的程序独占地使用内存系统。

6.2 简述壳Shell-bash的作用与处理流程

作用:Shell执行一系列的读取解析步骤,然后终止。读取步骤读取来自用户的一个命令行。解析步骤将命令行解析,并运行程序。

处理流程:

(1)终端进程读取用户由键盘输入的命令行。

(2)分析命令行字符串,获取命令行参数,并构造传递给execve的argv向量

(3)检查第一个(首个、第0个)命令行参数是否是一个内置的shell命令

(4)如果不是内部命令,调用fork( )创建新进程/子进程

(5)在子进程中,用步骤2获取的参数,调用execve( )执行指定程序。

(6)如果用户没要求后台运行(命令末尾没有&号)否则shell使用waitpid(或wait等待作业终止后返回。

(7)如果用户要求后台运行(如果命令末尾有&号),则shell返回;

6.3 Hello的fork进程创建过程

根据 shell 的处理流程,可以推断,输入命令执行 hello 后,父进程如果判断不是内部指令,即会通过fork函数创建子进程。子进程与父进程近似,并得到一份与父进程用户级虚拟空间相同且独立的副本——包括数据段、代码、共享库、堆和用户栈。父进程打开的文件,子进程也可读写。二者之间最大的不同或许在于 PID 的不同。Fork函数只会被调用一次,但会返回两次,在父进程中,fork返回子进程的 PID,在子进程中,fork返回0。

6.4 Hello的execve过程

当Hello的进程被创建之后,他会调用execve函数加载并调用程序。exevce函数在被调用时会在当前进程的上下文中加载并运行一个新程序。它被调用一次从不返回,执行过程如下:

1.删除已存在的用户区域

2.映射私有区:为 hello 的代码、数据、.bss 和栈区域创建新的区域结构,所有这些区域都是私有的、写时才复制的

3.映射共享区:比如 hello 程序与共享库 libc.so 链接

4.设置 PC:exceve() 做的最后一件事就是设置当前进程的上下文中的程序计数器,使之指向代码区域的入口点

5.execve() 在调用成功的情况下不会返回,只有当出现错误时,例如找不到需要执行的程序时,execve() 才会返回到调用程序

6.5 Hello的进程执行

6.5.1逻辑控制流:

一系列程序计数器PC的值的序列叫做逻辑控制流,这些值唯一地对应于包含在程序的可执行目标文件中的指令,或是包含在运行时动态链接到程序的共享对象中的指令。各个进程将轮流使用处理器,在同一个处理器核心中,每个进程执行它的流的一部分后被暂时挂起,然后执行其他进程。

6.5.2用户模式与内核模式:

处理器通过某个控制寄存器中的一个模式位来提供限制一个应用可以执行的指令以及它可以访问的地址空间范围的功能。该寄存器描述了当前进程运行的权限。当设置了模式位时,进程就运行在内核模式中。没有设置模式位时,进程就运行在用户模式中。一个运行在内核模式的进程可以执行指令集中的任何指令,并且可以访问系统中的任何内存;而用户模式的进程不允许和执行特权指令、也不允许用户模式中的进程直接引用地址空间中内核区内的代码和数据。

6.5.3上下文切换:

内核为每个进程维持一个上下文。上下文就是内核重新启动的一个进程所需的状态。这个状态包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构。

6.5.4进程时间片:

一个进程执行它的控制流的一部分的每一时间段叫做时间片。

6.5.5调度的过程:

在进程执行的某些时刻,内核可以决定抢占当前进程,并且可以重新开始一个先前被抢占了的进程,这种决策便称为调度。调度是由内核中的调度器代码处理的。当内核选择一个新进程运行时,即内核调度了这个进程。在内核调度一个新的进程运行之后,它就可以称之为抢占了当前进程,并使用上下文切换机制来将控制转移到新的进程。例如执行sleep()函数,sleep()函数请求调用休眠进程,sleep()将内核抢占,进入倒计时,当倒计时结束后,hello程序重新抢占内核,继续执行。

6.5.6  用户模式与核心模式的相互转换:

为了能让处理器安全运行,从而不至于损坏操作系统,必须提前已知应用程序可执行指令所能访问的地址空间范围。因此就存在用户模式与核心模式的划分:核心模式可以戏称为“创世模式”,因为它拥有最高访问权限,此时处理器有以一个寄存器当做模式位来描述当前进程的特权。进程只有故障、中断或陷入系统调用时才会得到内核访问权限,其他情况下始终处于用户权限之中,从而保证了系统的安全性。

6.6 hello的异常与信号处理

6.6.1异常的种类:

类别

原因

异步/同步

返回行为

中断

来自I/O设备的信号

异步

总是返回到下一条指令

陷阱

有意的异常

同步

总是返回到下一条指令

故障

潜在可恢复的错误

同步

可能返回到当前指令

终止

不可恢复的错误

同步

不会返回

表6-6-1:异常的种类

6.6.2产生信号:

信号可以被理解为一条小消息,他通知进程系统中发生了一个某种类型的事件。每种信号类型都对应于某种系统事件,它提供了一种机制,通知用户进程发生了这些异常。发生异常后,系统会根据异常的种类发出信号,每个信号都有对应的序号。如下图:

​​​​​​​                                               图6-6-2 Linux信号

6.2.3执行过程中操作实例:

正常执行:

​​​​​​​                         图6-2-3-1:正常执行

在运行过程中按下Ctrl-Z

​​​​​​​                         图6-2-3-2:按下Ctrl-Z

按下Ctrl-Z后接其他指令:

​​​​​​​                 图6-2-3-3:按下Ctrl-Z+ps

分析:分析进程仍然处于后台。

再按下jobs

​​​​​​​

图6-2-3-4:按下jobs

结果:进程停止。

按下fg

图6-2-3-5:按下fg

结果:使该进程转为前台,继续工作

按下kill

​​​​​​​

图6-2-3-6:按下kill

结果:杀死进程。

按下pstree

​​​​​​​                       图6-2-3-7:按下pstree

6.2.4异常与信号处理说明:

当按下键盘的时候, 引发了中断异常.如图:

​​​​​​​

图6-2-4-1中断异常实例

而当程序在进行显示屏输出的时候:

引发的是陷阱:

​​​​​​​

图6-2-4-1中断异常实例

通过陷阱,调用系统函数.将字符串打印到屏幕上面

信号处理:

对于kill -9 12350:直接将SIGKILL发送到12350进程,使其直接终止.

对于CTRL Z:将SIGINT/SIGSTP由内核发送到12350号进程.SIGINT直接终止12350.而SIGSTP将12350号进程进行停止与挂起.

6.7本章小结

本章介绍了进程的相关概念和作用,了解了shell的处理过程,以及如何利用fork创建子进程,介绍了进程调度和内核用户模式切换,好介绍了4种异常和各种信号,最后根据实际操作进行分析。

(第61分)

第7章 hello的存储管理

7.1 hello的存储器地址空间

结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。

7.2 Intel逻辑地址到线性地址的变换-段式管理

7.3 Hello的线性地址到物理地址的变换-页式管理

7.4 TLB与四级页表支持下的VA到PA的变换

7.5 三级Cache支持下的物理内存访问

7.6 hello进程fork时的内存映射

7.7 hello进程execve时的内存映射

7.8 缺页故障与缺页中断处理

7.9动态存储分配管理

Printf会调用malloc,请简述动态内存管理的基本方法与策略。

7.10本章小结

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:文件

设备管理:unix io接口

8.2 简述Unix IO接口及其函数

8.3 printf的实现分析

[转]printf 函数实现的深入剖析 - Pianistx - 博客园

从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等.

字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。

显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

8.4 getchar的实现分析

异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。

getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

8.5本章小结

结论

Hello程序自被编写出代码起,经历了以下几个阶段:

首先经过预处理解析宏定义、文件包含、条件编译等,生成hello.i文件,然后编译器将代码转化为汇编代码,生成hello.s文件,然后将汇编语言转换为机器语言,生成hello.o文件,然后链接器进行重定位动态解析等一系列操作,最终生成hello的可执行文件。然后父进程shell-bash进程调用fork函数,为hello创建进程,调用execve函数在当前进程的上下文加载并运行hello程序,该进程映射它的虚拟空间到文件,运行的过程当中伴随着虚拟地址到物理地址的转换,调用的函数与I/O设备紧密结合。运行结束后,子进程终止,最终被父进程回收。至此,hello程序结束了自己的一次运行。

一学期的计算机系统学习,虽然很累,不过收获真的很大,感谢一学期老师和助教的辛苦付出!

(结论0分,缺失 -1分,根据内容酌情加分)

附件

列出所有的中间产物的文件名,并予以说明起作用。

hello.c:源程序

hello.i:经过预处理过后的文件;

hello.s:经过编译生成的汇编文件;

hello.o:汇编生成的可重定位的文件;

hello:通过链接产生的可执行目标文件二进制文件;

elf.txt: 可重定位目标文件hello.o导出的ELF文件;

hello_elf.txt:可执行文件hello导出的ELF文件;

objdump_hello.s:可重定位目标文件hello.o反汇编生成的文件

hello_objdump.s:可执行文件hello反汇编生成的文件

(附件0分,缺失 -1分)

参考文献

为完成本次大作业你翻阅的书籍与网站等

[1] Randal E.Bryant, David O'Hallaron. 深入理解计算机系统[M]. 机械工业出版社.2019.6

[2] shell解析命令行的过程以及eval命令https://www.cnblogs.com/f-ck-need-u/p/7426371.html

[3] printf 函数实现的深入剖析 https://www.cnblogs.com/pianist/p/3315801.html

[4]老师上课讲的PPT

(参考文献0分,缺失 -1分)

程序人生-Hello’s P2P相关推荐

  1. 哈工大计算机系统2022大作业:程序人生-Hello‘s P2P

    计算机系统 大作业 题     目 程序人生-Hello's P2P 专       业 计算机类 学    号 120L022115 班    级 2003007 学       生 王炳轩 指 导 ...

  2. 哈工大计算机系统大作业:程序人生-Hello’s P2P

    题     目 程序人生-Hello's P2P 专       业 计算学部 学   号 120L022028 班   级 2003007 学       生 杨建中 指 导 教 师 吴锐 计算机科 ...

  3. 2022计算机系统大作业——程序人生-Hello’s P2P

    计算机系统 大作业 题     目 程序人生-Hello's P2P 专       业 计算机 学    号 120L021716 班    级 2003005 学       生 蔡泽栋 指 导 ...

  4. 哈工大计算机系统大作业——程序人生-Hello’s P2P

    计算机系统 大作业 题          目程序人生-Hello's P2P 专          业 计算机科学与技术 学       号120L022401 班          级 200300 ...

  5. 哈工大CSAPP大作业:程序人生-Hello’s P2P

    ​     计算机系统 大作业 题     目 程序人生-Hello's P2P 专       业 计算学部 学    号 120L021818 班    级 2003006 学       生 秦 ...

  6. HIT CS:APP 计算机系统大作业 《程序人生-Hello’s P2P》

    HIT CS:APP 计算机系统大作业 程序人生-Hello's P2P Hello的自白 我是Hello,我是每一个程序猿¤的初恋(羞羞--) l却在短短几分钟后惨遭每个菜鸟的无情抛弃(呜呜--), ...

  7. 【计算机系统】ICS大作业论文-程序人生-Hello’s P2P

    计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 计算机科学与技术专业 学 号 200111026 班 级 20级计算机9班 学 生 陈弘毅 指 导 教 师 郑贵滨 计算机科学与技 ...

  8. 程序人生-hello`s P2P

    计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 计算机系 学 号 1170300921 班 级 1703009 学 生 *** 指 导 教 师 史先俊 计算机科学与技术学院 20 ...

  9. HIT-ICS大作业-程序人生Hello‘s P2P

    计算机系统 大作业 题     目 程序人生-Hello's P2P 专       业 计算机类 学    号 120L021311 班    级 2003011 学       生 郭俊诚 指 导 ...

  10. HIT-ICS2022大作业(程序人生-Hello’s P2P)

    计算机系统 大作业 题     目 程序人生-Hello's P2P 专       业 计算机科学与技术 学    号 班    级 学       生 指 导 教 师 计算机科学与技术学院 202 ...

最新文章

  1. 全新目标检测范式SparseR-CNN,超详细实战教学(附数据集)
  2. 王璋等揭示慢性阻塞性肺疾病炎症内型与呼吸道微生物组的关系(IF 21)
  3. java 下一代,什么是&QUOT;下一代插件&QUOT;对Java
  4. 【计算理论】图灵机 ( 非确定性图灵机 | 非确定性图灵机指令分析 | 计算过程 | 非确定性指令出现多个分支 | 非确定性图灵机转为计算树 | 计算树 )
  5. Boost:reference wrapper参考包装的测试程序
  6. python列表的索引算法_Python-确定列表是否对称的算法
  7. Java实现Excel导入数据库,数据库中的数据导入到Excel
  8. 秒杀场景_Sentinel在秒杀场景的应用_05
  9. Outlook式样界面菜单和页面控制
  10. php编程模式,PHP编程之-设计模式简单实例
  11. 双目测距(四)--罗德里格斯变换
  12. SQL Server数据库的查询语句
  13. 利用HTML简单语句来制作个人简历
  14. 常用公差配合表图_车间里常用的测量器具,别说没见过!
  15. 如何删除360浏览器的桔梗导航
  16. 大数据处理技术-头歌平台-答案
  17. 在你的ipad上使用Vscode撸代码(快速操作向)
  18. iOS设置基于ikev2的协议连接企业虚拟专用网络
  19. 进神经网络的学习方式(译文)----中
  20. C++中头文件(.h)和源文件(.cpp)都应该写些什么

热门文章

  1. 2021数学建模国赛一等奖经验总结与分享
  2. 亲测可用sqlyog激活注册码
  3. JAVA实现网页版斗地主_通过Java实现斗地主
  4. rpg服务器修改数据,ATOM RPG 修改数据方法 怎么修改游戏数据-游侠网
  5. 复变函数在计算机科学中的应用,复变函数的应用以及发展史
  6. 成功解决微信浏览器实现自动下载功能
  7. 计算机软件水平考试中级题目,计算机软考中级考试试题及答案
  8. netcore读取json文件_NetCore 对Json文件的读写操作
  9. Java部署斗鱼直播,iOS斗鱼直播项目
  10. 斗鱼基于etcd和ZooKeeper的注册中心实践案例