哈工大计算机系统2022年秋季学期大作业

摘  要

    本论文详细研究了hello.c这一经典程序文件在ubuntu系统下的整个生命周期:从原始程序编写到程序执行再到运行终止以及进程回收的全过程。在此过程中实践了预处理、编译、汇编、链接、加载、运行、终止、存储、回收的实现形式,对程序的“一生”有了全方位的认识,更加加深了对计算机系统的理解。

关键词:计算机系统,《深入理解计算机系统》,程序执行过程,变量生存期,计算机组成原理,编译原理                           

目  录

第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的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。

1.1.1 P2P(from program to process)

由.c文件经过预处理、编译、汇编、链接过程后得到可执行文件hello。

 

linux系统中在shell中输入./hello运行hello,shell会调用fork函数为hello生成子进程,再调用加载器execve在子进程中加载hello程序,于是hello从程序转换为了进程。

      1.1.2 020(from zero-0 to zero-0)

zero-开始:程序本来在磁盘上保存,经过子进程调用execve加载后,内存创建了新的区域提供给hello的代码区、数据区以及运行时堆栈,映射虚拟内存,载入物理内存,然后进入程序入口处开始执行。

zero-结束:hello执行完成后,操作系统回收子进程,内核从系统中删除了hello的所有相关数据,hello所占的所有资源清零,hello以zero的形式结束了它的一生。

1.2 环境与工具

硬件环境:AMD Ryzen R7 5800H

软件环境:Ubuntu 22.04 LTS 64bit

开发工具:gedit,vim,edb,vscode,readelf,HexEdit

1.3 中间结果

文件名

文件功能

hello.c

源程序

hello.i

预处理得到的文本文件

hello.s

编译得到的汇编文件

hello.o

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

hello

链接得到的可执行目标文件

hello.elf

hello.o的ELF格式

hello1.elf

hello的ELF格式

表1 中间结果文件名及其作用

1.4 本章小结

本章整体介绍了hello的实现过程,简要介绍了hello.c的P2P与020,完成本文相关内容需要的实验环境以及工具,并展示了实验过程中的中间结果文件。

第2章 预处理

2.1 预处理的概念与作用

2.1.1预处理的概念

程序设计领域中,预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。典型地,由预处理器(preprocessor) 对程序源代码文本进行处理,得到的结果再由编译器核心进一步编译。这个过程并不对程序的源代码进行解析,但它把源代码分割或处理成为特定的单位——(用C/C++的术语来说是)预处理记号(preprocessing token)用来支持语言特性(如C/C++的宏调用)。

其不做语法检查,是为编译做的准备工作,对以“#”开头的命令(宏定义#define、包含文件#include、条件编译#ifdef)进行处理,最后生成.i文件。

 

2.1.2预处理的作用

在预处理的过程中,计算机利用预处理器(cpp)进行处理。而预处理主要有3 33个方面的内容,分别是根据字符“#”后所跟的具体语句进行不同处理。它们分别为宏定义、包含文件、条件编译。

(1)宏定义在预处理的过程中会进行宏替换。在具体语句中表现为#define。宏定义具体而言又分为两种,在不带参数的宏定义中,要用实际值替换用#define定义的字符或字符串;而在带参数的宏定义中,不仅仅要进行实际值的替换,还要将参数进行代换。在宏替换中,仅仅只是做替换,不做计算和表达式求值。

(2) 包含文件指的是对代码中出现的#include语句进行处理。#include指令能够告诉预处理器读取源程序中所引用的系统的源文件,并且将这一段代码直接插入到程序文件中,最终保存为.i文件中。而在Hello实例中,预处理会对#include< stdio.h>、#include<unistd.h>、#include< stdlib.h>三条语句进行文件包含的处理。

(3)条件编译指的是针对#ifdef、#ifndef等语句进行的处理。条件编译能够根据#if的不同条件决定需要进行编译的代码,#endif是结束这些语句的标志。使用条件编译可以使目标程序变小,在满足条件之后才会进行编译。

2.2在Ubuntu下预处理的命令

应截图,展示预处理过程!

 

