正在上传…重新上传取消
计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 计算学部
学 号 120L020311
班 级 2003003
学 生 杭乌日昵
指 导 教 师 史先俊
计算机科学与技术学院
2022年5月
摘 要
文章主要介绍了hello的一生。从hello的预处理开始,依次分析编译、汇编、链接等操作细节。之后介绍了linux上从hello的加载到运行等过程,查看了hello的进程管理、存储管理、IO管理等内容。
关键词:深入理解计算机系统;hello;预处理;编译;汇编;链接;进程;内存;IO
目 录
第1章 概述............................................................................................................. - 4 -
1.1 Hello简介...................................................................................................... - 4 -
1.2 环境与工具..................................................................................................... - 4 -
1.3 中间结果......................................................................................................... - 4 -
1.4 本章小结......................................................................................................... - 4 -
第2章 预处理......................................................................................................... - 5 -
2.1 预处理的概念与作用..................................................................................... - 5 -
2.2在Ubuntu下预处理的命令.......................................................................... - 5 -
2.3 Hello的预处理结果解析.............................................................................. - 5 -
2.4 本章小结......................................................................................................... - 5 -
第3章 编译............................................................................................................. - 6 -
3.1 编译的概念与作用......................................................................................... - 6 -
3.2 在Ubuntu下编译的命令............................................................................. - 6 -
3.3 Hello的编译结果解析.................................................................................. - 6 -
3.4 本章小结......................................................................................................... - 6 -
第4章 汇编............................................................................................................. - 7 -
4.1 汇编的概念与作用......................................................................................... - 7 -
4.2 在Ubuntu下汇编的命令............................................................................. - 7 -
4.3 可重定位目标elf格式................................................................................. - 7 -
4.4 Hello.o的结果解析...................................................................................... - 7 -
4.5 本章小结......................................................................................................... - 7 -
第5章 链接............................................................................................................. - 8 -
5.1 链接的概念与作用......................................................................................... - 8 -
5.2 在Ubuntu下链接的命令............................................................................. - 8 -
5.3 可执行目标文件hello的格式.................................................................... - 8 -
5.4 hello的虚拟地址空间.................................................................................. - 8 -
5.5 链接的重定位过程分析................................................................................. - 8 -
5.6 hello的执行流程.......................................................................................... - 8 -
5.7 Hello的动态链接分析.................................................................................. - 8 -
5.8 本章小结......................................................................................................... - 9 -
第6章 hello进程管理................................................................................... - 10 -
6.1 进程的概念与作用....................................................................................... - 10 -
6.2 简述壳Shell-bash的作用与处理流程..................................................... - 10 -
6.3 Hello的fork进程创建过程..................................................................... - 10 -
6.4 Hello的execve过程................................................................................. - 10 -
6.5 Hello的进程执行........................................................................................ - 10 -
6.6 hello的异常与信号处理............................................................................ - 10 -
6.7本章小结....................................................................................................... - 10 -
第7章 hello的存储管理................................................................................ - 11 -
7.1 hello的存储器地址空间............................................................................ - 11 -
7.2 Intel逻辑地址到线性地址的变换-段式管理............................................ - 11 -
7.3 Hello的线性地址到物理地址的变换-页式管理....................................... - 11 -
7.4 TLB与四级页表支持下的VA到PA的变换............................................. - 11 -
7.5 三级Cache支持下的物理内存访问.......................................................... - 11 -
7.6 hello进程fork时的内存映射.................................................................. - 11 -
7.7 hello进程execve时的内存映射.............................................................. - 11 -
7.8 缺页故障与缺页中断处理........................................................................... - 11 -
7.9动态存储分配管理....................................................................................... - 11 -
7.10本章小结..................................................................................................... - 12 -
第8章 hello的IO管理................................................................................. - 13 -
8.1 Linux的IO设备管理方法.......................................................................... - 13 -
8.2 简述Unix IO接口及其函数....................................................................... - 13 -
8.3 printf的实现分析........................................................................................ - 13 -
8.4 getchar的实现分析.................................................................................... - 13 -
8.5本章小结....................................................................................................... - 13 -
结论......................................................................................................................... - 14 -
附件......................................................................................................................... - 15 -
参考文献................................................................................................................. - 16 -
第1章 概述
1.1 Hello简介
Hello的P2P在linux上是由编译器驱动程序完成的。在预处理阶段,预处理器(cpp)根据以#字符开头的命令,修改原始程序,得到另一个以拓展名.i结尾的文件。编译阶段,编译器将文本文件hello.i翻译成hello.s,它包含一个汇编语言程序,以.s结尾。汇编阶段,汇编器将hello.s翻译成机器语言指令,把这些指令打包成一种叫可重定位目标程序的格式,将结果保存在hello.o中。链接阶段,链接器将程序涉及到的目标文件合并到hello.o中,得到一个可执行目标文件hello。
刚开始程序是不在内存空间中的,也就是在开始时为0,当在shell中使用execve加载并执行该程序时,操作系统为程序分配一部分虚拟空间,将程序加载到虚拟空间所映射的物理内存空间中,然后执行目标程序。在进程终止后,shell回收hello进程,操作系统会释放进程的虚拟空间、相关数据结构,变为0。这就是hello的020。
1.2 环境与工具
软件环境:Vmware; Ubuntu 19.04;codeblocks
1.3 中间结果
hello.c:hello源代码
hello.i:预处理后的文件
hello.s:编译之后的文件
hello.o:汇编之后的可重定位目标文件
hello:链接之后的可执行目标文件
elf.txt:hello.o的elf格式文件
hello_o_disass.s:hello.o的反汇编代码
elf_hello.txt:hello的elf文件
hello_objdump.s:hello的反汇编代码
1.4 本章小结
本章对本次大作业hello的一生进行了简要的描述并展示了使用的环境与工具与中间结果。
第2章 预处理
2.1 预处理的概念与作用
概念:在计算机科学中,预处理器是程序中处理输入数据,产生能用来输入到其他程序的数据的程序。输出被称为输入数据预处理过的形式,常用在之后的程序比如编译器中。所作处理的数量和种类依赖于预处理器的类型,一些预处理器只能够执行相对简单的文本替换和宏展开,而另一些则有着完全成熟的编程语言的能力。C预处理器(C Pre-Processor)也常简写为 CPP,是一个与 C 编译器独立的小程序,预编译器并不理解 C 语言语法,它仅是在程序源文件被编译之前,实现文本替换的功能。
作用:
1.文件包含:#include 是 C 程序设计中最常用的预处理指令。例如,几乎每个需要输入输出的 C 程序,都要包含 #include<stdio.h> 指令,表示把 stdio.h 文件中的全部内容,替换该行指令。
2.宏定义:包括定义宏 #define 和宏删除 #undef。
3.条件编译:主要是为了有选择性地执行相应操作,防止宏替换内容(如文件等)的重复包含。常见的条件编译指令有 #if、#elif、#else、#endif、#ifdef、#ifndef。
2.2在Ubuntu下预处理的命令
使用cpp hello.c >hello.i指令对.c文件进行预处理
正在上传…重新上传取消
图 1.1指令
得到的文件如下
正在上传…重新上传取消
图 1.2 hello.i文件
2.3 Hello的预处理结果解析
正在上传…重新上传取消
图1.3预处理后的文件
正在上传…重新上传取消
图 1.4 main函数部分
通过hello.i文件可以看到cpp将代码预处理部分替换为了对应代码。
2.4 本章小结
本节描述了预处理的概念和具体的作用,在虚拟机中也对hello.c进行了实际的预处理,生成了hello.i文件,通过浏览hello.i文件更好的了解预处理的作用。
第3章 编译
3.1 编译的概念与作用
概念:编译器将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序。它把预处理文件进行词法分析、语法分析、语义分析、优化,从C语言等高级语言转换为成机器更好理解的汇编语言程序,转换后的文件仍为ASCII文本文件。
作用:通过词法分析,语法分析,目标代码的生成进行词法分析和语法分析,分析过程中发现有语法错误,给出提示信息。
3.2 在Ubuntu下编译的命令
正在上传…重新上传取消
图 2.1 指令
3.3 Hello的编译结果解析
正在上传…重新上传取消
图 2.2编译后文件
编译后的.s文件如图所示。
3.3.1常量
正在上传…重新上传取消
图 2.3
正在上传…重新上传取消
图 2.4
如图所示,从编译后的文件可以看出printf中的字符串以常量形式保存(汉字为相应编码)。
3.3.2变量
全局变量:
程序中并未出现全局变量
局部变量:
局部变量i被存储在栈中,如图
正在上传…重新上传取消
图 2.5
3.3.3算术操作
正在上传…重新上传取消
图 2.6
i++的实现如图所示,使用addl。
3.3.4关系操作
正在上传…重新上传取消
图 2.7
argc!=4的实现如图,使用cmpl比较。
正在上传…重新上传取消
图 2.8
i<8的比较如图,这里编译器将i<8修改为了i<=7,在功能上这两个是等价的。
3.3.5控制转移
正在上传…重新上传取消
图 2.9
与4比较后会条件跳转,对应if==4
正在上传…重新上传取消
图 2.10
与7比较后条件跳转,对应for循环。
3.3.6数组
正在上传…重新上传取消
图 2.11
main中涉及到了指针数组argv,计算机将数组存储在栈中,因为是指针数组,所以一个元素大小为8字节,存储在位置-16(%rbp),-24(%rbp)中,如图。
3.3.7函数
1.main
参数传递:
正在上传…重新上传取消
图 2.12
参数分别在edi与rsi中。
函数调用:系统调用
返回值:
正在上传…重新上传取消
图 2.13
将eax设为0后返回。
2.printf
参数传递:
正在上传…重新上传取消
图 2.14
将字符串常量地址输入到rdi中作为参数传递
函数调用:在for中被调用
3.exit
参数传递:
正在上传…重新上传取消
图 2.15
传递参数1。
调用:在for中调用
4.atoi
参数传递:
正在上传…重新上传取消
图 2.16
首先取出位于-8(%rbp)位置的元素,赋值给rdi后作为参数传递。
调用:在for循环中被调用
返回值:通过eax返回
5.sleep
参数传递:
正在上传…重新上传取消
图 2.17
将atoi的返回值赋值到edi作为参数传递。
调用:在for中被调用
6.getchar
参数传递:无
函数调用:
正在上传…重新上传取消
图 2.18
在循环结束后调用。
3.4 本章小结
本章介绍了编译的概念以及过程。通过hello函数分析了c语言如何转换成为汇编代码。介绍了汇编代码如何实现变量、常量、传递参数以及分支和循环。
第4章 汇编
4.1 汇编的概念与作用
概念:驱动程序运行汇编器as,将汇编语言的ascii码文件(这里是hello.s)翻译成机器语言的可重定位目标文件(hello.o)的过程称为汇编。
作用:汇编就是将高级语言转化为机器可直接识别执行的代码文件的过程,汇编器将.s 汇编程序翻译成机器语言指令,把这些指令打包成可重定位目标程序的格式,并将结果保存在.o目标文件中。
4.2 在Ubuntu下汇编的命令
正在上传…重新上传取消
图 3.1 指令
4.3 可重定位目标elf格式
分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。
- linux下重定向命令
正在上传…重新上传取消
图 3.2重定向指令
2.ELF头
正在上传…重新上传取消
图 3.3
包含了系统信息,编码方式,ELF头大小,节的大小和数量等等一系列信息。
3.节头部表
描述了.o文件中出现的各个节的类型、位置、所占空间大小等信息。
正在上传…重新上传取消
图 3.4
4. 表述了各个段引用的外部符号等,在链接时,需要通过重定位节对这些位置的地址进行修改。链接器会通过重定位条目的类型判断该使用什么养的方法计算正确的地址值,通过偏移量等信息计算出正确的地址。
正在上传…重新上传取消
图 3.5
5.符号表
存放在程序中定义和引用的函数和全局变量的信息。
正在上传…重新上传取消
图 3.6
4.4 Hello.o的结果解析
正在上传…重新上传取消
图 3.7
如图为hello.o的反汇编。
与hello.s相比,hello.o的不同主要有如下几点。
- hello.o中显示了每条汇编代码对应的机器指令。
- hello.o中使用的都是16进制的数字。
- hello.o的跳转指令都是使用的相对地址进行跳转,而hello.s则是直接跳转到对应名称。
- 调用函数时call指令后不再是函数名,而是相对地址(相对于main)。
4.5 本章小结
在本章中,分析了linux下的hello.o的可重定位目标文件(elf),并于hello.s文件进行了比较。
第5章 链接
5.1 链接的概念与作用
概念:链接是将各种不同文件(主要是可重定位目标文件)的代码和数据综合在一起,通过符号解析和重定位等过程,最终组合成一个可以在程序中加载和运行的单一的可执行目标文件的过程。
作用:链接令分离编译成为可能,方便了程序的修改和编译:无需重新编译整个工程,而是仅编译修改的文件。链接还有利于构建共享库。源程序节省空间而未编入的常用函数文件(如printf.o)进行合并,生成可以正常工作的可执行文件。
5.2 在Ubuntu下链接的命令
正在上传…重新上传取消
图 4.1指令
5.3 可执行目标文件hello的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
ELF头
正在上传…重新上传取消
图 4.2
节头
正在上传…重新上传取消
图 4.3
正在上传…重新上传取消
图 4.4
程序头
正在上传…重新上传取消
图 4.5
段节
正在上传…重新上传取消
图 4.6
动态偏移表
正在上传…重新上传取消
图 4.7
重定位节
正在上传…重新上传取消
图 4.8
符号表
正在上传…重新上传取消
图 4.9
正在上传…重新上传取消
图 4.10
版本符号节
正在上传…重新上传取消
图 4.11
版本需求节
正在上传…重新上传取消
图 4.12
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.4 hello的虚拟地址空间
使用edb加载hello,虚拟地址如下。
正在上传…重新上传取消
图 4.13
可以看出程序从0x401000开始。
5.5 链接的重定位过程分析
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
使用命令objdump -d -r hello > hello_objdump.s
正在上传…重新上传取消
图 4.14
正在上传…重新上传取消
图 4.15
新增函数:
链接后新增了hello用到的外部函数printf,exit,sleep,atoi等等
正在上传…重新上传取消
图 4.16
函数调用地址:
与.o文件不同的是,链接后函数调用的是绝对地址,不再是相对地址。
正在上传…重新上传取消
图 4.17
控制转移地址:
同函数调用一样,跳转的是确切的地址
正在上传…重新上传取消
图 4.18
链接的过程就是将各个目标组装在一起,从.o提供的重定位条目将函数调用和控制流跳转的地址填写为最终的地址。
5.6 hello的执行流程
子程序名 | 地址 |
<_init> | 401000 |
<.plt> | 401020 |
<[email protected]> | 401090 |
<[email protected]> | 4010a0 |
< [email protected]> | 4010b0 |
<[email protected]> | 4010c0 |
<[email protected]> | 4010d0 |
<[email protected]> | 4010e0 |
<_start> | 4010f0 |
<_dl_relocate_static_pie> | 401120 |
<main> | 401125 |
<__libc_csu_init> | 4011c0 |
<__libc_csu_fini> | 401230 |
<_fini> | 401238 |
5.7 Hello的动态链接分析
在elf文件中查看
正在上传…重新上传取消
图 4.19
可以看到.got, .got.plt的位置。
进入edb查看
调用前:
正在上传…重新上传取消
图 4.20
正在上传…重新上传取消
图 4.21
调用后:
正在上传…重新上传取消
图 4.22
利用代码段和数据段的相对位置不变的原则计算变量的正确地址。而对于库函数,需要plt、got的协作。
plt初始存的是一批代码,它们跳转到got所指示的位置,然后调用链接器。初始时got里面存的都是plt的第二条指令,随后链接器修改got,下一次再调用plt时,指向的就是正确的内存地址。plt就能跳转到正确的区域。
5.8 本章小结
本章介绍了链接的相关内容,熟悉了链接的指令,如何将.o文件转为可执行文件。回忆了edb的使用方法,分析了重定位的相关知识。
第6章 hello进程管理
6.1 进程的概念与作用
概念:进程是程序的一个实例。系统中的每个程序都运行在某个进程的上下文中。上下文是由程序正确运行所需要的状态组成。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。
作用:通过进程,我们可以感觉到我们的程序是系统中当前运行的唯一的程序一样,独占得使用处理器和内存。
6.2 简述壳Shell-bash的作用与处理流程
作用:作为命令处理器,接受用户输入的命令,然后根据命令进行相关操作,比如调用相关的程序。作为命令语言,它交互式解释和执行用户输入的命令或者自动地解释和执行预先设定好的一连串的命令。作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支。
流程:
1.在shell命令行中输入命令:$./hello
2. shell命令行解释器构造argv和envp;
3. 调用fork()函数创建子进程,其地址空间与shell父进程完全相同,包括只读代码段、读写数据段、堆及用户栈等
4. 调用execve()函数在当前进程(新创建的子进程)的上下文中加载并运行hello程序。将hello中的.text节、.data节、.bss节等内容加载到当前进程的虚拟地址空间
5. 调用hello程序的main()函数,hello程序开始在一个进程的上下文中运行。
6.3 Hello的fork进程创建过程
在命令行中输入./hello,shell首先会检测到这不是一个内置命令,之后会将hello认为是一个可执行文件,shell会fork一个子进程,并在子进程的上下文中加载hello并运行。
6.4 Hello的execve过程
execve函数会在当前的上下文中加载并运行一个新程序,没有返回值。在shell的子进程中,execve接受hello的参数列表以及环境变量,并在当前上下文中运行hello。
6.5 Hello的进程执行
多个流并发地执行的一般现象被称为并发。一个进程和其他进轮流运行的概念称为多任务。而上下文是内核重新启动一个被抢占的进程所需的状态,它右通用寄存器、浮点你寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构等对象的值构成。
操作系统内核使用一种称为上下文切换的较高层次形式的异常控制流来实现多任务。在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占的进程。这种决策叫做调度,是由内核中被称为调度器的代码处理的。
而一个进程执行它的控制流的一部分的每一时间段叫做时间片。
处理器通常是用某个控制寄存器中的一个模式位来提供这种功能的,该寄存器描述了进程当前享有的特权。当设置了模式位时,进程就运行在内核态(又是也叫超级用户态)。一个运行在内核态的进程可以执行指令集中的任何指令,并且可以访问系统的任何内存位置。
正在上传…重新上传取消
图 5.1
在hello中,若在调用sleep函数之前,hello程序被抢占,就进行上下文切换,调用sleep时,进入内核态,处理器处理sleep并请求主动释放当前进程。之后计时器开始计时,内核进行上下文切换,执行其他的进程。当计时结束时,计时器发送一个中断信号,处理器处理中断信号,并进行上下文切换,重新回来执行hello进程。
6.6 hello的异常与信号处理
hello执行过程中有四种异常
类别 | 原因 | 异步/同步 | 返回行为 |
中断 | 来自I/O设备的信号 | 异步 | 总是返回到下一条指令 |
陷阱 | 有意的异常 | 同步 | 总是返回到下一条指令 |
故障 | 潜在可能恢复的错误 | 同步 | 可能返回到当前指令 |
终止 | 不可恢复的错误 | 同步 | 不会返回 |
信号处理方式:
正在上传…重新上传取消
图 5.2
发送信号:
中途乱按
不会影响程序执行,会被当作命令
正在上传…重新上传取消
图 5.3
SIGTSTP
正在上传…重新上传取消
图 5.4
停止前台作业
SIGINT
正在上传…重新上传取消
图 5.5
终止前台进程
Jobs,ps,fg,kill等命令
正在上传…重新上传取消
图 5.6
6.7本章小结
本章介绍了进程的概念和作用,描述了shell如何在用户和系统内核之间建起一个交互的桥梁。介绍了shell的基本操作以及各种内核信号和命令,之后通过实例和画图描述在shell中使用fork新建子进程、使用execve加载并运行进程、hello进程的上下文切换。
第7章 hello的存储管理
7.1 hello的存储器地址空间
(以下格式自行编排,编辑时删除)
结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。
逻辑地址:是指由程式产生的和段相关的偏移地址部分,hello.o的地址就是
逻辑地址。他是相对于当前数据段的地址。
线性地址:是逻辑地址到物理地址变换之间的中间层。程式代码会产生逻辑地址,或说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。
虚拟地址:可以看作是逻辑地址
物理地址:实际在物理内存中存储的地址
7.2 Intel逻辑地址到线性地址的变换-段式管理
一个逻辑地址由段标识符和段内偏移量组成。段标识符是一个16位长的字段(段选择符)。可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段。
全局的段描述符,放在“全局段描述符表(GDT)”中,一些局部的段描述符,放在“局部段描述符表(LDT)”中。
给定一个完整的逻辑地址段选择符+段内偏移地址,看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。拿出段选择符中前13位,可以在这个数组中,查找到对应的段描述符,就得到了其基地址。基地址+偏移量= 线性地址。
7.3 Hello的线性地址到物理地址的变换-页式管理
页式管理: 虚拟地址->物理地址
页式管理是一种内存空间存储管理的技术,页式管理分为静态页式管理和动态页式管理。
实现方式为将各进程的虚拟空间划分成若干个长度相等的页(page),页式管理把内存空间按页的大小划分成片或者页面(page frame),然后把页式虚拟地址与内存地址建立一一对应页表,并用相应的硬件地址变换机构,来解决离散地址变换问题。页式管理采用请求调页或预调页技术实现了内外存存储器的统一管理。
现代系统大多使用的是按需页面调度,就是只有当不命中发生时,才从磁盘换入页面。因为局部性原则,程序趋向于在一个小的活动页面集合上工作,这个集合称为工作集。在将工作集导入到内存之后,程序将不会频繁地发生不命中。但工作集大小超出物理内存则会造成抖动。
7.4 TLB与四级页表支持下的VA到PA的变换
(以下格式自行编排,编辑时删除)
TLB:每次CPU产生一个虚拟地址,MMU(内存管理单元)就必须查阅一个PTE(页表条目),以便将虚拟地址翻译为物理地址。在最糟糕的情况下,这会从内存多取一次数据,代价是几十到几百个周期。如果PTE碰巧缓存在L1中,那么开销就会下降1或2个周期。然而,许多系统都试图消除即使是这样的开销,它们在MMU中包括了一个关于PTE的小的缓存,称为快表(TLB)。
具体变换:虚拟地址VA被分成k个vpn和一个vpo。每个vpn i都是一个到第i级页表的索引。第i级页表的每个pte指向下一个页表。第k个页表的pte指向一个物理页。
也就是说在构造一个物理地址时,必须先访问k个页表。得益于TLB,这一操作实际上的时间开销并不大。
7.5 三级Cache支持下的物理内存访问
在将VA转换成PA后,根据cpu cache的参数,可将PA划分为CT(标记位)CI(组索引)CO(块偏移)。然后根据第六章内容,根据CI寻找组,然后比对CT,如果配对成功就根据CO取出相应内容。如果未找到匹配项就需要访问内存再逐级向上传递。
7.6 hello进程fork时的内存映射
当shell为hello程序fork一个子进程时,内核为新进程创建各种数据结构,并分配给它唯一的PID。之后会创建当前进程的mm_struct、区域结构和页表的副本,将两个进程中的每个页面都标记位只读,将两个进程中的每个区域结构都标记为私有的写时复制。
7.7 hello进程execve时的内存映射
在子进程调用函数execve运行hello时首先删除已存在的用户区域,之后映射私有区域,再映射共享区域,最后设置程序计数器。
7.8 缺页故障与缺页中断处理
缺页时的具体流程如下。
1.处理器生成一个虚拟地址,并将它传送给MMU
2.MMU生成PTE地址,并从高速缓存/主存请求得到它
3.高速缓存/主存向MMU返回PTE
4.如果PTE中的有效位是0,说明出现了缺页,所以MMU出发了一次异常,传递CPU中的控制到操作系统内核中的缺页异常处理程序。
5.缺页处理程序确认出物理内存中的牺牲页,如果这个页已经被修改了,则把它换到磁盘。
6.缺页处理程序页面调入新的页面,并更新内存中的PTE
7.缺页处理程序返回到原来的进程,再次执行导致缺页的命令。CPU将引起缺页的虚拟地址重新发送给MMU。因为虚拟页面已经换存在物理内存中,所以就会命中。
7.9动态存储分配管理
(以下格式自行编排,编辑时删除)
Printf会调用malloc,请简述动态内存管理的基本方法与策略。
动态储存分配管理使用动态内存分配器(如malloc)来进行。动态内存分配器维护着一个进程的虚拟内存区域,称为堆。分配器将堆视为一组不同大小的块的集合。每个块就是一个连续的虚拟内存页,要么是已分配的,要么是空闲的。
1.记录空闲块,可以选择隐式空闲链表,显示空闲链表,分离的空闲链表和按块大小排序建立平衡树
2.放置策略,可以选择首次适配,下一次适配,最佳适配
3.合并策略,可以选择立即合并,延迟合并
7.10本章小结
本章讨论了linux下hello的虚拟内存管理,虚拟地址空间与物理地址空间的映射与动态内存的分配。
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
(以下格式自行编排,编辑时删除)
设备的模型化:文件
设备管理:unix io接口
Linux将文件所有的I/O设备都模型化为文件,甚至内核也被映射为文件。这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,称为Unix I/O。Linux就是基于Unix I/O实现对设备的管理。
8.2 简述Unix IO接口及其函数
打开文件:int open(char *filename, int flags, mode_t mode);
关闭文件:int close(int fd);
读文件:ssize_t read(int fd, void *buf, size_t n);
写文件:ssize_t write(int fd, const void *buf, size_t n);
8.3 printf的实现分析
(以下格式自行编排,编辑时删除)
printf:
正在上传…重新上传取消
图 6.1
vsprintf:
正在上传…重新上传取消
图 6.2
vsprintf函数将所有的参数内容格式化之后存入buf,返回格式化数组的长度。write函数将buf中的i个元素写到终端。从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
当程序调用getchar时,程序等待用户按键,用户输入的字符被存放在键盘缓冲区中直到用户按回车(回车也在缓冲区中)。
当用户键入回车之后,getchar才开始从stdio流中每次读入一个字符。getchar函数的返回值是用户输入的第一个字符的ascii码,如出错返回-1,且将用户输入的字符回显到屏幕。如用户在按回车之前输入了不止一个字符,其他字符会保留在键盘缓存区中,等待后续getchar调用读取。也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完为后,才等待用户按键。
8.5本章小结
本章主要介绍了linux的io设备管理方法,unix io接口以及其函数,并分析了printf和getchar的实现。
结论
用计算机系统的语言,逐条总结hello所经历的过程。
你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
通过本次大作业,我将我之前一直忽视的helloworld程序从计算机系统的角度完完整整地回顾了一遍,在这个过程使我对本学期所学内容有了一个综合性的认知,回顾了每一章的知识点。最后总结hello的实现过程如下。
hello.c预处理到hello.i文本文件
hello.i编译到hello.s汇编文件
hello.s汇编到二进制可重定位目标文件hello.o
hello.o链接生成可执行文件hello
bash进程调用fork函数,生成子进程;
execve函数加载运行当前进程的上下文中加载并运行新程序hello
虚拟地址为hello提供了独立的地址空间。
hello的输入输出与外界交互,与linux I/O息息相关
hello最终被shell父进程回收,内核会收回为其创建的所有信息
附件
列出所有的中间产物的文件名,并予以说明起作用。
正在上传…重新上传取消
hello.c:hello源代码
hello.i:预处理后的文件
hello.s:编译之后的文件
hello.o:汇编之后的可重定位目标文件
hello:链接之后的可执行目标文件
elf.txt:hello.o的elf格式文件
hello_o_disass.s:hello.o的反汇编代码
elf_hello.txt:hello的elf文件
hello_objdump.s:hello的反汇编代码
参考文献
[1] (美)布赖恩特(Bryant,R.E.).深入理解计算机系统 机械工业出版社, 2016.
[2] ELF文件格式解析https://blog.csdn.net/mergerly/article/details/94585901.
[3] printf 函数实现的深入剖析 https://www.cnblogs.com/pianist/p/3315801.html