CSAPP大作业——程序人生

打印 上一主题 下一主题

主题 576|帖子 576|积分 1728






计算机体系


大作业



题     目  程序人生-Hellos P2P 
专       业    计算机科学与技术      






计算机科学与技术学院


摘  要

本文基于Linux体系下,对hello程序的生命周期展开研究。本文借助相关开辟工具,具体阐述了程序由C源文件经过预处置惩罚、编译、汇编以及链接四个阶段生成hello可执行文件的过程,并对程序的历程管理阶段举行分析。通过对hello程序生命周期的研究,有助于深入明确计算机体系,感受计算机体系计划与实现的精妙艺术。

关键词:计算机体系;Linux;汇编;编译;链接;历程                          








目  录


第1章 概述

1.1 Hello简介

1.2 情况与工具

1.3 中间结果

1.4 本章小结

第2章 预处置惩罚

2.1 预处置惩罚的概念与作用

2.2在Ubuntu下预处置惩罚的下令

2.3 Hello的预处置惩罚结果解析

2.4 本章小结

第3章 编译

3.1 编译的概念与作用

3.2 在Ubuntu下编译的下令

3.3 Hello的编译结果解析

3.4 本章小结

第4章 汇编

4.1 汇编的概念与作用

4.2 在Ubuntu下汇编的下令

4.3 可重定位目标elf格式

4.4 Hello.o的结果解析

4.5 本章小结

第5章 链接

5.1 链接的概念与作用

5.2 在Ubuntu下链接的下令

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

5.4 hello的假造地址空间

5.5 链接的重定位过程分析

5.6 hello的执行流程

5.7 Hello的动态链接分析

5.8 本章小结

第6章 hello历程管理

6.1 历程的概念与作用

6.2 简述壳Shell-bash的作用与处置惩罚流程

6.3 Hello的fork历程创建过程

6.4 Hello的execve过程

6.5 Hello的历程执行

6.6 hello的异常与信号处置惩罚

6.7本章小结

第7章 hello的存储管理

7.1 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动态存储分配管理

7.10本章小结

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

8.2 简述Unix IO接口及其函数

8.3 printf的实现分析

8.4 getchar的实现分析

8.5本章小结

结论

附件

参考文献



第1章 概述

1.1 Hello简介

1.1.1 P2P(From Program to Process)
在Linux体系中,hello的高级语言源程序hello.c首先经过预处置惩罚器的预处置惩罚,得到修改后的源程序hello.i。然后,hello.i经过编译器举行编译,得到汇编代码文件hello.s。接着,hello.s经过汇编器举行汇编,得到可重定位的目标文件hello.o,在经过链接器举行链接,得到可执行的目标文件hello。在shell的下令行中输入./hello来执行程序。shell会利用fork函数创建子历程,通过execve函数加载并运行hello程序。这样,hello就由源程序变成了正在被执行的历程。
1.1.2 O2O(From Zero-0 to Zero-0)
Shell通过execve函数加载并运行hello程序后,内核为hello历程映射假造内存。进入程序入口处开始载入物理内存,进入main函数执行代码。此时,CPU为hello历程分配时间片执行逻辑控制流。程序运行结束之后,shell的父历程回收hello历程,释放掉hello的相关数据布局。这样,hello就完成了O2O的过程。
1.2 情况与工具

硬件情况:X64 CPU;2GHz;2G RAM;256GHD Disk
软件情况:Windows 11 64位;Vmware 11;Ubuntu 22.04.1 LTS 64位
开辟与调试工具:gcc,edb,readelf。

1.3 中间结果

hello.i 预处置惩罚后得到的修改后的源代码文件
hello.s 经过编译得到的汇编语言文件
hello.o 经过汇编得到的可重定位的目标文件
hello 经过链接得到的可执行的目标文件
hello.elf hello.o的elf格式文件
hello_out.elf hello的elf格式文件

1.4 本章小结




第2章 预处置惩罚

2.1 预处置惩罚的概念与作用

