HIT-ICS 2023 大作业

计算机系统

大作业

题     目  程序人生-Hellos P2P  

专       业  未来技术人工智能(2+x) 

学     号     xx       

班   级       xx    

学       生       孔xx     

指 导 教 师        郑xx          

计算机科学与技术学院

2023年5月

摘  要

本文以hello.c为案例,详细介绍了一个程序在Linux下完整的生命周期。从程序代码的输入到编译、链接,再到程序的运行和最终结束及资源释放,我们逐步了解了计算机系统如何支持和管理程序。特别是对进程管理、存储管理和I/O管理方面的介绍,更加深刻地理解了程序的本质和构成,提高了对程序设计和开发的理解和技能。对程序的编译、加载、运行有了更深入的了解,将有助于开发出更高质量的程序。

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

目  录

第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章 概述

(0.5分)

1.1 Hello简介

Program to progress:首先将写好的C语言源程序保存为hello.c文件(program),通过预处理器cpp处理生成hello.i文件,此时仍为文本文件,通常为ASCII码表示的文本文件,然后通过编译器ccl生成汇编文件hello.s此时文件变成了机器级语言程序,经过编译器处理后生成了可重定位目标程序hello.o此时变为了二进制文件,最后hello.o以及静态库中的printf.o在链接器ld作用下生成可执行目标程序hello,接下来在bash中运行该程序,bash将为其创建一个进程,成为了progress。

0 to 0: 在bash中输入./hello运行hello程序,bash通过调用fork()创建子进程,同时在子进程中调用execve()函数,加载hello程序,CPU为运行的hello程序分配时间片并执行逻辑控制流。当程序执行结束后bash回收进程,释放内存,并删除与运行hello程序有关的数据结构,一切重新变为0,即为0 to 0。

1.2 环境与工具

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

软件环境:Windows10 64位以上;Vmware 11以上;Ubuntu 16.04 LTS 64位

开发工具:Visual Studio 2010 64位以上;CodeBlocks 64位;vi/vim/gedit+gcc

1.3 中间结果

名字

hello.i

hello的预处理结果

hello.s

hello.i的汇编结果

hello.o

hello.s翻译成的可重定位文件

hello

可执行文件

helloo.obj

hello.o的反汇编结果

helloo.elf

hello.oELF格式文件

hello.obj

hello的反汇编结果

hello.elf

helloELF格式文件

1.4 本章小结

本章介绍了Hello程序所经历的P2P和020过程的含义,紧接着列出了编写本论文过程中使用的软硬件环境,以及开发、调试工具,在最后列出了编写本论文生成的中间结果文件的名字及其作用。


第2章 预处理

(0.5分)

2.1 预处理的概念与作用

概念:预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程

作用:预处理器根据以‘#’开头的命令,修改原始的C程序,例如读取系统头文件,宏定义,模块化编程,条件编译

2.2在Ubuntu下预处理的命令

命令:gcc hello.c -E -o hello.i

图2.1 Ubuntu下预处理命令

2.3 Hello的预处理结果解析

图2.2 hello.i文件

使用指令:gedit hello.i

来打开hello.i程序并查看,发现原程序hello.c的include指令都被取代,这些头文件:stdio.h nistd.h stdlib.h在预处理过程中被取代

2.4 本章小结

本章介绍了预处理的概念和作用,并在Ubuntu下运行对hello.c预处理的命令“gcc -E hello.c -o hello.i”。在得到了预处理文件hello.i之后,通过对hello.i文件进行阅读,发现引入了头文件的内容导致代码长度增加至3091行,同时注释部分被删除。


第3章 编译

(2分)

3.1 编译的概念与作用

  1. 概念:广义的编译是说将某一种程序设计语言写的程序翻译成等价的另一种语言。此处是指利用编译程序从预处理文本文件(.i)产生汇编程序(.s)的过程。
  2. 流程: 编译包括词法分析,语法分析,中间代码,代码优化,目标代码等过程,在这个过程中高级语言被翻译成汇编语言,既可以被程序员理解,又更接近机器语言,是源程序转换成机器语言的关键环节。