图2 预处理命令

 

图3 预处理得到的文件

2.3 Hello的预处理结果解析

通过linux下的gedit工具打开hello.i文件,我们可以看到代码中包含了#include文件中的函数代码

 

图4 #include指令包含的文件

Main函数代码位于文件末端

 

图5 main函数在.i文件中的位置

由预处理结果我们可以看到,预处理器(cpp)按照#的顺序进行预处理内容展开(在hello.c中为stdio.h,unistd.h,stdlib.h),最终的hello.i文件中没有#开头的预处理命令,其包含了#include指示的文件的所有源代码以及源程序的命令行参数、环境变量配置以及用绝对路径显示的库头文件的位置、头文件中使用的数据类型声明、结构体定义、外部函数引用等等,并删除了所有注释,综合形成了hello.i文件。

2.4 本章小结

本章通过具体的hello.c实例,运用预处理的概念与作用知识,对所做的处理进行了详细的阐释,并对生成的hello.i文件做出了详细的结果解析。

第3章 编译

3.1 编译的概念与作用

3.1.1概念:

编译即为把代码转换为汇编指令的过程,是预处理完的文件进行一系列词法分析、语法分析、语义分析以及优化后生成相应的汇编代码文件的过程。

3.1.1作用:

      将源语言经过词法分析、语法分析、语义分析以及一系列优化后生成汇编代码,即将高级语言程序转化为机器可直接识别处理执行的的机器码的中间步骤。

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

2.语法分析:在词法分析的基础上将单词序列组合成各类语法短语,如“程序”,“语句”,“表达式”,使其构成一棵语法树。

3.语义分析:编译器完成词法与语法分析后仍不清楚语句是否有意义,因此需要语义分析,有且仅有静态语法检查,对结构上正确的源程序进行上下文有关性质的审查,进行类型审查。语义分析是审查源程序有无语义错误,为代码生成阶段收集类型信息。

4.代码优化:对程序进行多种等价变换,使得从变换后的程序出发,能生成更有效的目标代码,比如选择合适的寻址方式、使用唯一来代替乘除法、删除出多余的指令等。

3.2 在Ubuntu下编译的命令

图6 编译命令

3.3 Hello的编译结果解析

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

3.3.1 数据

(1)局部变量:

main的局部变量i,保存在栈中。

(2)常量字符串:

位于只读数据段(.rodata)中,作为printf函数的参数。

(3)立即数

以$开头的数字,直接出现于汇编代码中。

(4)函数参数argc、数组函数参数*argv[]

作为传给main函数的参数,均被放入栈中,前者指示参数个数,后者每个元素存放一个指向字符的指针。

3.3.2 赋值

在hello.s中,赋值操作由mov指令实现,使用了movl和movq指令。

movb:一个字节
movw:“字”
movl:“双字”
movq:“四字”

3.3.3 类型转换

hello.c中atoi(argv[3])将字符串类型转换为整型。int、float、double、short、char可以进行相互转化。

3.3.4 算数操作

(1)for循环中使用了自加运算:

对应的汇编代码为:

其他的汇编代码算术操作如下:

3.3.5 关系操作

判断argc是否为4,若不为4则打印用法后退出,若为4则继续。

为循环截止条件。

3.3.6 控制转移

在hello.s中通过cmp指令设置条件码,依据条件码进行控制转移。

如图所示,为两个跳转位置。各自对应的汇编代码为:

其中,je用于判断cmpl产生的条件码,若两个操作数的值不相等则跳转到指定地址,jle用于判断cmpl产生的条件码,若后一个操作数的值小于等于前一个则跳转到指定地址。

3.3.7函数操作

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

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

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

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

hello.c中的函数操作:

main函数:参数是int argc,char *argv[]

printf函数:参数是argv[1],argv[2],作用为打印所选内容。

exit函数:参数是1,功能为退出程序。

sleep函数:参数是atoi(argv[3]),功能为进程休眠atoi(argv[3])秒。

getchar函数:无参数,功能为从stdin流中截取一个字符。

atoi函数:参数是argv[3],功能为将所选字符串转化为整型变量。

