哈工大2024计算机系统大作业

哈尔滨工业大学

计算机系统

大作业

题     目  程序人生-Hello’s P2P 

专       业  未来技术学院            

学     号  2022113608             

班     级  WL021                 

学       生  俞捷                   

指 导 教 师  刘宏伟                   

计算机科学与技术学院

2024年5月

摘  要

本文以“HELLO”程序为例,概述了从源代码到可执行文件的构建过程,包括预处理、编译、汇编、链接等关键步骤。文中详细介绍了在Ubuntu系统下执行这些步骤的命令,并对HELLO程序的各个阶段结果进行了详细解析。此外,本文还探讨了进程管理、存储管理以及I/O管理等操作系统层面的概念,特别是在HELLO程序执行过程中涉及的进程创建、内存映射、缺页处理以及I/O操作等机制。这些分析不仅加深了对程序构建和执行过程的理解,也为操作系统和编译原理等领域的学习提供了有价值的参考。

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

目  录

第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。指将c语言编写的文件hello.cProgram)变为运行的进程(Process)。这个过程包括几个步骤:预处理,编译,汇编,链接,得到可执行文件,最后在shell中执行,创建子进程调用加载并执行hello程序。

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

1.2 环境与工具

硬件环境:

处理器:12th Gen Intel(R) Core(TM)i7-12700H

机带RAM16.0GB

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

软件环境:Windows11 64位,VMwareUbuntu 22.04.4

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

1.3 中间结果

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

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

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

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

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

1.4 本章小结

本章介绍了Hello的P2P与020的两个流程,包括流程的具体含义与实际步骤。随后说明了本实验的硬件环境、软件环境、开发与调试工具,以及实验生成的各个中间结果的文件名及其由来。

第2章 预处理

2.1 预处理的概念与作用

预处理概念:指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。主要进行代码文本的替换工作,处理以#开头的指令,删除程序中的注释和多余的空白字符。

预处理作用:允许程序员使用特定的预处理指令,来修改或控制源代码的编译过程。还可以用来过滤无效代码,通过条件编译指令将源程序中的某部分包含进来或排除在外,从而提高代码的可读性和可维护性。

2.2在Ubuntu下预处理的命令

预处理的命令:gcc -E hello.c -o hello.i

2.3 Hello的预处理结果解析

打开预处理后文件hello.i,发现预处理后程序被扩展为三千多行,原本hello.c的指令内容被保留在最末端,注释部分消失的同时,增加大量额外内容。

这部分额外内容源于头文件 <stdio.h> <unistd.h> <stdlib.h> 的展开。三个头文件分别对应标准输入输出库的头文件、POSIX系统服务API的头文件、标准库的头文件。在C语言程序中使用#include预处理指令来包含头文件,预处理器会在编译的早期阶段将这些头文件的内容插入到源文件中。

如:当预处理器遇到#include<stdio.h>时,它会在系统的头文件路径下查找stdio.h文件,然后把stdio.h文件中的内容复制到源文件中。当你在main()函数中调用stdio.h库中函数时,编译器已经知道了这些函数的声明,可以正确地编译代码。

2.4 本章小结

本章讲述了预处理的含义即作用,以及如何在Linux环境中对源文件hello.c进行预处理操作。对预处理的结果进行了分析,可以得出,预处理后文件在保留原代码的同时,去除注释,插入了来自头文件的内容,还有一些宏和常量的定义,一些行号信息和条件编译指令。

第3章 编译

3.1 编译的概念与作用

编译的概念:编译是将源代码(源程序)转换为汇编语言的过程。

编译的作用:编译的主要作用是将人类可读的高级语言源代码转换为汇编语言。提高编程效率和可移植性。分为词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成等步骤。

3.2 在Ubuntu下编译的命令

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

3.3 Hello的编译结果解析

3.3.1 main函数前的字段

展示了节的名称

.file               源文件

.text               代码节

.section   .rodata    只读数据段

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

.string              一个字符串

.globl              全局变量

.type               一个符号的类型

3.3.2 数据部分

(1)字符串

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

程序分别将%rax设置为两个字符串起始位置

(2)参数argc

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

程序将寄存器%edi地址压入栈中

程序将对应地址上数值与5比较大小

这与源文件中对argc的操作对应

(3)数组