3.2 在Ubuntu下编译的命令

命令:gcc -S hello.i -o hello.s

图3.1 Ubuntu下编译命令

图3.2 hello.s部分截图

3.3 Hello的编译结果解析

3.3.1 数据

1.常量

(1)立即数

图3.3 立即数4的表示

  1. 字符串

图3.4 字符串的表示

  1. 变量

图3.5 变量i的表示

局部变量i存放在栈上-4(%rbp)中

3.3.2 赋值

图3.6 赋值

变量i被赋值为0

3.3.3 类型转换

图3.7 类型转换

调用atoi函数将字符串转换成整型数据

3.3.4 算术操作

图3.8 算术操作

i++操作,通过该条语句将i=0进行不断加1

hello.s中的算数操作汇总如下:

subq      $32, %rsp

%rsp中的值减32

addq      $24, %rax

%rax中的值加24

addq      $8, %rax

%rax中的值加8

addl      $1, -4(%rbp)

地址(%rbp)-4中的值加1

leaq     .LC1(%rip), %rdi

加载有效地址到寄存器%rdi中

3.3.5 关系操作

图3.9 关系操作

该条语句会比较存放在-20(%rbp)中的argc与立即数4

3.3.6 数组/指针/结构操作

图3.10 数组操作

char *argv[]首地址保存在-32(%rbp)中,在输出argv[1]与argv[2]时,在数组首地址上加上对应的偏移量获得数组元素

3.3.7 控制转移

图3.11 控制转移

将argc与4比较,如果二者不相等则输出LC0中的字符串,如果相等,根据je命令,跳转到L2

3.3.8 函数操作

1.main函数

参数传递:传入参数argc,argv[],用寄存器%rdi和%rsi存储

函数调用:C语言程序会从main函数开始执行

函数返回:返回时将%eax赋值为0

图3.12 main函数

  1. printf函数

参数传递:传入了argv[1]和argv[2]的地址

图3.13 printf函数

  1. exit函数

参数传递:将%edi赋值为1

  1. atoi函数

参数传递:将%rdi赋值为argv[3]

  1. sleep函数

参数传递:将%edi赋值为atoi作用后的argv[3]

3.4 本章小结

本章通过gcc -S hello.i -o hello.s编译hello.i文件,获得hello.s文件,结合编译的知识,分析了此过程发生的相应处理,并详细的阐述了数据、赋值、类型转换、算术操作、逻辑/位操作、关系操作、数组/指针/结构操作、控制转移、函数操作的过程,对hello.s文件的上述过程进行了相应的解析。


第4章 汇编

2分)

4.1 汇编的概念与作用

  1. 概念:汇编是指汇编器将.s文件翻译成机器语言指令,并把这些指令打包成可重定位目标程序的格式,并将结果保存在.o文件中
  2. 作用:将汇编代码转换为机器指令,便于程序链接与运行

4.2 在Ubuntu下汇编的命令

命令:gcc -c hello.s -o hello.o

图4.1 汇编操作

4.3 可重定位目标elf格式

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

(示意图来自网络)

使用指令:readelf -a hello.o > helloo.elf

可读取hello.o的ELF文件内容,并存入helloo.elf中

ELF

图4.3 hello.o ELF头

ELF头(ELF header)以一个16字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。以hello.o为例,这个16字节序列为7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00

ELF头剩下的部分包含了帮助链接器语法分析和解释目标文件的信息,其中包括ELF头的大小、目标文件的类型(如可重定位、可执行或者共享的)、机器类型(如x86-64)、节头部表(section header table)的文件偏移,以及节头部表中条目的大小和数量。不同节的位置和大小是有节头部表描述的,其中目标文件中每个节都有一个固定大小的条目(entry)。

节头

图4.4 hello.o节头

节头: 包含了文件中出现的各个节的语义,包括节的类型、位置和大小等信息。

以hello.s为例,节头部表一共描述了13个不同节的位置、大小等信息。

1.text节,大小为0x92,类型为PROGBITS,标志为AX(只读可执行),偏移量为0X40,

