程序人生-Hello’s P2P

news2024/11/23 4:54:45

摘要

        本文讨论了与编程和软件开发相关的几个关键概念和过程。首先介绍了链接的概念和作用,它是将代码和数据片段组合成单一文件的过程,使得分离编译成为可能,从而可以更好地管理和修改模块。接下来探讨了进程的概念和作用,进程是正在执行的程序的实例,包括程序的代码、数据和运行状态。然后讨论了Shell的作用和处理流程,它是用户与操作系统之间的交互界面,并负责进程的创建、程序加载、前后台控制和信号处理等任务。本文还涉及了执行新程序的过程,包括execve函数的调用和进程上下文的转换。最后,介绍了逻辑地址、线性地址、虚拟地址和物理地址的概念,以及多级页表和缓存对虚拟地址到物理地址的转换过程。这些概念和过程在软件开发和计算机系统的运行中起着重要的作用,帮助理解和管理程序的执行和内存的使用。

关键词:链接、进程、Shell、执行、缓存、模块、代码、数据、程序加载

                   

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

目录

摘要

第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 本章小结


第1章 概述

1.1 Hello简介

根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。

1.1.1 P2P

程序的生命周期始于一个高级C语言程序。为了在系统上运行hello.c程序,需要将每条C语句转化为一系列低级机器语言指令。首先,hello.c源程序经过预处理器(cpp)转化为修改后的hello.i源程序文本。预处理器执行诸如宏展开、条件编译等操作,生成一个经过处理的源代码文件。

接下来,编译器(cc1)将hello.i编译为汇编程序文本hello.s。编译器执行词法分析、语法分析和语义分析等步骤,将高级语言代码转换为汇编语言代码。

然后,汇编器(as)将hello.s转换为可重定位目标程序二进制文件hello.o。汇编器将汇编语言代码转化为机器语言指令,并生成与机器硬件体系结构兼容的目标文件。

同时,链接器(ld)将hello.o与共享链接库(如printf.o)一起进行链接,生成最终的可执行目标程序二进制文件hello。链接器的任务包括解析符号引用、符号重定位和地址分配等,以将不同目标文件和库文件整合在一起,形成可执行文件。

在Shell中输入./hello后,程序通过fork()函数创建新的进程,并使用execve加载程序。fork()函数用于复制当前进程,创建一个新的子进程。子进程在execve调用中加载可执行文件,并取代当前进程的内存空间,从而执行相应的程序。这个过程涉及到虚拟内存映射(mmap),其中内核将可执行文件的内容映射到进程的虚拟地址空间。

通过这一过程,程序从程序(program)转变为进程(process),实现了从P2P(程序到进程)的转换。进程是正在运行的程序的实例,它具有独立的内存空间和系统资源,并由操作系统进行调度和管理。

1.1.2 020

在Shell中,通过调用fork()函数创建子进程,然后使用execve进行虚拟内存映射,分配物理内存,并在代码段中执行程序,实现了包括打印printf等操作。在这个过程中,内存管理器和CPU利用L1、L2和L3高速缓存以及TLB(Translation Lookaside Buffer)和多级页表来提高数据访问速度。

当调用fork()函数时,操作系统会为子进程创建一个独立的虚拟地址空间,但实际的物理内存并没有复制,而是采用了写时复制(copy-on-write)的技术。这意味着在子进程修改内存内容之前,它们与父进程共享相同的物理内存页面。

通过execve函数,子进程的虚拟内存空间被映射到物理内存中,并加载可执行文件的代码段到内存中的相应位置。当子进程执行程序时,CPU从内存中获取指令并逐条执行,包括执行printf等打印操作。

为了提高数据访问速度,现代计算机系统通常采用多级缓存和页表结构。L1、L2和L3高速缓存是位于CPU内部的快速存储器,用于暂时存储频繁访问的数据和指令。TLB是一个快速查找表,用于将虚拟内存地址转换为物理内存地址,以减少访问页表的次数。多级页表是一种层次化的数据结构,用于将虚拟内存地址映射到物理内存地址。

当程序运行完成后,Shell会回收子进程,内核最后删除相关数据结构,从系统中清除与子进程相关的资源。这样,整个过程就实现了进程的创建、执行和终止,即所提到的“020”的过程。

1.2 环境与工具

列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。

1.2.1 硬件环境

CPU:Intel Core i7

1.2.2 软件环境

Windows11 专业版;VMware 17;Ubuntu 20.04

1.2.3 调试工具

Visual Studio2017;codeblocks 64位;gcc

1.3 中间结果

列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。

hello.c:源程序

hello.i:预处理后文件

hello.s:编译后的汇编文件

hello.o:可重定位目标程序

hello.out:可执行目标程序

hello_o.elf:hello.o的ELF格式

hello_o.txt:hello.o的反汇编代码

hello.elf:hello的ELF格式

hello.txt:hello的反汇编代码

1.4 本章小结

1.本章主要介绍了hello程序的p2p过程和020过程

2.介绍了硬件环境、软件环境和开发工具

3.描述了中间文件的名字和作用

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