hello.c中数组是main函数中的第二个参数,即char*argv[],数组的每个元素都是一个指向字符类型的指针。数组起始地址存放在寄存器%rsi中,被多次调用作为参数传到printf中。

数组起始地址压入栈中

分别调用数组中的值

 

(4)局部变量

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

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

3.3.3全局函数

hello.c中只声明了一个全局函数int main(int argc,.char*argv[])

通过汇编代码可知。

3.3.4赋值操作

hel1o.c中的赋值操作有for循环开头的i=0

在汇编代码上,用mov指令实现

3.3.5算术操作

hello.c中的算术操作为for循环的每次循环结束后i++

在汇编代码则使用指令add实现

3.3.6关系操作

hello.c中有两个关系操作:

  1. 条件判断语句if(argc!=5)

 

  1. 在for循环每次循环结束要判断一次i<10

3.3.7控制转移指令

在本程序中存在两个控制转移:(分别与3.3.6关系操作中(1)(2)对应,图见3.3.6)

(1) 判断argc是否为5,如果不为5,则执行if语句,否则执行其他语句

(2) 在for循环每次结束判断一次i<10,进而选择继续循环还是退出

3.3.8函数操作

(1)main函数

参数传递:该函数的参数为int argc,,char*argv[]

函数调用:通过call内部指令调用语句进行函数调用, main函数里调用了printf、exit、sleep、atoi等函数。

局部变量:使用了局部变量i用于for循环。

(2)printf函数

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

函数调用:调用两次。第一次将寄存器%rax设置为待传递字符串"用法:Hello学号姓名 秒数!\n"的起始地址;第二次设置为“Hello %s  %s\n”的起始地址。

(3)exit函数

参数传递与函数调用:

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

(4)atoi函数

参数传递与函数调用:

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

(5) sleep函数

将转换完成的秒数从%eax传递到%edi中用作参数传递,使用call调用。

(6)getchar函数

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

3.3.9类型转换

atoi函数将字符串转换为sleep函数需要的整型参数.

3.4 本章小结

本章介绍了编译的概念与作用,并演示了Ubuntu下对hello.c的编译,并对编译所得文件hello.s进行解析,从数据处理,函数调用,赋值、算术、关系等运算以及控制跳转和类型转换等方面,与源代码比较,分析生成的hello.s文件中的汇编代码。

第4章 汇编

4.1 汇编的概念与作用

汇编的概念:将汇编语言源程序转换为机器指令序列,即将.s汇编程序翻译成机器语言指令,并把这些指令打包成可重定位目标程序的格式,然后将结果保存在.o目标文件中。这个文件是一个二进制文件,包含程序的指令编码

汇编的作用:汇编就是将高级语言转化为机器可直接识别执行的代码文件的过程,将.s 汇编程序翻译成机器语言指令,打包成可重定位目标程序的格式。汇编使开发者可以直接使用汇编语言编写程序,进而带来一系列优势及实现不同的操作

4.2 在Ubuntu下汇编的命令

汇编的命令为:gcc -m64 -no-pie -fno-PIC -c hello.s -o hello.o

4.3 可重定位目标elf格式

使用readelf -a hello.o > hello.elf 指令获得 hello.o 文件的 ELF 格式

ELF格式文件内容分为ELF头,节头,重定位节,符号表

  1. ELF头

ELF头ELF格式的文件首部,以一个16字节的序列开始,这个序列用于标识文件格式并指明生成该文件的系统的字的大小(32位或64位)和字节顺序(大端或小端)。ELF头的剩余部分包含了帮助链接器(linker)解析和解释目标文件所需的关键信息。包括ELF头的大小、目标文件的类型、机器类型、节头部表的文件偏移,以及节头部表中条目的大小和数量。每个节在节头部表中都有一个固定大小的条目(entry),该条目包含了节的名称、大小、类型、标志等信息

  1. 节头

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

  1. 重定位节

.rel.text 节是一个重定位表的子集,它专门列出了 .text 节(代码段)中需要进行重定位的位置。在链接过程中,链接器会遍历这些重定位条目,并根据目标文件或库中的实际地址来更新这些位置的值。在目标文件中,编译器会为每个需要重定位的指令生成一个重定位条目,使得链接器能够找到正确的地址并更新指令中的值。

