资讯详情

HIT-ICS2021大作业论文

摘要是论文内容的高度总结,应具有独立性和自含性,即不阅读论文全文即可获得必要的信息。摘要应包括本论文的目的、主要内容、方法、结果及其理论和实际意义。摘要不得使用公式、结构、图表和未知公共符号和术语,不得标记引用文献编号,避免将摘要写入目录内容介绍。

Hello World!;预处理、编译、汇编、链接、运行;

Hello我们成为程序员后的第一个朋友,他阳光明媚,乐于助人。但在他引导我们进入这个神奇的领域并帮助我们建立信心之后,它就消失了。你想知道他经历了什么吗?从他出生起,他就开始了他传奇的生活。他经历了预处理、编译、汇编、链接、操作;他探索了外部存储设备,I/O桥梁、内存、各级cache。接下来,让我们接近他,感受他的生活!

1.1 Hello简介... - 4 -

1.2 环境与工具... - 4 -

1.3 中间结果... - 4 -

1.4 本章小结... - 5 -

2.1 预处理的概念和作用... - 6 -

2.2在Ubuntu下一个预处理命令... - 6 -

2.3 Hello预处理结果分析... - 6 -

2.4 本章小结... - 9 -

3.1 概念与作用的编译... - 10 -

3.2 在Ubuntu下面编译的命令... - 10 -

3.3 Hello分析编译结果... - 10 -

3.4 本章小结... - 12 -

4.1 汇编的概念和功能... - 13 -

4.2 在Ubuntu下面汇编的命令... - 13 -

4.3 可重定位目标elf格式... - 13 -

4.4 Hello.o的结果解析... - 16 -

4.5 本章小结... - 17 -

5.1 链接的概念和功能... - 18 -

5.2 在Ubuntu下链接命令... - 18 -

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

5.4 hello虚拟地址空间... - 21 -

5.5 链接重定位过程分析... - 22 -

5.6 hello的执行流程... - 25 -

5.7 Hello动态链接分析... - 25 -

5.8 本章小结... - 26 -

6.1 进程的概念与作用... - 27 -

6.2 简述壳Shell-bash作用及处理过程... - 27 -

6.3 Hello的fork创建过程的过程... - 27 -

6.4 Hello的execve过程... - 28 -

6.5 Hello的进程执行... - 28 -

6.6 hello异常和信号处理... - 29 -

6.7本章小结... - 34 -

7.1 hello存储地址空间... - 35 -

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

7.3 Hello从线性地址到物理地址的转换-页面管理... - 36 -

7.4 TLB支持四级页面VA到PA的变换... - 37 -

7.5 三级Cache支持的物理内存访问... - 37 -

7.6 hello进程fork时间内存映射... - 37 -

7.7 hello进程execve时间内存映射... - 37 -

7.8 缺页故障与缺页中断处理... - 38 -

7.9动态存储分配理... - 38 -

7.10本章小结... - 39 -

8.1 Linux的IO设备管理方法... - 40 -

8.2 简述Unix IO接口及其函数... - 40 -

8.3 printf的实现分析... - 40 -

8.4 getchar的实现分析... - 42 -

8.5本章小结... - 43 -

1.1 Hello简介

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

P2P  

编写源程序  hello.c(源程序)

经过cpp预处理器进行预处理   hello.i(修改了的源程序)

经过ccl编译器进行编译处理    hello.s(汇编程序)

经过as汇编器进行汇编处理     hello.o(可重定位目标程序)

经过ld连接器将标准c库中的printf.o函数进行合并  hello(可执行目标程序)

经过shell进行运行处理 进程将为其fork()一个子程序

020  

经过execve加载器载入,建立虚拟内存映射。CPU为其分配相应的时间分片

CPU上的内存管理单元MMU根据页表将CPU生成的虚拟地址翻译成物理地址,将相应的页面调度

printf调用malloc进行动态内存分配

程序终结后后,内核向父进程发送SIGCHLD信号,此时终止的hello被父进程回收

1.2 环境与工具

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

硬件环境:X64 CPU;2GHz;2G RAM;256GHD Disk 以上

软件环境:Windows7 64位以上;VirtualBox/Vmware 11以上;Ubuntu 16.04 LTS 64位/优麒麟 64位

开发工具:gcc   gdb   objdump   vscode   readlf   HexEdit

1.3 中间结果

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

