HIT CSAPP 程序人生(Hello‘s P2P)

计算机系统大作业

题  目  程序人生-Hello’s P2P

专  业  计算机科学与技术

学  号  **********

班  级  *******

学  生  陈一豪

指导教师  刘宏伟

计算机科学与技术学院

2022年5月

摘 要

一个程序从编码完成到成功运行,事实上要经历很多的过程。从最开始的预处理和编译,到之后的汇编和链接;从最开始被加载,到申请空间开始进程;从默默运行,到与我们输入输出进行交互。这一切的发生都有着非常巧妙的机制,每一个动作都包含了许多严谨有趣的细节。本文通过对“Hello”程序的精析巧读,展示了“Hello”作为一个程序的一生。

关键词:**预处理;编译;汇编;链接;进程

(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)

第1章 概述

1.1 Hello简介

P2P:首先编写hello.c文件(Program),而后经过cpp处理、ccl编译、as汇编、ld链接四个过程,形成可执行文件hello。键入./hello运行该程序时,在Bash中,进程管理(OS)为其从父进程中fork(Process),使之从程序变为进程。

020:shell为此进程execve,映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入 main函数执行目标代码,CPU为运行的hello分配时间片执行逻辑控制流。当程序运行结束后,shell父进程负责回收hello进程,内核删除相关数据结构。

1.2 环境与工具

硬件环境:X64 CPU;2.70GHz;2G RAM;256GHD Disk 以上

软件环境:Windows10 64位;Vmware 11以上;Ubuntu 22.04 64位

开发与调试工具:gcc,vim,geany,edb,readelf,HexEdit

1.3 中间结果

文件名称文件作用
hello.ihello.c预处理之后文本文件
hello.shello.i编译后的汇编文件
hello.ohello.s汇编之后的可重定位目标文件
hello链接之后的可执行目标文件
hello.outhello反汇编之后的可重定位文件

1.4 本章小结

本章简单介绍了程序hello.c的P2P/020过程,对于程序运行整体提纲挈领,并列出了环境信息和使用工具、说明了从源代码文件hello.c到可执行文件hello的一系列过程。

第2章 预处理

2.1 预处理的概念与作用

预处理器:预处理器cpp根据以字符#开头的命令(宏定义、条件编译),修改原始的C程序,将引用的所有库展开合并成为一个完整的文本文件。

预处理阶段作用:

1.处理宏定义指令预处理器根据#if和#ifdef等编译命令及其后的条件,将源程序中的某部分包含进来或排除在外,通常把排除在外的语句转换成空行。

2. 处理条件编译指令。条件编译指令如#ifdef,#ifndef,#else,#elif,#endif等。这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。

3.处理头文件包含指令头文件包含指令如#include "FileName"或者#include 等。该指令将头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。

4.处理特殊符号。预编译程序可以识别一些特殊的符号。例如在源程序中出现的LINE标识将被解释为当前行号,FILE则被解释为当前被编译的C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。

2.2在Ubuntu下预处理的命令

输入cpp hello.c hello.i或gcc hello.c -E hello.i命令进行预处理。

在这里插入图片描述

2.3 Hello的预处理结果解析

在这里插入图片描述

hello.i文件截图如上。可以看到最后一部分(也就是编写的源代码部分)与原来区别不大,重点在于上方#include的头文件被尽数替换,头文件中的宏定义、宏常量、函数声明、结构体声明等被替换到此处。另外,如果有#define等命令,对应内容也会被完全替换回去。同时,源文件注释全部删除。

源文件名和行号信息通过格式为# linenum文件名标志的行来传递。这些被称为行标记,意思是,下一行起源于文件filename中的linenum行。文件名后面有零个或多个标志,分别是’ 1 ‘、’ 2 ‘、’ 3 ‘或’ 4 ‘。如果有多个标志,则用空格隔开。’ 1 ‘表示新文件的开始。’ 2 ‘表示返回到一个文件(在包含了另一个文件之后)。’ 3 ‘这表示以下文本来自系统头文件,因此某些警告应该被抑制。’ 4 '表明下面的文本应该被包装在隐式extern“C”块中。