当链接器将多个目标文件链接成一个可执行文件或共享库时,它会根据符号解析(Symbol Resolution)阶段得到的信息,来更新 .rel.text 节中列出的所有需要重定位的位置。

  1. 符号表

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

4.4 Hello.o的结果解析

使用指令objdump -d -r hello.o >hello.asm获得hello.o的反汇编文件

与hello.s对照分析

(1)每一条指令增加了一个十六进制的表示,即该指令的机器语言,如图,左为hello.asm,右为hello.s

(2)反汇编文件中的所有操作数都改为十六进制。如图,左为hello.asm,右为hello.s,立即数由hello.s中32变为0x20

(3)反汇编的跳转指令的跳转位置被表示为主函数+段内偏移量,而不再是段名称。如图,左为hello.asm,右为hello.s

(4)函数调用

反汇编文件中对函数的调用与重定位条目相对应。在可重定位文件中call后面不再是函数名称,而是一条位置信息。

4.5 本章小结

本章介绍了汇编的概念及作用,并在Ubuntu下执行汇编命令生成hello.o,以及生成可重定位目标elf格式。对ELF格式文件中各节内容进行说明,结合hello.s与hello.o文件进行对比分析。表现汇编语言到机器语言中发生的改变。

5章 链接

5.1 链接的概念与作用

链接的概念:链接是从目标文件生成可执行文件的关键步骤,通过读取目标文件,并根据符号表中的信息将它们合并成一个可执行文件,即将各个目标文件合并、分配地址、解析符号引用和重定位代码和数据等操作,确保程序能够正确地加载和执行。

链接的作用:链接是将各种代码和数据片段收集并组合成为一个单一的可执行文件的过程。在这个过程中,链接器会处理各个目标文件之间的引用和依赖关系。为程序中的每个符号(如函数名、变量名等)分配一个唯一的内存地址。解析目标文件之间的符号引用。在链接过程中,链接器会修改目标文件中的代码和数据,以便它们能够正确地指向其他目标文件中的代码和数据。

5.2 在Ubuntu下链接的命令

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的格式

(1)ELF头

hello1.elf中的ELF头与hello.elf中的ELF头包含的信息种类基本相同。与hello.elf相比较,hello1.elf中的基本信息未发生改变,而类型发生改变,程序头大小和节头数量增加,并且获得了入口地址。

(2)节头

依次对应,记录各节名称、类型、地址、偏移量、大小、全体大小、旗标、链接、信息、对齐。链接器链接时,各个文件的相同段会合并成一个大段,并根据这个大段的大小以及偏移量重新设置各个符号的地址。

(3)程序头

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

(4)动态节

(5)符号表

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

5.4 hello的虚拟地址空间

在edb中打开hello,在Data Dump窗口可以看到hello加载到虚拟地址的相关信息

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

如图中,.plt节信息与.plt.sec节信息分别对应地址401020与401090,节头部表地址与edb中虚拟地址对应

5.5 链接的重定位过程分析

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

与由hello.o得到的.asm文件对比,有以下不同

(1)链接后反汇编文件中,在main之前部分新增函数代码,如下图中.plt,puts@plt,printf@plt,getchar@plt等函数。这是动态链接器将共享库中hello.c用到的函数插入到可执行文件中的结果

(2)函数调用指令call之后的内容改变,直接指向对应函数的代码段位置,如图,调用puts与exit的call指令不同,左为hello1.asm,右为hello.asm

(3)跳转指令也发生变化,跳转的内容变为与main的相对距离以及计算得出的实际地址,如图左为hello1.asm,右为hello.asm

由(2)(3)可见在链接过程中,链接器解析了重定位条目,重新计算得出函数调用,跳转的实际地址

重定位过程分为两步

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

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

重定位时地址计算方式如图(来自课本)

5.6 hello的执行流程

使用edb执行hello

观察调用函数call指令,可得出函数运行过程的主要函数

主要函数(按顺序):

开始到执行main: _start、_libe_start_main、main

执行过程:_printf、_puts、_exit、_sleep、_atoi、_getchar

子程序名与地址

程序名               程序地址

_start                0x4010f0

_libc_start_main       0x2f12271d

main                0x401125

_puts                0x401090

_printf               0x4010a0

_sleep               0x4010e0

_getchar             0x4010b0

