计算机系统

大作业

题     目 程序人生-Hello’s P2P

专       业 计算机科学与技术

学     号 2021113637

班     级 2103103

学       生 杨智文 

指 导 教 师 刘宏伟  

计算机科学与技术学院

2022年5月

摘  要

本文从头开始跟踪了编程界的传奇hello的一生,通过了解hello的预处理、编译、汇编和链接的过程从而了解到一个伟大的程序以及可执行目标文件是如何一步一步诞生的。接着又跟踪了hello作为进程诞生到消失的过程,从而理解系统中进程是如何运作的。

从hello的一生可以映射出庞大的计算机系统中的各种知识,跟着传奇的hello,通过各种实验于分析来深入理解计算机系统。

关键词:计算机系统、P2P、020、预处理、编译、汇编、链接、进程

目  录

第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 -

结论............................................................... - 14 -

附件............................................................... - 15 -

参考文献....................................................... - 16 -

第1章 概述

1.1 Hello简介

根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。

P2P:

P2P就是From Program to Process,指的是从程序到进程的过程。为了在系统上运行hello.c程序,hello.c首先经过预处理器(cpp)得到修改了的源程序hello.i;接着,编译器(cc1)将其翻译为汇编程序hello.s,然后经过汇编器(as)翻译成机器语言指令,把这些指令打包成可重定位目标程序hello.o,接下来,经过链接器,将调用的标准C库中的函数(如printf等)对应的预编译好了的目标文件以某种方式与hello.o文件进行合并,得到可执行目标程序hello 。

图1.1.1程序编译过程

当我们运行时,在shell中利用fork()函数创建子进程,再用execve加载hello程序,这时,hello就由程序(program)变成了一个进程(process),完成了P2P的过程。

020:

020就是From Zero to Zero,指的是在shell中用fork()函数创建子进程,再用execve加载可执行目标程序hello,映射虚拟内存,程序开始时载入物理内存,进入CPU处理。CPU为执行文件hello分配时间片,进行取指、译码、执行等流水线操作。内存管理器和CPU在执行过程中通过L1、L2、L3三级缓存和TLB多级页表在物理内存中取的数据,通过I\O系统根据代码指令进行输出。在程序运行结束后,父进程会对其进行回收,内核把它从系统中清除,这时hello就由无又变成了无,完成了020的过程。

1.2 环境与工具

硬件环境:

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

软件环境:

Windows 10、VirtualBox6.1、Ubuntu 20.04.4

开发工具:

Vi/Vim/gedit/gcc

1.3 中间结果

1.3.1     hello.c                 C语言源文件

1.3.2     hello.i                  预处理产生的文件

1.3.3     hello.s                  编译产生的汇编代码文件

1.3.4.1   hello.o                 汇编产生的可重定位目标文件

1.3.4.2   asm.txt                hello.o反汇编生成的文本文件

1.3.5.1   hello                    链接产生的可执行目标文件

1.3.5.2   asm_2.txt            hello返回百年生成的文本文件

1.4 本章小结

本章主要介绍了hello程序的P2P(From Program to Process) 及020(From Zero-0 to Zero-0)的过程。同时介绍了此次大作业完成的硬件环境、软件环境以及开发工具。还列出了为完成本次大作业,生成的中间结果文件的名字以及文件的作用。

第2章 预处理

2.1 预处理的概念与作用

预处理的概念:

预处理是指预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。预处理后的文件后缀为(.i),如hello.c预处理后成为hello.i。预处理的本质实际是文本替换,处理过后的文件同hello.c一样都是由ASCII码组成的文件,在大小端表现形式都一样,是文本文件。

预处理的作用:

预处理可以处理C语言中以#开头的语句以及对注释的删除:

1.可以进行宏替换,用实际的常量或字符串常量来替换它的符号;(#define)

2.处理文件包含,将包含的文件插入到程序文本中;(#include)

3.条件编译,选择符合条件的代码送至编译器编译,实现有选择地执行相关操作(#if,#elif,#else)

4.实现特殊控制指令。(#error)

5.删除C语言源程序中所有的注释;

2.2在Ubuntu下预处理的命令

命令为:

gcc -E hello.c -o hello.i

在虚拟机上预处理过程如下:

图2.2.1预处理命令

通过命令生成的文件hello.i如下图所示:

图2.2.2生成的hello.i文件

2.3 Hello的预处理结果解析

首先查看对比hello.c与hello.i两文件的内容,可以发现主函数代码段是相同的,即预处理没有对代码段进行处理,并且可以看出hello.i文件中没有注释中的内容,即预处理会删除注释的内容,如2.3.1与2.3.2图:

图2.3.1源文件hello.c

图2.3.2文件hello.i中与hello.c中相同部分

也可以看到除了原本代码中没有的部分,如下图:

图2.3.3 hello.i文件中新增部分

可以看出,新增的部分中有在源程序中被引用的stdio.h,unistd.h,以及stdlib.h的代码,它们都是被直接插入了程序文本中,以及在hello.c中引用的头文件中引用的另一些文件,他们在预处理的过程中都需要被直接插入到hello.i文件中,使得文件代码量相比源程序大大增加。并且还可以看到在hello.i文件开头,即被红色方框圈起来的部分,有着一些对于这个与处理文件的解释。

2.4 本章小结

本章主要介绍了预处理的概念以及预处理在五个方面的作用与功能,在Ubuntu下将hello.c文件预处理生成了hello.i文件,并且对照hello.c与hello.i文件分析了hello.i文件与源程序hello.c文件的相同与不同之处,发现预处理的过程会保留注释与#开头语句之外的部分,删除注释部分,同时将头文件与头文件所包含的文件直接插入到代码中,在文件开头增加说明信息。

第3章 编译

3.1 编译的概念与作用

编译的概念:

编译是指将便于人编写、阅读、维护的高级计算机语言所写作的源代码程序,翻译为计算机能解读、运行的低阶机器语言的程序。即编译器(ccl)将文本文件hello.i翻译成汇编语言程序hello.s的过程。

编译的作用:

编译的作用是将高级计算机语言所写作的源代码程序翻译为汇编语言程序,在这个过程中,会进行以词法分析、语法分析、语义分析来生成汇编语言程序,且编译器可能在这个过程中根据编译选项对程序进行一些适当的优化。

编译可以将高级程序设计语言翻译成较为底层的汇编语言,在这个过程中产生的汇编程序里的指令更加贴近于机器指令也更为贴近机器真正执行程序的过程,可以让程序员更好明白程序在机器层面上的底层实现。

3.2 在Ubuntu下编译的命令

命令为:

gcc -S hello.i -o hello.s

在虚拟机上编译过程如下:

图3.2.1编译命令

通过命令生成的文件hello.s如下图所示:

图3.2.2生成的hello.s文件

3.3 Hello的编译结果解析

3.3.1数据

1. 数字常量:

图3.3.1文件hello.s中的数字常量

用了立即数$4

图3.3.2文件hello.s中的数字常量

在i<9的表达式中,hello.s文件中用$8来表示

2. 字符串常量:

在hello.s中可以看见:

图3.3.3文件hello.s中的字符串常量

如图,根据所学知识文件hello.s中将字符串常量存储在只读段.rodata段

关于输出字符串常量的不同如下图:

图3.3.3文件hello.s中引用字符串常量

从图中可以看出,打印字符串常量时,编译器将语句翻译为先将字符串存放的地址存入寄存器%rdi,再进行打印

3. 局部变量:

编译器一般将局部变量存放在寄存器中或者栈中

如下图:

图3.3.4文件hello.s中局部变量

如上图,%edi为传入的参数argc

-20(%rbp)表示将argc存储在栈上

图3.3.5文件hello.s中局部变量

传入参数char *argv[]存放在栈中,其中首地址存放在栈中-32(%rbp)的位置,argv[1]地址为-32(%rbp)+$8,argv[2]地址为-32(%rbp)+$16,argv[3]地址为-32(%rbp)+$24。在hello.s中的使用如下图所示

图3.3.6文件hello.s中局部变量

局部变量int i存放在栈中-4(%rbp)的位置

3.3.2 赋值

编译器将赋值的操作主编为对相应的寄存器或栈进行赋值

如下图:

图3.3.7文件hello.s的赋值

在hello.s的汇编代码中,采用mov指令对变量进行赋值

3.3.3 算数操作

如下图:

图3.3.8文件hello.s的算数操作

程序中的算数操作i++在hello.s文件中的汇编代码呗翻译为用add指令通过操作数$1来实现,通过对汇编代码的分析可以知道栈中-4(%rbp)的位置存放的是局部变量i,每次执行这条指令,实现的是i自增1。

3.3.4 关系操作

如下图:

图3.3.9文件hello.s的比较操作

图3.3.10文件hello.s的比较操作

上两图体现出比较操作在汇编代码中的体现,通过CMP和条件跳转指令联合进行实现

3.3.5 数组/指针/结构操作

图3.3.11文件hello.s中对数组的访问

编译器对源代码中数组的操作往往翻译为对地址的加减操作,其中首地址存放在栈中-32(%rbp)的位置,argv[1]地址为-32(%rbp)+$8,argv[2]地址为-32(%rbp)+$16,argv[3]地址为-32(%rbp)+$24即进行了地址的加减操作以访问数组。

3.3.6 控制转移

如下图:

图3.3.12文件hello.s中的条件转移

上图中第一个为条件跳转,当argc=4时才发生跳转

第二个为无条件跳转,当i赋初值为0后跳转进入循环体

图3.3.13文件hello.s中的条件转移

上图的也是条件跳转,当循环次数未到时,不断通过跳转重复执行循环体中的内容。

3.3.7 函数调用

在程序中一共有6个函数调用,如下图:

图3.3.14程序中函数调用

对于第一个printf函数,在hello.s中有:

图3.3.15文件hello.s中的函数调用

从图中可以看到,在C语言源程序中执行该命令用的是printf函数,由于打印的是一个单纯的字符串,因此编译器对它进行了优化,改用puts函数进行打印,所以hello.s调用puts()函数。首先将调用函数所需要的参数,即需要打印的字符串常量的地址存放在寄存器%rdi中,然后执行call puts@PLT指令打印字符串。

对于第二个函数调用,即exit(1):

图3.3.16文件hello.s中的函数调用

对于第三个函数调用:

图3.3.17文件hello.s中的函数调用

对于第四个和第五个函数调用:

图3.3.18文件hello.s中的函数调用

关于第六个函数调用:

图3.3.19文件hello.s中的函数调用

3.4 本章小结

1. 本章主要介绍了编译的的概念以及编译的作用与功能,包括将高级语言指令翻译为汇编语言指令,同时对与编译器的优化解释做了一些解释。

2. 在Ubuntu下将hello.i文件编译生成了hello.s文件。

3. 按照C语言的不同数据与操作类型,分析了源程序hello.c文件中的语句是怎样转化为hello.s文件中的语句的。其中数据类型包括数字常量、字符串常量和局部变量;操作类型包括赋值、算术操作、关系操作、数组\指针\结构操作控制转移以及函数调用。

第4章 汇编

4.1 汇编的概念与作用

汇编的概念:

汇编是指将汇编语言程序经过编译器(as)转化为二进制的机器语言指令,并把这些指令打包成可重定位目标程序的格式,并保存在目标文件.o中。即汇编器器(as)将文本文件hello.s转换为可重定位目标程序hello.o的过程。

由于生成的是二进制文件,且于机器指令相关,所以在不同机器上的表现会有所不同,即可移植性差。

汇编的作用:

汇编的作用是把汇编语言翻译成机器语言,用二进制码0、1代替汇编语言中的符号,即让它成为机器可以直接识别的程序。最后把这些指令打包成可重定位目标程序的格式,并保存在目标文件.o中。

4.2 在Ubuntu下汇编的命令

命令为:

gcc -C hello.s -o hello.o

在虚拟机上汇编过程如下:

图4.2.1汇编命令

通过命令生成的文件hello.o如下图所示:

图4.2.2生成的hello.o文件

4.3 可重定位目标elf格式

查看hello.o的ELF格式可以通过在命令行中输入命令:

readelf  -a  hello.o

也可以通过>输入到文件中

图4.3.1通过readelf指令查看文件ELF格式

4.3.1 ELF头

hello.o的ELF格式的开头是ELF头,它以一个16字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。ELF头剩下的部分包含帮助链接器语法分析和解释目标文件的信息:包括ELF头的大小、目标文件的类型、处理器体系结构、节头部表的文件偏移,以及节头部表中条目的大小和数量。

hello.o文件的ELF头如下图:

图4.3.2文件hello.o的ELF头

4.3.2 节头目表

ELF文件格式中的节头部表描述了目标文件中不同节的类型、地址、大小、偏移等信息,以及可以对各部分进行的操作权限。

hello.o文件的节头目表如下图:

图4.3.3文件hello.o的节头目表

4.3.3 重定位节

ELF文件格式中的重定位节包含两个部分:.rela.text节与.rela.eh_frame节。

图4.3.4文件hello.o的重定位节

.rela.text节包含.text节中的位置的列表,含有该.text中所需要进行重定位操作的信息,当链接器(ld)将目标文件与其他文件由进行结合时,需要修改这些位置

.rela.eh_frame节包含了对en_frame节的重定位信息。

表头信息含义为:

  1. 偏移量:需要重定位的信息的字节偏移位置(代码节/数据节)
  2. 信息:重定位目标在.symtab中的偏移量和重定位类型
  3. 类型:表示不同的重定位类型
  4. 符号值:符号的数值
  5. 符号名称:被重定位时指向的符号
  6. 加数:偏移

4.3.4 符号表

ELF文件格式中的符号表中存放了程序中所定义和引用的的全局变量以及函数的信息。(不包含局部变量)

图4.3.4文件hello.o的符号表

4.4 Hello.o的结果解析

通过用命令行objdump -d -r hello.o > asm.txt将hello.o的反汇编保存于文件asm.txt中:

图4.4.1文件hello.o的反汇编过程

图4.4.2文件hello.o生成的反汇编文件asm.txt

通过对比asm.txt即hello.o的反汇编与hello.s可以发现有些许不同:

4.4.1 数字

在汇编过程中,数字的进制发生了改变

图4.4.3数字进制对比

可以看到数字从10进制变成了16进制,对应机器代码二进制数

4.4.2 对字符串常量的引用

在汇编过程中对于常量引用的表达不同

图4.4.4对字符串常量引用的对比

通过分析可知,hello.s中是用的全局变量所在的那一段的名称加上%rip的值,而重定位目标文件hello.o中用的是0加%rip的值。

4.4.3 分支转移

在汇编过程后对于分支跳转的表达形式发生改变:

图4.4.5跳转的对比

可以看到hello.s通过段名进行分支转移,跳转指令后用对应的段的名称表示跳转位置,而在hello.o的反汇编代码中每个段都有明确的地址,跳转指令后用相应的绝对或者相对地址表示跳转位置

4.4.4 函数调用

在汇编过程后对于函数调用的表达方式也与汇编代码有所不同

图4.4.6函数调用的对比

可以看到,hello.s调用函数在call指令之后直接引用函数名称,而在hello.o的反汇编代码中,在call指令后加上下一条指令的地址来表示。

4.5 本章小结

1. 本章主要介绍了汇编的的概念以及汇编的作用,主要是将汇编语言程序经过编译器(as)转化为二进制的机器语言指令,并把这些指令打包成可重定位目标程序的格式,并保存在目标文件.o中,成为机器可以识别的程序。在Ubuntu下将hello.s文件经过汇编器(as)生成了hello.o文件。

2. 通过readelf分析了hello.o的ELF格式,用readelf等列出了其各节的基本信息,包括ELF头、节头部表、重定位节以及符号表的功能与包含的信息。

3. 并且用objdump反汇编生成了asm.txt文本文件。对hello.o二进制文件进行反汇编,得到了反汇编程序,并分析了该反汇编程序与汇编语言程序hello.s中语句的对应关系。从数字进制、字符串常量的引用、分支转移以及函数调用的不同四个方面分析了二者的关系。

第5章 链接

5.1 链接的概念与作用

链接的概念:

链接(linking)就是将不同部分的代码和数据收集和组合成为一个单一文件的过程,这个文件可被加载或拷贝到内存并执行。

链接的作用:

链接的作用是将预编译好了的一个目标文件(hello.o)或若干目标文件外加链接库合并成为一个可执行目标文件(hello)。使得分离编译称为可能,不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为可独立修改和编译的模块。当改变这些模块中的一个时,只需简单重新编译它并重新链接即可,不必重新编译其他文件。

链接器在软件开发中扮演了关键的角色,它使得分离编译成为可能。我们不用将一个大小的应用程序组织成一个巨大的源文件,而是可以把它分解成为更小的,更好管理的模块,可以独立的修改和编译这些模块,最终由链接器链接成为一个可执行文件。

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.1链接命令

通过命令生成的文件hello如下图所示:

图5.2.2生成的hello可执行文件

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

通过指令headelf -a hello查看可执行目标文件hello的ELF格式

图5.3.1可执行文件hello的ELF节信息

从图中可以看出执行文件hello的ELF的文件格式

其中,第一列为节的名称,第二列为节的类型,第三列为节开头地址,第四列为节的偏移量。

5.4 hello的虚拟地址空间

通过readelf查看hello的程序头,可以发现其中的虚拟地址在EDB的Data Dump中都能找到相应的位置,且大小也相对应。

在EDB的symbol窗口,可以查看各段对应的名称以及各段的起始位置与结束的位置,与5.3中所展示出来的elf格式展示出来的相对应。

如下图:

图5.4.1虚拟地址对应

通过Data Dump通过地址进行查找的结果(给出一个示例)

图5.4.2ELF头

在命令行中用readelf -a hello查看可执行文件hello的elf中的程序头:

图5.4.3程序头

可以通过EDB的Data Dump查看,如下图(给出一个示例):

图5.4.4虚拟地址对应

5.5 链接的重定位过程分析

通过指令objdump -d -r hello > asm_2.txt将可执行文件hello反汇编到文本文件asm_2.txt中

图5.5.1对可执行文件hello进行反汇编

图5.5.2生成的反汇编文件

将asm_2.txt与asm.txt进行对比,即将可执行目标文件的反汇编结果与可重定位目标文件的反汇编结果进行比较,可以发现有如下几个方面的区别

5.5.1 代码量

图5.5.3两文件代码量不同

从图中可以看出,可执行文件反汇编生成的代码量远大于可重定位目标文件反汇编生成的代码量。

原因:在hello.o的反汇编程序中,只有main函数,没有调用的函数段;经过链接过程后,原来调用的C标准库中的代码都被插入了代码中,并且每个函数都被分配了各自的虚拟地址

如下图:

图5.5.4插入了C标准库中的代码

5.5.2 指令分配虚拟地址

如下图:

图5.5.5指令分配虚拟地址

从图中可以看出在hello.o的反汇编程序中, main函数中的所有语句前面的地址都是从main函数开始从0开始依次递增的,而不是虚拟地址;经过链接后,每一条语句都被分配了虚拟地址。

5.5.3 字符常量的引用

如下图:

图5.5.6字符常量的引用得到修正

从图中可以看到,在hello.o的反汇编程序中,字符串常量的位置是用0加%rip的值来表示的,这是由于当时字符串常量并未分配虚拟内存,而在hello的反汇编程序中,因为字符串常量都有了相应的位置,所以用实际的相对下一条语句的偏移量加%rip即PC的值来描述其位置。

5.5.4 函数调用

如下图:

图5.5.7函数调用

从图中可以看出,在hello.o的反汇编程序中,由于当时函数未被分配地址,所以调用函数的位置都用call加下一条指令地址来表示,而在hello的反汇编程序中,由于各函数已拥有了各自的虚拟地址,所以在call后加其虚拟地址来实现函数调用。

5.5.5 跳转指令

如下图:

图5.5.8跳转

从图中可以看出,同函数调用一样对跳转指令进行修正。在hello.o的反汇编程序中,对于跳转指令,在其后加上目的地址,为main从0开始对每条指令分配的地址,而在hello的反汇编程序中,由于各语句拥有了各自的虚拟地址,所以同样加上目的地址,但这里是每条指令的虚拟地址。

5.5.7 对于链接的过程的分析

从上述分析结合课本上知识,可以对链接的过程总体进行分析

首先,链接的过程主要分为两个过程:

1. 符号解析:符号解析解析目标文件定义和引用符号,并将每个符号引用和一个符号定义相关联。

2. 重定位:编译器和汇编器生成从0开始的代码和数据节。而链接器通过把每个符号定义与一个虚拟内存地址相关联,从而将这些代码和数据节重定位,然后链接器会修改对所有这些符号的引用,使得它们指向这个虚拟内存地址。

将可重定位目标文件hello.o链接成为可执行目标文件hello的过程中,链接器(ld)把hello中的符号定义都与一个虚拟内存位置关相关联,对这些节进行重定位,并在之后对符号的引用中把它们指向重定位后的地址。

从上述对比图可以看出hello中每条指令都对应了一个虚拟地址,而且对每个函数,全局变量也都它关联到了一个虚拟地址,在函数调用,全局变量的引用,以及跳转等操作时都通过虚拟地址来进行,从而执行这些指令。

5.6 hello的执行流程

使用edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。

<_init>:0x4e8

<.plt>:0x500

<puts@plt>:0x510

<__cxa_finalize@plt>:0x520

<_start>:0x 00530

<deregister_tm_clones>:0x560

<register_tm_clones>:0x5a0

<__do_global_dtors_aux>:0x5f0

<frame_dummy>:0x630

<main>:0x63a

<__libc_csu_init>:0x660

<__libc_csu_fini>:0x6d0

<_fini>:0x6d4

5.7 Hello的动态链接分析

分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。

当程序调用一个共享库的函数时,编译器不能预测这个函数在什么地址,因为定义它的共享模块在运行时可以加载到任何位置。这时,编译系统提供了延迟绑定的方法,即:将过程地址的加载推迟到第一次调用该过程时。通过观察edb对hello的执行情况,便可发现dl_init前后后.got.plt节发生的变化。

首先,通过readelf找到.got.plt节在地址为0x404000的地方开始,大小为0x48。因此,结束地址为0x40400047,这两个地址之间部分便是.got.plt的内容。

图5.7.1 .got.plt的虚拟地址

在EDB中的Data Dump中找到这个地址,观察.got.plt节的,发现在dl_init前后,.got.plt的第8到15个字节发生了变化。

在这里,这些变化的字节分别对应GOT[1]和GOT[2]的位置。其中, GOT[1]包括动态链接器在解析函数地址时使用的信息,而GOT[2]则是动态链接器ld-linux.so模块中的入口点。加载时,动态链接器将重定位GOT中的这些条目,使它们包含正确的地址。内存的变化如下图所示:

图5.7.2 .got.plt的变化

5.8 本章小结

1. 本章主要介绍了本章主要介绍了链接的的概念以及链接的作用

2. 将可重定位目标文件hello.o和若干目标文件外加链接库合并成为一个可执行目标文件hello,即在Ubuntu下将hello.o文件经过链接器(ld)生成了可执行目标文件hello。

3. 分析了hello的ELF格式,并用readelf等列出了其各节的基本信息,包括起始位置、大小等信息。

4. 用edb查看了hello的虚拟地址空间,发现各节的名称都与相应的一段虚拟地址相对应,同时查看了各节的起始位置与大小。

5. 用objdump对可执行目标文件hello进行反汇编,生成反汇编文本文件asm_2.txt,并与hello.o的反汇编程序进行比较。发现相比于hello.o的反汇编程序来说,经过链接过程后,hello的反汇编程序代码量增加,插入了C标准库中的函数代码,指令都分配了虚拟地址,字符串常量的引用、函数调用以及跳转指令的地址都替换为了虚拟地址。

6. 分析了链接的过程,包括符号解析以及重定位。

7. 使用edb执行hello,说明了从加载hello到_start,到call main,以及程序终止的所有过程,并列出了其调用与跳转的各个子程序名以及程序地址。

8. 分析了hello程序的动态链接项目,通过edb调试,分析了在dl_init前后,这些.got.plt节的的内容变化,是由动态链接的延迟绑定造成的。

第6章 hello进程管理

6.1 进程的概念与作用

进程的概念:

进程就是一个执行中程序的实例。系统中的每个程序都运行在某个进程的上下文中。上下文是由程序正确运行所需的状态组成的。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器地内容、程序计数器、环境变量以及打开文件描述符的集合。

进程的作用:

提供两个抽象:

  1. 进程提供独立的逻辑控制流,好像我们的程序独占地使用处理器
  2. 提供一个私有的地址空间,好像我们的程序独占地使用内存系统

使CPU被科学有效地划分成多个部分以并行地运行多个进程。

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

Shell-bash的作用:

shell应用程序提供了一个界面,用户通过访问这个界面访问操作系统内核的服务。

每次用户通过向shell输人一个可执行目标文件的名字,运行程序时, shell就会创建个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件。

Shell-bash的处理流程:

1. 从终端读入输入的命令。

2. 将输入字符串切分获得所有的参数。

3. 如果是内置命令则立即执行。

4. 否则调用相应的程序执行。

5. shell 应该接受键盘输入信号,并对这些信号进行相应处理。

6.3 Hello的fork进程创建过程

在终端输入./hello时shell进行对命令行的解释,因为第一个命令行参数不是内置shell命令,所以shell会fork一个子进程并执行可执行程序hello。新建的子进程几乎和父进程相同;子进程得到与父进程用户级虚拟地址空间相同的但独立一份副本,与父进程任何打开的文件描述符相同的副本。

处理过程如下图:

图6.3.1 shell流程

6.4 Hello的execve过程

execev过程:在shell给hello进行fork()函数创建子进程之后,会调用execve函数,在进程的上下文中加载并运行hello,调用_start创建新的且被初始化为0的栈等,随后将控制给主函数main,并传入参数列表和环境变量列表。当出现错误时,exceve才会返回到调用程序,否则,exceve调用一次且不返回。在exceve加载完毕可执行目标文件hello后,会调用启动代码,启动代码设置栈,将可执行目标文件中的代码和数据从磁盘复制到内存中,然后通过跳转到程序的第一条指令或入口点来运行该程序,由此将控制转移给新程序的主函数。

通过exeve函数生成后用户栈的典型结构为:

图6.4.1 用户栈的典型结构

6.5 Hello的进程执行

结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。

基本概念:

控制流:计算从加电开始,到断点位置,程序计数器的一系列PC的值的序列叫做控制流。

逻辑控制流:使用调试器单步执行程序时,会看到一系列的程序计数器(PC)的值,这些值唯一地对应于包含在程序的可执行目标文件中的指令,或是包含在运行时动态链接到程序的共享对象中的指令。这个PC值的序列叫做逻辑控制流,或者简称逻辑流。即逻辑控制流是一个进程中PC值的序列

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

用户模式和内核模式:处理器通常使用一个寄存器提供两种模式的区分,该寄存器描述了进程当前享有的特权,当没有设置模式位时,进程就处于用户模式中,用户模式的进程不允许执行特权指令,也不允许直接引用地址空间中内核区内的代码和数据;设置模式位时,进程处于内核模式,该进程可以执行指令集中的任何命令,并且可以访问系统中的任何内存位置。

上下文:上下文就是内核重新启动一个被抢占的进程所需要的状态,它由通用寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构等对象的值构成。

进程执行:

当开始运行hello时,内存为hello分配时间片,如一个系统运行着多个进程,那么处理器的一个物理控制流就被分成了多个逻辑控制流,逻辑流的执行是交错的,它们轮流使用处理器,会存在并发执行的现象。其中,一个进程执行它的控制流的一部分的每一时间段叫做时间片。然后在用户态下执行并保存上下文。如果在此期间内发生了异常或系统中断,则内核会休眠该进程,并在核心态中进行上下文切换,控制将交付给其他进程。当hello 执行到 sleep时,hello 会休眠,再次上下文切换,控制交付给其他进程,一段时间后再次上下文切换,恢复hello在休眠前的上下文信息,控制权回到 hello 继续执行。hello在循环后,程序调用 getchar() , hello 从用户模式进入内核模式,并再次上下文切换,控制交付给其他进程。最终,内核从其他进程回到 hello 进程,在return后进程结束。

下图为进程上下文切换的示意图:

图6.5.1 进程的上下文切换

6.6 hello的异常与信号处理

6.6.1 正常运行:

图6.6.1 程序正常运行

程序正常运行时,会循环9次输出hello 2021113637 杨智文,循环结束后会调用函数getchar(),键入回车后程序结束

6.6.2 在运行过程中不断乱按

图6.6.2 程序运行过程中不断乱按

在程序运行的过程中不断乱按,在程序结束后仍会通过getchar()函数让程序停止,键入回车后程序结束,之前乱按的信息都在输入缓冲区中和程序一起消失了。

6.6.3 在程序运行过程中键入回车

图6.6.3 程序运行过程中键入回车

如图所示,在程序运行过程中键入了四个回车,程序循环结束后,getchar()函数只会读取走第一个回车,所以后面的三个回车会在终端被当作时输入

综合上述两种情况,如下图:

图6.6.4程序运行过程中键入回车后输入信息

可以看到,在程序运行过程中,先输入一个回车后再输入ls+回车,再程序结束后,ls就作为终端中的输入。

6.6.4 再程序运行中键入Ctrl+Z

如下图:

图6.6.4程序运行过程中键入回车后输入信息

可以看到,再程序运行过程中键入Ctrl+Z会让程序产生中断异常,发送信号SIGSTP,这时hello的父进程shell会接收到信号SIGSTP并运行信号处理程序让hello进程被挂起,并打印相关信息。

6.6.4.1 ps

图6.6.5输入ps指令

Ctrl-z之后,在shell命令行中输入ps,打印出各进程的pid,再其中可以找到被挂起的hello。

6.6.4.2 jobs

图6.6.6输入jobs指令

Ctrl-z之后,在shell命令行中输入jobs,打印出被挂起的hello的相关信息。

6.6.4.3 pstree -p

在命令行中输入pstree -p指令:

图6.6.7输入pstree -p指令

在其中可以找到进程hello:

图6.6.8进程树中hello的位置

6.6.4.4 fg

如下图:

图6.6.9输入fg指令

Ctrl-z之后,在shell命令行中输入fg,被挂起在后台的hello进程被重新调到前台执行,打印出剩余部分,按回车后终止程序,由于之前在程序输出1个信息时停止,所以继续后会输出8个信息。

6.6.4.5 kill

图6.6.10输入kill指令

Ctrl-z之后,输入ps,得到hello的pid为5527,因此,在shell中输入kill -9 5527,可以发送信号SIGKILL给进程5527,从而杀死该进程。之后通过输入ps查看,可以看到提示显示hello进程已经被杀死

6.6.5 在程序运行过程中键入Ctrl+C

图6.6.11程序运行过程中键入Ctrl+C

运行hello时按Ctrl-C,会导致断异常,从而内核产生信号SIGINT,发送给hello的父进程,父进程收到它后,向子进程发生SIGKILL来强制终止子进程hello并回收它。这时再运行ps,可以发现并没有进程hello,可以说明他已经被终止并回收了。

可以通过以下方法进一步验证:

图6.6.12对Ctrl+C作用的进一步验证

6.7本章小结

1. 本章主要介绍了进程的概念与作用。进程就是一个执行中程序的实例,CPU为每个进程提供独立的逻辑控制流,和一个私有的地址空间,从而将CPU科学有效地划分成多个部分并行地运行多个进程。

2. 介绍了Shell-bash作用与处理流程。Shell为用户提供命令行界面,使用户可以在这个界面中输入shell命令,然后shell执行一系列的读/求值步骤,读步骤读取用户的输入的命令行,求值步骤则解析命令行,并运行程序。完成后重复上述步骤,直到用户退出shell。

3. 说明了了hello的fork进程创建过程与execve过程,通过调用fork()函数与execve()来实现。同时结合了进程上下文信息、进程时间片、用户态与核心态转换等,介绍了hello是如何在shell中作为一个子进程执行的。

4. 分析并测试了了hello执行过程中按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等出现的异常、产生的信号,在Ctrl-z后运行ps、jobs、pstree、fg、kill等命令,查看这些指令对应的内容,进程的状态等,说明了异常与信号的处理。

5.在处理中通过结合两种方式得到了新奇的结果并分析了其中原因

 

结论

Hello所经历的过程:

1.为了在系统上运行hello.c程序,hello.c首先经过预处理器(cpp)得到修改了的源程序hello.i。hello.i中头文件与注释以外的源代码保持不变,注释被被编译器删除,而代码量比hello.c大大增加,原因是引用的头文件被插入到了源代码中。

2.接着,编译器(cc1)将hello.i翻译为汇编程序hello.s。在这个过程中,高级语言被翻译为机器逻辑下的汇编语言。而其中编译器会根据编译选项的不同对程序进行一些优化,所以不同编译方式得到的结果会不相同。

3.然后hello.s经过汇编器(as)翻译成机器语言指令,把这些指令打包成可重定位目标程序hello.o。hello.o是一个二进制文件,可以查看它的ELF格式和相应的反汇编代码。查看ELF格式可以发现,hello.o是由不同的节组成的,每个节都有相应的大小和功能。

4.接下来,经过链接器,将调用的标准C库中的函数(如printf等)对应的预编译好了的目标文件以某种方式合并到hello.o文件中,得到可执行目标程序hello。hello也是一个二进制文件,可以查看它的ELF格式与反汇编代码。相比于hello.o,hello的代码量大大增加,这是由于一些C标准库中的函数(如printf)等也被组合进入了可执行目标文件hello中。同时,每一行代码、字符串常量等,都会被分配相应的虚拟地址,其中的数字以二进制表示,并以十六进制的形式显示出来。Hello是可以直接运行的文件。

5.当我们运行hello时,在shell中利用fork()函数创建子进程,为子进程分配一个与父进程相同但独立的虚拟内存空间,再用execve()加载hello程序,这时,hello就由程序(program)变成了一个进程(process)。

6.在进程中,hello进程可以被各种信号影响,并且hello程序在运行过程中涉及到并行与进程的上下文切换。

创新:

在hello的异常与信号处理中,通过结合在运行过程中不断乱按和按回车产生了新奇的结果,可以在程序运行过程中,直接输入终端的下一个命令甚至几个命令。

附件

hello.i                  预处理产生的文件

hello.s                  编译产生的汇编代码文件

hello.o                 汇编产生的可重定位目标文件

asm.txt                hello.o反汇编生成的文本文件

hello                    链接产生的可执行目标文件

asm_2.txt            hello返回百年生成的文本文件

参考文献

[1]  《深入理解计算机系统》

[2]  理解链接之链接的基本概念 - 简书 (jianshu.com)

程序人生 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. 如何理解“跳出率”,它对SEO有什么影响?
  2. ubuntu 18.04 配置notebook远程连接的坑
  3. Android Studio:依赖异常问题解决
  4. GIS空间数据结构 - 矢量和栅格
  5. Ubuntu下在线安装mysql
  6. oracle用户登录的认证方式
  7. H3C 常用接口和线缆
  8. leetcode 257. 二叉树的所有路径(Java版)
  9. unity2D技术学习与整理
  10. C++——容器小整理
  11. Maven的-pl -am -amd参数学习 -am 踩过的坑
  12. 关于HRESULT判断的宏(SUCCEEDED/FAILED)
  13. 三星固态硬盘linux驱动,三星固态硬盘驱动NVMe Driver
  14. 【JZOJ A组】时空幻境
  15. 山东大学计算机组成课设,山东大学计算机组成原理课程设计实验报告.pdf
  16. 前端记住密码功能实现
  17. 360视频:二十面体投影ISP
  18. 关于LM2596S-5.0电流声问题——输出电容选择
  19. cla作用matlab,健身搞肌运动补剂小讲堂:共轭亚油酸CLA功效解析
  20. AI配对真能解决“单身公害”?

热门文章

  1. 童星养成系统的文推荐_几部养成系列的现言宠文推荐啦,都是不错的大叔文,十分治愈哦~...
  2. 对标yaahp的python工具包
  3. 【附源码】计算机毕业设计java校园志愿者服务系统设计与实现
  4. ethtool用法 linux_Linux命令--ethtool
  5. 计算机在智能交通系统中应用,计算机技术在智能交通系统中的应用.doc
  6. 避免2.4GHz ISM频段各种类型无线设备干扰的技术 1
  7. 批量替换excel中的换行
  8. mysql 多租户_数据层的多租户浅谈
  9. X-VLM: Multi-Grained Vision Language Pre-Training
  10. 网页设计之栅格系统及应用