哈工大HIT-2024CSAPP 程序人生-Hello‘s P2P大作业

计算机系统

大作业

题     目  程序人生-Hello’s P2P 

专       业    人工智能领域                   

学     号                  

班     级                    

学       生                  

指 导 教 师      刘宏伟                 

计算机科学与技术学院

2024年5月

摘  要

本次作业完成对hello从program到process的全过程分析。涉及预处理,编译汇编,链接,存储,进程等多方面内容。本文不仅理论上探讨了这些工具的原理和方法,还实际演示了它们的操作和结果,阐述了计算机系统的工作原理和体系结构,帮助读者更深入地理解和掌握C语言程序的编译和执行过程。

关键词:计算机系统;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简介

P2P:即From Program to Process。指从hello.cProgram)变为运行时进程(Process)。要让hello.c这个C语言程序运行起来,需要先把它变成可执行文件,这个变化过程有四个阶段:预处理,编译,汇编,链接,完成后就得到了可执行文件,然后就可以在shell中执行它,shell会给它分配进程空间。

020:即From Zero-0 to Zero-0。指最初内存并无hello文件的相关内容,shell用execve函数启动hello程序,把虚拟内存对应到物理内存,并从程序入口开始加载和运行,进入main函数执行目标代码,程序结束后,shell父进程回收hello进程,内核删除hello文件相关的数据结构。

1.2 环境与工具

硬件环境:

处理器:i7处理器   2.50 GHz

机带RAM:16.0GB

系统类型:64位操作系统,基于x64的处理器

软件环境:Windows11 64位,VMware,Ubuntu 20.04 LTS

开发与调试工具:Visual Studio 2021 64位;vim objump edb gcc readelf等工具

1.3 中间结果

hello.i         预处理后得到的文本文件

hello.s         编译后得到的汇编语言文件

hello.o        汇编后得到的可重定位目标文件

hello.asm      反汇编hello.o得到的反汇编文件

hello1.asm     反汇编hello可执行文件得到的反汇编文件

1.4 本章小结

本章首先介绍了hello的P2P,020流程,包括流程的设计思路和实现方法;然后,详细说明了本实验所需的硬件配置、软件平台、开发工具以及本实验生成的各个中间结果文件的名称和功能。

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

预处理步骤是指预处理器在程序运行前,对源文件进行简单加工的过程。预处理过程主要进行代码文本的替换工作,用于处理以#开头的指令,还会删除程序中的注释和多余的空白字符。预处理指令可以简单理解为#开头的正确指令,它们会被转换为实际代码中的内容

预处理过程中并不直接解析程序源代码的内容,而是对源代码进行相应的分割、处理和替换,主要有以下作用:

头文件包含:将所包含头文件的指令替代。

宏定义:将宏定义替换为实际代码中的内容。

条件编译:根据条件判断是否编译某段代码。

其他:如注释删除等。

简单来说,预处理是一个文本插入与替换的过程预处理器。

2.2在Ubuntu下预处理的命令

gcc -E hello.c -o hello.i

2.3 Hello的预处理结果解析

Linux下打开hello.i文件,我们对比了源程序和预处理后的程序。结果显示,除了预处理指令被扩展成了几千行之外,源程序的其他部分都保持不变,说明.c文件的确是被修改过了。

在main函数代码出现之前的大段代码源自于的头文件<stdio.h>  <unistd.h>  <stdlib.h> 的依次展开。

以 stdio.h 的展开为例:预处理过程中,#include指令的作用是把指定的头文件的内容包含到源文件中。stdio.h是标准输入输出库的头文件,它包含了用于读写文件、标准输入输出的函数原型和宏定义等内容。

当预处理器遇到#include<stdio.h>时,它会在系统的头文件路径下查找stdio.h文件,一般在/usr/include目录下,然后把stdio.h文件中的内容复制到源文件中。stdio.h文件中可能还有其他的#include指令,比如#include<stddef.h>或#include<features.h>等,这些头文件也会被递归地展开到源文件中。

预处理器不会对头文件中的内容做任何计算或转换,只是简单地复制和替换。