_atoi                0x4010c0

_exit                0x4010d0

5.7 Hello的动态链接分析

   动态链接使用延迟绑定的机制,推迟函数地址的绑定过程,直到该函数第一次被调用时。该过程主要依赖于两个关键的数据结构:全局偏移表(GOT)和过程链接表(PLT)。其中.got.plt是专门为PLT准备的GOT的一部分。

PLT条目中保存了跳转到.got.plt中相应条目的指令。.got.plt中的这个条目初始时并不包含函数的实际地址,而是指向PLT中的另一个条目。

当外部函数首次被调用时,程序跳转到PLT条目,然后执行到.got.plt中的相应条目。因为这是首次调用,.got.plt中的条目并不包含函数的实际地址,而是指向解析器存根。解析器存根负责调用动态链接器,来查找并解析该函数的实际地址,并将找到的实际地址写回到.got.plt中的相应条目,并跳转到该地址执行函数。

在hello的ELF文件可查看到.got.plt起始位置

使用edb查看虚拟地址对应位置

程序调用函数后,可发现对应虚拟地址的值发生改变

5.8 本章小结

本章阐述了链接的基本概念和作用,并使用命令链接生成hello可执行文件,观察了hello文件ELF格式下的内容,使用edb观察了hello文件的虚拟地址空间使用情况,最后分析了程序重定位过程、执行过程以及动态链接。

6章 hello进程管理

6.1 进程的概念与作用

进程的概念:进程是计算机中已分配内存,并处于可执行状态的一个程序。它包含程序计数器、寄存器的当前值和内存状态的信息,是线程容器。进程拥有独立的内存空间和系统资源文件,在操作系统中可以独立调度。一个进程可以包括多个线程,线程是进程内的执行单元,负责在进程地址空间中执行代码。多个线程可以并发执行,共享进程的资源。

进程的作用:多个进程可以同时运行在计算机上,使得计算机可以同时处理多个任务,提高了系统的吞吐量和效率。操作系统为每个进程分配独立的内存空间和其他系统资源,使得不同进程之间可以相互隔离,互不影响

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

Shell-bash的作用:shell是一个交互型的应用级程序,它代表用户运行其他程序。执行一系列的来自用户命令行的任务,并解析命令行并代表用户运行程序。

Shell-bash的处理流程:通过终端进行交互,读入指令,对输入指令进行解析,若为内置指令则立即执行,否则调用fork创建子进程,在子进程上下文执行对应程序。再判断指令为前台还是后台程序,前台则等待至完成,后台则无需等待。在进程完成任务后再回收僵死进程。

6.3 Hello的fork进程创建过程

用户在命令栏输入命令,shell判断指令类型,当不为内置指令时,父进程调用fork创建一个子进程。子进程与父进程拥有相同但独立的地址空间,共享文件,并发执行。二者有不同的PID。子进程中fork返回值为0,在父进程中返回子进程的PID,以此区分二者。

6.4 Hello的execve过程

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

6.5 Hello的进程执行

进程执行时,提供给应用程序两个关键抽象:(1)一个独立的逻辑控制流,它提供一个假象,好像我们的进程独占地使用处理器;(2)一个私有的地址空问,它提供一个假象,好像我们的程序独占地使用CPU内存。

(1)逻辑控制流。进程给每个程序提供一种假象,好像程序在独占使用处理器。如果想用调试器单步执行程序,我们会看到一系列的程序计数器(PC)的值,这些值唯一地对应于包含在程序的可执行目标文件中的指令,或是包含在运行时动态链接到程序的共享对象中的指令。这个PC值的序列叫做逻辑控制流,或者简称为逻辑流。进程是轮流使用处理器的,每个进程执行它的流的一部分,然后被抢占,轮到其他程序。

(2)并发流与时间片。一个逻辑流的执行在时间上与另一个重叠称为并发流。多个流并发的执行的一般现象称为并发,一个进程执行它的控制流的一部分的每一时间段叫做时间片。因此,多任务也叫做时间分片。

(3)私有地址空间。进程为每个程序提供一种假象,好像它独占使用系统地址空间。进程为每个程序提供它自己的私有地址空间。和这个空间中某个地址相关联的那个内存字节不能被其他进程读写。