hello.i 预处理之后的源程序

hello.s 编译之后的汇编程序

hello.o 汇编之后的可重定位目标程序

hello.elf hello.o的ELF格式

helloexe.elf hello的ELF格式

hello 可执行目标程序

hello2.s 反汇编后输出的程序

Helloobj.txt Hello可执行程序的反汇编代码

Temp.c 临时数据存放

1.4 本章小结

本章节我们简单的讲述了hello程序P2P与020的过程,对程序的编写到运行有了初步的认识。

我们提供了这次实验需要的软硬件环境与开发工具的信息

对程序从编写到运行所有的中间文件有了详细的了解

2.1 预处理的概念与作用

预处理:根据以字符#开头的编译预处理指令,进行代码文本编译前的处理并且最后生成.i的文本文件。

作用:

文件包含命令,引入对应头文件

设定编译器的状态或者是指示编译器完成一些特定的动作

将宏名替换为字符串(宏替换)

2.2在Ubuntu下预处理的命令

cpp hello.c > hello.i

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

2.3 Hello的预处理结果解析

 通过上述的内容我们可以发现进行预处理后用绝对路径替代了.h头文件的预处理,并引入了标准c库中的一些数据类的声明和结构体的定义,同时还有对外部函数的引用,除此之外就没有其他的变化。

2.4 本章小结

通过本章我们了解到了预处理器将hello.c进行预处理形成hello.i的第一步,对预处理有了较深的了解。

3.1 编译的概念与作用

概念:编译便是将程序转化为指令集

编译器(ccl)将后缀为.i的文本文件翻译成后缀为.s的文本文件

不同的高级语言经过编译器编译后都会输出统一的汇编代码

注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序

3.2 在Ubuntu下编译的命令

gcc –S hello.i –o hello.s

应截图,展示编译过程!

3.3 Hello的编译结果解析

    3.3.1 伪指令

.file 声明源文件   “hello.c”

.text 代码段

.rodata 只读数据段

.align声明对齐方式  8  进行双字对齐

3.3.2 只读数据段

在此段我们可以读出函数常用字符串

其格式为Hello %s %s\n

声明为:"\347\224\250\346\263\225: Hello \345\255\246\345\217\267 \345\247\223\345\220\215 \347\247\222\346\225\260\357\274\201"

3.1.3  main函数

两个参数存储在寄存器%rdi和%rsi中

将两个参数入栈,将第一个参数与立即数4进行比较

若相等则跳转到L2  否则输出参数并退出

L2 将局部变量赋值为0并跳转到L3

L3 局部变量i与立即数7进行比较

   若小于等于则跳转到L4  否则调用getchar函数并返回

L4 调用printf函数打印字符串数组的第一和第二个元素 调用sleep函数并将字符串第三个元素作为参数传入  局部变量i进行加1操作

   

3.3.3数据

局部变量存放在栈中

输入参数存放在%rdi与%rsi当中

带打印的String存放在.string节中

argc, *argv参数局部变量存放在栈中

3.3.2 赋值

用movx src,dec,由x控制字长

如图通过movl将参数传入栈中

3.3.3运算

加法addx 操作数1,操作数2

减法subx 操作数1,操作数2

3.3.4if条件语句判断

Cmpx操作数1,操作数2 jxx条件跳转语句实现,往下跳

3.3.5循环控制语句

Cmpx操作数1,操作数2 jxx条件跳转语句实现,往上跳

3.3.6关系运算

Cmpx操作数1,操作数2

3.3.7函数返回

pushq %rbp,将上一个栈顶地址压栈,为函数返回时,出栈做准备

通过%rax 返回返回值

3.3.8参数传递,通过栈底指针间接寻址,暂时存放在寄存器%rdi,%rsi中

3.3.9函数调用

call 函数名,在调用前,准备好参数,存放在寄存器%rdi, %rsi当中

3.3.10数组

通过偏移地址+基址寻址

movq -32(%rbp), %rax

addq $16, %rax

3.4 本章小结

本章我们学会了编译器是如何处理程序的  同时我们接触了一些基本指令集的汇编代码  如数据:常量、变量的存放,通过movx的赋值 = ,算术操作:+、 - 、++,关系操作: != 、<=,数组/指针的引用:A[i]、*p 控制转移:if、for的使用,函数操作:参数传递(地址/值)、函数调用()、函数返回 return的实现。