3.4 本章小结

本章内容围绕编译展开,介绍了编译的概念以及作用,说明了ubuntu下的编译命令,对hello.i程序进行编译获得了hello.s程序,并对hello.s程序进行了解析,分析了其数据、操作等,加深了对汇编文件的理解。

第4章 汇编

4.1 汇编的概念与作用

4.1.1汇编的概念

汇编程序即为将汇编语言书写的程序翻译成与之等价的机器语言程序的翻译程序。汇编时,输入的文件是汇编语言书写的源程序,输出的是机器语言表示的目标程序。它是将汇编指令文本文件打包成可重定位目标文件,结果保存在.o文件中,这是一个二进制文件。

4.1.2 汇编的作用

完成汇编指令文件向可重定位目标文件的转化。

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

   

4.3.1 ELF头

ELF头以一个16字节的序列(Magic,魔数)开始,它描述了系统的字的大小和字节顺序(大端序或者小端序)。ELF头剩下部分的信息包含帮助连接器语法分析和解释目标文件的信息。其中包括ELF头的大小、目标文件的类型和机器类型等。由上图我们可以看出,Data表示了系统采用小端法,文件类型Type为REL(可重定位文件),节头数量Number of section headers为14个等信息。

4.3.2节头部表:

我们知道夹在ELF头与节头部表之间的都为节。其描述了.o文件中出现的各个节的信息,包括节的名称、类型、地址、偏移量、所占空间大小等信息。其中书上描述过的有:

名称

内容

.text

已编译程序的机器代码

.rodata

只读数据

.data

已初始化的全局和静态C变量

.bss

未初始化的全局和静态C变量,以及所有被初始化为0的全局或静态变量

.symtab

一个符号表,存放一些在程序中定义和引用的函数和全局变量的信息

.rel.text

一个.tex节中位置的列表

.rel.data

被模块引用或定义的所有全局变量的重定位信息

.debug

一个调试符号表

.line

原始C源程序中的行号和.text节中机器指令之间的映射

.strtab

一个字符串表(包括.symtab和.debug节中的符号表)

4.3.3重定位节

.rela.text中保存了.text节中需要被修正的信息,注明了偏移量、寻址方式等信息。

.rela.eh_frame中保存了.eh_frame节重定位信息。

4.3.4 符号表

这其中,Num为某个符号的编号,Name是符号的名称。Size表示他是一个位于.text节中偏移量为0处的146字节函数。Bind表示这个符号是本地的还是全局的,由上图可知main函数名称这个符号变量是本地的。

4.4 Hello.o的结果解析

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

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

机器语言是二进制机器指令的集合,每一条机器代码都对应一条机器指令。每一条汇编指令以一种映射的方式与机器语言进行对应。

不同之处在于:

  1. 全局变量访问:

在hello.s文件中,使用段名称+%rip进行对格式串(位于rodata中)的访问,而在可重定位目标文件中使用0+%rip进行访问,原因是rodata数据地址重定位结束时才可以确定,因此访问时需要重定位。

  1. 分支转移:

hello.s中跳转指令的目标地址直接记为段名称,例如.L2,.L3等。

而在hello.o中跳转的目标为确定的地址(主函数地址+地址偏移量)表示。例如:

  1. 函数调用

hello.s中函数调用为函数名称。

而hello.o中函数调用call目标地址是当前指令的下一条指令地址。这是因为hello.c中调用的函数都是共享库中的函数,需要等待链接之后才能确定响应函数的地址。因此,机器语言中,对于这种不确定地址的调用,会先将下一条指令的相对地址设置为0,然后再.rela.text节中为其添加重定位条目,等待链接时确定地址。

4.5 本章小结

本章介绍了hello.s变为hello.o的过程,对可重定位目标文件ELF格式进行了详细的解释分析,还着重分析了hello.o的反汇编文件,剖析了汇编代码到机器指令的深刻联系。

5章 链接

5.1 链接的概念与作用

注意:这儿的链接是指从 hello.o 到hello生成过程。

5.1.1链接的概念