2.rela.text节,大小为0xc0,类型为RELA,标志为I,偏移0x380字节

3.data节,已初始化的全局变量节,类型为PROGBITS,标志为WA(可读写),偏移0xd2

4.bss节,未初始化的全局和静态变量或初始化为0的全局或静态变量,大小为0,类型为PROGBITS,标志为WA,偏移0xd2

5.rodata节,只读数据,大小为0x33,标志为A(只读),偏移0xd8

6.comment节,版本控制信息,大小为0x24,标志为MS,偏移为0xef

7.note.GNU_stack节:标记可执行堆栈,大小为0x0字节,类型为PROGBITS,偏移量为0x12f

8.eh_frame节:处理异常,大小为0x38字节,类型为PROGBITS,偏移量为0x150,标志为A(表明该节的数据只读)。

9.rela.eh_frame节:.eh_frame节的重定位信息,大小为0x18字节,类型为RELA,偏移量为0x440,标志为I。

10.shstrtab节:包含节区名称,大小为0x61字节,类型为STRTAB,偏移量为0x188

11.symtab节:一个符号表,存放在程序中定义和引用的函数和全局变量的信息。大小为0x1b0字节,类型为SYMTAB,偏移量为0x338

12.strtab节:一个字符串表,包括.symtab和.debug节中的符号表,以及节头,偏移量为0x458字节

13.shstrtab节:大小为0x74字节,也是一个字符串表,偏移量为0x458字节

重定位节

图4.5 hello.o 重定位节

重定位节:包含text节中需要重定位的信息,当链接器将这个文件和其它文件链接时,这个位置的信息需要更新,也就是所说的重定位过程,八条重定位函数,分别对于.L0,puts,exit,.L1,printf,atoi,sleep,getchar进行重定位。

每个重定位条目包括offset:需要被修改的引用的节偏移;symbol:标识被修改引用应该指向的符号;type:重定位类型,告知链接器如何修改新的引用;attend:一些重定位要使用它对被修改引用的值做偏移调整。ELF定义了32种不同的重定位类型,两种最基本的重定位类型包括R_X86_64_PC32(重定位使用32位PC相对地址的引用)和R_X86_64_32(重定位使用32位绝对地址的引用)。

符号表

图4.6 hello.o 符号表

符号表: 目标文件的符号表中包含用来定位、重定位程序中符号定义和引用的信息。符号表索引是对此数组的索引。索引 0 表示表中的第一表项,同时也作为定义符号的索引。

4.4 Hello.o的结果解析

4.4.1 hello.o的反汇编

使用指令:objdump -d -r hello.o > helloo.objdump

图4.7 使用objdump反汇编

图4.8 hello.o反汇编结果

4.4.2 与hello.s对比

1.hello.o的反汇编代码在指令前增加了其十六进制表示,即机器指令码

2.分支转移:hello.s中直接跳转的跳转目标是用.L2,.L3,.L4等标号表示的,而hello.o的反汇编中跳转目标用main+偏移量来表示

3.函数调用:hello.s中call后跟的是函数名,而hello.o的反汇编中,call后跟的是下一条指令

4.4.3 机器语言的构成

机器指令通常由操作码和操作数两部分组成,操作码指出该指令所要完成的操作,操作数指出参与运算的对象,以及运算结果所存放的位置

4.4.4 与汇编语言的映射关系

1.操作数:汇编语言的操作数是十进制,机器语言的操作数是十六进制

2.分支转移:汇编语言中直接跳转的跳转目标是用.L2,.L3,.L4等标号表示的,而机器语言中跳转目标用main+偏移量来表示

3.函数调用:汇编语言中call后跟的是函数名,而机器语言中,call后跟的是下一条指令

4.5 本章小结

本章介绍了汇编的过程,经过汇编器,汇编语言转化为极其语言,分析了ELF文件的内容,另外比较了重定位前汇编程序和重定位后反汇编的差别,了解从汇编语言翻译成机器语言的转换处理和机器语言和汇编语言的映射关系。


5链接

(1分)