4.1 汇编的概念与作用

     汇编:通过汇编器将.s后缀文件翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在.o后缀文件中。

    通过汇编翻译成机器语言指令后机器可以进行直接的读取与分析

注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。

4.2 在Ubuntu下汇编的命令

gcc -c hello.s -o hello.o

4.3 可重定位目标elf格式

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

elf头   表示大小端信息、字的大小,文件的类型,程序头起点,段表头起点等信息

节头部表   表示各个节名称、类型、起始地址和偏移量等信息。

重定位节   表示重定位的偏移量,类型。符号值等信息

偏移量:需要进行重定向的代码在.text或.data节中的偏移位置.

信息:包括symbol和type两部分,其中symbol占前4个字节,type占后4个字节,symbol代表重定位到的目标在.symtab中的偏移量,type代表重定位的类型:由信息决定的替换类型

符号名称:重定位目标的名字

加数:重定位过程需要加减的常量

符号表  表示程序中定义与引用的函数与全局变量的信息

4.4 Hello.o的结果解析

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

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

objdump -d -r hello.o > objdump.txt

两者间的差异

hello.s文件中使用.L2, .L3等注记符   在反汇编汇编语言中,分支转移时的跳转目标地址为相对偏移量

在hello.s中操作数采用十进制表示  反汇编后使用16进制表示

Hello.s中直接使用函数名来进行函数调用   反汇编后使用相对偏移地址来调用指令

Hello.s使用标记符来访问全局变量   反汇编后使用rodata节的偏移量来表示

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

4.5 本章小结

这一张我们通过编译将.s文件转化为.o文件准备下一步的链接操作。

我们进行了反汇编并与编译后的代码进行对比 我们发现进行反汇编后分支函数调用 操作数 全局变量的访问均发生了变化

5.1 链接的概念与作用

链接:通过链接器,将程序调用的外部函数(.o文件)与当前.o文件进行合并,并得到可执行目标文件。

作用:链接模块是实现分离编译的基础

      为模块化管理代码带来了便利  

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

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.3 可执行目标文件hello的格式

  Readelf -a hello > hello.elf

   

  ELF头   表示机器大小端信息,类型,系统结构,入口点地址,段头表地址,程序头等地址

  

  节头   表示各节的信息,包括名称、类型、起始地址等。这些节已经被重定位至最终运行时的地址

程序头部表    描述了可执行文件连续的片和连续的内存段的映射关系。其中包括段:

PHDR:程序头表

INTERP:程序执行前需要调用的解释器

LOAD:程序目标代码和常量信息

DYNAMIC:动态链接器使用信息

NOTE:保存辅助信息

GNU_EH_FRAME:保存异常信息

GNU_STACK:标志栈是否可用的标志信息

GNU_RELRO:保存在重定位之后只读信息的位置

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

5.4 hello的虚拟地址空间

  通过上述我们可以发现init函数的地址为:40100

 

我们使用edb进行hello的加载来查看其地址空间信息

  

便可以发现与elf文件中所示一致

我们查看elf文件可知程序头表PHDR的地址为 00400040

通过edb进行查看发现结果一致

5.5 链接的重定位过程分析

objdump -d -r hello > hellolink.txt

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

hello.o的汇编代码中只出现了main函数的名字  hello的汇编代码中出现了_init,.plt等函数名

在hello.o汇编代码中函数的地址不确定   在hello汇编代码中地址确定下来

在hello.o汇编代码中很多数据的地址不确定,在hello汇编代码中地址确定下来

   

结合hello.o的重定位项目,链接器将符号解析完成后,就将代码中的所有符号引用都与一个符号定义关联。这样链接器就可以按照各个内容的具体大小来进行重定位,合并输入模块,并为每个符号分配运行时的地址。

5.6 hello的执行流程

ld-2.27.so!_dl_start 0x7fce 8cc38ea0

ld-2.27.so!_dl_init 0x7fce 8cc47630

hello!_start 0x400500

libc-2.27.so!__libc_start_main 0x7fce 8c867ab0

-libc-2.27.so!__cxa_atexit 0x7fce 8c889430

-libc-2.27.so!__libc_csu_init 0x4005c0

hello!_init 0x400488

libc-2.27.so!_setjmp 0x7fce 8c884c10

-libc-2.27.so!_sigsetjmp 0x7fce 8c884b70