预处理器(cpp)是编译过程中的第一个阶段,根据以字符#开头的命令,对原始的C程序进行修改和处理。它主要执行以下几个功能:

宏定义和常量标识符替换:预处理器通过使用#define指令,将宏定义替换为相应的代码片段。宏定义可以是函数、常量或表达式,通过在程序中使用宏名称,预处理器会将其展开为对应的代码。

文件包含(#include):预处理器使用#include指令来读取其他文件的内容,并将其插入到当前程序文本中。典型的例子是使用#include<stdio.h>来包含系统头文件stdio.h,这样程序可以使用stdio.h中定义的函数和变量。

预处理指令:预处理器还支持其他预处理指令,如条件编译指令(如#ifdef、#ifndef、#if、#else、#endif),用于根据条件选择性地包含或排除一些代码片段。

注释和空白字符删除:预处理器会删除程序中的注释(以//或/* */形式出现)和多余的空白字符,以减小程序的大小和简化后续编译阶段的处理。

通过这些处理步骤,预处理器可以修改原始的C程序,生成一个经过处理的中间文件(通常以.i作为文件扩展名),这个文件会被编译器进一步处理和编译。预处理器的主要目的是为了准备好进行后续编译所需的代码和环境。

2.2 在Ubuntu下预处理的命令

 

2.3 Hello的预处理结果解析

 

 在经过预处理后,代码的长度大大的增加,达到了3060

当我们在寻找main函数时,我们可以在预处理后的.i文件中进行查找。该文件是经过预处理器处理后的中间文件。

.i文件中,你会发现两个函数的代码几乎相同,唯一的区别是.i文件删除了#include部分的内容和注释部分的内容。这与之前分析预处理器的作用是一致的,预处理器会将#include指令所引用的内容直接插入程序文本中,并删除注释和多余的空白字符。

继续查找,你会发现.i文件中引用了stdio.h头文件。这是因为在原始的C程序中,通过#include<stdio.h>指令包含了stdio.h头文件,其中定义了输入输出相关的函数和变量。在预处理阶段,预处理器会将stdio.h的内容插入程序文本中,从而使得程序可以使用stdio.h中定义的函数和变量。

通过预处理器的处理,我们可以看到预处理阶段的一些效果:宏替换、文件包含以及注释和空白字符的删除。这些步骤为后续的编译阶段提供了经过处理和准备好的代码和环境,使得程序可以顺利进行编译和执行。

2.4 本章小结

1.预处理可以初步翻译源文件,进行宏替换,文件包含,删除注释等操作

2.熟悉了Ubuntu下预处理的命令

3.解析了hello.c预处理的结果,并和前面的理解进行对比和验证

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译是软件开发中的一个重要阶段,它将源代码翻译成机器能够执行的形式。在这里,我们来讨论编译器(如cc1)将文本文件hello.i翻译成文本文件hello.s的过程。

编译器的主要任务是将高级语言代码转换为等价的中间代码表示或汇编代码,使得计算机能够理解和执行这些指令。

3.2 在Ubuntu下编译的命令

3.3 Hello的编译结果解析

字符串常量:

对应

printf("用法: Hello 学号 姓名 秒数!\n");

printf("Hello %s %s\n",argv[1],argv[2]);

关系操作:

 对应:if(argc!=4)

算数操作:

  和

对应:for(i=0;i<8;i++)

局部变量:

将argc存入-20(%rbp),将argv[]存入-32(%rbp)

数组、指针:

再次可见,将argv[]存入-32(%rbp),后续对应基地址+8、+16,sleep对应+24

赋值:

int i存入-4(%rbp),i=0,movl对应“双字”mov

类型转换:

call atoi@PLT;

对应:atoi(argv[3])

函数操作:

call puts@PLT;

call exit@PLT;

call printf@PLT;

call atoi@PLT;

call sleep@PLT;

call getchar@PLT;

参数传递:

在其中main函数传递参数为int argc,char *argv[],printf函数传递参数为argv[1], argv[2],exit函数传递参数为1,sleep函数传递参数为atoi(argv[3]),getchar函数无传递参数。

函数返回:

main函数通过ret返回了0,其他的函数的返回值保存在%rax寄存器中。

3.4 本章小结

1.了解并介绍了编译的概念和作用

2.熟悉了在Ubuntu下编译的指令

3.S文件中找到了C语言的数据与操作的编译表示,进一步熟悉了汇编指令

(第32分)

第4章 汇编

4.1 汇编的概念与作用

汇编是将汇编语言代码转换为机器语言指令的过程。在这里,我们来探讨汇编器(如as)将文本文件hello.s翻译成机器语言指令,并将其打包成可重定位目标程序的格式,并将结果保存在目标文件hello.o中。

汇编的主要作用是将汇编语言翻译成机器指令,这些指令是计算机能够直接执行的指令。汇编器将汇编语言代码转换为机器指令,并生成目标文件,为后续的链接和执行提供了准备。目标文件中包含了可重定位的指令编码和相关的符号信息,可以与其他目标文件进行链接,形成最终的可执行目标程序。

4.2 在Ubuntu下汇编的命令

 

4.3 可重定位目标elf格式

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

 ELF(Executable and Linkable Format)是一种用于可执行文件、目标文件和共享库的文件格式。在ELF文件中,它的头部包含了一些描述文件信息的字段。其中,ELF头的开始部分是一个16字节的序列,描述了生成该文件的系统的字的大小和字节顺序。

 节头部表(Section Header Table)是ELF文件中的一个重要部分,用于描述目标文件中不同节的信息。每个节头部表条目对应一个节,包含了关于该节的类型、地址、大小、偏移等信息。节头部表的条目数和大小在ELF头中有相应的字段指定。

 在链接过程中,链接器会将所有相同类型的节合并为同一类型的新的聚合节。例如,来自不同输入模块的.data节会被合并为输出的可执行目标文件的一个.data节。这个过程称为节的重定位(Section Relocation)。

本程序中,需要进行重定位的是printf、puts、exit、sleep、getchar以及.rodata节中的.L0和.L1等符号。这些符号需要在链接过程中被解析和赋予运行时内存地址。 

符号表(Symbol Table)是链接器中的一种数据结构,用于存储模块定义和引用的符号的信息。在链接器的上下文中,可以将符号分为三种不同类型:全局符号、外部符号、局部符号。符号表记录了这些不同类型的符号的名称、地址、大小和其他属性信息。链接器在处理模块之间的符号引用和定义时,会使用符号表来解析符号,并进行正确的链接和重定位,以确保程序的符号引用能够正确解析到符号的定义位置。

4.4 Hello.o的结果解析

 分支转移:

 

 由分析知S文件中用的是段名称,如图中为.L3,而在反汇编代码中发现跳转为80<main+0x80>发现为一个相对的地址而不是助记符。

函数调用:

 

 发现在可重定位文件中call后不是函数的具体名称,而是一条重定位条目指引的信息。而汇编文件中直接加的是相对地址

数字进制:

 

反汇编代码中为16进制

4.5 本章小结

1.了解并介绍了汇编的概念和作用

2.熟悉了在Ubuntu下反汇编的指令

3.了解了elf文件各个部分的作用如ELF头、节头部表、符号表和可重定位节等

4.对比了hello.ohello.s之间的差距,分析了汇编语言和机器语言的对应关系

(第41分)

第5章 链接

5.1 链接的概念与作用

链接是将各种代码和数据片段收集并组合成一个单一文件的过程,使得该文件能够被加载到内存并执行。在现代系统中,这个过程通常由称为链接器(Linker)的程序自动执行。

链接的主要作用是解决分离编译(Separate Compilation)所带来的模块化开发的需求。而不是将一个大型的应用程序组织为一个巨大的源文件,链接器允许我们将程序分解为更小、更易管理的模块,每个模块可以独立地进行修改和编译。当我们修改其中一个模块时,只需重新编译该模块,并将其重新链接到其他已编译的模块上,而不必重新编译整个程序。

5.2 在Ubuntu下链接的命令

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

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

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

 ELF头效果和汇编时基本一样,唯一的不同是hello文件ELF头中type变为EXEC格式,原来为REL格式

 

 

 节头部表、重定位节和符号表也相同

5.4 hello的虚拟地址空间

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

datadump0x400000开始,最初是ELF表,和前ELF相同,包含了此段的相关信息。

根据节头部表可以知道个各节所在的位置,如.text节在偏移量为0x40的地方,即0x400040

5.5 链接的重定位过程分析

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

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

变为192

hello_.s中调用相应函数时使用的是已经进行重定位的虚拟地址,区别于未经过链接中的只有指令

 在进行链接过程时,hello_.s文件中新增了hello.c代码中使用的库函数,例如exitprintfsleepatoigetchar等函数。这使得程序的各个节变得更加完整,并包含了这些库函数的代码和数据。而未链接的hello_o.s文件中则没有这些库函数的代码和数据。

通过重定位节和符号定义、重定位节中的符号引用这两个重定位步骤,链接器能够确保程序中的每条指令和全局变量都具有正确的运行时地址。这使得程序能够顺利执行,并正确调用和访问所需的库函数和全局变量。重定位过程的完成使得程序的各个模块之间能够正确地链接和协作,最终生成可执行文件或共享库等输出文件。

5.6 hello的执行流程

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

ld-2.27.so!_dl_start

ld-2.27.so!_dl_init

hello!_star

libc-2.27.so!__libc_start_main

-libc-2.27.so!__cxa_atexit

-libc-2.27.so!__libc_csu_init

hello!_init

libc-2.27.so!_setjmp

libc-2.27.so!exit

5.7 Hello的动态链接分析

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

helloelf文件可知,got表是0x404000为首地址,大小为0x48的表

查询gdb可以发现,在运行dl_init之前

运行后变为如上图所示

共享库(Shared Library)是一种目标模块,可以在运行或加载时加载到任意内存地址,并与在内存中的程序进行链接。与静态库不同,共享库的链接发生在程序运行时,而不是编译时。这意味着共享库可以独立于程序进行开发和编译,并且可以在不同的程序之间共享和复用。

在调用共享库函数时,编译器无法预测函数的运行时地址,因为定义该函数的共享库模块在运行时可以加载到任何内存位置。为了解决这个问题,使用了延迟绑定(Lazy Binding)的方法。

5.8 本章小结

1.了解并介绍了链接的概念和作用

2.熟悉了在Ubuntu下链接的指令

3.了解了elf文件各个部分的作用如ELF头、节头部表、符号表和可重定位节等并和hello.o的进行比较

4.edb中查找了hello的虚拟空间地址,并分析了运行程序前的链接过程和整个程序的执行流程

(第51分)

第6章 hello进程管理

6.1 进程的概念与作用

进程是指在操作系统中正在执行的程序的实例。每个进程都是独立的,具有自己的内存空间、寄存器集合、程序计数器和其他与执行相关的状态信息。进程是操作系统进行任务调度和资源分配的基本单位。

进程的作用是实现并管理程序的执行。每个程序在运行时都需要一个进程来提供执行环境和资源支持。进程为程序提供了一个隔离的执行环境,使得不同程序可以同时运行而互不干扰。

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

Shell是一种用于与操作系统交互的命令行解释器或图形界面的应用程序。它充当用户与计算机系统之间的接口,接收用户输入的命令,并将其传递给相应的程序进行执行。Shell具有以下作用:

用户界面:Shell提供了用户与操作系统之间的交互界面。用户可以通过在Shell中输入命令来执行各种操作,如运行程序、管理文件和目录、设置系统参数等。

程序执行:Shell可以执行用户输入的命令或脚本,并将其传递给相应的程序进行处理。它可以调用系统内置的命令,也可以执行外部程序或脚本文件。

程序加载和运行控制:Shell负责加载和运行程序。它可以根据用户的输入启动和管理进程,并提供前台和后台程序的控制,如程序的挂起、继续、终止等。

作业调度:Shell可以接收和管理作业。作业是一系列相关的任务或命令,Shell可以根据用户的需求调度和管理这些作业的执行,包括作业的启动、停止、暂停、恢复等。

信号处理:Shell作为进程管理的代表,负责处理各种信号。它可以接收来自操作系统或其他程序发送的信号,并根据信号的类型执行相应的操作,如终止程序、忽略信号、捕获信号等。

Shell的处理流程一般包括以下步骤:

从终端读入输入的命令:Shell会读取用户在终端输入的命令或指令。

切分参数:Shell会解析输入的字符串,并将其切分为命令和参数,以便进行后续处理。

内置命令执行:如果输入的命令是Shell内置的命令,Shell会直接执行相应的操作,而不需要调用外部程序。

调用程序执行:如果输入的命令是外部程序或脚本文件,Shell会调用相应的程序进行执行。它会在系统中查找并加载程序,然后将命令传递给程序进行处理。

处理键盘输入信号:Shell作为交互式程序,需要接收并处理键盘输入的信号。它可以响应特定的按键或组合键,执行相应的操作,如中断程序、退出Shell等。

6.3 Hello的fork进程创建过程

 输入如图所示命令,首先shell对输入的命令进行解析,在输入以后会为程序创建一个进程(使用fork函数),由于是子进程,因此与父进程共用一个虚拟地址空间,但是进程PID并不相同

6.4 Hello的execve过程

execve函数在当前进程的上下文中加载并运行一个新程序,它会替换当前进程的地址空间,将新程序的代码、数据、堆和栈段映射到进程的虚拟地址空间中。执行execve函数后,当前进程的代码、数据和状态都会被新程序取代,从而开始执行新程序的指令。

当运行hello程序时,操作系统会为新程序创建一个新的虚拟地址空间。在这个虚拟地址空间中,操作系统将加载可执行代码和数据,将其从磁盘复制到内存中。虚拟地址空间还可以包括堆和栈段,用于存储动态分配的内存和函数调用的局部变量。

此外,虚拟地址空间还可以映射共享区域,这使得多个进程可以共享相同的内存区域,从而实现进程间的通信和数据共享。

最后,操作系统会将控制权转移到新程序的第一条指令,从而开始执行新程序的逻辑。

需要注意的是,execve函数在成功时不会返回,而是直接开始执行新程序。只有在发生错误时,execve函数才会返回-1,表示执行失败。

6.5 Hello的进程执行

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

进程调度是操作系统中的重要任务之一,它决定了哪些进程能够获得处理器的使用权以及使用的时间片长度。调度器根据一定的策略从就绪队列中选择一个进程,将处理器的控制权分配给它,使其在处理器上执行一段时间。

当运行hello程序时,Shell会创建一个新的进程,并在该进程的上下文中运行可执行目标文件。进程开始时处于用户态(用户模式)中,只能执行受限的操作。进程从用户态转换到内核态的唯一途径是通过异常,如中断、故障或系统调用。当异常发生时,控制权被传递给异常处理程序,处理器将模式从用户态转换为内核态。处理器在内核态执行异常处理程序,可以执行更高特权级的操作。

例如,在hello程序中,调用sleep函数或getchar函数会触发系统调用,这将导致进程从用户态切换到内核态。操作系统的内核会处理这些系统调用,并执行相应的操作,如暂停进程一段时间或等待用户输入

内核有权利决定何时抢占当前运行的进程,并重新启动先前被抢占的进程。这种决策称为调度决策。当一个进程被抢占时,当前进程的上下文将被保存,以便稍后恢复执行。然后,被抢占的进程的上下文将被恢复,并将控制权传递给该进程,使其继续执行

调度器的工作包括选择合适的进程进行调度,保存和恢复进程的上下文,以及处理进程切换的细节。它使用各种调度算法和策略来平衡系统的性能、响应时间和公平性,以满足不同的需求。

总结起来,进程调度涉及选择合适的进程并分配处理器时间片,通过上下文切换在不同进程间切换执行。用户态和内核态之间的转换通过异常和系统调用实现。调度器的任务是决定进程切换的时机,并保存和恢复进程的上下文。

6.6 hello的异常与信号处理

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

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

回车:

 对程序运行没有影响,但是运行完循环后会自动输入回车,因此程序直接终止

Ctrl+Z

 停止进程,发送信号SIGSTP,父进程接收到信号并进行处理

 使用ps指令发现进程并没有终止

输入fg后又会继续进行被挂起的进程

输入jobs可以观察到hello程序当前的状态

 输入pstree可以查看当前进程树,找到hello程序发现是在terminalbash下的

Kill

 使用kill发送信号9SIGKILL)给进程17385,负的PID会导致信号被发送到进程组PID中的每个进程,终止以后再ps发现已经没有hello进程

Ctrl-C

 Ctrl-C发送信号2SIGINT),是来自键盘的中断,默认行为是终止进程,因此程序直接退出并被回收

乱按:

6.7本章小结

1.了解了进程的概念与作用

2.熟悉了shell-bash的作用和处理流程

3.了解了fork函数如何创建进程,execve函数如何执行函数

4.了解了多种信号的处理和异常中断

(第61分)

第7章 hello的存储管理

7.1 hello的存储器地址空间

结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。

逻辑地址是指在段式存储管理机制下,由段基址和段偏移量组成的地址。在这种相对寻址方式下,逻辑地址是相对于段基址的偏移量,用于访问存储器中的数据或指令。在hello程序的反汇编代码中,给出的地址是逻辑地址,它是基于段式存储管理的相对地址。

线性地址是在分段机制与分页机制结合的情况下产生的地址。当分段和分页同时使用时,线性地址是逻辑地址到物理地址转换的中间结果。它是一个连续的地址空间,是在逻辑地址和物理地址之间的一个抽象概念。在hello程序的反汇编代码中,通过偏移量可以得知地址是线性连续的,属于线性地址。

虚拟地址是在使用虚拟存储器技术时生成的地址。它是由CPU生成的用于访问主存的中间地址,需要通过内存管理单元(MMU)硬件将虚拟地址转换为物理地址。虚拟地址空间是程序在运行时的地址空间,它可以大于物理内存的实际空间。在hello程序中,代码段的地址0x400000是虚拟地址,需要经过MMU的转换映射到物理地址。

物理地址是计算机系统中主存的实际物理地址,它对应于内存中的存储单元。物理地址是由MMU翻译后得到的最终地址,用于实际的数据或指令访问。在hello程序中,MMU将虚拟地址翻译成物理地址,以便访问实际的存储单元。

综上所述,逻辑地址是相对于段基址的偏移量,线性地址是逻辑地址到物理地址转换的中间结果,虚拟地址是CPU生成的用于访问主存的中间地址,物理地址是最终的实际地址。这些地址在不同的内存管理机制下进行转换和映射,以实现程序的正确执行和存储器的管理。

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

Intel的段式管理中,逻辑地址通过段描述符进行转换为线性地址。每个段描述符占据8个字节,其中包含了有关段的基址、长度和类型等信息。

段描述符的格式如下:

段的基址:由B31-B24B23-B16B15-B0构成,共32位。基址可以是4GB空间中的任意地址,用于指定段的起始位置。

段的长度:由L19-L16L15-L0构成,共20位。如果G位为0,表示段的长度单位为字节,段的最大长度为1M2^20)。如果G位为1,表示段的长度单位为4KB,段的最大长度为4G4KB * 2^20)。

