HIT-CSF2019 大作业

计算机系统基础

大作业

题 目 程序人生-Hello’s P2P
专 业 机械电子工程
学   号 1160800704
班   级 1608503
学 生 袁子寒  
指 导 教 师 史先俊

计算机科学与技术学院
2019年3月
摘 要
本文以hello为例,使用各种工具,分析和介绍了程序在Linux下的P2P和020过程。详细介绍了hello程序的生命周期

关键词:hello;生命周期;O2O;P2O

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

目 录

第1章 概述 - 4 -
1.1 HELLO简介 - 4 -
1.2 环境与工具 - 4 -
1.3 中间结果 - 4 -
1.4 本章小结 - 5 -
第2章 预处理 - 6 -
2.1 预处理的概念与作用 - 6 -
2.2在UBUNTU下预处理的命令 - 6 -
2.3 HELLO的预处理结果解析 - 6 -
2.4 本章小结 - 8 -
第3章 编译 - 9 -
3.1 编译的概念与作用 - 9 -
3.2 在UBUNTU下编译的命令 - 9 -
3.3 HELLO的编译结果解析 - 10 -
3.4 本章小结 - 15 -
第4章 汇编 - 16 -
4.1 汇编的概念与作用 - 16 -
4.2 在UBUNTU下汇编的命令 - 16 -
4.3 可重定位目标ELF格式 - 17 -
4.4 HELLO.O的结果解析 - 20 -
4.5 本章小结 - 22 -
第5章 链接 - 23 -
5.1 链接的概念与作用 - 23 -
5.2 在UBUNTU下链接的命令 - 23 -
5.3 可执行目标文件HELLO的格式 - 24 -
5.4 HELLO的虚拟地址空间 - 26 -
5.5 链接的重定位过程分析 - 28 -
5.6 HELLO的执行流程 - 30 -
5.7 HELLO的动态链接分析 - 30 -
5.8 本章小结 - 30 -
第6章 HELLO进程管理 - 31 -
6.1 进程的概念与作用 - 31 -
6.2 简述壳SHELL-BASH的作用与处理流程 - 31 -
6.3 HELLO的FORK进程创建过程 - 31 -
6.4 HELLO的EXECVE过程 - 31 -
6.5 HELLO的进程执行 - 31 -
6.6 HELLO的异常与信号处理 - 32 -
6.7本章小结 - 32 -
第7章 HELLO的存储管理 - 33 -
7.1 HELLO的存储器地址空间 - 33 -
7.2 INTEL逻辑地址到线性地址的变换-段式管理 - 33 -
7.3 HELLO的线性地址到物理地址的变换-页式管理 - 33 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 34 -
7.5 三级CACHE支持下的物理内存访问 - 34 -
7.6 HELLO进程FORK时的内存映射 - 34 -
7.7 HELLO进程EXECVE时的内存映射 - 35 -
7.8 缺页故障与缺页中断处理 - 35 -
7.9动态存储分配管理 - 35 -
7.10本章小结 - 36 -
第8章 HELLO的IO管理 - 37 -
8.1 LINUX的IO设备管理方法 - 37 -
8.2 简述UNIX IO接口及其函数 - 37 -
8.3 PRINTF的实现分析 - 38 -
8.4 GETCHAR的实现分析 - 38 -
8.5本章小结 - 39 -
结论 - 39 -
附件 - 40 -
参考文献 - 41 -

第1章 概述
1.1 Hello简介
根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
P2P(Program to Process):是指将hello.c(Program),经过预处理(Precompile),编译(Compile),汇编(Assemble),链接(Link)四个步骤之后生成可执行文件(Program),然后由shell创建新的建进程(Process)运行它。
020(Zero to Zero):从Zero(一无所有)开始,shell执行execve,为其映射出虚拟内存,然后在开始运行进程的时候分配并载入物理内存, hello的程序,将其output的东西显示到屏幕,然后hello进程结束,shell回收内存空间,最后再到Zero。
1.2 环境与工具
列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。
硬件环境:

  X64CPU;     8GHz;   4GRAM;      1TB HD