2.1.1预处置惩罚的概念
预处置惩罚也称为预编译,它为编译做准备工作。预处置惩罚是在程序源代码被编译之前,由预处置惩罚器对程序源代码举行的处置惩罚。预处置惩罚器根据以字符#开头的下令,修改原来的C程序,举行代码文本的替换工作,得到另一个C程序,通常文件拓展名为.i。预处置惩罚阶段所举行的工作仅仅是文本的替换,并不举行计算功能。
2.1.2预处置惩罚的作用
预处置惩罚可以拓展源代码。合理地使用预处置惩罚功能,可以使得程序更加便于阅读、修改、移植和调试,同时,也有利于模块化程序计划。预处置惩罚重要包括文件包含、宏界说以及条件编译。
 宏界说是指用宏名表示一个字符串,在宏展开时又以该字符串代替宏,以简化编程,提高程序的可读性。宏展开只是简单的字符串替换,不做精确性检测。
文件包含就是在预处置惩罚阶段,将#include包含的头文件插入程序文本中,便于进一步的编译。
条件编译可以根据#ifdef、#ifndef以及#endif等语句的不同条件决定需要举行编译的代码部分。[1]
2.2在Ubuntu下预处置惩罚的下令

预处置惩罚下令为:gcc -E hello.c -o hello.i


图 2-1 Ubuntu下预处置惩罚结果


2.3 Hello的预处置惩罚结果解析

        
 图 2-2 预处置惩罚结果文件内容


经过预处置惩罚,得到了一个以.i为后缀名的一个文本文件。打开文件,发现一共有3000多行。这是由于预处置惩罚对源代码举行了拓展,把头文件stdio.h、unist.h以及stdlib.h中的内容插入到源代码中。与此同时,对宏界说#define也举行宏替换处置惩罚,并删除源文件中的注释内容。
2.4 本章小结

本章重要介绍了预处置惩罚的概念及作用,列出预处置惩罚的下令并对结果举行解析。


第3章 编译


3.1 编译的概念与作用

3.1.1编译的概念
编译指编译器(cc1)将预处置惩罚后的文本文件翻译为文件拓展名为.s的文本文件的过程,它包含一个汇编语言程序。[2]编译会对预处置惩罚后的文件举行词法分析、语法分析以及优化等操纵,终极生成汇编代码文件。
3.1.2编译的作用
对源代码举行词法分析、语法分析、语义分析以及目标代码生成与优化。
词法分析这一阶段实现从左到右每个字符依次读入源程序,即对构成源程序的字符流举行扫描然后根据构词规则识别单词。
语法分析是编译过程的一个逻辑阶段。语法分析的使命是在词法分析的基础大将单词序列组合成各类语法短语。
语义分析的使命是对布局上精确的源程序举行上下文有关性质的检察。
目标代码生成与优化这一过程,是将程序举行多种等价变换,并把中间代码变换成目标代码。[3]
3.2 在Ubuntu下编译的下令

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



图 3-1 Ubuntu下编译结果

3.3 Hello的编译结果解析

3.3.1 数据
(1)字符串
程序中有两个字符串常量生存在.rodata段中,是只读的,不可以大概被修改。具体内容如下图所示。



图 3-2 hello.s中字符串内容


  • 函数参数
main函数有两个参数,分别为整型变量argc与char型指针数组argv,分别由寄存器%rdi和%rsi通报。



图 3-3 main函数参数分析

由上图可知,这里把函数的两个参数分配到栈中,作为局部变量。其中argc被分配到%rbp-20的位置,argv被分配到%rbp-32的位置。

  • 局部变量
局部变量i存储在栈上,通过%rbp相对寻址方式赋初值。汇编代码指令如下图所示。


图 3-4 局部变量赋值方式


3.3.2 数组


图 3-5 数组操纵

数组操纵部分的汇编代码如上图所示。这里%rax记载偏移量,%rbp-32是指针数组argv的首地址。通过M[%rbp-32+%rax]的寻址方式来找到对应的字符串的首地址。
3.3.3 关系操纵
在C源代码中,对应着条件判定的部分有if条件语句以及循环条件的判定。
在if条件语句中,要举行判定 argc!=4。对应的汇编代码如下图所示。


图 3-6 if条件分支cmp指令