2.4 本章小结

本章介绍了预处理的相关概念及其对应指令,并以hello.i为例具体说明了预处理器cpp的主要作用。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译的概念:将文本文件 hello.i 翻译成一个包含汇编语言程序的文本文件 hello.s。其以高级程序设计语言书写的源程序作为输入,而以汇编语言或机器语言表示的目标程序作为输出。这个过程称为编译。

编译程序的基本功能是把源程序翻译成目标程序(文本文件)。除了基本功能之外,编译程序还具备语法检查、调试措施、修改手段、覆盖处理、目标程序优化、不同语言合用等重要功能。

3.2 在Ubuntu下编译的命令

在命令行中输入gcc -S hello.i -o hello.s命令,可以进行编译。
在这里插入图片描述

3.3 Hello的编译结果解析

3.3.1 汇编命令初解析

在这里插入图片描述

其中:

名称作用
.file指明源文件,这里是hello,c
.text代码节
.section
.rodata只读代码段,存放只读数据、跳转表等
.align数据或指令的地址及其方式
.string声明一个字符串(.LC0/.LC1)
.globl声明全局变量(main)
.type指明全局变量类型(main为函数)

3.3.2 全局变量

由hello.s第10-11行可知,hello.c声明了一个全局变量函数int main,经过编译之后,该函数所使用的字符串等变量均存储于数据区。

3.3.3 赋值操作

汇编代码中采用数据传输指令mov进行赋值:

指令字节数
movb1
movw2
movl4
movq8

同时还有改变大小的数据传输指令,如movsbl/movzbl等,以及堆栈操作指令pushq/popq等其他赋值操作。

3.3.4 算术操作

源程序中出现的算术操作主要为for循环中的i++操作。观察可知,为通过第53行指令“addl 1, -4(%rbp)”实现,表明循环变量i并未放在寄存器中,而是存储于内存中。

其他类型的算出操作还有:

在这里插入图片描述

3.3.5 关系操作

在这里插入图片描述

关系操作主要涉及判断大小关系等。在源代码主要为判断argc与4是否相等、i与条件码是否相等。在hello.s中,对argc的判断被编译为:

在这里插入图片描述

也就是采用cmp指令,基于做减法之后标志位ZF来判断两个数据是否相等。

3.3.6 控制转移

汇编语言中首先设置条件码,然后根据条件码,通过与指定值的判断,来进行控制转移。如对argc的判断:

在这里插入图片描述

通过cmp判断,使用条件跳转指令je控制跳转。

对循环变量的判断:

在这里插入图片描述

在每次执行addl $1, -4(%rbp)之后,判断循环变量与8的大小关系,小于等于就跳回.L4再次执行循环体。

3.3.7 函数操作

本汇编代码中对函数的操作主要为调用字符串输出函数puts(char*)及格式化输出函数printf()。

在这里插入图片描述

可见函数操作主要采用call指令进行调用。

调用函数时有以下操作:(假设函数P调用函数Q)

1.传递控制:进行过程 Q 的时候,程序计数器必须设置为 Q的代码的起始地址,然后在返回时,要把程序计数器设置为 P 中调用 Q 后面那条指令的地址。

2.传递数据:P必须能够向Q提供一个或多个参数,Q必须能够向 P中返回一个值。

3.分配和释放内存:在开始时,Q可能需要为局部变量分配空间,而在返回前,又必须释放这些空间。

hello.c涉及的函数操作有:

main函数,printf,exit,sleep ,getchar函数

main函数的参数是argc和argv;两次printf函数的参数恰好是那两个字符串

exit参数是1,sleep函数参数是atoi(argv[3])

函数的返回值存储在%eax寄存器中。

3.3.8 类型转换

hello.c中涉及的类型转换是:atoi(argv[3]),将字符串类型转换为整数类型其他的类型转换还有int、float、double、short、char之间的转换。

3.4 本章小结

本章主要对hello.i编译为汇编代码文本文件hello.s的过程进行了大致说明,给出了编译过程的指令,并根据所得汇编文件对C语言编译过程中各种数据类型及操作对应的汇编代码进行了梳理。通过理解了这些编译器编译的机制,我们可以很容易的将汇编语言翻译成c语言。