2.4 本章小结

预处理所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

第二个阶段编译、优化阶段,经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及c语言的关键字,如main,if,else,for,while,{,}, +,-,*,\等等。

编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。

       

3.2 在Ubuntu下编译的命令

编译的命令:gcc -S hello.i -o hello.s

3.3 Hello的编译结果解析

.file               声明出源文件

.text               表示代码节

.section   .rodata    表示只读数据段

.align              声明对指令或者数据的存放地址进行对齐的方式

.string              声明一个字符串

.globl              声明全局变量

.type               声明一个符号的类型

3.3.1数据部分

字符串程序有两个字符串存放在只读数据段中

hello.c中唯一的数组是main函数中的第二个参数(即char**argv),数组的每个元素都是一个指向字符类型的指针。由知数组起始地址存放在栈中-32(%rbp)的位置,被两次调用作为参数传到printf中。

如图,分别将rdi设置为两个字符串的起始地址:

参数argc是main函数的第一个参数,被存放在寄存器%edi中,由语句

可见寄存器%edi地址被压入栈中,而语句

可知该地址上的数值与立即数4判断大小,从而得知argc被存放在寄存器并被压入栈中。

程序中的局部变量只有i,我们根据

可知局部变量i是被存放在栈上-4(%rbp)的位置。

3.3.2全局函数

hello.c中只声明了一个全局函数int main(int arge,.char*argv[]),我们通过汇编代码

可知。

3.3.3赋值操作

hel1o.c中的赋值操作贝有for循环开头的i-0,该赋值操作体现在汇编代码上,则是用mov指令实现,如图:

。由于int型变量i是一个32位变量,使用movl传递双字实现。

3.3.4算术操作

hello.c中的算术操作为for循环的每次循环结束后i++,该操作体现在汇编代码则使用指令add实现,问样,由丁变量i为32位,使用指令addl。指令如下:

3.3.5关系操作

hello.c中存在两个关系操作,分别为:

  1. 条件判断语句if(argc!=4):汇编代码将这条代码翻译为:

使用了cmp指令比较立即数4和参数argc大小,并且设置了条件码。根据条件码,如果不相等则执行该指令后面的语句,否则跳转到.L2。

  1. 在for循环每次循环结束要判断一次i<8,判断循环条件被翻译为:

同(1),设置条件码,并通过条件码判断跳转到什么位置。

3.3.6控制转移指令

设置过条件码后,通过条件码来进行控制转移,在本程序中存在两个控制转移:

(1)

判断argc是否为4,如果不为4,则执行if语句,否则执行其他语句,在汇编代码中则表现为如果条件码为1,则跳到.L2,否则执行cmpl指令后的指令。

(2)

在for循环每次结束判断一次i<8,翻译为汇编语言后,通过条件码判断每次循环是否跳转到.L4。而在for循环初始要对i设置为0,如下:

然后直接无条件跳转到.L3循环体。

3.3.7函数操作

(1)main函数

参数传递:该函数的参数为int argc,,char*argv[]。具体参数传递地址和值都在前面阐述过。

函数调用:通过使用call内部指令调用语句进行函数调用,并且将要调用的函数地址数据写入栈中,然后自动跳转到这个调用函数内部。main函数里调用了printf、exit、sleep函数。

局部变量:使用了局部变量i用于for循环。具体局部变量的地址和值都在前面阐述过。

(2)printf函数

参数传递:printf函数调用参数argv[1],argv[2]。

函数调用:该函数调用了两次。第一次将寄存器%rdi设置为待传递字符串"用法:Hello学号姓名 秒数!\n"的起始地址;第二次将其设置为“Hello %s  %s\n”的起始地址。具体已在前面讲过。使用寄存器%rsi完成对argv[1]的传递,用%rdx完成对argv[2]的传递。

(3)exit函数

参数传递与函数调用:

将rdi设置为1,再使用call指令调用函数。

(4)atoi、sleep函数

参数传递与函数调用:

可见,atoi函数将参数argv[3]放入寄存器%rdi中用作参数传递,简单使用call指令调用。