这里通过cmpl指令实现。同时设置条件码,便于下一步跳转指令的实现。
在循环体中,需要判定i是否小于9。对应汇编代码如下图所示。

  图 3-7 循环判定cmp指令

同样通过cmpl指令实现,设置条件码,便于下一步jle条件跳转指令判定是否跳转。

3.3.4 控制转移
C源代码中的if布局与循环体布局,在汇编代码中通过条件跳转来实现。汇编代码如下图所示。


图 3-8 条件跳转

上图对应着C源代码中的if布局,这里表明假如M4[%rbp-20]的数值,也就是参数argc的值等于4,则跳转到.L2处。否则输出相应的字符串提示信息,程序终止。


图 3-9 条件跳转

上图对应着C源代码中循环体的循环次数控制部分。假如满足M4[%rbp-4]≤8,即局部变量i≤8,则跳转到.L4处继续循环,否则跳出循环。
3.3.5 赋值
关于赋值操纵,此程序中包括对局部变量i赋值为1的操纵。在汇编代码的体现如下图所示。


图 3-10 赋值操纵

这里使用了movl指令,对局部变量i举行赋值。
3.3.6算术操纵
由C源代码可知,程序中的算术仅有i++。对比汇编代码,这里通过addl指令实现循环体变量i的增加,如下图所示。



图 3-11 算术操纵

3.3.7 函数操纵
使用call指令举行函数调用,通过寄存器%rdi,%rsi,%rdx,%rcx,%r8以及%r9通报参数,若参数超过6个,则别的参数被分配到栈中。汇编代码如下图所示。





图 3-12 函数调用

call指令将首地址压入栈中,为局部变量与函数参数创建栈帧,转移到对应函数的地址。末了使用ret指令返回。
3.4 本章小结

本章介绍了编译的概念与作用,给出编译的下令,基于hello.s文件具体分析了编译的结果。

第4章 汇编


4.1 汇编的概念与作用

4.1.1汇编的概念
汇编指汇编器将.s文件翻译成呆板语言指令,把这些指令打包成可重定位目标程序的格式,并将结果生存在以.o为后缀的目标文件的过程。
4.1.2汇编的作用
汇编器将汇编代码翻译成呆板语言,便于呆板直接读取分析。
4.2 在Ubuntu下汇编的下令

汇编的下令:gcc hello.s -c -o hello.o


图 4-1 汇编结果展示

4.3 可重定位目标elf格式

    首先用指令readelf -a hello.o > hello.elf生成elf格式的文件。


图 4-2 ELF头的信息

ELF头的信息如上图所示,其中的16字节的序列形貌了生成该文件的体系的字的巨细和字节次序。ELF头剩下的部分包含帮助链接器语法分析和表明目标文件的信息。其中包括ELF头的巨细、目标文件的类型、呆板类型、节头部表的文件偏移,以及节头部表中条目标巨细和数量。不同节的位置和巨细是由节头部表形貌的,其中目标文件中每个节都有一个固定巨细的条目。


图 4-3 节头表的内容

节头表的内容如上图所示。节头表形貌了每一个节的名称、在文件中的偏移量以及对齐方式等信息。


图 4-4 重定位节的信息

重定位节的信息如上图所示。重定位节中生存了.text节中需要被重定位的信息,在链接过程中,链接器把这个可重定位的目标文件与其他目标文件结适时,就需要修改这些信息。这里展示的重定位信息有.rodata节中的模式串,puts函数,exit函数,printf函数,sleep函数,getchar函数以及atoi函数。


图 4-5 符号表的内容

符号表的内容如上图所示。符号表存放着界说和引用的函数和全局变量的信息。其中,name是字符串表中的字节偏移,value是符号的地址,size是目标的巨细,type是数据大概函数,bind表示符号是本地的照旧全局的。
4.4 Hello.o的结果解析

接纳objdump -d -r hello.o指令举行反汇编,得到结果如下图所示。



图 4-6 hello.o反汇编代码