5.1 链接的概念与作用

  1. 概念:链接(linking)是将各种代码和数据片段收集并合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时(compile time),也就是在源代码被翻译成机器代码时;也可以执行于加载时(load time),也就是在程序被加载器(loader)加载到内存并执行时;甚至执行于运行时(run time),也就是在由应用程序来执行。
  2. 作用:链接器在软件开发过程中扮演着一个关键的角色,因为它们使得分离编译(separate compilation)成为可能。我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其它文件。

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 Ubuntu下链接命令及结果

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

    使用指令:readelf -a hello > hello.elf

图5.2 获取hello的ELF文件

ELF

图5.3 hello的ELF头

节头

图5.4 hello的节头

程序头

图5.5 获取hello的程序头

段节

图5.6 hello的段节

重定位节

图5.7 hello的重定位节

符号表

图5.8 hello的符号表

动态符号表

图5.9 hello的动态符号表

5.4 hello的虚拟地址空间

    

图5.7 edb查看hello的虚拟地址空间

hello的虚拟空间从0x401000开始,结束于0x401ff0

图5.8 readelf查看.text段信息

.text节开始于0x4010f0,大小为0xcd,可在edb中查看

图5.9 edb查看.text段信息

5.5 链接的重定位过程分析

使用指令:objdump -d -r hello > hello.objdump

图5.10 hello反汇编

hello与hello.o的不同之处在于以下两个方面:

1. hello中的汇编代码已经使用虚拟内存地址来标记了,从0x400000开始;而hello.o中的汇编代码是从0开始的,还没有涉及到虚拟内存地址,但经过链接,这些地址被确定下来了。

2.在hello.o中,只存在main函数的汇编指令;而在hello中,由于链接过程中引入了其他库的各种数据和函数,因此hello中除了main函数的汇编指令外,还包括大量其他的函数和指令,hello比起hello.o多出很多节:

例如:interp .note.ABI-tag .note.gnu.build-i:编译信息表.gnu.hash:gnu的扩展符号hash表.dynsym:动态符号表.dynstr:动态符号表中的符号名称.gnu.version:符号版本.gnu.version_r:符号引用版本.rela.dyn:动态重定位表.rela.plt:.plt节的重定位条目.init:程序初始化.plt:动态链接表.fini:程序终止时需要的执行的指令.eh_frame:程序执行错误时的指令.dynamic:存放被ld.so使用的动态链接信息.got:存放程序中变量全局偏移量.got.plt:存放程序中函数的全局偏移量.data:初始化过的全局变量或者声明过的函数。

重定位分析:

main函数中涉及重定位的指令的二进制代码被修改。在之前汇编的过程中,汇编器遇到对最终位置未知的目标引用,会产生一个重定位条目,告诉链接器在将目标文件合并成可执行文件时如何修改这个引用。因此在链接的过程中,链接器会根据重定位条目以及已知的最终位置对修改指令的二进制码,这个过程就是重定位的过程。下面以hello.o为例,说明hello如何进行重定位。

这里涉及到两种不同的重定位类型,分别是R_X86_64_PC32(重定位使用32位PC相对地址的引用)和R_X86_64_32(重定位使用32位绝对地址的引用)。对于第一种重定位类型,以第一个条目为例,第一个条目的信息说明需要重定位的位置在.text中偏移量为0x1b的地方。在hello.o中找到相应的位置。

Bf00000000这条指令的目的是传一个数据,这个数据应该是字符串常量,但它的最终定位未知,所以需要重定位,将地址改为绝对地址。

对于第二种类型,类似e800000000这条指令,这是为了调用函数,地址为puts的相对低者。

5.6 hello的执行流程

首先调用_init函数加载hello,然后调用main中用到的函数,例如printf函数,puts函数等,接着调用_start函数,然后开始执行main函数,main函数执行结束后还会执行_libc_csu_init 函数,_libc_csu_fini函数,_fini函数,最终程序结束

子程序名:_init

puts@plt

getchar@plt

printf@plt

exit@plt

sleep@plt

_start

_dl_relocate_static_pie

main

_libc_csu_init