段的类型:指示段是代码段还是数据段,以及可读写权限等。

假设我们将段的基址设置为0,段的长度设置为4G,这样就构成了一个从0地址开始覆盖整个4G空间的段。通过索引可以定位到相应的段描述符,从而获取段的基址。将段基址与偏移量相加,就得到了线性地址,也就是虚拟地址。

当访存指令中给出的逻辑地址通过段描述符转换为线性地址后,线性地址会被放置到地址总线上,从而成为物理地址。需要注意的是,逻辑地址与基址加偏移构成的层次式地址是不同的概念。逻辑地址是经过段式管理转换后的地址,用于访问内存中的数据或指令。

总结而言,通过段描述符的基址和偏移量的组合,可以得到线性地址,即虚拟地址。逻辑地址是经过段式管理转换后的地址,最终会映射到物理地址上,用于实际的数据访问。这种机制可以实现对4GB空间的灵活管理和保护。

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

 获取页表基址:通过页表基址寄存器(Page Table Base Register,PTBR)获取页表的基址。页表是一种数据结构,用于存储虚拟页号与物理页号的映射关系。

虚拟地址解析:根据虚拟地址,提取虚拟页号和虚拟页偏移量。虚拟页号用于在页表中查找对应的页表项,而虚拟页偏移量与物理页偏移量相同。