然后,将转换完成的秒数从%eax传递到%edi中,edi存放sleep的参数,再使用call调用。

(5)getchar函数

无参数传递,直接使用call调用即可。

此部分是重点,说明编译器是怎么处理C语言的各个数据类型以及各类操作的。应分3.3.1~ 3.3.x等按照类型和操作进行分析,只要hello.s中出现的属于大作业PPT中P4给出的参考C数据与操作,都应解析

3.4 本章小结

这一章介绍了C编译器如何把hello.i文件转换成hello.s文件的过程,简要说明了编译的含义和功能,演示了编译的指令,并通过分析生成的hello.s文件中的汇编代码,探讨了数据处理,函数调用,赋值、算术、关系等运算以及控制跳转和类型转换等方面,比较了源代码和汇编代码分别是怎样实现这些操作的。

(第32分)

第4章 汇编

4.1 汇编的概念与作用

汇编实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件由段组成。

4.2 在Ubuntu下汇编的命令

gcc -m64 -no-pie -fno-PIC -c hello.s -o hello.o

4.3 可重定位目标elf格式

    分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。

(1)ELF头

ELF头(ELF header)以一个l6字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。ELF头剩下的部分包含了帮助链接器语法分析和解释目标文件的信息,其中包括ELF头的大小、目标文件的类型(如可重定位、可执行或者共享的)、机器类型(如x86-64)、节头部表(section header table)的文件偏移,以及节头部表中条目的大小和数量。不同节的位置和大小是有节头部表描述的,其中目标文件中每个节都有一个固定大小的条目(entry)。ELF头展示如下:

(2)节头(section header)

记录各节名称、类型、地址、偏移量、大小、全体大小、旗标、链接、信息、对齐。

(3)重定位节

.rel.text节是一个.text节中位置的列表,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。一般而言,任何调用外部函数或者引用全局变量的指令都需要修改,而调用本地函数的指令不需修改。可执行目标文件中不包含重定位信息。如图,需要重定位的内容如下:

(4)符号表

.symtab节中包含ELF符号表,这张符号表包含一个条目的数组,存放一个程序定义和引用的全局变量和函数的信息。该符号表不包含局部变量的信息。符号表如下:

4.4 Hello.o的结果解析

objdump -d -r hello.o  分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。

说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。

(1)增加机器语言

每一条指令增加了一个十六进制的表示,即该指令的机器语言。例如,在hello.s中的一个cmpl指令表示为

而在反汇编文件中表示为

(2)操作数进制

反汇编文件中的所有操作数都改为十六进制。如(1)中的例子,立即数由hello.s中的$4变为了$0x4,地址表示也由-20(%rbp)变为-0x14(%rbp)。可见只是进制表示改变,数值未发生改变。

(3)分支转移

反汇编的跳转指令中,所有跳转的位置被表示为主函数+段内偏移量这样确定的地址,而不再是段名称(例如.L3)。例如下面的jmp指令,反汇编文件中为

而hello.s文件中为

(4)函数调用

反汇编文件中对函数的调用与重定位条目相对应。观察下面两个call指令调用函数,在hello.s中为

而在反汇编文件中调用函数为

在可重定位文件中call后面不再是函数名称,而是一条重定位条目指引的信息。

4.5 本章小结

这一章介绍了汇编的含义和功能。以Ubuntu系统下的hello.s文件为例,说明了如何把它汇编成hello.o文件,并生成ELF格式的可执行文件hello.elf。将可重定位目标文件改为ELF格式观察文件内容,对文件中的每个节进行简单解析。通过分析hello.o的反汇编代码(保存在hello.asm中)和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.3 可执行目标文件hello的格式

    分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。

(1)ELF头(ELF Header)

       hello1.elf中的ELF头与hello.elf中的ELF头包含的信息种类基本相同,以描述了生成该文件的系统的字的大小和字节顺序的16字节序列Magic开始,剩下的部分包含帮助链接器语法分析和解释目标文件的信息。与hello.elf相比较,hello1.elf中的基本信息未发生改变(如Magic,类别等),而类型发生改变,程序头大小和节头数量增加,并且获得了入口地址。