–libc-2.27.so!__sigjmp_save 0x7fce 8c884bd0

hello!main 0x400532

hello!puts@plt 0x4004b0

hello!exit@plt 0x4004e0

*hello!printf@plt

*hello!sleep@plt

*hello!getchar@plt

ld-2.27.so!_dl_runtime_resolve_xsave 0x7fce 8cc4e680

-ld-2.27.so!_dl_fixup 0x7fce 8cc46df0

–ld-2.27.so!_dl_lookup_symbol_x 0x7fce 8cc420b0

libc-2.27.so!exit 0x7fce 8c889128

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

5.7 Hello的动态链接分析

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

程序调用一个由共享库定义的函数时,编译器没有办法预测这个函数的运行时地址,因为定义它的共享模块在运行时可以加载到任意位置。GNU编译系统使用延迟绑定的技术解决这个问题,将过程地址的延迟绑定推迟到第一次调用该过程时。

延迟绑定要用到全局偏移量表(GOT)和过程链接表(PLT)两个数据结构。如果一个目标模块调用定义在共享库中的任何函数,那么它就有自己的GOT和PLT。PLT是一个数组,其中每个条目是16字节代码。PLT[0]是一个特殊条目,跳转到动态链接器中。每个条目都负责调用一个具体的函数。PLT[[1]]调用系统启动函数 (__libc_start_main)。从PLT[[2]]开始的条目调用用户代码调用的函数。GOT是一个数组,其中每个条目是8字节地址。和PLT联合使用时,GOT[0]和GOT[[1]]包含动态链接器在解析函数地址时会使用的信息。GOT[[2]]是动态链接器在ld-linux.so模块中的入口点。其余的每个条目对应于一个被调用的函数,其地址需要在运行时被解析

所以我们首先需要找到GOT的位置

在edb来查看引用前与引用后的差别

引用前:

5.8 本章小结

本章我们进行链将多个代码、数据合并为一个可执行文件的过程

我们进行了hello虚拟空间的展示

进行了反汇编的查看

对重定位进行了深入的分析。

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

6.1 进程的概念与作用

进程: 指程序的一次运行过程。进程是具有独立功能的一个程序关于某个数据集合的一次运动活动,因为进程具有动态含义。进程具有自己的生命周期。

进程的作用:提供了一个独立的逻辑控制流和一个私有的虚拟地址空间。进程的引入简化了程序员的编程以及语言处理系统的处理。

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

Shell 是一种命令行解释器, 其读取用户输入的字符串命令, 解释并且执行命令. 它是一种特殊的应用程序, 介于系统调用/库与应用程序之间, 其提供了运行其他程序的的接口.它可以是交互式的, 即读取用户输入的字符串;也可以是非交互式的, 即读取脚本文件并解释执行, 直至文件结束. 无论是在类 UNIX, Linux 系统, 还是 Windows, 有很多不同种类的 Shell: 如类 UNIX, Linux 系统上的 Bash, Zsh 等; Windows 系统上的 cmd, PowerShell 等.shell可以合并编程语言以控制进程和文件,以及启动和控制其他程序。shell能够减少大量的重复输入和交互操作,能够进行批量的处理和自动化完成维护,减轻管理层的负担。

Shell-bash处理流程:

(1)从终端读入输入的命令。

(2)将输入字符串切分,分析输入内容,解析命令和参数。

(3)如果命令为内置命令则立即执行,如果不是内置命令则创建新的进程调用相应的程序执行。

(4)在程序执行期间始终接受键盘输入信号,并对输入信号做相应处理。

6.3 Hello的fork进程创建过程

在终端输入./hello

由于./hello不是终端命令,而是当前目录下的可执行文件,于是终端将会调用fork函数在当前进程中创建一个新的子进程。子进程得到与父进程用户级虚拟地址空间相同的副本,包括代码、数据、堆、共享库和用户栈。

6.4 Hello的execve过程

子进程创建成功后,再通过execve系统调用启动加载器,加载器删除子进程现有的虚拟内存段,并创建一组新的代码、数据、堆和栈段。新的堆和栈段被初始化为零。通过虚拟地址空间中的页映射到可执行文件的页大小的片。新代码被初始化为可执行文件的内容。最后加载器跳转到__start地址,它最终会调用应用程序的main函数。

6.5 Hello的进程执行

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