由上图可知,对hello.o反汇编处置惩罚不仅得到了汇编代码,还有呆板语言。呆板语言是用二进制代码表示的计算性能直接识别和执行的一种呆板指令的聚集,它由操纵代码与操纵数构成。呆板语言与汇编语言是一一对应关系,两者创建起一个双射。[4]通过对比反汇编的代码内容和hello.s中的汇编语言,不同之处重要为以下几点:

  • 操纵数表示不同。在反汇编代码中,操纵数用十六进制表示;而hello.s中的汇编代码操纵数是用十进制表示的。
  • 分支跳转。在反汇编代码中,跳转目标地址是通过main函数的起始地址加上偏移量计算得到;而在hello.s中的汇编代码中,分支跳转的目标地址是使用段名称举行跳转的,如.L3。
  • 函数调用。在反汇编代码中,call指令中地址是使用main函数起始地址加上偏移量计算而来,并且指令下面还有重定位信息,在链接之后通过重定位计算出函数的地址信息。在hello.s的汇编代码中,call指令是直接使用函数名称。
4.5 本章小结

本章从汇编的概念与作用出发,给出Ubuntu体系下对于hello.s汇编的指令,利用readelf下令分析介绍了elf文件的格式。此外,对于hello.o文件利用objdump指令举行反汇编,分析了呆板语言与汇编语言的关系,并对反汇编代码与hello.s举行比力,分析两者的不同之处。

5链接


5.1 链接的概念与作用

5.1.1链接的概念
链接是将各种代码和数据片段网络并组合成为一个单一文件的过程,这个文件可以复制到内存中执行。
5.1.2链接的作用
链接使得分离编译成为可能。对于大型应用程序而言,不必将它构造成一个大型的源文件,而是可以把它分解为更小的、更好管理的模块,可以独立地修改和编译这些模块。若要改变其中的一个模块,只需要编译它并重新链策应用,不必重新编译其他文件。[2]
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-1 链接指令与结果


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

使用终端下令:readelf -a hello > hello_out.elf
分析hello的ELF格式,用readelf等列出其各段的根本信息,包括各段的起始地址,巨细等信息。
ELF头的信息如下图所示。其中,类型是EXEC(可执行文件),入口点地址也已经确定为0x4010f0,重定位已经完成,有27个节。



图 5-2 ELF头的信息

节头部表的信息如下图所示。这里列出了各节的名称、类型、地址以及偏移量等信息。可见,这些节的地址都已经确定,而不是0,可通过偏移量计算得到终极的地址。


图 5-3 节头部表的信息

重定位节的内容如下图所示。


图 5-4 重定位节的内容

符号表的内容如下图所示。


图 5-5 符号表的内容


5.4 hello的假造地址空间

使用edb加载hello,在data dump窗口查看本历程的假造地址空间。假造地址部分信息如下图所示。


图 5-6 假造地址空间信息

由5.3中所分析的ELF节的内容可知,.text节地址为0x4010f0,在edb中查看该处信息,如下图所示。


图 5-7 .text节内容

利用同样的方法,.rodata节的地址为0x402000,在edb中找到信息如下图所示。


图 5-8 .rodata节内容

可知,.rodata中包含了printf的格式串内容。
.init节的地址为0x401000,通过edb查看假造地址空间的信息。


图 5-9 .init节内容

.interp节的地址是0x4002e0,它的假造地址空间的信息如下图所示。


图 5-10 .interp节内容

可知,这里生存了共享库的信息。
5.5 链接的重定位过程分析

使用指令objdump -d -r hello举行反汇编。下面分析hello与hello.o反汇编结果的不同之处。
(1)hello的反汇编结果出现了程序中所用到的库函数,例如getchar以及sleep等函数,在链接时举行了符号解析与重定位。而hello.o中的反汇编结果中没有。


图 5-11 调用的库函数


  • 在hello中,jmp以及call等指令使用确定地址。而在hello.o中,地址是不确定的。这是由于在链接时完成了重定位,使得运行时的地址被确定。


图 5-12 寻址方式分析


  • 在hello中,新增了.init与.plt节的内容。


图 5-13 .init与.plt节的部分内容