(4)用户模式和内核模式。处理器需要一种机制,限制一个应用可以执行的指令以及它可以访问的地址空间。处理器通常使用某个控制寄存器中的一个模式位来提供这种功能。当设置了模式位时,进程就运行在内核模式里。一个运行在内核模式的进程可以执行指令集中的所有指令且可以访问系统中的任何内存位置。没有设置模式位时,进程就运行在用户模式中。用户模式中的进程不允许执行特权指令,也不能直接引用地址空间中内核区内的代码和数据。

(5)上下文信息。操作系统内核使用一种上下文切换的较高层形式的异常控制流来实现多任务。内核为每个进程维持一个上下文。上下文就是内核重新启动一个被抢占的进程所需要的状态,它由通用寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构等对象的值构成。在进程执行的某些时刻,内核可以决定抢占进程,并重新开始一个先前被抢占的进程,这个决策称为调度。在内核调度一个新进程运行后,使用上下文切换的机制来将控制转移到新进程。1)保留当前进程上下文2)恢复某个先前被抢占的进程被保存的上下文3)将控制传递给这个新恢复的进程

6.6 hello的异常与信号处理

hello执行过程中会出现异常有中断(来自I/O设备信号、异步、总返回到下一条指令)、陷阱(有意的异常、同步、总返回到下一条指令)、故障(潜在的可恢复错误、同步、可能返回到当前指令)、终止(不可恢复的错误、同步、不会返回)四种异常。对应处理如下图

产生的信号种类即其处理如下:

程序运行过程:

(1)正常运行

程序正常运行,打印八次信息后停止并等待下一条指令,在输入回车后程序终止退出,进程被回收

(2)运行时按回车

程序仍然正常运行,在输出内容中多了数次来自键盘输入的换行。此时回车命令缓存到stdin未处理。直到程序运行结束后才开始读入stdin中字符,并将一个换行字符‘\n’作为命令结尾,即‘\n’前部分为一次输入。此时shell读取到多次空输入。

(3)运行时不停乱按

原理和按回车相同,命令缓存到stdin未处理。直到程序运行结束后才开始读入stdin中字符,并将一个换行字符‘\n’作为命令结尾,即‘\n’前部分为一次输入。并将最后一次’\n’后内容视为一次未输入完成的命令,保留在输入栏中。

(4)运行时按Ctrl-Z

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

1)运行ps

查看进程列表

2)运行jobs

查看当前作业表,可看见hello命令被暂停(挂起)

3)运行pstree

所有进程以树状图显示

4)运行fg

将hello调到前台再次执行,先输出hello命令行命令,再从挂起处继续执行

5)运行kill

杀死指定进程

(5)运行时按Ctrl-C

运行时输入Ctrl + C,Shell收到SIGINT信号,结束并回收hello进程。

6.7本章小结

本章阐述了进程的概念与作用,对于shell的作用与处理流程以及fork、execve进行说明。并总结了异常的分类及对应处理方式,信号的分类。并结合hello运行示例展示了进程的各种异常处理。

7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:逻辑地址,也被称为相对地址,是程序指令中给出的地址。这些地址是程序视角中的地址,它们需要经过某种形式的转换或变换才能映射到实际的物理内存。在分段内存管理中,逻辑地址由两部分组成:一个段标识符(通常是一个段选择子)和一个指定段内相对位置的偏移量。对于程序hello来说,它产生的代码和数据引用通常使用逻辑地址。

线性地址:线性地址是逻辑地址到物理地址转换过程中的一个中间步骤。在分段式内存管理系统中,逻辑地址的段标识符和偏移量会被转换为线性地址。这通常涉及将段标识符映射到一个基地址(段基址),然后将这个基地址与偏移量相加来得到线性地址。线性地址空间是一个连续的、单一的地址空间,其中包含了程序可以访问的所有内存。

虚拟地址:虚拟地址是程序访问存储器时所使用的逻辑地址。它与逻辑地址相似,但通常与分页式内存管理相关联。在分页式内存管理中,虚拟地址空间被划分为固定大小的页面,每个页面可以映射到物理内存中的任何位置(或者可能是磁盘上的交换空间)。虚拟地址是程序视角下的地址,它们需要经过地址翻译(通常是使用页表)才能转换为物理地址。虚拟地址空间的大小通常远大于实际的物理内存大小,这使得程序能够使用比实际物理内存更多的内存空间。