(第3章2分)

第4章 汇编

4.1 汇编的概念与作用

汇编器(as)将编译器给出的.s文本文件翻译为机器语言,打包成可重定位目标文件的形式,保存在.o文件中。它是一个二进制文件。

4.2 在Ubuntu下汇编的命令

在命令行中输入命令gcc hello.s -c -o hello.o,进行汇编过程。

在这里插入图片描述

4.3 可重定位目标elf格式

4.3.1 .elf头

命令行键入命令:readelf -h hello.o可观察elf头情况。

在这里插入图片描述

elf头以 16B 的序列 Magic 开始,Magic 描述了生成该文件的系统的字的大小和字节顺序,ELF 头剩下的部分包含帮助链接器语法分析和解释目标文件的信息,其中包括 ELF 头的大小、目标文件的类型、机器类型、字节头部表(section header table)的文件偏移,以及节头部表中条目的大小和数量等信息。根据头文件的信息,可以知道hello.o是可重定位目标文件,节头部表的文件偏移是1240字节,节头部表有14个条目。

4.3.2 .section节

键入命令:readelf -S hello.o

在这里插入图片描述

Section:节头部表,包含了文件中出现的各节语义,包括节类型、位置和大小等信息。 每个节都从0开始,用于重定位。在文件头中得到节头表的信息,然后再使用节头表中的字节偏移信息得到各节在文件中的起始位置,以及各节所占空间的大小。同时可以观察到,代码是可执行的,但不能写;数据段和只读数据段都不可执行,而且只读数据段也不可写。

4.3.3 .symtab符号表

键入命令:read -s hello.o

在这里插入图片描述

symtab中存放程序中定义的各种函数和全局变量。Name为符号名称。对于可重定位目标模块,Value是其相对于目标节的起始位置的偏移;对于可执行目标文件,Value是其绝对地址。size为目标大小,type表明该符号是数据还是函数。Bind表明是本地还是全局。

4.3.4 .rela.text重定位节

键入命令:readelf -r hello.o

在这里插入图片描述

这是一个.text节中位置的列表,包含.text节中需要进行重定位的信息,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。如我们的hello.o中对puts,exit,printf,sleep,getchar等的重定位信息。

各列包含以下信息:

a.Offset:需要进行重定向文件在.text或者.data中的偏移量。

b.Info:包括symbol和type两部分,其中symbol占前4个字节,type占后4个字节,symbol代表重定位到的目标在.symtab中的偏移量,type代表重定位的类型。

c.Type:重定向的目标类型。。

d.Sym.Name:重定向到目标名称。

e.Addend:重定向位置的辅助信息。

4.4 Hello.o的结果解析

键入命令:objdump -d -r hello.o

在这里插入图片描述

通过此处反汇编代码与第3章hello.s内容对比可以发现,可知汇编语言指令部分基本未变,但是反汇编代码不只有汇编内容,还有机器代码、机器语言程序。机器指令有操作码和操作数组成。汇编语言是人们比较熟悉的词句直接表述CPU动作形成的语言,是最接近CPU运行原理的语言。

进一步观察发现二者区别:

1)分支转移:hello.s中对跳转指令使用的是如.L1这样的名称,而hello.o的反汇编中的跳转指令直接使用其地址。

2)函数调用:hello.s中函数调用只写了函数名称,hello.o的反汇编中的函数调用则是使用了当前指令的下一个字节(即地址)。原因是因为该函数迟绑定,该函数为共享库中函数,只有运行时,动态链接器作用后才能确定相应的PLT条目地址。

4.5 本章小结

本章我们分析了hello.o的elf文件格式的信息,以及通过汇编代码查看了其与hello.s的区别。我们看到编译后的汇编,是将我们的代码向更加有利于机器处理的进一步。在汇编中,我们进行了更多的处理,使其成为一个可重定位目标文件。这就是我们“hello的程序人生”的一个重大突破,就好像一个人从单打独斗到合作协同一样,我们的“hello”正走向人生中更加光明的未来。

(第4章1分)

