程序人生-Hello’s P2P

商道如狼道  论坛元老 | 2024-9-26 08:27:51 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1034|帖子 1034|积分 3102

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
摘要

        本文讨论了与编程和软件开发相干的几个关键概念和过程。首先先容了链接的概念和作用,它是将代码和数据片断组合成单一文件的过程,使得分离编译成为可能,从而可以更好地管理和修改模块。接下来探讨了进程的概念和作用,进程是正在执行的程序的实例,包括程序的代码、数据和运行状态。然后讨论了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.o和hello.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对照分析阐明。  


datadump从0x400000开始,最初是ELF表,和前ELF相同,包罗了此段的相干信息。


根据节头部表可以知道个各节所在的位置,如.text节在偏移量为0x40的地方,即0x400040
5.5 链接的重定位过程分析

objdump -d -r hello 分析hello与hello.o的不同,阐明链接的过程。
联合hello.o的重定位项目,分析hello中对其怎么重定位的。


变为192行

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


 在进行链接过程时,hello_.s文件中新增了hello.c代码中利用的库函数,例如exit、printf、sleep、atoi、getchar等函数。这使得程序的各个节变得更加完备,并包罗了这些库函数的代码和数据。而未链接的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前后,这些项目的内容变化。要截图标识阐明。


由hello的elf文件可知,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程序发现是在terminal的bash下的


Kill

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

Ctrl-C

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

乱按:

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-B24、B23-B16和B15-B0构成,共32位。基址可以是4GB空间中的恣意地址,用于指定段的起始位置。
段的长度:由L19-L16和L15-L0构成,共20位。如果G位为0,表现段的长度单位为字节,段的最大长度为1M(2^20)。如果G位为1,表现段的长度单位为4KB,段的最大长度为4G(4KB * 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.MMU从TLB中取出相应的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分)


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

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

商道如狼道

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表