链接是指通过链接器,将各种代码与数据片段收集并合成为一个完全链接的单一文件的过程,可被加载到内存中并执行。其可以执行于编译时,可以执行于加载时(加载器加载到内存执行),或者执行与运行时,由应用程序加载执行。

5.1.2 链接的作用

链接器在软件开发过程中扮演着一个关键的角色,因为它们使得分离编译成为可能。我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其它文件。

5.2 在Ubuntu下链接的命令

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

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

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

在终端shell中输入readelf -a hello > hello1.elf生成hello程序的ELF格式文件,并保存为hello1.elf(与第四章中的elf文件区分)

5.3.1 ELF头

在ELF头中,与hello.o不同的是,其被标记为一个可执行目标文件,且节数较多,有27个节,并且增加了入口地址。

5.3.2 节头

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

5.3.3 程序头

其为一个结构数组,描述了执行程序时系统所需要准备的段等信息。

5.3.4 符号表

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

5.4 hello的虚拟地址空间

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

根据5.3中的节头部表,我们可以通过edb找到各个节的信息。

其包含了5.3中所示的各节名称和位置。

5.5 链接的重定位过程分析

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

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

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

5.5.1 反汇编指令及结果

5.5.2与hello.o的区别

(1)hello.o没有经过链接,所以main的地址从0开始(可重定位目标文件地址开始于0),并且不存在调用的库函数的代码(如printf)。hello将hello.o需要重定位的部分重定位后将其地址转变成了虚拟空间中绝对地址,并在后面标出执行函数的地址和函数名;将库函数添加到了文件中,并添加了节。

图  添加进的调用的库函数

(2)链接器解析了重定位条目,call之后的字节代码被链接器修改为目标地址(函数地址)与call指令下一地址的差值,直接指向对应代码段,得到完整的反汇编代码,同时跳转参数也被修改。

5.5.3 hello进行重定位的方式

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

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

(3)重定位条目当编译器遇到对最终位置未知的目标引用时,它就会生成一个重定位条目,代码的重定位条目放在.rel.txt中

(4)重定位过程的地址计算算法如图所示:(只显示了书上所示的最基本的两种地址计算算法)

5.6 hello的执行流程

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

ld-2.27.so!_dl_start

ld-2.27.so!_dl_init

hello!_start

libc-2.27.so!_libc_start_main

libc-2.27.so!_cxa_atexit

libc_2.27.so!_new_exitfn

hello!_libc_csu_init

hello!_init

libc-2.27.so!_sigsetjmp

libc-2.27.so!_sigjmp

Call main hello!main

libc-2.27.so!exit

如图所示:

5.7 Hello的动态链接分析

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

动态链接项目是指依靠模块化的思想,将程序拆分为多个独立的子模块,并在程序开始运行时才将他们加载并完全链接形成一个完整程序,由于无法预测函数的运行时地址,编译器需要添加重定位记录等待动态链接器处理。链接器采用延迟绑定的策略,延迟绑定通过使用过程链接表PLT+全局偏移量表GOT实现函数的动态链接,GOT中存放函数目标地址,PLT使用GOT中地址跳转到目标函数。

  1. dl_init前

查看helloELF文件(hello1.elf)可知got起始位置位于0x404000处

查看edb中的相关信息,可以得到GOT表位置在调用dl_init之前0x404008后的16个字节均为0:

  1. dl_init后

调用dl_init之后:

可以看到16字节的内容已经改变,即.got.plt的条目已经发生变化。

5.8 本章小结

本章围绕链接展开,阐述了链接的概念以及作用,分析了ubuntu下ELF文件的各部分信息,并且使用edb加载hello,查看了进程的虚拟地址空间各段信息,并通过反汇编hello文件,将其与hello.o反汇编文件进行了对比,详细了解了重定位过程;然后,对hello的执行过程做了深度探究,并且在最后对hello进行了动态链接分析。

6章 hello进程管理

6.1 进程的概念与作用

6.1.1 进程的概念

狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。

广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

进程的概念主要有两点:第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。第二,进程是一个执行中的程序。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程 [3] 