第5章 链接

5.1 链接的概念与作用

概念:链接是指将预处理、编译、汇编后的文件链接使其成为可执行目标文件的过程。

作用:链接将多个代码和数据合并成一个单一的文件。链接可以执行于编译时,也就是在源代码被编译成机器代码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;甚至于运行时,也就是由应用程序来执行。链接使得分离编译成为可能。

5.2 在Ubuntu下链接的命令

使用ld的链接命令,应截图,展示汇编过程! 注意不只连接hello.o文件

在这里插入图片描述

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

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

5.3.1 .elf头

键入命令:readelf -h hello

在这里插入图片描述

ELF头以一个16字节的序列开始,该序列描述了生成该文件的系统的字大小和字节顺序。ELF头剩下的部分包含帮助链接器语法分析和解释目标文件的信息,其中包括ELF头的大小、目标文件的类型、机器类型、节头部表的文件偏移,以及节头部表中条目的大小和数量。

5.3.2 section节

键入命令:readelf -S hello

在这里插入图片描述

.section是节头部表,包含了文件中出现的各个节的语义,包括节的类型、位置和大小等信息。

5.3.3 .rela.text重定位节

键入命令:readelf -r hello

在这里插入图片描述

.rela.text是一个.text节中位置的列表,包含.text节中需要进行重定位的信息,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。如我们的hello.o中对puts,exit,printf,sleep,getchar等的重定位信息。

各列包含以下信息:

a.Offset:需要进行重定向文件在.text或者.data中的偏移量。

b.Info:包括symbol和type两部分,其中symbol占前4个字节,type占后4个字节,symbol代表重定位到的目标在.symtab中的偏移量,type代表重定位的类型。

c.Type:重定向的目标类型。

d.Sym.Name:重定向到目标名称。

e.Addend:重定向位置的辅助信息。

5.4 链接的重定位过程分析

键入命令:objdump -d -r hello,可以得到链接后的可执行文件hello的反汇编代码。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

通过对比可知,在主函数main的汇编代码上,hello与hello.o并未发生太大变化,但已经有了自己确定的地址,并且将条件跳转、函数调用等所使用的占位符改为对应的地址(绝对寻址)或者相对寻址编码。

同时,hello的反汇编代码从.init节开始,而hello.o的反汇编代码从.text节开始。hello的反汇编代码中导入了puts、printf、atoi、getchar、sleep等在主程序中使用过的函数,而hello.o的反汇编代码中不包含这些函数。这些函数是来自库中的函数,在链接器解析引用的时候会从库中复制对应的目标模块(如printf.o)与hello.o进行链接,每输入一个存档文件,链接器就会检查未定义的符号集,如果这个输入文件能够解析这个符号,那就加入到可执行文件中,一般这样的静态解析我们都把存档文件放在最后链接,以防一些符号不能被解析。

5.5 hello的执行流程

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

在这里插入图片描述

首先加载器将可执行目标文件中的代码和数据从磁盘复制到内存中,然后通过跳转到程序的第一条指令或者入口点来运行该程序。这里的jmp r12就是跳转到程序的入口点0x4010f0(见elf头)。

在这里插入图片描述

_start函数调用系统启动函数_libc_start_main,定义在libc.so中,初始化执行环境,调用用户层的main函数,处理main函数的返回值,并且在需要的时候把控制返回给内核。这就是到调用main函数之前的准备工作。

main调用的库函数:

名称地址
puts0x401090
exit0x4010d0
printf0x4010a0
atoi0x4010c0
sleep0x4010e0
getchar0x4010b0

5.6 Hello的动态链接分析

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

.section节中有这样一条:

在这里插入图片描述

说明hello程序有.got节。打开edb查看:

在这里插入图片描述

可以看到,在dl_init之前,got节中8个字节都是0。在调用_init函数之后再查看.got节:

在这里插入图片描述