(2)节头

       描述了各个节的大小、偏移量和其他属性。链接器链接时,会将各个文件的相同段合并成一个大段,并且根据这个大段的大小以及偏移量重新设置各个符号的地址。

(3)程序头

程序头部分是一个结构数组,描述了系统准备程序执行所需的段或其他信息。

(4)Dynamic section

(5)Symbol table

       符号表中保存着定位、重定位程序中符号定义和引用的信息,所有重定位需要引用的符号都在其中声明。

5.4 hello的虚拟地址空间

    使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。

       观察程序头的LOAD可加载的程序段的地址为0x400000。如图:

使用edb打开hello从Data Dump窗口观察hello加载到虚拟地址的情况,查

看各段信息。如图:

程序从地址0x400000开始到0x401000被载入,虚拟地址从0x4000000x400f0结束,根据5.3中的节头部表,可以通过edb找到各段的信息。

如.interp节,在hello.elf文件中能看到开始的虚拟地址:

在edb中找到对应的信息:

同样的,我们可以找到如.text节的信息:

5.5 链接的重定位过程分析

objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。

结合hello.o的重定位项目,分析hello中对其怎么重定位的。

使用命令objdump -d -r hello > hello1.asm生成反汇编文件hello1.asm

(1)链接后函数数量增加

       链接后的反汇编文件hello2.asm中,多出了.plt,puts@plt,printf@plt,getchar@plt,exit@plt,sleep@plt等函数的代码。这是因为动态链接器将共享库中hello.c用到的函数加入可执行文件中。

(2)函数调用指令call的参数发生变化

       在链接过程中,链接器解析了重定位条目,call之后的字节代码被链接器直接修改为目标地址与下一条指令的地址之差,指向相应的代码段,从而得到完整的反汇编代码。

(3)跳转指令参数发生变化

       在链接过程中,链接器解析了重定位条目,并计算相对距离,修改了对应位置的字节代码为PLT 中相应函数与下条指令的相对地址,从而得到完整的反汇编代码。

重定位由两步组成:

(1)重定位节和符号定义。在这一步中,链接器将所有相同类型的节合并为同一类型的聚合节。然后链接器将运行时的内存地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号。至此程序中每条指令和全局变量都有唯一的运行内存地址。

(2)重定位节中的符号引用。这一步中链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行时地址。要执行这一步,链接器依赖于可重定位目标模块中称为重定位条目的数据结构。

(3)重定位过程地址计算方法如下:

5.6 hello的执行流程

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

5.6.1过程

通过edb的调试,一步一步地记录下call命令进入的函数。

(I)开始执行:_start、_libe_start_main

(2)执行main:_main、printf、_exit、_sleep、getchar

(3)退出:exit

5.6.2子程序名或地址

程序名               程序地址

_start                0x4010f0

_libc_start_main       0x2f12271d

main                0x401125

_printf               0x4010a0

_sleep               0x4010e0

_getchar             0x4010b0

_exit                0x4010d0

5.7 Hello的动态链接分析

  

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

动态链接的基本思想是将程序的链接过程推迟到程序运行时进行,而不是在编译时完成。这种方法使得程序可以在启动时加载所需的动态链接库(如 .so 文件或 DLL 文件),而不必将库的代码在编译时静态地链接到程序中。通过动态链接,多个程序可以共享同一个库的实现,减少内存占用和磁盘空间使用,并且使得库的更新和维护更为灵活和简便。在运行时,动态链接器负责解析程序对库的符号引用,将它们链接到正确的库实现,从而确保程序能够正常运行并访问所需的函数和数据。动态链接技术不仅提高了程序的效率和资源利用率,还支持灵活的库版本管理和动态加载与卸载,为软件开发和维护带来了便利。

根据hello.elf文件可知,GOT起始表位置为:0x404000:

GOT表位置在调用dl_init之前0x404008后的16个字节均为0:

调用了dl_init之后字节改变了:

对于变量而言,利用代码段和数据段的相对位置不变的原则去计算正确地址。