_libc_csu_fini

_fini

5.7 Hello的动态链接分析

1. 在hello的elf文件中找到.got起始地址为0x403ff0

2. 如下图所示,在edb只显示了从0x401000到0x401ff0的数据,查找失败

5.8 本章小结

本章概括了链接的概念与作用,并且详细分析了hello.o是怎么链接成为一个可执行目标文件的过程。介绍了hello.o的ELF格式和各个节的含义,并且分析了hello的虚拟地址空间、重定位过程、执行流程、动态链接过程。通过可执行文件的程序头来分析重定位的过程,并解析了一个程序运行的全过程。最后简单介绍了动态链接这一现代计算机中极为重要的部分是怎么运作的。


6hello进程管理

(1分)

6.1 进程的概念与作用

概念:进程的经典定义就是一个执行中的程序的实例。系统中的每个程序都运行在某个进程的上下文中。每一个进程都有它自己的地址空间,一般情况下,包括文本区域、数据区域、和堆栈。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储区着活动过程调用的指令和本地变量。

作用:进程作为一个执行中程序的实例,系统中每个程序都运行在某个进程的上下文中,上下文是由程序正确运行所需的状态组成的。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。我们的程序好像是独占的使用处理器和内存,处理器好像是无间断的执行我们程序中的指令。

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

shell是指为使用者提供操作界面的软件,是一个交互型应用级程序,它接收用户命令,然后调用相应的应用程序。shell是系统的用户界面,提供了用户与内核进行交互操作的接口。

shell的作用:shell最重要的功能是命令解释,可以说shell是一个命令解释器。Linux系统上的所有可执行文件都可以作为shell命令来执行,同时它也提供一些内置命令。此外,shell还包括通配符、命令补全、命令历史、重定向、管道、命令替换等很多功能。

处理流程:从终端读入输入的命令。将输入字符串切分获得所有的参数,如果是内置命令则立即执行,否则调用相应的程序为其分配子进程并运行,shell应该接受键盘输入信号,并对这些信号进行相应处理

6.3 Hello的fork进程创建过程

shell通过调用fork函数创建一个新的子进程,子进程与父进程拥有相同但独立的地址空间,每个进程有相同的用户栈、相同的本地变量值、相同的堆、相同的全局变量值以及相同的代码,父进程与子进程的区别在于他们的pid不同,他们是并发执行的独立进程

6.4 Hello的execve过程

execve函数在当前进程的上下文中加载并运行一个新程序,execve函数加载并运行可执行目标文件,传递参数列表argv和环境变量envp,如果成功则不返回,如果错误则返回到调用程序

6.5 Hello的进程执行

1.上下文信息:上下文就是内核重新启动一个被抢占的进程所需要的状态,它由一些对象的值组成,包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构,包含有关当前进程信息的进程表,包含进程已打开文件的信息的文件表

2.进程时间片:一个进程执行它的控制流的一部分的每一时间段叫做时间片

3.进程调度:调度器采用上下文切换的机制将控制转移到新进程,首先保存当前进程的上下文,然后恢复某个先前被抢占的进程被保存的上下文,最后将控制传递给这个新恢复的进程

4.用户态与内核态转换:处理器使用一个寄存器区分这两种状态,当没有设置模式位时,为用户模式;设置模式位时,为内核模式。用户模式就是运行相应进程的代码内容,此时进程不允许运行特权指令,也不允许直接引用地址空间中内核区内的代码和数据,内核模式中,进程可以运行任何指令

6.6 hello的异常与信号处理

hello执行过程中出现的异常

1.中断:在执行hello程序时,由处理器外部的I/O设备的信号引起

2.陷阱:陷阱是有意的异常,例如hello执行sleep函数

3.故障:故障是可能恢复的错误,可能被故障处理程序修正

4.终止:终止是不可恢复的致命错误造成的结果,通常是一些硬件错误

图6.1 正常运行hello,ctrl-Z中断程序

图6.2 ps 查看进程,jobs查看挂起进程

图6.3 pstree 查看hello的位置

图6.4 fg 1 调到前台