可以看到.got节开端有了一个8字节的条目,指向PLT的第二个条目,也就是_libc_start_main。对于动态共享链接库中PIC函数,编译器没有办法预测函数的运行地址,所以需要添加重定位记录,等待动态链接器的处理,为避免运行时修改调用的代码段,链接器采用延迟绑定的策略。动态链接器使用过程链接表PLT + 全局变量偏移表GOT实现函数的动态链接,GOT中存放目标函数的地址,PLT使用该地址跳转到目标位置,其中GOT[1]指向重定位表,GOT[2]指向动态链接器ld-linux.so运行地址。

5.7 本章小结

链接使我们的程序变为真正的可执行目标文件。链接器对我们汇编之后的程序以及我们在程序中调用的库进行连接,使我们包含了不存在于我们的代码中的内容可以正常运行。这其中包括了静态链接,链接重定位,动态链接等。

链接使我们的程序从“文件”到可以执行的“程序”。我们的“hello程序人生”就好像是一滴水融入了大海,不再是孤零零的一堆代码,而是可以和各种库联合起来,可以跑的程序了!

真正的“hello”程序人生,开始了!

(第5章1分)

第6章 hello进程管理

6.1 进程的概念与作用

概念:进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。它不只是程序的代码,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。进程是一个执行中的程序的实例,每一个进程都有它自己的地址空间,一般情况下,包括文本区域、数据区域、和堆栈。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储区着活动过程调用的指令和本地变量。

作用:它提供给应用程序两个假象:

  1. 一个独立的逻辑控制流,好像我们的程序独占的使用处理器

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

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

Shell是操作系统(内核)与用户之间的桥梁,充当一个命令解释器,将用户命令翻译给系统执行。

处理流程:

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

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

3.检查首个命令行参数是否是一个内置的shell命令

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

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

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

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

6.3 Hello的fork进程创建过程

在这里插入图片描述

终端程序通过调用fork()函数创建一个子进程,子进程得到与父进程完全相同但是独立的一个副本,包括代码段、段、数据段、共享库以及用户栈等。子进程还获得与父进程任何打开文件描述符相同的副本。二者最大的不同是PID是不同。父进程与子进程是并发运行的独立进程,内核能够以任意方式交替执行它们的逻辑控制流的指令。在子进程执行期间,父进程默认选项是显示等待子进程的完成。

以我们的hello为例,当我们输入“./hello 2021110884 陈一豪 1”的时候,首先shell对我们输入的命令进行解析,由于我们输入的命令不是一个内置的shell命令,因此shell会调用fork()创建一个子进程。

6.4 Hello的execve过程

当创建了一个子进程之后,子进程调用exceve函数在当前子进程的上下文加载并运行一个新的程序即hello程序,加载并运行需要以下几个步骤:

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

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

3.映射共享区域。如果hello程序与共享对象链接,比如标准C库libc.so,那么这些对象都是动态链接到这个程序的,然后再映射到用户虚拟地址空间中的共享区域。

4.设置程序计数器(PC)。exceve做的最后一件事就是设置当前进程的上下文中的程序计数器,使之指向代码区域的入口点。下一次调用这个进程时,它将从这个入口点开始执行。Linux将根据需要换入代码和数据页面。

除了一些头部信息,在加载过程中没有任何从磁盘到内存的数据 复制。直到 CPU 引用一个被映射的虚拟页时才会进行复制,这时,操作系统利用 它的页面调度机制自动将页面从磁盘传送到内存。

在这里插入图片描述

6.5 Hello的进程执行

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

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

c. 进程调度:在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占的进程,这种决策就叫做调度。

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

看起来,程序是在独占的使用整个cpu,但是实际上,我们知道我们同时运行了许多程序,这些程序看起来都在独占整个cpu。事实上,程序都是在内核模式和用户模式中不断的切换以“同时”执行多个程序。

比如hello在运行到sleep()时,程序就会把控制交给内核,让内核决定执行其他程序的指令。当sleep()结束时,内核会把控制交回给程序。

在这里插入图片描述

6.6 hello的异常与信号处理

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

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

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

异常和信号异常可以分为四类:中断、陷阱、故障、终止,各自的属性

在这里插入图片描述

图6.6.1

hello执行的过程中会出现:

中断:系统默认通过定时器的中断来进行上下文的切换,通常为1ms/10ms,当定时器发出中断信号时,系统就会判断当前进程已经运行了足够长的时间之后进行上下文切换。