进程是操作系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态情况,描述系统内部各道程序的活动规律引进的一个概念,所有多道程序设计操作系统都建立在进程的基础上。

6.1.2 进程的作用

进程提供给应用程序两个关键抽象:一个独立的逻辑控制流,程序貌似独占处理器;一个私有的地址空间,如同程序独占内存地址。

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

6.2.1 shell的作用

shell是一个交互型应用程序,为使用者提供操作界面,接受用户命令,然后调用内核相关的服务。

6.2.2 shell的处理流程

①从终端读入输入的命令。
②将输入字符串切分获得所有的参数。
③检查第一个命令行参数是否是一个内置的shell命令,如果是则立即执行。
④如果不是内部命令,调用fork( )创建新进程/子进程执行指定程序。

⑤如果不能检测到正确的输入信息则打印错误信息。
⑥shell应当可以处理完当前参数后继续处理下一参数,直到处理完毕。

6.3 Hello的fork进程创建过程

根据shell的处理流程,可以推断,输入命令执行hello后,父进程如果判断不是内部指令,即会通过fork函数创建子进程。子进程获取了与父进程的上下文,包括栈、通用寄存器、程序计数器,环境变量和打开的文件相同的一份副本。子进程与父进程的最大区别是有着跟父进程不一样的PID。Fork函数只会被调用一次,但会返回两次,在父进程中,fork返回子进程的PID,在子进程中,fork返回0。子进程可以读取父进程打开的任何文件。当子进程运行结束时,父进程如果仍然存在,则执行对子进程的回收,否则就由init进程回收子进程。

6.4 Hello的execve过程

Execve函数加载并运行可执行目标文件hello,且包含相对应的一个带参数的列表argv和环境变量的列表exenvp,,只有当出现错误时,例如找不到hello文件时,execve才会返回-1到调用程序,execve调用成功则不会产生返回。

1. 为子进程调用函数fork之后,子进程调用execve函数(传入命令行参数)在当前进程的上下文中加载并运行一个新程序hello。

2. 为执行hello程序加载器、删除子进程现有的虚拟内存段,execve 调用驻留在内存中的、被称为启动加载器的操作系统代码,并创建一组新的代码、数据、堆和栈段。

3. 新的栈和堆段被初始化为零,通过将虚拟地址空间中的页映射到可执行文件的页大小的片,新的代码和数据段被初始化为可执行文件中的内容。最后加载器(execve)设置PC指向_start 地址,_start 最终调用 hello中的 main 函数。

6.5 Hello的进程执行

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

6.5.1 上下文信息

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

6.5.2进程时间片

一个进程的控制流的每一个时间段叫做时间片。

6.5.3进程调度过程

在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程,这种决策就叫做调度,是由内核中称为调度器的代码处理的。当内核选择一个新的进程运行,我们说内核调度了这个进程。在内核调度了一个新的进程运行了之后,它就抢占了当前进程,并使用上下文切换机制来将控制转移到新的进程。

以执行sleep函数为例,sleep函数请求调用休眠进程,sleep将内核抢占,运行其他程序所挂载的进程,进入sleep倒计时,当倒计时结束后,hello程序重新抢占内核,返回hello所在的进程继续执行。

6.6 hello的异常与信号处理

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

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

6.6.1 可能出现的异常

①中断:来自I/O设备的信号,异步发生,中断处理程序对其进行处理,返回后继续执行调用前待执行的下一条代码

②故障:由错误情况引起的,它可能能够被故障处理程序修正。如果修正成功,则将控制返回到引起故障的指令,否则将终止程序。

③陷阱:有意的异常,是执行一条指令的结果,调用后也会返回到下一条指令,用来调用内核的服务进行操作。帮助程序从用户模式切换到内核模式。

④终止:不可恢复的致命错误造成的结果,通常是一些硬件的错误,处理程序会将控制返回给一个abort例程,该例程会终止这个应用程序。

6.6.2 可能产生的信号

①不停乱按:

若不是shell命令或ctrl+c或ctrl+z,乱按不会造成影响。

②ctrl+z:SIGTSTP

进程被暂时挂起,如图所示