综上所述,链接的过程就是将编译好的把编译好的目标文件和其他的一些目标文件和库链接在一起,形成终极的可执行文件的过程。此过程包含符号解析和重定位。
链接器在完成符号解析以后,就把代码中的每个符号引用和恰好一个符号界说关联起来。在重定位的过程中,链接器将所有链接文件中相同的节合并,链接器将运行时地址赋给新的聚合节,依次举行赋予界说的每个节、每个符号。然后修改hello.o中的代码节和数据节中符号的引用,使得它们指向精确的运行地址。
5.6 hello的执行流程

表 1 子程序名与地址

子程序名

地址

hello!_start

0x4010f0

hello!_init

0x401000

hello!main

0x401125

hello!puts@plt

0x401030

hello!printf@plt

0x401040

hello!getchar@plt

0x401050

hello!atoi@plt

0x401060

hello!exit@plt

0x401070

hello!sleep@plt

0x401080

hello! fini

0x4011c0

libc.so.6!nanosleep

0x7fa0290fb619

libc.so.6!printf

0x7f4bc4244770

libc.so.6!exit

0x7fa02903ad92


5.7 Hello的动态链接分析

通过查看elf文件,得到.got.plt节的地址是0x404000。在edb中找到对应的地址部分,查看调用dl_init前后的内容变化如下图所示。

图 5-14 调用dl_init前.got.plt节内容



图 5-15 调用dl_init后.got.plt节内容

通过对比可知,调用dl_init函数前后,内容发生了明显变化。这是由于动态链接接纳了延时绑定的计谋,将过程地址的绑定推迟到第一次调用该过程时。延迟绑定通过全局偏移量表(GOT)和过程链接表(PLT)的协同工作实现函数的动态链接,其中GOT中存放函数目标地址,PLT使用GOT中地址跳转到目标函数。
过程链接表PLT是一个数组,每个条目是16字节代码。PLT[0]是一个特殊条目,它会跳转到动态链接器中。每个被可执行程序调用的库函数都有它自己的PLT条目。
全局偏移量表GOT是一个数组,每个条目为8字节地址。和PLT连合使用时,GOT[0]和GOT[1]包含动态链接器在解析函数地址时会使用的信息,GOT[2]是动态链接器在1d-linux.so模块中的入口点。别的的每个条目对应于一个被调用的函数,其地址需要在运行时被解析。每个条目都有一个相匹配的PLT条目。在函数第一次被调用时,动态链接器会修改相应的GOT条目。之后对函数的调用就可以直接根据GOT中的值举行跳转。[2]


5.8 本章小结

本章首先介绍了链接的概念和作用,给出了Ubuntu下链接的下令。分析可执行目标文件hello的ELF格式,比力hello的反汇编代码与hello.o的反汇编代码的不同之处。此外,本章还分析了重定位过程、程序执行流程以及动态链接过程。

第6章 hello历程管理

6.1 历程的概念与作用

6.1.1历程的概念
历程的经典界说就是一个执行中程序的实例。历程的广义界说是指一个具有肯定独立功能的程序关于某个数据聚集的一次运行活动。它是操纵体系动态执行的根本单位,在传统的操纵体系中,历程既是根本的分配单位,也是根本的执行单位。每一个历程都有它自己的地址空间,一般情况下,包括文本地域、数据地域和堆栈。文本地域存储处置惩罚器执行的代码;数据地域存储变量和历程执行期间使用的动态分配的内存;堆栈地域存储着活动过程调用的指令和本地变量。[5]
6.1.2历程的作用
历程提供了一种假象,似乎我们的程序是体系中当前运行的唯一的程序一样。我们的程序似乎是独占地使用处置惩罚器和内存,处置惩罚器就似乎是无中断地一条接一条地执行我们程序中的指令。我们程序中的代码和数据似乎是体系内存中唯一的对象。[2]
6.2 简述壳Shell-bash的作用与处置惩罚流程

Shell是一个交互型的应用级程序,它执行一系列的读、求值步骤,解析下令行,并代表用户运行其他程序。
Shell的处置惩罚流程如下:
(1)终端历程读取用户由键盘输入的下令行。
(2)分析下令行字符串,获取下令行参数,并构造通报给execve的argv向量。
(3)检查第一个下令行参数是否是一个内置的shell下令。
(4)假如不是内置下令,调用fork函数创建子历程。
(5)在子历程中,用步骤2获取的参数,调用execve函数执行指定程序。
(6)假如用户没要求后台运行,即下令末了没有&号,否则shell使用waitpid函数期待作业终止后返回。
(7)假如用户要求后台运行,即下令末了有&号,则shell返回。