图6.5 运行进程后 ctrl-C结束进程

6.7本章小结

本章介绍了计算机中最重要的概念之一——进程。可以说现代计算机离不开进程的概念,只有充分了解进程的创建与异常流控制,才可能成为一个优秀的程序员。本章介绍了Shell的一般处理流程,调用 fork 创建新进程,调用 execve 执行 hello,hello的进程执行,hello 的异常与信号处理。


7hello的存储管理

( 2分)

7.1 hello的存储器地址空间

  1. 逻辑地址:逻辑地址是指由程序产生的与段相关的偏移地址部分。例如,在进行C语言指针编程中,可以使用&操作读取指针变量的值,这个值就是逻辑地址,是相对于当前进程数据段的地址。一个逻辑地址由两部份组成:段标识符和段内偏移量。
  2. 线性地址:线性地址是逻辑地址到物理地址变换之间的中间层。程序代码会产生逻辑地址,或者说是段中的偏移地址,加上相应段的基地址生成了一个线性地址。如果启用了页式管理,那么线性地址可以再变换产生物理地址。若没有启用页式管理,那么线性地址直接就是物理地址。
  3. 虚拟地址:因为虚拟内存空间的概念与逻辑地址类似,因此虚拟地址和逻辑地址实际上是一样的,都与实际物理内存容量无关。
  4. 物理地址:存储器中的每一个字节单元都给以一个唯一的存储器地址,用来正确地存放或取得信息,这个存储器地址称为物理地址,又叫实际地址或绝对地址。

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

8086处理器的寄存器是16位的,后来引入了段寄存器,可以访问更多的地址空间但不改变寄存器和指令的位宽。8086共设计了20位宽的地址总线,逻辑地址为段寄存器左移4位加上偏移地址得到20位地址。

内存被分为了不同的段,段寄存器有一个栈、一个代码寄存器和两个数据寄存器,在实模式下,逻辑地址、线性地址、实际的物理地址是完全一样的,保护模式下线性地址还需要经过分页机制才能够得到物理地址,线性地址也需要逻辑地址通过段机制来得到

所有的段由段描述符描述,而多个段描述符能组成一个数组,我们称成功数组为段描述表。段描述符中的BASE字段对我们翻译线性地址至关重要的。

BASE字段表示的是包含段的首字节的线性地址,也就是一个段的开始位置的线性地址。

为了得到BASE字段,我们利用索引号从GDT(全局段描述表)或LDT(局部段描述符表)中得到段描述符。选择GDT还是LDT取决于段选择符中的T1,若T1等于0则选择GDT,反之选择LDT。

这样我们就得到了BASE。最后通过BASE加上段偏移量就得到了线性地址

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

通过分页机制实现线性地址(书里的虚拟地址VA)到物理地址(PA)之间的转换。

分页机制是指对虚拟地址内存空间进行分页。首先Linux系统有自己的虚拟内存系统,Linux将虚拟内存组织成一些段的集合,段之外的虚拟内存不存在因此不需要记录,当hello运行时内核为hello进程维护一个段的任务结构(task_struct)。

虚拟页是指系统将每个段分为大小固定的块,linux下一页为4KB

物理页和虚拟页一样,他是物理内存的分割,MMU使用页表来实现二者之间的映射

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

每次CPU产生一个虚拟地址,MMU就必须查阅一个PTE,以便将虚拟地址翻译为物理地址,时间开销较大,因此TLB诞生了,对于四级页表,MMU必须访问4个PTE,这里TLB起了作用,将不同层次上页表的PTE缓存起来,使得多级页表的地址翻译不比单级页表慢很多

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

1.组选择:Cache从地址中抽取出组索引位,这些位被解释成一个对应一个组号的无符号整数,从而找到相应的组

2.行匹配:将地址标记位与组中每行的标记位相比较,找到相应的行且该行有效位为1,则缓存命中

3.字选择:缓存命中时,通过块偏移位可知第一个字节的偏移量,即可取出这个字节内容返回CPU

4.缓存不命中:从存储器层次结构中的下一层取出被请求的块,将新块存储在组索引位指示的组中的一个高速缓存行中