对于库函数而言,需要plt、got合作。plt初始存的是一批代码,它们跳转到got所指示的位置,然后调用链接器。初始时got里面存的都是plt的第二条指令,随后链接器修改got,下一次再调用plt时,指向的就是正确的内存地址。接下来执行程序的过程中,就可以使用过程链接表plt和全局偏移量表got进行动态链接。

5.8 本章小结

本章首先阐述了链接的基本概念和作用,并随后使用链接命令生成hello可执行文件,除此之外,还观察了hello文件ELF格式下的内容,并利用edb探索了hello文件的虚拟地址空间使用情况,最后对hello的重定位和动态链接过程进行了分析。

(第51分)

6章 hello进程管理

6.1 进程的概念与作用

进程定义为运行中的程序实例,每个程序在操作系统中以进程形式存在,包含程序代码、数据、栈、寄存器状态、程序计数器、环境变量和打开的文件描述符集合等运行所需状态。

进程的作用是提供两个重要抽象。首先,提供独立的逻辑控制流,使程序似乎独占处理器资源;其次,提供私有的地址空间,使程序似乎独占内存系统资源。这种抽象使得操作系统能够有效地管理和调度多个程序,并确保它们之间的互相隔离和安全执行。

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

6.2.1 Shell-bash的作用

Shell是一个交互型应用级程序,也被称为命令解析器,它为用户提供一个操作界面,接受用户输入的命令,并调度相应的应用程序。

6.2.2 Shell-bash的处理流程

首先从终端读入输入的命令,对输入的命令进行解析,如果该命令为内置命令,则立即执行命令,否则调用fork创建一个新的子进程,在该子进程的上下文中执行指定的程序。判断该程序为前台程序还是后台程序,如果为前台程序则等待程序执行结束,若为后台程序则将其放回后台并返回。在过程中shell可以接受从键盘输入的信号并对其进行处理。

6.3 Hello的fork进程创建过程

首先读取用户在终端中输入./hello命令,shell判断该指令不是内置指令,于是调用fork函数创建一个新的子进程。子进程得到与父进程用户级虚拟地址空间相同的一份副本,包括代码和数据段、堆、共享库以及用户栈。子进程与父进程最大的区别就是具有不同的PID,在父进程中fork返回子进程的PID,而在子进程中fork返回0。子进程与父进程并发执行,具有相同但独立的地址空间,并且共享文件。

6.4 Hello的execve过程

execve函数在当前进程的上下文中加载并运行一个新程序,函数格式为:

int execve(const char *filename, const char *argv[], const char *envp[]);

execve函数加载并运行可执行目标文件filename,且带参数列表argv和环境变量envp。只有当出现错误时,例如找不到filename,execve才会返回到调用程序。所以,与fork一次调用返回两次不同,execve调用一次并不返回。

execve函数启动加载器,映射虚拟内存,进入程序入口后,程序开始载入物理内存,然后进入main函数。

6.5 Hello的进程执行

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

分析hello在shell中执行的整体过程:

在shell命令行中输入命令:$ ./hello

Shell命令行解释器解析命令:当用户在命令行输入./hello后,shell命令行解释器(例如bash)会解析这个命令。它识别出./hello是一个可执行文件,并准备执行该文件。

构造argv和envp:在执行./hello之前,shell会为将要执行的程序构造argv和envp数组。argv是一个指向参数的数组,通常包括程序名本身和传递给程序的任何命令行参数。envp是一个指向环境变量的数组,其中包含了环境变量的键值对信息。

调用fork()函数创建子进程:Shell调用fork()系统调用创建一个新的子进程。子进程的地址空间与父进程(即Shell进程)完全相同,包括代码段、数据段、堆和用户栈等。此时,子进程是一个几乎完全复制了父进程的副本,但处于相同的运行状态。

调用execve()函数加载并运行hello程序:子进程在调用execve()系统调用时,会加载并执行./hello程序。execve()会在当前进程(即新创建的子进程)的上下文中加载指定的可执行文件,并且将该文件的内容加载到当前进程的虚拟地址空间中。包括:.text节(代码段):包含程序的可执行指令。.data节(数据段):包含初始化的全局和静态变量。.bss节:包含未初始化的全局和静态变量。