对于程序hello来说,它使用的所有内存引用(如变量和函数地址)都是虚拟地址。这些虚拟地址在程序执行期间会被操作系统和硬件协作转换为物理地址。

物理地址:物理地址是实际内存中的地址,用于指定存储器中的特定位置。在存储器中以字节为单位存储信息,每个字节都有一个唯一的物理地址。物理地址是硬件级别的地址,用于CPU和内存之间的通信。当CPU需要读取或写入某个内存位置时,它会使用物理地址来指定这个位置。

在程序hello执行过程中,操作系统和硬件会协作将程序使用的虚拟地址转换为物理地址。这样,程序就可以透明地访问比实际物理内存更大的内存空间,同时操作系统也可以管理物理内存的使用,包括将不常用的页面交换到磁盘上,以及将新的页面加载到内存中。

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

逻辑地址由两部分组成:一个段标识符(段选择符)和一个指定段内相对位置的偏移量。段标识符是一个16位的字段,用于在全局描述符表(GDT)或局部描述符表(LDT)中查找对应的段描述符。

段描述符表GDT和LDT是用于存储段描述符的表。段描述符包含了段的基地址、长度和其他属性。GDT通常用于操作系统和关键系统程序,而LDT可以用于用户空间程序。

从逻辑地址到线性地址转换时,使用段选择符的index字段(它指定了GDT或LDT中的段描述符的索引)和描述符的大小(通常为8字节)来计算段描述符在GDT或LDT中的地址。再从计算出的地址读取段描述符。将逻辑地址的偏移量与段描述符中的base字段值相加,得到的结果就是线性地址。

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

页式管理是一种内存空间存储管理的技术,它将各进程的虚拟空间划分成若干个长度相等的页(page),并将内存空间也按页的大小划分成片或页面(page frame)。页式管理通过建立页表,将页式虚拟地址与内存地址建立一一对应的关系。

在页式管理中,线性地址(也称为虚拟地址)被进一步划分为页号和页内偏移。页号用于在页表中找到对应的页表项(PTE),而页内偏移则用于在物理页内定位具体的字节。

线性地址到物理地址的变换时,先从线性地址中提取出页号和页内偏移。使用页号在页表(Page Table)中查找对应的页表项(PTE)。从找到的页表项(PTE)中获取物理页框号,物理页框号表示物理内存中对应页面的起始地址。将物理页框号与页内偏移组合起来,形成物理地址。物理地址是实际访问内存时所使用的地址。

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

TLB本质上是一个高速缓存,用于存储虚拟地址(VA)到物理地址(PA)的映射关系。当CPU尝试访问某个虚拟地址时,它会首先检查TLB中是否有对应的映射关系。如果有(TLB命中),则直接从TLB中获取物理地址,避免了对页表的直接访问,从而提高了地址转换的速度。

在64位系统中,页表通常是分级的,常见的配置是四级页表。这四级页表分别是PGD、PUD、PMD和PTE。每一级页表都包含指向下一级页表的指针或物理地址的映射信息。

在VA到PA变换时,虚拟地址被分解为多个部分,每个部分对应于页表中的一级。CPU首先检查TLB中是否有与虚拟地址对应的物理地址映射。如果TLB命中,则直接返回物理地址。如果TLB未命中,则进行下一步的页表查找。

在页表查找时,CPU从PGD页表开始,根据虚拟地址中的相应部分找到PUD页表的地址。然后,从PUD页表中找到PMD页表的地址。接着,从PMD页表中找到PTE页表的地址。最后,在PTE页表中找到与虚拟地址对应的物理地址映射,同时会将找到的虚拟地址和物理地址的映射关系存储到TLB中,以便后续的快速访问。最后使用从页表或TLB中获取的物理地址访问物理内存。

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

CPU缓存(Cache Memory)位于CPU与内存之间,用于临时存储CPU即将访问的数据。缓存的存在基于程序局部性原理,即最近被CPU访问的数据或附近的数据在短期内很可能再次被访问。缓存分为多个层次越高级别的缓存,容量相对较大,但访问速度稍慢。