7.6 hello进程fork时的内存映射

fork函数被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的pid。为了给新进程创建虚拟内存,它创建了当前进程的mm_struct、区域结构和页表的原样副本。当fork函数在新进程中返回时,新进程现在的虚拟内存和调用fork时存在的虚拟内存相同,进行写操作时复制机制就会创建新页面,保持了私有地址空间的抽象概念

7.7 hello进程execve时的内存映射

execve函数用a.out程序替代了当前程序,加载并运行a.out需要以下几个步骤

1.删除已存在的用户区域

2.映射私有区域

3.映射共享区域

4.设置程序计数器

下一次调度该进程时,它将从程序计数器指向的入口点开始执行,linux根据需要换入代码和数据页面

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

缺页故障是一种常见的故障,当指令引用一个虚拟地址,在 MMU 中查找页表时发现与该地址相对应的物理地址不在内存中,因此必须从磁盘中取出的时候就会发生故障。

如果程序执行过程中遇到了缺页故障,则内核调用缺页处理程序。处理程序会进行如下步骤:检查虚拟地址是否合法,如果不合法则触发一个段错误,程序终止。然后检查进程是否有读、写或执行该区域页面的权限,如果不具有则触发保护异常,程序终止。在两步检查都无误后,内核选择一个牺牲页面,如果该页面被修改过则将其交换出去,换入新的页面并更新页表。然后将控制转移给hello进程,再次执行触发缺页故障的指令。

7.9动态存储分配管理

动态内存分配器维护着一个进程的虚拟内存区域,称为堆,它紧接在未初始化的数据区域后开始,向更高的地址生长,分配器有两种基本风格

1.显式分配器,要求应用显式地释放任何已分配的块

2.隐式分配器,要求分配器检测一个已分配块何时不再被程序所使用,那么就释放这个块

7.10本章小结

本章介绍了程序是如何组织储存器的。

先从程序所使用的不同地址开始,分别介绍了逻辑地址、虚拟地址(线性地址)以及物理地址。并介绍了计算机是怎么一步步将地址从逻辑地址变化到虚拟地址再从虚拟地址变化到物理地址的。其中着重介绍了虚拟地址和物理地址之间的映射,以及进程是怎么映射到虚拟地址空间的。之后还介绍程序是怎么利用Cache来获取物理地址中所存放的数据的。最后简单介绍了虚拟地址中极为重要的概念——缺页异常,以及简单介绍了动态内存分配机制。


结论

hello所经历过程:

首先,hello.c程序经过预处理,头文件被插入,变成了hello.i文件,编译器再将文本文件hello.i翻译成新的文本文件hello.s,高级程序设计语言编写的源程序转化为了以汇编语言表示的目标程序,hello.s文件再经过汇编,得到可重定位目标文件hello.o,hello.o还需要链接才能变成可执行文件hello。

在终端输入./hello,shell解析输入的命令,调用fork函数创建子进程,然后调用execve函数加载程序,MMU,三级Cache等各个组成部件共同运转,最终,结果被输出到终端上,程序被回收并删除相关数据。

感悟:

hello从诞生到结束,依靠硬件、操作系统、软件进行一系列协作配合下才得以完成,需要多方面协作与配合才可以实现这样一个复杂的工作系统,而维护这个系统,又引入了许多机制,如:分页,信号控制等等,以准确及时对系统状态进行调整,实现各组分之间的协作配合。而老师上课也经常强调“想要写出更高效的代码必须要了解计算机系统底层的知识”,在hello的历程中我也学到了许多了解程序的技巧,从而支撑对程序的理解与后续优化,计算机系统给我带来了很多实用的知识。


附件

文件名字

hello.i

hello的预处理结果

hello.s

hello.i的汇编结果

hello.o

hello.s翻译成的可重定位文件

hello

可执行文件

helloo.obj

hello.o的反汇编结果

helloo.elf

hello.oELF格式文件

hello.obj

hello的反汇编结果

hello.elf

helloELF格式文件


参考文献

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值