调用hello程序的main()函数:一旦execve()成功加载并运行./hello程序,程序控制流将转移到该程序的入口点,即main()函数。这标志着./hello程序开始在一个进程的上下文中运行,执行其预定的任务。

其中,上下文切换是一种建立在较低层异常机制上的一种异常控制流,用于实现多任务,内核为每一个进程维持一个上下文,用于储存一些进程的状态。而如果内核决定抢占当前进程,就会使用一种称为上下文切换的机制来将控制转移到 新的进程。

在运行原有进程时,由于我们的键盘输入,内核希望将进程切换到hello,于是这时内核进行上下文切换,此时从用户模式切换到内核模式,并返回用户模式执行进程hello,直到hello执行完毕,再进行一次上下文切换,恢复到进程1运行。

而进程时间片就是一个进程执行他控制流的每一时间段,在这里指的就是进程hello执行的每一段时间。

6.6 hello的异常与信号处理

hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。

 程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps  jobs  pstree  fg  kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。

6.6.1异常的分类

6.6.2异常的处理方式

(1)正常运行状态

在程序正常运行时,打印提示信息,以输入回车为标志结束程序,并回收进程。

(2)运行时按下Ctrl + C

按下Ctrl + C,Shell进程收到SIGINT信号,Shell结束并回收hello进程。

(3)运行时按下Ctrl + Z

按下Ctrl + Z,Shell进程收到SIGSTP信号,Shell显示屏幕提示信息并挂起hello进程。

(5)在Shell中输入pstree命令,可以将所有进程以树状图显示:

不停乱按

       在程序执行过程中乱按所造成的输入均缓存到stdin,当getchar的时候读出一个’\n’结尾的字串(作为一次输入),hello结束后,stdin中的其他字串会当做Shell的命令行输入。

6.7本章小结

本章的主要内容是探讨计算机系统中的进程和shell,首先通过一个简单的hello程序,简要介绍了进程的概念和作用、shell的作用和处理流程,还详细分析了hello程序的进程创建、启动和执行过程,最后,本章对hello程序可能出现的异常情况,以及运行结果中的各种输入进行了解释和说明。

(第61分)

7章 hello的存储管理

7.1 hello的存储器地址空间

7.1.1逻辑地址

在有地址变换功能的计算机中,访问指令给出的地址(操作数)叫逻辑地址,也叫相对地址。要经过寻址方式的计算或变换才得到内存储器中的物理地址。逻辑地址是由一个段标识符加上一个指定段内相对地址的偏移量,由程序hello产生的与段相关的偏移地址部分

7.1.2线性地址

线性地址是逻辑地址到物理地址变换之间的一步,程序hello的代码会产生逻辑地址,在分段部件中逻辑地址是段中的偏移地址,加上基地址就是线性地址。

7.1.3虚拟地址

程序访问存储器所使用的逻辑地址称为虚拟地址。虚拟地址经过地址翻译得到物理地址。与实际物理内存容量无关,是hello中的虚拟地址

7.1.4物理地址

在存储器里以字节为单位存储信息,每一个字节单元给一个唯一的存储器地址,这个地址称为物理地址,是hello的实际地址或绝对地址

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

段式管理是指把一个程序分成若干个段进行存储,每个段都是一个逻辑实体。段式管理是通过段表进行的,包括段号(段名)、段起点、装入位、段的长度等。程序通过分段划分为多个块,如代码段、数据段、共享段等。

一个逻辑地址是两部分组成的,包括段标识符和段内偏移量。段标识符是由一个16位长的字段组成的,称为段选择符。其中前13位是一个索引号,后3位为一些硬件细节。索引号即是“段描述符”的索引,段描述符具体地址描述了一个段,很多个段描述符就组成了段描述符表。通过段标识符的前13位直接在段描述符表中找到一个具体的段描述符。

全局描述符表(GDT)整个系统只有一个,它包含:(1)操作系统使用的代码段、数据段、堆栈段的描述符(2)各任务、程序的LDT(局部描述符表)段。