输入ps指令后,发现进程hello只是暂时挂起,并没有终止:

③ctrl+c:SIGINT

进程被SIGINT信号中断。

fg命令:使后台挂起的进程继续运行。

可以看到,被SIGTSTP挂起的进程hello可以用fg指令继续运行。

⑤jobs:显示目前运行的关键作业

⑥pstree:以树状图的形式展现所有运行的进程

⑦kill命令:向指定的进程或进程组传递指定的信息

此处以杀死进程为例,可以看到显示“killed”,代表进程已被杀死。

6.7本章小结

本章介绍了进程的概念与作用,以及Shell-bash的基本概念。针对进程,在这一章中根据hello可执行文件的具体示例研究了fork, execve函数的原理与执行过程。在hello运行过程中,内核有选择对其进行管理,决定何时进行上下文切换。并且当接受到不同的异常信号时,异常处理程序将对异常信号做出相应,执行相应的代码,每种信号都有不同的处理机制,对不同的异常信号,hello也有不同的处理结果。

结论

用计算机系统的语言,逐条总结hello所经历的过程。

你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。

  • hello经历的过程:

(1)编写:程序员编写高级语言程序代码。

(2)预处理(cpp):对hello.c进行预处理,将文件包含(#include)、宏定义(#define)、条件编译(#ifdef)的代码直接插入hello.c中

(3)编译(ccl):将hello.i文件翻译为汇编语言程序hello.s

(4)汇编(as):将hello.s文件翻译为可重定位目标文件hello.o

(5)链接(ld):将hello.o文件与各种调用的动态链接库、静态链接库连接成为可执行目标文件。

(6)运行:在shell中输入运行所需参数

(7)创建子进程:命令行参数并非内置命令,因此shell调用fork()函数创建子进程。

(8)加载(execve):shell调用execve函数映射虚拟内存,进入程序入口后程序开始载入物理内存,最后进入main函数执行

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

(10)访问内存:mmu通过页表将程序中使用的虚拟内存地址映射成物理地址。

(11)动态申请内存:printf调用malloc动态申请内存

(12)信号:程序运行时,我们可以向其中发送信号。

(13)终止:当子进程执行完成时,内核使用waitpid函数调用父进程回收子进程,并将子进程的退出状态传递给父进程,最终内核完全删除子进程的所有数据结构以及系统资源。是谓ZERO TO ZERO.

②对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法

本学期学习了计算机系统这门课,帮助我们建立了系统级观察审视程序的视角,培养了从计算机底层深入解决问题的能力,这门课对从事后续课程的学习奠定了理论与思维基础!

附件

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

文件名

文件功能

hello.c

源程序

hello.i

预处理得到的文本文件

hello.s

编译得到的汇编文件

hello.o

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

hello

链接得到的可执行目标文件

hello.elf

hello.o的ELF格式

hello1.elf

hello的ELF格式

附表1 中间产物文件名及其作用

参考文献

网站:

  1. https://blog.csdn.net/wy122222222/article/details/111177535?ops_request_misc=&request_id=&biz_id=102&utm_term=%E7%BC%96%E8%AF%91%E7%9A%84%E6%A6%82%E5%BF%B5%E4%B8%8E%E4%BD%9C%E7%94%A8&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-3-111177535.142^v63^js_top,201^v3^add_ask,213^v2^t3_esquery_v3&spm=1018.2226.3001.4187 C语言编译概念理解
  2. https://blog.csdn.net/BUFANG_XF/article/details/101621613?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166832344116782414983764%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=166832344116782414983764&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-14-101621613-null-null.142^v63^js_top,201^v3^add_ask,213^v2^t3_esquery_v3&utm_term=%E5%8F%AF%E9%87%8D%E5%AE%9A%E4%BD%8D%E7%9B%AE%E6%A0%87%E6%96%87%E4%BB%B6&spm=1018.2226.3001.4187 通过readelf工具解析ELF可重定位目标文件
  3. 进程(一段程序的执行过程)_百度百科 进程

文献:

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

[2] Pianistx.printf 函数实现的深入剖析[EB/OL].2013[2021-6-9].

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值