查询页表项:使用虚拟页号作为索引,在页表中查找对应的页表项。每个页表项包含有效位、物理页号等信息。

判断有效位:检查页表项中的有效位。如果有效位为有效(有效位被设置),则表示虚拟页号与物理页号的映射有效,并且可以获取物理页号。

物理地址生成:将物理页号与虚拟页偏移量组合,形成物理地址。物理地址表示在物理内存中的实际位置。

地址转换完成:此时,虚拟地址已经成功转换为物理地址。可以使用物理地址来进行实际的数据读取或写入操作。

如果页表项的有效位为无效(有效位未被设置),则表示虚拟页号没有与物理页号进行映射,即发生了缺页(Page Fault)。这时会触发缺页中断,操作系统会根据缺页中断处理程序的逻辑,将缺失的页从辅存(如硬盘)加载到内存中,并更新页表,使得虚拟地址能够成功转换为物理地址。

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

 1.CPU产生一个虚拟地址

2.MMUTLB中取出相应的PTE

3.MMU将这个虚拟地址翻译成一个物理地址,并且将它发送到高速缓存/主存

4.高速缓存/主存将所请求的数据字返回给CPU

 多级页表结构:多级页表由多个级别的页表组成,每个级别的页表大小相等。通常使用两级页表结构作为示例进行说明,其中一级页表称为目录表(Page Directory),二级页表称为页表(Page Table)。