每个任务程序有一个独立的LDT,包含:(1)对应任务/程序私有的代码段、数据段、堆栈段的描述符(2)对应任务/程序使用的门描述符:任务门、调用门等。

段式管理图示如下:

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

虚拟内存被组织为一个由存放在磁盘上的N个连续的字节大小的单元组成的数组。VM系统将虚拟内存分割,称为虚拟页,类似地,物理内存也被分割成物理页。利用页表来管理虚拟页,页表就是一个页表条目(PTE)的数组,每个PTE由一个有效位和一个位地址字段组成,有效位表明了该虚拟页当前是否被缓存在DRAM中,如果设置了有效位,那么地址字段就表示DRAM中相应的物理页的起始位置,如果发生缺页,则从磁盘读取。

MMU利用页表来实现从虚拟地址到物理地址的翻译。

下面为页式管理的图示:

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

Core i7采用四级页表的层次结构。CPU产生虚拟地址VA,虚拟地址VA传送给MU,MMU使用VPN高位作为TLBT和TLBI,向TLB中寻找匹配。如果命中,则得到物理地址PA。如果TLB中没有命中,MMU查询页表,CR3确定第一级页表的起始地址,VPN1确定在第一级页表中的偏移量,查询出PTE,以此类推,最终在第四级页表中找到PPN,与VPO组合成物理地址PA,添加到PLT。工作原理如下:

多级页表的工作原理展示如下:

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

如图为高速缓存存储器组织结构:

高速缓存的结构将m个地址位划分成了t个标记位,s个组索引位和b个块偏移位:

如果选中的组存在一行有效位为1,且标记位与地址中的标记位相匹配,我们就得到了一个缓存命中,否则就称为缓存不命中。如果缓存不命中,那么它需要从存储器层次结构的下一层中取出被请求的块,然后将新的块存储在组索引位指示组中的一个高速缓存行中,具体替换哪一行取决于替换策略,例如LRU策略会替换最后一次访问时间最久远的那一行。

7.6 hello进程fork时的内存映射

当fork函数被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID。为了给这个新进程创建虚拟内存,它创建了当前进程的mm_ struct、.区域结构和页表的原样副本。当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个进程中的任何一个。后来进行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间的抽象概念。

7.7 hello进程execve时的内存映射

execve函数调用驻留在内核区域的启动加载器代码,在当前进程中加载并运行包含在可执行目标文件hello中的程序,用hello程序有效地替代了当前程序。加载并运行hello需要以下几个步骤:

(1)删除已存在的用户区域。删除当前进程虚拟地址的用户部分中的已存在的区域结构。

(2)映射私有区域。为新程序的代码、数据、.bss和栈区域创建新的区域结构,所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为hello文件中的.text和.data区,.bss区域是请求二进制零的,映射到匿名文件,其大小包含在hello中,栈和堆地址也是请求二进制零的,初始长度为零。

(3)映射共享区域。hello程序与共享对象1ibc.so链接,libc.so是动态链接到这个程序中的,然后再映射到用户虚拟地址空间中的共享区域内。

(4)设置程序计数器。execve做的最后一件事情就是设置当前进程上下文的程序计数器,使之指向代码区域的入口点。如图所示:

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

如果程序执行过程中发生了缺页故障,则内核调用缺页处理程序。处理程序执行如下步骤:

(1)检查虚拟地址是否合法,如果不合法则触发一个段错误,终止这个进程。

(2)检查进程是否有读、写或执行该区域页面的权限,如果不具有则触发保护异常,程序终止。

(3)两步检查都无误后,内核选择一个牺牲页面,如果该页面被修改过则将其交换出去,换入新的页面并更新页表。然后将控制转移给hello进程,再次执行触发缺页故障的指令。

7.9动态存储分配管理

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