6.3 Hello的fork历程创建过程

当在下令行中输入./hello时,下令行判定该下令不是内置下令,于是shell将hello视为可执行目标文件。使用fork函数创建子历程。新创建的子历程得到与父历程假造地址空间相同但独立的一份副本,包括代码和数据段、堆、共享库以及用户栈。fork函数调用一次,返回两次,一次返回到父历程,一次返回到新创建的子历程。父历程与子历程可以大概并发运行。
6.4 Hello的execve过程

execve函数的作用是在当前历程的上下文中加载并运行程序。execve函数加载并运行可执行目标文件hello,且带参数列表argv和情况变量envp,只用当出现错误时,execve函数才会返回,调用成功则不会返回。execve调用启动加载器来执行hello程序。加载器首先删除子历程现有的假造内存段,并创建新的代码段、数据段、堆和栈段。代码和数据段被映射为hello文件的.txt和.data区,堆和栈被置空。末了加载器将PC指向hello程序的起始位置。
6.5 Hello的历程执行

上下文信息:上下文就是内核重新启动一个被抢占的历程所需的状态。它由一些对象的值组成,包括通用目标寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据布局。
历程时间片:一个历程执行它的控制流的一部分的每一时间段叫做时间片。
内核态:处置惩罚器通过设置某个控制寄存器的模式位,限制应用可以执行的指令以及它可以访问的地址空间范围。当设置了模式位时,历程就运行在内核模式中。一个运行在内核模式的历程可以执行指令集中的任何指令,并且可以访问体系中的任何内存位置。
用户态:没有设置模式位时,历程运行在用户态中。用户模式的历程不允许执行特权指令,也不允许直接引用地址空间中内核区的代码和数据。
历程调度:当内核选择一个新的历程运行时,内核调度了这个历程。在内核调度了一个新的历程运行时,它就抢占当前的历程,并使用上下文切换的机制将控制转移到新的历程。上下文切换的实现包括以下三个步骤:

  • 生存当前历程的上下文;
  • 规复某个先前被抢占的历程被生存的上下文;
  • 将控制偿通报给这个新规复的历程。[2]
当内核代表用户执行体系调用时,可能会发生上下文切换。当hello程序调用sleep函数时,内核可以选择举行上下文切换,历程由用户态转变为内核态,运行另外一个历程并切换为用户态。此外,当分配给历程的时间片用尽,内核也会执行上下文切换。
6.6 hello的异常与信号处置惩罚


6.6.1异常的种类及处置惩罚方式
下表给出了异常的四种种别。Hello在执行过程中以下几种异常都可能会发生。

表 2 异常的种别

种别

原因

异步/同步

返回行为

中断

来自I/O设备的信号

异步

总是返回到下一条指令

陷阱

有意的异常

同步

总是返回到下一条指令

故障

潜伏的可规复的错误

同步

可能返回到当前指令

终止

不可规复的错误

同步

不会返回

出现异常时,首先将控制通报给异常处置惩罚程序,运行异常处置惩罚程序。当异常处置惩罚程序执行完毕后,类型为中断或陷阱的异常会返回到下一条指令;类型为故障的异常,假如故障被成功解决,那么返回到当前指令,假如故障未能解决,则程序终止;类型为终止的异常,不会返回。
6.6.2正常运行的程序
在下令行中输入./hello 2021111902 滕浩 2。其中“2”表示每隔断两秒输出一次“Hello 2021111902 滕浩”信息。运行时状态如下图所示。


图 6-1 正常运行时程序输出

6.6.3键入Ctrl-C
键入Ctrl-C之后,内核向历程发送SIGINT信号,使得前台历程被终止。


图 6-2 键入Ctrl-C之后程序输出

6.6.4键入Ctrl-Z
键入Ctrl-Z之后,内核向前台历程发送SIGTSTP信号,使得前台历程被暂时挂起。