分级索引:地址转换通过多级索引进行。例如,对于一个32位的虚拟地址,可以将其划分为多个部分,如目录索引、页表索引和页内偏移量。通过目录索引找到目录表中对应的目录项(Page Directory Entry),再通过页表索引找到页表中对应的页表项(Page Table Entry)。

索引与物理页框映射:目录项和页表项中存储着与物理内存页框的映射关系。例如,页表项中存储着物理页框号,用于将虚拟地址映射到物理地址。

多级页表的灵活性:多级页表的一个重要特点是它的灵活性。只有在需要时,才会创建和加载二级页表,从而减少了内存消耗。例如,当某个虚拟页被访问时,如果对应的二级页表不存在,操作系统会进行相应的页调入(Page In)操作,将二级页表加载到内存中,然后继续进行地址转换。

缓存页表项:由于多级页表结构中只有最常使用的页表项会被缓存在主存中,而其他页表项只在需要时才被加载到主存,从而减少了主存的压力。这种策略使得主存的资源可以更加高效地利用。

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

 MMU查询页表:当CPU发出一个虚拟地址时,MMU会首先查询页表以获取对应的页表项(Page Table Entry,PTE)。MMU使用虚拟页号(VPN)从页表中查找对应的PTE。

物理地址生成:从页表项中获取物理页号(Physical Page Number,PPN),并与虚拟地址中的页内偏移量(Virtual Page Offset,VPO)组合形成物理地址。