时间片是指一个进程执行它的控制流的一部分的每一个时间段。

调度是指在执行过程中,内核可以决定抢占当前进程,并重新开始一个先前被抢占的进程。

用户态是指进程运行在用户模式中时,不允许执行特权指令,比如停止处理器、改变模式位,或者发起一个I/O操作,也不允许用户模式中的进程直接引用地址空间中内核区内的代码和数据。

核心态是指进程运行在内核模式中时,可以执行指令集中的任何指令,并且可以访问内存中的任意位置。

用户态与核心态转换是指程序在涉及到一些操作时,例如调用一些系统函数,内核需要将当前状态从用户态切换到核心态,执行结束后再改回用户态。

shell进程在运行,等待命令行上的输入。

当我们运行hello程序时,shell通过系统调用来执行我们的请求。系统调用会将控制权传递给操作系统。

操作系统保存shell进程的上下文,依次通过fork,execve创建一个新的hello进程及其上下文,然后将控制权传递给新的hello进程。

此时hello在用户模式,当sleep函数调用时,触发陷阱,内核保存hello的上下文,加载其它进程上下文,控制传递给其它进程,此时hello进入内核模式。

当sleep结束,定时器发送中断信号,系统接收这个信息,hello切换回用户模式。调用getchar函数时,触发陷阱,hello进入内核模式,输入一个字符后触发键盘中断,hello回到用户模式并执行下一个程序return 0,程序结束。

hello程序结束后,操作系统恢复shell进程的上下文,并将控制权传回给shell,shell进程会继续等待下一个命令行输入。

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

6.6 hello的异常与信号处理

异常是指为响应某个事件将控制权转移到操作系统内核中的情况

Hello执行过程中会出现如下的异常:

陷阱   故障    终止    中断

Hello执行过程中会产生如下的信号:

SIGSTP   SIGCONT  SIGKILL  SIFGINT

陷阱   有意的,执行指令的结果 § Examples: 系统调用(System Call),用户程序和内核之间 的一个接口 § 陷阱处理程序将控制返回到下一条指令

故障 (Faults) § 不是有意的,但可能被修复 § Examples: 缺页(可恢复),保护故障(protection faults,不 可恢复), 浮点异常(floating point exceptions) § 处理程序要么重新执行引起故障的指令(已修复),要么 终止

终止 (Aborts) § 非故意,不可恢复的致命错误造成 § Examples: 非法指令,奇偶校验错误(parity error),机器检 查(machine check) § 中止当前程序

中断  处理器外部I/O设备引起 § 由处理器的中断引脚指示 § 中断处理程序返回到下 一条指令处

不停乱按

回车

Ctrl-c

Ctrl-z

Ctrl-z后运行ps

Ctrl-z后运行jobs

Ctrl-z后运行pstree

Ctrl-z后运行fg

Ctrl-z后运行kill

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

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

6.7本章小结

本章节我们学习了进程与shell的概念以及作用

 我们进行了hello程序被父程序fork一个子程序,被execve加载到上下文进行模式切换。

我们学习了异常处理与信号处理

7.1 hello的存储器地址空间

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

逻辑地址:由选择符和偏移量组成,经过寻址方式的计算或变换得到内存储器中的物理地址。表示为 [段标识符:段内偏移量]。hello程序经过编译后出现在汇编代码中的地址。

线性地址:某地址空间中的地址是连续的非负整数

虚拟地址:CPU通过生成一个虚拟地址来访问主存,这个虚拟地址在被送到内存之前先通过MMU转换为物理地址。

物理地址:程序运行时加载到内存地址寄存器中的地址,是物理内存意义上真正的地址。表示hello程序运行时某指令或某数据在内存地址上具体的地理位置。

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

段式管理是实现逻辑地址到线性地址转换机制的基础,段的特征有段基址、段限长、段属性。这三个特征存储在段描述符中,用以实现从逻辑地址到线性地址的转换。段描述符存储在段描述符表中,通常,我们使用段选择符定位段描述符在这个表中的位置。每个逻辑地址由16位的段选择符和32位的偏移量组成。

段基址规定了线性地址空间中段的开始地址。在保护模式下,段基址长32位。因为基址长度和寻址地址的长度相同,所以段基址可以是0-4GB范围内的任意地址。