软件环境:

  Windows10 64位;VMware;     Ubuntu 16.04 LTS 64位

使用工具:

  gcc,codeblocks,objdump,gdb,edb,hexedit

1.3 中间结果
hello.c(源文件)
hello.i(hello.c预处理之后的程序文本)

hello.s(hello.i编译后的汇编文件)
hello.o(hello.s生成的可重定位文件)
hello_o.s(hello.o反汇编的结果)
hello(链接后可执行的hello二进制文件)
1.4 本章小结
总结了整个实验中的中间结果,总结了所在的软硬件环境还有使用的工具。

第2章 预处理
2.1 预处理的概念与作用
预处理是指编译器在编译之前进行的处理,预处理器会对以字符#开头的预处理指令(宏定义,文件包含,条件编译)做解释,修改原始的c程序。生成.i文件。
预处理的作用:方便编译器正确编译其为汇编文件。

2.2在Ubuntu下预处理的命令
linux 下命令为 gcc -E xxx.c -o xxx.i
截图,展示预处理过程!
在这里插入图片描述
图1 hello.c->hello.i
2.3 Hello的预处理结果解析
预处理后的.i文件有3118行,预处理后,注释等一些信息被删除了。#include消失了,一开始的这三个头文件变成了源码复制进了这个.i文件,包括可能使用到的函数的名字,运行库的位置等。这样方便了编译器的操工作。原本的内容很少,集中在最后几行。
在这里插入图片描述
图2 hello.i
在这里插入图片描述
图3 hello.i
2.4 本章小结
本章节简单介绍了C语言编译前的预处理过程,介绍了预处理过程的概念和作用,对预处理过程进行演示,对hello.i进行了解析。

第3章 编译
3.1 编译的概念与作用
编译是指将高级语言实现的代码,翻译成为机器能够理解的机器语言的过程。它将高级语言转换成更接近机器语言的汇编语言。
编译时将高级语言转换为二进制文件的中间步骤,它使得将高级语言转换成计算机可执行的二进制文件这个操作更加方便
注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序