缓存访问:MMU将生成的物理地址发送给缓存。在缓存中,物理地址被拆分为缓存偏移量(Cache Offset,CO)、缓存组索引(Cache Index,CI)和缓存标记(Cache Tag,CT)。

命中检测:缓存通过比较缓存组索引和缓存标记,判断是否命中。如果缓存组索引与标记匹配,则表示发生了命中。

数据返回:如果发生了命中,缓存将从偏移量位置读取对应的数据字节,并将其返回给MMU。随后,MMU将数据传递回CPU,供后续处理使用。

如果在缓存中发生了命中,可以快速地获取所需数据,这是因为缓存具有更快的访问速度和较短的访存延迟。而如果发生了未命中(缓存失效),则需要从主存中获取相应的数据,这会引入较长的访存延迟。

7.6 hello进程fork时的内存映射

当fork函数被当前进程调用时,操作系统会为新进程创建一系列的数据结构,并为它分配一个唯一的进程标识符(PID)。为了创建新进程的虚拟内存,内核会创建当前进程的内存管理结构(mm_struct)、区域结构(vma)以及页表的副本。

在刚开始时,新进程的虚拟内存与调用fork函数时的进程完全相同。为了实现写时复制(Copy-On-Write,COW)机制,内核将这两个进程中的每个页面标记为只读,并将每个区域结构标记为私有的。这样,当其中一个进程尝试进行写操作时,COW机制会触发。

具体来说,当某个进程尝试修改一个只读页面时,操作系统会为该进程创建一个新的页面,并将新的数据复制到该页面中。这样,原始页面仍然是只读的,并且新页面成为进程的私有页面。通过这种方式,每个进程都保持了一个独立的地址空间,尽管它们最初共享相同的物理页面。