当CPU需要访问某个数据时,它会首先检查各级缓存中是否包含所需数据。从L1 Cache开始检查,如果未命中则继续检查L2 Cache,最后检查L3 Cache。如果数据在缓存中(任何一级),则直接从缓存中读取数据,无需访问物理内存。如果数据不在缓存中,则需要从物理内存中读取数据。此时,数据通常会被同时加载到L3 Cache(如果空间允许)以及下一级缓存(L2 Cache或L1 Cache)中,以便未来再次访问时能够快速读取。

7.6 hello进程fork时的内存映射

在fork调用之前,父进程(例如,一个shell进程)已经加载了hello程序的可执行文件到其虚拟地址空间中,并建立了相应的内存映射。当父进程调用fork时,内核会创建一个新的子进程。子进程是父进程的一个副本,它继承了父进程的内存布局。虽然子进程继承了父进程的内存布局,但这些内存页在物理上并没有被真正复制。相反,它们被标记为只读,并共享相同的物理页面。

对于代码段、数据段和堆在父进程和子进程中是共享的,但标记为只读。对于栈,fork会复制父进程的栈,但栈上的具体数据通常只存在于父进程或子进程的上下文中。除此之外父进程和子进程都有自己的虚拟地址空间,但它们共享相同的物理页面

7.7 hello进程execve时的内存映射

在execve调用之前,进程已经具有一个包含代码、数据、栈等的虚拟内存映射。当execve被调用时,系统首先会销毁这个旧的内存映射,释放与之关联的所有物理页面。系统根据提供的文件名找到可执行文件,并读取其头部信息以获取程序的布局和依赖。程序的可执行代码、数据等被加载到新的虚拟地址空间中。加载器根据可执行文件的头部信息创建新的内存映射。

当所有的内存映射和页表都更新完毕后,系统开始执行新加载的程序。此时,进程的虚拟地址空间已经完全被新的程序所占据,旧的程序已经不存在了。

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

当CPU尝试访问一个不在物理内存中的页面时,会触发缺页故障。这通常发生在虚拟内存系统中,当程序的某个部分被映射到虚拟地址空间,但尚未加载到物理内存中时。

操作系统首先检查访问的虚拟地址是否有效。如果地址无效或访问权限不匹配(如试图写入只读页面),则操作系统会向进程发出一个信号或终止该进程。

如果地址有效,操作系统会检查是否有空闲的物理内存页框(frame)可用于加载所需的页面。如果有空闲页框,则直接使用该页框加载页面。如果没有空闲页框,操作系统需要使用对应的页面置换算法选择一个页面进行替换。如果被选中的页面是自从上次写入磁盘后已被修改的,则需要将该页面写回磁盘。

随后从磁盘上的虚拟内存文件(或交换空间)中加载所需的页面到空闲页框中。更新页表以反映新页面的物理地址和状态。当页面被加载并准备好,操作系统会恢复产生缺页中断的指令的执行。程序计数器(PC)被重置为中断发生时的值,以便从断点处继续执行。

7.9动态存储分配管理

动态内存管理是指在程序运行时根据需要分配和释放内存空间的过程。这种内存管理方式允许程序在运行时根据实际需求动态地分配和释放内存,从而更加灵活地管理内存资源。

在c语言中,使用 malloc 或 calloc 函数从堆(heap)中分配内存。malloc 分配指定字节数的内存,而 calloc 会额外初始化分配的内存为零。在分配了内存之后,可以正常使用。使用完内存后,必须调用 free 函数来释放它,以防止内存泄漏。

在某些情况下,printf的实现可能会使用动态内存管理来处理一些内部数据结构或缓冲区。

7.10本章小结

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

结论

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、运行:在shel1中输入./hello 2022113608 俞捷 19959624706 1运行程序。

6、创建进程:shell读取指令,调用fork函数创建一个新的子进程。

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

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

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

10、信号处理:当程序在运行时,从命令栏输入信号,shell会对这些信号做出相应反应。

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

感悟:

通过本次实验,我深切了解到计算机系统对程序的复杂处理过程,对于处理中使用的精妙方法,处理时要考虑的复杂情况与因素,让我为前人的智慧感叹,更为计算机系统本身的构成而赞叹。

附件

hello.c        源程序

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

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

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

hello.elf       由hello.o得到的ELF格式信息

hello1.elf      由hello可执行文件得到的ELF格式信息

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

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

hello          可执行文件

参考文献

[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.

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值