3.2 在Ubuntu下编译的命令
gcc -S hello.i -o hello.s
在这里插入图片描述
在这里插入图片描述
图5 hello.s
应截图,展示编译过程!
3.3 Hello的编译结果解析
打开hello.s.hello.s相较于.i文件来说小了很多,他只有66行。因此可以全部粘贴出来。
.file “hello.c”
.text
.globl sleepsecs
.data
.align 4
.type sleepsecs, @object
.size sleepsecs, 4
sleepsecs:
.long 2
.section .rodata
.align 8
.LC0:
.string “\347\224\250\346\263\225: Hello \345\255\246\345\217\267 \345\247\223\345\220\215\357\274\201”
.LC1:
.string “Hello %s %s\n”
.text
.globl main
.type main, @function
main:
.LFB5:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movl %edi, -20(%rbp)
movq %rsi, -32(%rbp)
cmpl $3, -20(%rbp)
je .L2
leaq .LC0(%rip), %rdi
call puts@PLT
movl $1, %edi
call exit@PLT
.L2:
movl $0, -4(%rbp)
jmp .L3
.L4:
movq -32(%rbp), %rax
addq $16, %rax
movq (%rax), %rdx
movq -32(%rbp), %rax
addq $8, %rax
movq (%rax), %rax
movq %rax, %rsi
leaq .LC1(%rip), %rdi
movl $0, %eax
call printf@PLT
movl sleepsecs(%rip), %eax
movl %eax, %edi
call sleep@PLT
addl $1, -4(%rbp)
.L3:
cmpl $7, -4(%rbp)
jle .L4
call getchar@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE5:
.size main, .-main
.ident “GCC: (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0”
.section .note.GNU-stack,"",@progbits
在这里插入图片描述
图5 hello.s
在这里插入图片描述
图6 hello.s
在这里插入图片描述
图7 hello.s
3.3.1全局变量sleepsecs
对于全局变量int sleepsecs编译结果见6,7行。而sleepsecs的赋值语句编译结果见9,10行。

3.3.2局部变量i
i=0这一赋值语句编译结果见37行,i明显是4个字节的。
i++这一算术操作编译结果见53行。
i<10这一操作编译结果见55,56行。

3.3.3参数argc
见30至36行,计算机使用-0x20(%rbp)代表的栈空间来保存argc。

3.3.4立即数
见34,35行为立即数exit(1)的表示。

3.3.5字符串
字符串被保存在了rodata中,见12至15行。

3.3.6数组
数组char argv[],argv指针的起始地址为argv,在源程序中cha分别有arg[1],arg[2]两个,在汇编语言中发现分配的寄存器为%rax,%rdx。见39至53行。

3.3.7if的实现
汇编语言中运用条件跳转和比较实现if else。在本例中,具体汇编语言见30,31行。用je,cmpl实现。

3.3.8for循环的实现
汇编语言中运用条件跳转和比较实现for循环。见55,56行的cmpl和jle。

3.3.9函数的实现
对hello.c,通过call调用函数,通过ret实现return 0这一操作。

此部分是重点,说明编译器是怎么处理C语言的各个数据类型以及各类操作的。应分3.3.1~ 3.3.x等按照类型和操作进行分析,只要hello.s中出现的属于大作业PPT中P4给出的参考C数据与操作,都应解析。

3.4 本章小结
本章分析了一个C语言程序是如何被编译器编译成一个汇编程序的。分析了编译器是怎么处理C语言的各个数据类型以及各类操作的。

第4章 汇编
4.1 汇编的概念与作用
汇编指将汇编语言翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将保存结果保存在在.o的二进制文件中。
汇编的作用是将汇编指令根据汇编指令和机器指令的对照表一一翻译转换成机器可以直接读取分析的机器指令。
注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。
4.2 在Ubuntu下汇编的命令
命令为gcc -c hello.s -o hello.o
在这里插入图片描述
图8 hello.o
应截图,展示汇编过程!
4.3 可重定位目标elf格式
输入readelf -a hello.o指令。
在这里插入图片描述
图9 ELF头
如图为节头ELF Header,用于总体描述ELF文件各个信息的段。包括ELF头的大小、目标文件的类型、机器类型、字节头部表的文件偏移,以及节头部表中条目的大小和数量等信息。
在这里插入图片描述
图10 节头部表
如图为节头,包含文件中出现的各个节的语义,包括节的类型、位置和大小等信息。
重定位节有.rela.text以及.rela.eh_frame。
在这里插入图片描述
图11 重定位节.rela.text
.rela.text:这个节包含了.text节中需要进行重定位的信息。这些信息描述的位置,在由.o文件生成可执行文件的时候需要被修改(重定位)。进行重定位声明的有sleepsecs ,printf , puts , exit, getchar , sleep ,rodata里面的.L0和.L1。
在这里插入图片描述
图12 重定位节.rela.eh_frame
rela.eh_frame是eh_frame节的重定位信息
分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。
4.4 Hello.o的结果解析
采用objdump -d -r hello.o分析hello.o的反汇编。结果如下。经由与hello.s的比较可以看出,二者的区别不大。主要区别有:
1.分支转移:反汇编代码跳转指令的操作数使用的不是段名称而是确定的地址,因为段名称只是在汇编语言中便于编写的助记符,所以在汇编成机器语言之后显然不存在。
2. 操作数在hello.s里面是十进制,在hello.o里面是十六进制。
3. 在hello.s中,函数调用直接跟着函数名称,而在反汇编程序中,call的目标地址是当前下一条指令。
hello.o: 文件格式 elf64-x86-64

Disassembly of section .text:

0000000000000000 :
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 20 sub $0x20,%rsp
8: 89 7d ec mov %edi,-0x14(%rbp)
b: 48 89 75 e0 mov %rsi,-0x20(%rbp)
f: 83 7d ec 03 cmpl $0x3,-0x14(%rbp)
13: 74 16 je 2b <main+0x2b>
15: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 1c <main+0x1c>
18: R_X86_64_PC32 .rodata-0x4
1c: e8 00 00 00 00 callq 21 <main+0x21>
1d: R_X86_64_PLT32 puts-0x4
21: bf 01 00 00 00 mov $0x1,%edi
26: e8 00 00 00 00 callq 2b <main+0x2b>
27: R_X86_64_PLT32 exit-0x4
2b: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
32: eb 3b jmp 6f <main+0x6f>
34: 48 8b 45 e0 mov -0x20(%rbp),%rax
38: 48 83 c0 10 add $0x10,%rax
3c: 48 8b 10 mov (%rax),%rdx
3f: 48 8b 45 e0 mov -0x20(%rbp),%rax
43: 48 83 c0 08 add $0x8,%rax
47: 48 8b 00 mov (%rax),%rax
4a: 48 89 c6 mov %rax,%rsi
4d: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 54 <main+0x54>
50: R_X86_64_PC32 .rodata+0x1b
54: b8 00 00 00 00 mov $0x0,%eax
59: e8 00 00 00 00 callq 5e <main+0x5e>
5a: R_X86_64_PLT32 printf-0x4
5e: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 64 <main+0x64>
60: R_X86_64_PC32 sleepsecs-0x4
64: 89 c7 mov %eax,%edi
66: e8 00 00 00 00 callq 6b <main+0x6b>
67: R_X86_64_PLT32 sleep-0x4
6b: 83 45 fc 01 addl $0x1,-0x4(%rbp)
6f: 83 7d fc 07 cmpl $0x7,-0x4(%rbp)
73: 7e bf jle 34 <main+0x34>
75: e8 00 00 00 00 callq 7a <main+0x7a>
76: R_X86_64_PLT32 getchar-0x4
7a: b8 00 00 00 00 mov $0x0,%eax
7f: c9 leaveq
80: c3 retq
objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。
说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。
4.5 本章小结
本章节进行并分析了汇编文件hello.s到机器指令hello.o的过程,分它们的不同。了解了汇编语言到机器语言的变化
同时,本章还分析了分析elf文件的结构。

第5章 链接
5.1 链接的概念与作用
链接是指将各种代码和数据片段收集并组合成一个单一文件的过程,这个文件可被加载到内存并执行。链接可以执行于编译时,也可以执行于加载时,甚至于运行时。
链接的作用是实现分离编译,目标代码不能直接执行,要想将目标代码变成可执行程序,还需要进行链接操作。才会生成真正可以执行的可执行程序。链接操作最重要的步骤就是将函数库中相应的代码组合到目标文件中。lg
注意:这儿的链接是指从 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/libc.so /usr/lib/x86_64-linux-gnu/crtn.o
在这里插入图片描述
图13 链接命令
使用ld的链接命令,应截图,展示汇编过程! 注意不只连接hello.o文件
5.3 可执行目标文件hello的格式
在这里插入图片描述
图14 ELF头
如图为节头ELF Header,用于总体描述ELF文件各个信息的段。包括ELF头的大小、目标文件的类型、机器类型、字节头部表的文件偏移,以及节头部表中条目的大小和数量等信息。
在这里插入图片描述
图15 节头
在这里插入图片描述
图16 节头
如图为节头,如图为Section Header,包含文件中出现的各个节的语义,包括节的类型、位置和大小等信息。

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

在这里插入图片描述
图17 虚拟空间(图像文件名称为虚拟空间.png)
在这里插入图片描述
图19 程序头
可执行文件中加载的信息从0x400000处开始存放,hello中存放位置为0x400000—0x400ff0。从5.3节(图为上图)可以发现可执行文件共分为8个节每个节对应的偏移地址,虚拟地址位置,物理地址位置,文件大小,存储大小,标志和对齐方式都存储在该节中。
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
5.5 链接的重定位过程分析
生成反汇编文件,发现与hello.o相比,hello反汇编文件多了许多章节,例如
.init:程序初始化执行代码
.plt:动态链接表
.fini:程序终止时需要的执行的指令
.dynamic:存放被ld.so使用的动态链接信息
.got.plt:存放程序中函数的全局偏移量

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
图20,21,22 反汇编文件
重定位:重定位时,将合并输入模块,并为每个符号分配运行时的地址。在hello到hello.o中,首先是重定位节和符号定义,链接器将所有输入到hello中相同类型的节合并为同一类型的新的聚合节。然后,链接器将运行时内存地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每一个符号。当这一步完成时,程序中的每条指令和全局变量都有唯一的运行时内存地址了。然后是重定位节中的符号引用,链接器会修改hello中的代码节和数据节中对每一个符号的引用,使得他们指向正确的运行地址。
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
5.6 hello的执行流程
_dl_start
_dl_init
__stat
_cax_atexit
_new_exitfn
_libc_start_main
_libc_csu_init
_main
_printf
_exit
_sleep
_getchar
_dl_runtime_resolve_xsave
_dl_fixup
_dl_lookup_symbol_x
说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。
5.7 Hello的动态链接分析
分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。
5.8 本章小结
本章分析了hello的elf文件,分析了虚拟地址空间的分配,重定位和执行过程还有动态链接的过程。并将其反汇编与hello.o进行了对比。

第6章 hello进程管理
6.1 进程的概念与作用
进程是计算机程序需要进行对数据集合进行操作所运行的一次活动,它好像是独占地使用处理器和内存。
作用:用户通过向shell输入一个可执行目标文件的名字,运行程序时,shell 就会创建一个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件。
6.2 简述壳Shell-bash的作用与处理流程
作用:Shell是一个交互型的应用级程序,调用系统来执行程序,控制各个程序的运行。
处理流程:shell从终端读入输入的命令。将输入字符串切分获得所有的参数。如果是内置命令则立即执行,否则调用相应的程序为其分配子进程并运行。shell也键盘输入信号,并对这些信号进行相应处理
6.3 Hello的fork进程创建过程
Hello程序运行过程中shell调用fork(),内核创建一个新的子进程,为其分配与其父进程相同的上下文,新创建的子进程几乎完全与父进程相同(PID不同)。fork()调用一次,返回两次。父进程与子进程是并发运行的独立进程,内核能够以任意方式交替执行它们的逻辑控制流的指令。子进程执行期间,父进程默认选项是显示等待子进程的完成。
6.4 Hello的execve过程
execve 函数加载并运行可执行目标文件filename, 且带参数列表argv 和环境变量列表envp 。它一般调用一次后不返回,只有当出现错误时才会返回到调用程序。
创建进程后,execve函数加载并运行hello,加载器删除子进程的虚拟内存段,并创建新的代码,数据,堆和段栈,并将其初始化,将可执行文件的片复制到代码段和数据段,最后设置PC指向开始的地址,使得下次调用直接执行。

6.5 Hello的进程执行
Linux系统中的每个程序都运行在一个进程上下文中,有自己的虚拟地址空间。
当shell 运行一个程序时,父进程生成一个子进程。通过execve 系统子进程调用启动加载器。加载器删除子进程现有的虚拟内存段,并创建一组新的代码、数据、堆和栈段。新的栈和堆段被初始化为零。通过将虚拟地址空间中的页映射到可执行文件的页大小的片。新的代码和数据段袚初始化为可执行文件的内容。最后,加载器跳转到_start地址,它最终会调用应用程序的main函数。
处理器通常使用一个寄存器提供两种模式的区分,该寄存器描述了进程当前享有的特权,当没有设置模式位时,进程就处于用户模式中,用户模式的进程不允许执行特权指令,也不允许直接引用地址空间中内核区内的代码和数据;设置模式位时,进程处于内核模式,该进程可以执行指令集中的任何命令,并且可以访问系统中的任何内存位置。hello初始运行在用户模式,在hello进程调用sleep之后陷入内核模式
结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。
6.6 hello的异常与信号处理

hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
6.7本章小结
本章介绍了进程,介绍了shell的处理流程,fork的创建过程,execve过程等。

第7章 hello的存储管理
7.1 hello的存储器地址空间
线性地址是指分段机制下CPU寻址是二维的地址。即段地址:偏移地址,CPU不可能认识二维地址,因此需要转化成一维地址即,段地址*16+偏移地址,这样得到的地址便是线性地址。
虚拟地址是指逻辑地址,又叫虚地址。现代计算机通过虚拟地址寻址,通过MMU(内存管理单元)翻译成物理地址。Hello程序中,各个节的地址都是指的虚拟地址。
逻辑地址是指程序运行由CPU产生的与段相关的偏移地址部分。他是描述一个程序运行段的地址。
物理地址是指计算机系统的主存被组织成一个由M个连续的字节大小单元组成的数组,每字节都有一个唯一的物理地址。
结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。
7.2 Intel逻辑地址到线性地址的变换-段式管理
逻辑地址由两部分组成,段标识符,段内偏移量。段标识符是一个16位长的字段组成,称为段选择符,其中前13位是一个索引号。后面三位包含一些硬件细节。索引号,可以直接理解成数组下标,对应的“数组”就是段描述符表,段描述符具体描述了一个段地址,这样,很多段描述符就组成段描述符表。可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段。
动态分区分配方式中,系统为整个进程分配一个连续的内存空间。而在段式存储管理系统中,系统则为每个段分配一个连续的分区,而进程中的各个段可以不连续地存放在内存的不同分区中。程序加载时,操作系统为所有段分配其所需内存,这些段不必连续,物理内存的管理采用动态分区的管理方法。在为某个段分配物理内存时,可以采用首先适配法、下次适配法、最佳适配法等方法。在回收某个段所占用的空间时,要注意将收回的空间与其相邻的空间合并。段式存储管理也需要硬件支持,实现逻辑地址到物理地址的映射。

7.3 Hello的线性地址到物理地址的变换-页式管理
线性地址被分以固定长度为单位的组,称为页。线性地址最大可以为4G,用4KB来划分的话整个地址就被划分为2^20个页,这个数组称为页目录,目录中的每个目录项,就是对应页的地址。而另一类“页”,我们称之为物理页,或者是页框、页桢的。是分页单元把所有的物理内存也划分为固定长度的管理单位,它的长度一般与内存页是一一对应的。
1.分页单元中,页目录是唯一的,它的地址放在CPU的cr3寄存器中,是进行地址转换的开始点。
2.每一个活动的进程,因为都有其独立的对应的虚似内存(页目录也是唯一的),那么它也对应了一个独立的页目录地址。——运行一个进程,需要将它的页目录地址放到cr3寄存器中
3.每一个32位的线性地址被划分为三部份,面目录索引(10位):页表索引(10位):偏移(12位)
依据以下步骤进行变换:
1.从cr3中取出进程的页目录地址(操作系统负责在调度进程的时候,把这个地址装入对应寄存器)。
2.根据线性地址前十位,在数组中,找到对应的索引项,因为引入了二级管理模式,页目录中的项,不再是页的地址,而是一个页表的地址。(又引入了一个数组),页的地址被放到页表中去了。
3.根据线性地址的中间十位,在页表(也是数组)中找到页的起始地址。
4.将页的起始地址与线性地址中最后12位相加,得到最终我们想要的物理地址。
7.4 TLB与四级页表支持下的VA到PA的变换
36位VPN被划分成四个9位的片,每个片被用作到一个页表的偏移量。CR3寄存器包含Ll页表的物理地址。VPN1提供到一个Ll PET 的偏移量,这个PTE包含L2页表的基地址。VPN2提供到一个L2 PTE 的偏移量,以此类推。
7.5 三级Cache支持下的物理内存访问
使用CI进行组索引,每组8路,对8路的块分别匹配CT如果匹配成功且块的有效位为1则命中,根据CO取出数据返回
如果有效位为1但未匹配成功,则不命中,向下一级缓存中查询数据(L1>L2>L3>主存)查到数据后,一种简单的放置策略如下:如果映射到的组内有空闲块,则直接放置,否则组内都是有效块,产生冲突,采用LRU策略进行替换。
7.6 hello进程fork时的内存映射
用fork创建虚拟内存的时候,要经历以下步骤:

  1. 创建当前进程的mm_struct,vm_area_struct和页表的原样副本
  2. 两个进程的每个页面都标记为只读页面
  3. 两个进程的每个vm_area_struct都标记为私有,这样就只能在写入时复制。
    7.7 hello进程execve时的内存映射
  4. 删除已存在的用户区域
  5. 创建新的私有区域(.malloc,.data,.bss,.text)
  6. 创建新的共享区域(libc.so.data,libc.so.text)
  7. 设置PC,指向代码的入口点
    7.8 缺页故障与缺页中断处理
    缺页处理程序不是直接就替换,它会经过一系列的步骤:
    1.虚拟地址是合法的吗?如果不合法,它就会触发一个段错误
    2.试图进行的内存访问是否合法?意思就是进程是否有读,写或者执行这个区域的权限
    3.经过上述判断,这时才能确定这是个合法的虚拟地址,然后才会执行上述的替换。
    7.9动态存储分配管理
    C程序需要额外的虚拟内存区域,就会使用动态内存分配来得到,动态内存分配器维持着一个进程的虚拟内存区域,称为堆,分配器将堆视作一组大小不同的块,每个块就是一个连续的虚拟内存块,这些块要么是已分配的,要么是空闲的。
    对于C程序,使用的是显式分配器,能够显式地释放任何已分配的块。C标准库通过malloc程序包的显示分配器。
    分配器有两种基本风格:
    1.显式分配器:要求应用显式的释放任何已分配的块。
    2.隐式分配器:也叫做垃圾收集器,自动释放未使用的已分配块的过程叫做垃圾收集。
    空闲块合并:
    因为有了 Footer,所以我们可以方便的对前面的空闲块进行合并。合并的 情况一共分为四种:前空后不空,前不空后空,前后都空,前后都不空。对于 四种情况分别进行空闲块合并,我们只需要通过改变 Header 和 Footer 中的值 就可以完成这一操作。
    分离的空闲链表:
    维护多个空闲链表,其中每个链表中的块有大致相等的大小。分配器维护着一个空闲链表数组,每个大小类一个空闲链表,按照大小的升序排列。当分配器需要一个大小为n的块时,它就搜索相应的空闲链表。如果不能找到合适的块与其匹配,它就搜索下一个链表,以此类推。
    Printf会调用malloc,请简述动态内存管理的基本方法与策略。

7.10本章小结
本章介绍了hello的储存器的地址空间,讲述了虚拟地址、物理地址、线性地址、逻辑地址的概念,还有进程fork和execve时的内存映射,缺页故障与缺页中断处理、动态存储分配管理。。

第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:所有的IO设备都被模型化为文件,而所有的输入和输出都被 当做对相应文件的读和写来执行,这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单低级的应用接口,称为Unix I/O。
设备的模型化:文件
设备管理:unix io接口
8.2 简述Unix IO接口及其函数
Unix I/O接口:
1.打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访间一个I/O 设备。内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件。内核记录有关这个打开文件的所有信息。应用程序只需记住这个描述符。
2.Linux shell 创建的每个进程开始时都有三个打开的文件:标准输入(描述符为0) 、标准输出(描述符为1) 和标准错误(描述符为2) 。头文件< unistd.h> 定义了常量STDIN_FILENO 、STOOUT_FILENO 和STDERR_FILENO, 它们可用来代替显式的描述符值。
3.改变当前的文件位置。对于每个打开的文件,内核保持着一个文件位置k, 初始为0。这个文件位置是从文件开头起始的字节偏移量。应用程序能够通过执行seek 操作,显式地设置文件的当前位置为K 。
4.读写文件。一个读操作就是从文件复制n>0 个字节到内存,从当前文件位置k 开始,然后将k增加到k+n 。给定一个大小为m 字节的文件,当k~m 时执行读操作会触发一个称为end-of-file(EOF) 的条件,应用程序能检测到这个条件。在文件结尾处并没有明确的“EOF 符号” 。类似地,写操作就是从内存复制n>0 个字节到一个文件,从当前文件位置k开始,然后更新k 。
5.关闭文件。当应用完成了对文件的访问之后,它就通知内核关闭这个文件。作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。无论一个进程因为何种原因终止时,内核都会关闭所有打开的文件并释放它们的内存资源。
Unix I/O函数:
1.进程是通过调用open 函数来打开一个已存在的文件或者创建一个新文件的:int open(char *filename, int flags, mode_t mode);
open 函数将filename 转换为一个文件描述符,并且返回描述符数字。返回的描述符总是在进程中当前没有打开的最小描述符。flags 参数指明了进程打算如何访问这个文件,mode 参数指定了新文件的访问权限位。
返回:若成功则为新文件描述符,若出错为-1。
2.进程通过调用close 函数关闭一个打开的文件。int close(int fd);
返回:若成功则为0, 若出错则为-1。
3.应用程序是通过分别调用read 和write 函数来执行输入和输出的。ssize_t read(int fd, void *buf, size_t n);
read 函数从描述符为fd 的当前文件位置复制最多n 个字节到内存位置buf 。返回值-1表示一个错误,而返回值0 表示EOF。否则,返回值表示的是实际传送的字节数量。
返回:若成功则为读的字节数,若EOF 则为0, 若出错为-1。ssize_t write(int fd, const void *buf, size_t n);
write 函数从内存位置buf 复制至多n 个字节到描述符fd 的当前文件位置。
返回:若成功则为写的字节数,若出错则为-1。
8.3 printf的实现分析
printf接受一个fmt的格式,然后将匹配到的参数按照fmt格式输出。调用了两个外部函数,一个是vsprintf,还有一个是write。vsprintf的作用是将所有的参数内容格式化之后存入buf,然后返回格式化数组的长度。write函数是将buf中的i个元素写到终端的函数。
https://www.cnblogs.com/pianist/p/3315801.html
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
getchar调用了一个read函数,这个read函数是将整个缓冲区都读到了buf里面,然后将返回值是缓冲区的长度。我们可以发现,如果buf长度为0,getchar才会调用read函数,否则是直接将保存的buf中的最前面的元素返回。
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章介绍了linux下的I/O设备管理机制,分析了I/O对接口以及操作方法,分析了printf和getchar函数的实现方法以及操作过程。
结论
hello在计算机中要经历以下步骤:
1.写程序:通过I/O设备写下,hello程序的代码。
2.预处理:对带#的指令解析,生成hello.i文件
3.编译:把我们的C语言程序编译成汇编语言程序,生成.s文件
4.汇编:把汇编语言转换成机器代码,生成重定位信息,生成.o文件。
5.链接:与动态库链接,生成可执行文件hello
6.创建进程:在shell利用./hello运行hello程序,父进程通过fork函数为hello创建进程
7.加载程序:通过加载器,调用execve函数,删除原来的进程内容,加载我们现在进程的代码,数据等到进程自己的虚拟内存空间。
8.执行指令:CPU取指令,顺序执行进程的逻辑控制流。这里CPU会给出一个虚拟地址,通过MMU从页表里得到物理地址, 在通过这个物理地址去cache或者内存里得到我们想要的信息
9.异常(信号):程序执行过程中,如果从键盘输入Ctrl-C等命令,会给进程发送一个信号,然后通过信号处理函数对信号进行处理。
10.结束:程序执行结束后,父进程回收子进程,内核删除为这个进程创建的所有数据结构。
用计算机系统的语言,逐条总结hello所经历的过程。
你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
(结论0分,缺失 -1分,根据内容酌情加分)

附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.i(hello.c预处理之后的程序文本)
hello.s(hello.i编译成汇编语言之后的程序文本)
hello3.c(用于测试sleepsecs的值的程序文本)
hello3(hello3.c生成的可执行二进制文件)
hello.o(hello.s生成的二进制文件)
hello_o.s(hello.o反汇编的结果)
hello(可执行的hello二进制文件)
hello_.s(可执行文件hello,直接用objdump反编译之后的汇编代码)
hello.elf(可执行文件hello的elf表)
(附件0分,缺失 -1分)

参考文献
为完成本次大作业你翻阅的书籍与网站等
[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分,缺失 -1分)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值