这种写时复制机制的好处是节省了内存开销。在fork函数调用后,新进程可以共享相同的物理页面,直到其中一个进程尝试进行写操作。这样可以减少内存的冗余拷贝,提高了系统的效率。

7.7 hello进程execve时的内存映射

删除已存在的用户区域:在执行execve函数之前,当前进程的虚拟地址空间中可能存在先前映射的用户区域。这些区域结构会被删除,释放相应的资源。

映射私有区域:在execve函数中,为新程序的代码、数据、bss和栈区域创建新的区域结构。这些区域都是私有的,并且采用写时复制的机制。

代码区域(.text):映射到hello文件中的可执行代码部分。

数据区域(.data):映射到hello文件中的初始化数据部分。

bss区域:映射到匿名文件,并被初始化为二进制零。bss区域包含在hello程序中,它用于存储未初始化的全局和静态变量。

栈和堆区域:在execve函数调用时,栈和堆的初始大小为零。它们在后续的程序执行中可以根据需要进行动态调整。

映射共享区域:如果hello程序链接了共享对象(或共享库),那么这些对象会被动态链接到该程序中,并映射到用户虚拟地址空间中的共享区域内。共享区域允许多个进程共享相同的代码和数据,以节省内存空间。

设置程序计数器(PC):在execve函数完成后,当前进程的上下文被更新,包括设置程序计数器(PC)。PC指向代码区域的入口点,即新程序的起始指令,以便开始执行新程序的代码。

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

 DRAM缓存不命中即称为缺页,如图所示,需要返回磁盘中找到需要的数据并修改页表。这时会找到一个牺牲页替换这个页表

 如图所示修改后重新执行刚刚触发中断的程序,此时会命中,程序正常进行。

7.9动态存储分配管理

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

7.10本章小结

本章主要介绍了hello的存储器地址空间、Intel逻辑地址到线性地址的变换-段式管理、Hello的线性地址到物理地址的变换-页式管理、TLB与四级页表支持下的VA到PA的变换、三级Cache支持下的物理内存访问,分析了hello进程fork时的内存映射,hello进程execve时的内存映射、缺页故障与缺页中断处理和动态存储分配管理。

(第7 2分)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/555863.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图像数据处理

文章目录 1&#xff1a;TFRecords1-1 将MNIST数据集转换成TFRecord格式1-2 读取TFRecord文件中的数据 2&#xff1a;图像数据的预处理2-1 处理图像编码2-2 调整图像大小2-3 剪裁和填充2-4 按比例剪裁2-5 图像翻转2-6 图像亮度调整2-7 图像对比度调整2-8 图像色相调整2-9 图像饱…

chatgpt赋能Python-python_99乘法

Python编程实现——99乘法表的生成 Python编程语言是一种高级程序设计语言&#xff0c;具有简单易学、可移植性强、功能强大等特点&#xff0c;受到广大开发者的喜爱。Python可以被应用于网站开发、数据分析、人工智能、机器学习等多个领域。而在Python编程中&#xff0c;生成…

《程序员面试金典(第6版)》面试题 02.06. 回文链表(双指针(快慢指针),查找链表中间节点,反转链表)

题目描述 编写一个函数&#xff0c;检查输入的链表是否是回文的。 题目传送门~&#xff1a;面试题 02.06. 回文链表 示例 1&#xff1a; 输入&#xff1a; 1->2 输出&#xff1a; false 示例 2&#xff1a; 输入&#xff1a; 1->2->2->1 输出&#xff1a; true 进…

蓝桥杯单片机串口通信学习提升笔记——部分2

今日继续学习提升蓝桥杯国赛能力水平。 有道是&#xff1a;卜心事、灯花无语&#xff0c;百感孤单&#xff0c;鸳被羞展...... 梦方圆&#xff0c;又丛钟、声声惊断。 诗人杨玉衔孤单影只&#xff0c;偏偏又多遭磨难&#xff0c;一路坎坷...... 正如我近日来学习提升串口通信…

数据结构学习分享之链式二叉树(一)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:数据结构学习分享⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你了解更多数据结构的知识   &#x1f51d;&#x1f51d; 1. 前言 在学习链式二叉树…

【Linux】shell编程—awk编辑器

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、awk编辑器1.工作流程3.常用选项 二、awk的基础用法1.输出文件中的某一列2.根据特定条件筛选数据3.按照分隔符进行切割4.在匹配到特定字符串时执行操作5.BEGIN打…

chatgpt赋能Python-pythonwxpy

Python的wxpy模块&#xff1a;一款强大的微信机器人框架 在当今数字时代&#xff0c;微信已经成为了大家日常生活中不可缺少的应用。wxpy是一款使用Python语言的微信机器人框架&#xff0c;可以帮助用户实现诸如自动回复、消息提醒、定时发送消息等自动化操作。它的易用性、强…

(5)---STM32 的时钟系统