图 6-3 键入Ctrl-Z之后程序状态

由上图可知,使用ps下令,可以找到hello历程的pid,阐明hello历程并没有终止。使用jobs下令,看到hello程序的状态是停止状态。


图 6-4 使用fg下令继续运行历程

使用fg下令,使得被暂时挂起的hello程序再次在前台运行。
6.6.5乱按键盘
假如乱按键盘,假如不按回车,输入的字符串仅会被读入缓冲区;假如按回车,则会把之前读入的字符串作为shell下令执行。


图 6-5 乱按键盘情形结果展示

6.7本章小结

本章介绍了历程的概念与作用,分析了shell的执行流程。侧重论述了用fork函数创建新历程和execve函数加载hello程序的过程。此外,本章分析了hello的执行过程以及异常与信号处置惩罚。














7hello的存储管理

7.1 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动态存储分配管理

7.10本章小结


8hello的IO管理


8.1 Linux的IO设备管理方法

8.2 简述Unix IO接口及其函数

8.3 printf的实现分析

8.4 getchar的实现分析

8.5本章小结


结论

hello程序的一生包括以下阶段。

  • 预处置惩罚阶段,预处置惩罚器将hello.c转化为hello.i文件。
  • 编译阶段,编译器将hello.i转化为hello.s汇编语言文件。
  • 汇编阶段,汇编器将hello.s文件转化为hello.o可重定位的目标文件。
  • 链接阶段,链接器将hello.o与其他目标文件举行链接,得到可执行文件hello。
  • 程序加载阶段,在shell中输入./hello 2021111902 滕浩 3,shell就会通过fork函数创建子历程,调用execve函数加载并执行hello程序。
  • 程序执行阶段,调用execve函数时,execve 调用启动加载器,内核为hello历程映射假造内存。进入程序入口处开始载入物理内存,进入main函数执行相应代码。
  • 上下文切换:当执行到调用sleep函数的时候,会触发陷阱异常举行体系调用。这时历程进入内核态,发生上下文切换,将控制转移给其他历程。直到收到某种信号大概sleep的休眠时间结束,内核再将控制转移给hello历程。
  • 信号处置惩罚:用户通过键盘键入Ctrl-Z,将会导致SIGTSTP信号发送给当前历程,使得历程停止;用户通过键盘键入Ctrl-C,将会导致SIGINT信号发送给当前历程,使得历程终止。
  • 终止:子历程执行完成后,发送信号SIGCHLD给shell,shell回收当前历程,并且删除hello的所有数据布局。
通过计算机体系课程的学习,我熟悉到计算机体系的计划十分的伟大精妙,是计算机科学家的杰作,智慧的结晶。从最底层的计划出发,我明确了计算机中数据的表示,流水线的原理,存储器层次布局等等知识,明确怎样编写编译器友好,局部性精良的程序。学习这门课程也让我有很深的感触,一名优秀的程序员不仅要关注顶层的实现,同时还要明确底层的计划,做到软件与硬件的融会贯通,这样才气编写性能更加优秀的程序。
附件

hello.i 预处置惩罚后得到的修改后的源代码文件
hello.s 经过编译得到的汇编语言文件
hello.o 经过汇编得到的可重定位的目标文件
hello 经过链接得到的可执行的目标文件
hello.elf hello.o的elf格式文件
hello_out.elf hello的elf格式文件

参考文献

[1] laolitou_ping. C语言——预处置惩罚[DB/OL]. CSDN,2021.05.21. http://t.csdn.cn/dja6H.
[2]  兰德尔 E. 布莱恩特,大卫 R. 奥哈拉伦. 深入明确计算机体系(第3版)[M]. 北京:机器工业出书社. 2016.7.
[3] JackYang. 词法分析、语法分析、语义分析[DB/OL].博客园,2016-01-07.https://www.cnblogs.com/BlogNetSpace/p/5108845.html.
[4]  anton_99.呆板语言概念[DB/OL]. CSDN,2021-08-13 .http://t.csdn.cn/SQKU9
[5]  叫我小者呀. 历程的概念[DB/OL].CSDN, 2021-05-06.http://t.csdn.cn/ficbV.




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

道家人

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表