printf 调用 malloc 来动态分配内存,以便存储和格式化输出数据,这涉及到动态内存管理的基本方法与策略。动态内存管理是指在程序运行时,根据需要分配和释放内存的过程,主要通过 malloc、calloc 和 realloc 等函数来实现分配内存,通过 free 函数来释放内存。常见的内存分配策略包括首次适应、最佳适应、最差适应和快速适应等,这些策略各有优缺点,分别适用于不同的应用场景。动态内存管理面临的主要挑战包括内存碎片、内存泄漏和管理开销等问题。合理的内存管理可以提高程序的运行效率,减少资源浪费和潜在的内存问题,从而确保程序的稳定性和可靠性。

7.10本章小结

本章主要介绍了hello的存储器地址空间、intel的段式管理、hello的页式管理,以intel Core i7在指定环境下介绍了虚拟地址VA到物理地址PA的转换、物理内存访问,分析了hello进程fork时的内存映射、hello进程、execve时的内存映射、缺页故障与缺页中断处理。

(第7 2分)

8章 hello的IO管理

8.1 Linux的IO设备管理方法

以下格式自行编排,编辑时删除

设备的模型化:文件

设备管理:unix io接口

8.2 简述Unix IO接口及其函数

以下格式自行编排,编辑时删除

8.3 printf的实现分析

以下格式自行编排,编辑时删除

https://www.cnblogs.com/pianist/p/3315801.html

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

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

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

8.4 getchar的实现分析

以下格式自行编排,编辑时删除

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

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

8.5本章小结

以下格式自行编排,编辑时删除

(第81分)

结论

hello所经历的过程:

首先由程序员将hello代码从键盘输入,依次要经过以下步骤:

1、预处理(cpp)。将hello.c进行预处理,将文件调用的所有外部库文件合并展开,生成一个经过修改的hello.i文件。

2、编译(ccl)。将hello.i文件翻译成为一个包含汇编语言的文件hello.s。

3、汇编(as)。将hello.s翻译成为一个可重定位目标文件hello.o。

4、链接(ld)。将hello.o文件和可重定位目标文件和动态链接库链接起来,生成一个可执行目标文件hello。

5、运行。

6、创建进程。终端判断输入的指令不是shell内置指令,于是调用fork函数创建一个新的子进程。

7、加载程序。shell调用execve函数,启动加载器,映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入main函数。

8、执行指令:CPU为进程分配时间片,在一个时间片中,hello享有CPU资源,顺序执行自己的控制逻辑流。

9、访问内存:MU将程序中使用的虚拟内存地址通过页表映射成物理地址。

10、信号管理:当程序在运行的时候我们输入Ctrl+c,内核会发送SIGINT信号给进程并终止前台作业。当输入Ctrl+z时,内核会发送SIGTSTP信号给进程,并将前台作业停止挂起。

11、终止:当子进程执行完成时,内核安排父进程回收子进程,将子进程的退出状态传递给父进程。内核删除为这个进程创建的所有数据结构。

感悟本次大作业以hello执行为例,探讨计算机系统在其整个生命周期中的行为,从程序员视角理解程序的执行过程。我深知,只有理解计算机系统的本质,才能对代码的行为了然于心,在此基础上,才能对程序进行合理的优化,减少bug的出现,让我们编写出更美妙的代

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

附件

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

文件名

功能

hello.c

源程序

hello.i

预处理后得到的文本文件

hello.s

编译后得到的汇编语言文件

hello.o

汇编后得到的可重定位目标文件

hello.elf

readelf读取hello.o得到的ELF格式信息

hello.asm

反汇编hello.o得到的反汇编文件

hello1.asm

反汇编hello可执行文件得到的反汇编文件

hello

可执行文件

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

参考文献

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

[1]  林来兴. 空间控制技术[M]. 北京:中国宇航出版社,1992:25-42.

[2]  辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.

[3]  赵耀东. 新时代的工业工程师[M/OL]. 台北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).

[4]  谌颖. 空间交会控制理论与方法研究[D]. 哈尔滨:哈尔滨工业大学,1992:8-13.

[5]  KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.

[6]  CHRISTINE M. Plant Physiology: Plant Biology in the Genome Era[J/OL]. Science,1998,281:331-332[1998-09-23]. http://www.sciencemag.org/cgi/ collection/anatmorp.

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

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值