和一个段有关的信息需要8个字节来描述,这就是段描述符。为了存放这些描述符,需要在内存中开辟出一段空间。在这段空间里所有的描述符都在一起集中存放,这就构成了一个描述符表,描述符表分为两种,GDT和LDT。

一些全局的段描述符,就放在"全局段描述符表(GDT)"中,一些局部的,例如每个进程自己的段描述符,就放在的"局部段描述符表(LDT)"中。

介绍一个完整的变换过程,给出一个完整的逻辑地址[段选择符:段内偏移地址]。首先看段选择符判断当前转换时GDT中的段还是LDT中的段,再根据相应寄存器得到其地址和大小。之后拿出段选择符中的前13位,在对应地址中查找到对应的段描述符,这样就知道了基址。根据基址和偏移量结合,就得到了所求的线性地址。

段地址转换过程:

1.IA-32首选确定要访问的段,然后决定使用的段寄存器。

2.根据段选择符号的TI字段决定是访问GDT还是LDT,他们的首地址则通过GTDR和LDTR来获得。

3.将段选择符的Index字段的值*8,然后加上GDT或LDT的首地址,就能得到当前段描述符的地址。(乘以8是因为段描述符为8字节)

4.得到段描述符的地址后,可以通过段描述符中BASE获得段的首地址。

5.将逻辑地址中32位的偏移地址和段首地址相加就可以得到实际要访问的物理地址

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

分页机制是实现虚拟存储的关键,位于线性地址与物理地址的变换之间设置。虚拟内存被组织为一个由存放在磁盘上的N个连续的字节大小的单元组成的数组。每字节都有一个唯一的虚拟地址,作为到数组的索引。磁盘上数组的内容被缓存在主存中。和存储器层次结构中其他缓存一样,磁盘上的数据被分割成块,这些块作为磁盘和主存之间的传输单元。VM系统通过将虚拟内存分割为称为虚拟页为大小固定的块来处理这个问题。每个虚拟页的大小固定。类似地,物理内存被分割为物理页,大小与虚拟页相同。

同任何缓存一样,虚拟内存系统必须用某种方法来判定一个虚拟页是否缓存在DRAM中的某个地方。如果是,系统还必须确定这个虚拟页存放在哪个物理页中。如果不命中,系统必须判断这个虚拟页存放在磁盘的哪个位置,在物理内存中选择一个牺牲页,并将虚拟页从磁盘复制到DRAM,替换这个牺牲页。

页表是一个存放在物理内存中的数据结构,将虚拟页映射到物理页。每次地址翻译硬件将一个虚拟地址转换为物理地址时读取页表。操作系统负责维护页表中的内容,以及再磁盘与DRAM之间来回传送页。

一级页表中的每个PTE负责映射虚拟地址空间中的一个内存片,这里每个片都是由连续的页面组成,如PTE0映射第一片,PTE1映射第二片,以此类推。最后PTE即为对应的PPN,再结合原先VA中的偏移量VPO=PPO,将PPN与PPO结合,即为所求的物理地址。

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

CPU生成一个VA,到MMU的TLB中寻找相应的VPN,若命中,则读取相应的PPN与原VA中的VPO结合即为所求的PA;

倘若TLB不命中,则将虚拟地址划分为4个VPN和一个VPO,每个VPN i都是从一个i级页表的索引,其中,1≤j≤4.当1≤j≤3时,第j级页表的每个PTE均为执行j+1级页表的基址,第4级页表的每个PTE为物理地址的VPN,而原VA的VPO=PPO,将PTE与PPO结合之后,便是物理地址PA。

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

得到物理地址PA后,通过其访问物理内存,物理地址由CI(组索引)、CT(标记位)、CO(偏移量)组成。首先使用CI进行组索引,如果匹配成功且块的有效位为1则命中,根据数据偏移量CO取出数据返回。如果没有匹配成功则不命中,向下一级缓存中查询数据。查询到数据后,放置策略是如果映射到的组有空闲块则直接放置,否则产生冲突,采用最近最少使用策略驱逐块并替换新块进入。

7.6 hello进程fork时的内存映射

当fork函数被当前进程调用时,内核为新进程创建数据结构,并分配给它一个唯一的PID。为了给这个新进程创建虚拟内存,它创建了当前进程的mm_struct、区域结构和页表的原样副本。它将两个进程中的页面标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。

当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面。

标签: 各式连接器fce17

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台