陷阱:hello

程序调用sleep函数显示地进行系统调用,让当前进程休眠,进入内核模式将控制转移给别的进程

故障:执行hello程序的时候有可能发生缺页故障

终止:在hello执行过程可能会出现DRAM或者SRAM位损坏的奇偶错误。

测试如下:

在这里插入图片描述

在这里插入图片描述

由以上内容可知,在程序执行过程按下Ctrl+z会使得向当前进程组的每个进程发送SIGTSTP信号。当前进程被挂起直到收到下一个SIGCONT信号。键入ps可以看到进程状态;输入jobs可以看到当前后台有一个被挂起的进程hello;输入pstree可以看到各个进程之间的树形关系图,从中可以找到hello进程;输入fg [当前挂起的进程编号]也就是fg 1,可以看到进程恢复并接续执行,fg命令通过发送SIGCONT信号让hello进程转到前台继续运行;最后,按下Ctrl+c,内核会向hello进程发送一个SIGINT信号,终止hello进程。

6.7本章小结

与预处理、编译、汇编等以单个文件为中心进行的处理相比,进程管理显然要更复杂一些,因为这涉及到操作系统对程序运行的一些操作,如程序通过fork和execve执行,对进程间进行切换,以及出现异常时的处理等等,这并不是我们在编码hello时就想到的,而是我们在执行hello时不得不面对的。

“hello”的人生路已经开始了,它面对的是一个复杂的世界,从以个人为中心到需要调和融洽多个程序间的关系。尽管我们的“hello”并不需要考虑这些问题,但他仍要经历这一切,成为一个轻车熟路的“老程序”。

(第6章1分)

结论

1.逐条总结hello所经历的过程

hello.c:编写c程序,hello.c诞生,它是一个二进制文本文件,hello.c中的每个字符都是用ascall编码表示。

hello.i:hello.c经过预处理阶段变为hello.i。

hello.s:hello.i经过编译阶段变为hello.s。

hello.o:hello.s经过汇编阶段变为hello.o。

hello:hello.o与可重定位目标文件和动态链接库链接成为可执行文件hello。至此可执行hello程序正式诞生。

运行:在终端输入./hello 2021110884 陈一豪 3。

创建子进程:由于终端输入的不是一个内置的shell命令,因此shell调用fork函数创建一个子进程。

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

上下文切换:hello调用sleep函数之后进程陷入内核模式,处理休眠请求主动释放当前进程,内核进行上下文切换将当前进程的控制权交给其他进程,当sleep函数调用完成时,内核执行上下文切换将控制传递给当前进程。

动态申请内存:当hello程序执行printf函数是, 会调用 malloc 向动态内存分配器申请堆中的内存。

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

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

对计算机系统的设计与实现的深切感悟

计算机系统的设计思想和实现都是基于抽象实现的。从最底层的信息的表示用二进制表示抽象开始,到实现操作系统管理硬件的抽象:进程是对处理器、主存和I/O设备的抽象。虚拟内存是对主存和磁盘设备的抽象。文件是对I/O设备的抽象。

计算机系统的设计精巧:为了解决快的设备存储小、存储大的设备慢的不平衡,设计了高速缓存来作为更底层的存储设备的缓存,大大提高了CPU访问主存的速度。

计算机系统的设计考虑全面:计算机系统设计考虑一切可能的实际情况,设计出一系列的满足不同情况的策略。比如写回和直写,写分配和非写分配,直接映射高速缓存和组相连高速缓存等等。

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

附件

文件名称文件作用
hello.ihello.c预处理之后文本文件
hello.shello.i编译后的汇编文件
hello.ohello.s汇编之后的可重定位目标文件
hello链接之后的可执行目标文件
hello.outhello反汇编之后的可重定位文件

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

参考文献

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

[1] https://www.cnblogs.com/diaohaiwei/p/5094959.html

[2] [深入理解计算机系统原书第3版-文字版.pdf](file://C:\Users\m1777\Desktop\深入理解计算机系统原书第3版-文字版.pdf)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值