目录 1.时钟基本概念 时钟源常见振荡器 振荡电路 晶体振荡器 RC振荡器 2.G030时钟源 3.时钟树 4.STM32CubeMX时钟树配置 1.时钟基本概念 1&#xff09; 时钟是嵌入式系统的脉搏&#xff0c;处理器内核在时钟驱动下完成指令执行&#xff0c;状态变换等动作&#xff0c; 外设部件…

基于redis客户端缓存机制实现本地缓存

文章目录 前言一、本地缓存和分布式缓存二、redis客户端缓存机制1.客户端缓存实现原理普通模式广播模式重定向模式redirect 2.优势和误区3.客户端缓存机制请求流程 三、项目实战1.引入依赖2.redis连接属性配置3.开启客户端缓存4.使用本地缓存5.测试 总结 前言 采用缓存一直是我…

VMware ESXi 6.0 U3 Final - ESXi 6 系列最终版下载

VMware ESXi 6.0 U3 Final - ESXi 6 系列最终版下载 VMware ESXi 6 Standard 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-esxi-6/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org VersionRelease NameRelease …

RLHF中的PPO算法原理及其实现

RLHF中的PPO算法原理及其实现 ChatGPT是基于InstructGPT实现的多轮对话生成式大模型。ChatGPT主要涉及到的技术包括&#xff1a; 指令微调&#xff08;Instruction-tuning&#xff09;&#xff1b;因果推断&#xff08;Causal Language Modeling&#xff09;&#xff1b;人类…

从零开始Vue3+Element Plus后台管理系统(十五)——多语言国际化vue I18n

i18n国际化的内容比较多&#xff0c;写文章的时间也用得比较长&#xff0c;从上周五开始到本周一&#xff0c;断断续续完成了。 虽然实际工作中很多项目都不需要国际化&#xff0c;但是了解国际化的用法还是很有必要的。 i18n Vue I18n 是 Vue.js 的国际化插件。它可以轻松地…

PFC-FLAC3D Coupling Examples

目录 PFC-FLAC3D Coupling Examples Punch Indentation of a Bonded Material Sleeved Triaxial Test of a Bonded Material 命令流 结果 PFC-FLAC3D Coupling Examples Punch Indentation of a Bonded Material 这个例子展示了一个粘合颗粒模型&#xff08;BPM&#xff0…

项目经历该如何写?

大家好&#xff0c;我是帅地。 这不春招来了吗&#xff0c;帮训练营的帅友们修改了很多简&#xff0c;其中问题最多的就是项目经历 专业技能这块了&#xff0c;特别是项目经历这块&#xff0c;很多人写了一大堆描述功能描述&#xff0c;但是自己具体干了什么却没怎么写&#…

研发工程师玩转Kubernetes——使用Deployment进行多副本维护

多副本维护是指&#xff0c;对一组在任何时候都处于运行状态的 Pod 副本的稳定集合进行维护。说的直白点&#xff0c;就是保证某种的Pod数量会被自动维持——增加了该类Pod会自动删除多余的&#xff0c;减少了该类Pod会自动新增以弥补&#xff0c;以保证Pod数量不变。 Kubernet…

day37_Tomcat_Maven

今日内容 一、Maven 二、Tomcat 一、Maven 1.1 引言 项目管理问题 项目中jar包资源越来越多&#xff0c;jar包的管理越来越沉重。 繁琐 要为每个项目手动导入所需的jar&#xff0c;需要搜集全部jar 复杂 项目中的jar如果需要版本升级&#xff0c;就需要再重新搜集jar 冗余 相…

基于Spring-动态调整线程池阻塞队列长度

最近在做一个动态线程池的组件&#xff0c;遇到了关于阻塞队列长度刷新的问题,所以记录下来&#xff0c;很有意思 我们都知道常用线程池分为二类&#xff0c;Spring-ThreadPoolTaskExecutor和JDK-ThreadPoolExecutor的&#xff0c;当然了Spring也是基于JDK做一步封装&#xff0…

​数据库原理及应用上机(实验四 SQL连接查询)

✨作者&#xff1a;命运之光 ✨专栏&#xff1a;数据库原理及应用上机实验 目录 ✨一、实验目的和要求 ✨二、实验内容及步骤 ✨三&#xff0e;实验结果 ✨四、实验总结 &#x1f353;&#x1f353;前言&#xff1a; 数据库原理及应用上机实验报告的一个简单整理后期还会不…

Zerto 10.0 发布 - 勒索软件防护、灾难恢复和多云移动性的统一解决方案

Zerto 10.0 发布 - 勒索软件防护、灾难恢复和多云移动性的统一解决方案 请访问原文链接&#xff1a;https://sysin.org/blog/zerto-10/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 携手 ZERTO 提升勒索软件保护与灾难恢复水…

Python异常处理

1. 异常概述 在程序运行过程中&#xff0c;经常会遇到各种错误&#xff0c;这些错误称为“异常”。这些异常有的是由于开发者一时疏忽将关键字敲错导致的&#xff0c;这类错误多数产生的是SyntaxError:invalid syntax&#xff08;无效的语法&#xff09;&#xff0c;这将直接导…