程序人生-Hello‘sP2P

计算机系统

大作业

题 目 程序人生-Hello’s P2P
专 业 计算机学部
学   号 11803004xx
班   级 1903004
学 生 xxx    
指 导 教 师 史先俊

计算机科学与技术学院
2021年5月
摘 要
摘要是论文内容的高度概括,应具有独立性和自含性,即不阅读论文的全文,就能获得必要的信息。摘要应包括本论文的目的、主要内容、方法、成果及其理论与实际意义。摘要中不宜使用公式、结构式、图表和非公知公用的符号与术语,不标注引用文献编号,同时避免将摘要写成目录式的内容介绍。

关键词:Hello程序;预处理;编译;汇编;链接;进程;存储;虚拟内存;I/O ;

本文将通过hello从C代码文件,经过预处理,编译,汇编,链接,处理成可执行文件,以及后续的运行和系统处理过程。并结合课本的知识详细阐述我们的计算机系统是如何对hello进行进程管理、存储管理和I/O管理。通过本文,可以更加系统的了解和总结这一学期内学习的计算机系统的知识,通过完整的过程全面加深对计算机系统的理解

目 录

第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的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
P2P:linux中hello.c经过ccp(产生hello.s),ccl(产生.i),as(产生.o-可重定位目标文件),ld处理以后最终成为可执行目标文件。
在shell中输入启动命令以后,判断指令不是内置指令或者预设文件以后,寻找可执行文件hello同时使用fork函数生成一个进程并使用execve覆盖进程为hello,将hello可执行目标文件加载到栈。在加载进程结束以后,开始执行进程,最终调用main函数,执行会分为许多块。当main函数执行结束以后,shell调用waitpid回收,全部结束。
020:hello执行前后,进程不会有痕迹残留,shell调用waitpid以后,进程的虚拟内存被删除,内存段被覆盖,shell状态未发生改变。

1.2 环境与工具
硬件环境:intel® Core™ i7-8565U CPU @ 1.80GHz 1.99GHz 16G RAM 512G HD disk
软件环境:VMware workstation 15 PRO/visual studio 2019
系统环境:windows 10//Ubuntu 20.04.1
1.3 中间结果
hello.c 程序源代码(文本)
hello.i 修改了的源程序(文本)
hello.s 汇编程序(文本)
hello.o 可重定位目标程序(二进制)
hello.elf readelf读取hello.o生成的段可视化可重定位目标文件
hello 可执行目标程序(二进制)
hello_out.elf readelf读取hello生成的段可视化可执行目标文件
1.4 本章小结
本章主要概述了完成hello进程的环境以及各过程文件,以及全过程大概描述

第2章 预处理
2.1 预处理的概念与作用
预处理概念预处理器根据字符#开头的指令修改c程序。、引用库展开并合并为一个文本文件
预处理作用:处理宏定义/处理条件编译指令/处理头文件/处理特殊符号

2.2在Ubuntu下预处理的命令

gcc hello.c -E -o hello.i
2.3 Hello的预处理结果解析

可见hello.c转化为了hello.i文件,可发现文件加入了许多内容,但仍然可以阅读,进行了宏展开,头文件中内容也被包含进来
2.4 本章小结
介绍了什么叫做预处理,以及预处理的部分处理比如宏展开等

第3章 编译
3.1 编译的概念与作用
编译概念:将文本文件hello.i转化为文本文件hello.s,包含汇编语言程序。
作用:编译将c代码转换成与机器代码相近的汇编代码,将c语言定义的抽象的模型转化为处理器可以执行的指令

3.2 在Ubuntu下编译的命令
gcc -S hello.i -o hello.s

3.3 Hello的编译结果解析
3.3.1数据
1,常量

.LC0: .string “\347…”
“Hello %s %s\n”
实现:字符串常量
.LC1: .string “Hello %s %s\n”
2,变量
int i;
实现:编译器储存在堆栈中
movl $0, -4(%rbp)
调用函数时将i存储在堆栈,使用相对寻址比较操作
3.3.2,赋值//i=0操作
movb:一字节
movw:两字节
movl:四字节
movq:八字节
3.3.3类型转换

3.3.4算术操作:
i++;只需要使用add即可
3.3.5关系操作
for循环中i <8
if中agrc!=4
3.3.6数组
Argv(main函数的操作)
编译器使用寄存器偏移寻址
3.3.7控制转移
if(argv!=4)
cmpl $4, -20(%rbp)
je .L2
l2是不满足情况也就是else的部分
for(i=0;i<8;i++)
cmpl $7, -4(%rbp)
jle .L4
满足则跳转到.l4
3.3.8函数操作
调用函数时有以下操作:(假设函数P调用函数Q)
(1)传递控制:进行过程 Q 的时候,程序计数器必须设置为 Q 的代码的起始 地址,然后在返回时,要把程序计数器设置为 P 中调用 Q 后面那条指令的 地址。
(2)传递数据:P 必须能够向 Q 提供一个或多个参数,Q 必须能够向 P 中返回 一个值。
(3) 分配和释放内存:在开始时,Q 可能需要为局部变量分配空间,而在返回 前,又必须释放这些空间。
hello.c涉及的函数操作有:
main的参数是argc和argv;
printf函数的参数是两个字符串
exit参数是1,
sleep函数参数是atoi(argv[3])
以及getchar()
3.4 本章小结
使用编译器的过程便是将高级语言转化到汇编语言的过程。本节讲述了编译阶段中编译器处理数据与操作的方法,以及汇编语言。再仔细了解了这些机制以后,可以很容易的阅读汇编语言。
第4章 汇编
4.1 汇编的概念与作用
汇编:将汇编语言翻译成及其语言。同时生成可重定位目标文件的过程
gcc使用as将编译器输出的.i文件抓换为以.o的可重定位目标文件
4.2 在Ubuntu下汇编的命令
gcc hello.s -c -o hello.o

4.3 可重定位目标elf格式
1,ELF Header使用readelf -h hello.o

可见该文件为可重定位目标文件总共13个节。
从16B 的序列 Magic 开始,剩下部分包含了各类信息,包括elf头大小,目标文件类型,机器类型等等
2,Section Headers
节头部表:包含了哥哥姐的语义包括每个节的类型,位置以及大小。可见,每个节都是从0开始,用于重定位。同时也可以看到,代码可执行但是不可以写;数据段以及只读数据段无法执行,只读段无法写

3,查看.symtab
符号表:存放程序中定义和引用的函数和全局变量的信息。name:符号名。value:符号相对目标节位置便宜。size:目标大小。type:数据or函数。bind字段:表明符号本地or全局

4.4 Hello.o的结果解析
汇编器为每一条汇编语句加上了具体地址(相对文件头的),以便重定位器寻找对应位置,操作数由十进制改成了机器指令识别的十六进制,jmp语句和call语句从位置字符串修改为了指令相对偏移量,objdump同时将重定位条目转化为字符串标明在每一个重定位位置之后,我们可以获取每一个重定位条目的具体位置和指向。
4.5 本章小结
汇编器将程序由机器语言代码转化为可重定位目标文件,过程中加入了重定位条目使得重定位器可以根据条目属性以及偏移量进行重定位将编译器产生文件加上了偏移地址。同时将汇编文件格式化为二进制指令文件,并在重定位目标文件中生成了符号表以及重定位表项的文本段,以方便重定位器将其转化为可执行目标文件
第5章 链接
5.1 链接的概念与作用
将汇编器生成的可重定位文件连接到指令位置,将多个程序块组合贷一起形成一个每条指令由唯一运行地址的可执行文件。
作用:可重定位目标文件缺少对应的跳转地址,对于部分块,其中不含有调用的功能块,链接器将这些快链接,并生成唯一的地址供判断跳转指令运作
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的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
1,ELF Headers:

可以看到与hello.o不一样的地方在于Type类型为EXEC说明是可执行目标文件,同时有27个节
2,Section Headers
对hello中所有节的信息声明了,包括了size以及偏移量offset。我们可以根据section headers的信息利用hexedit确定各个节所占的区间。adress是程序被载入虚拟地址的起始地址

3,.symtab

5.4 hello的虚拟地址空间
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。

5.5 链接的重定位过程分析
(以下格式自行编排,编辑时删除)
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
1,hello反汇编有确定虚拟地址而hello.o则为0
2,hello反汇编代码中有更多的节以及更多的函数的汇编代码
hello重定位全过程:
在连接器合并了所有类型相同的节以后。连接器为新的聚合姐赋予内存地址,每个输入模块定义的每个节以及每个符号都会被赋予。在这以后都有唯一运行时的地址。重定位节中的符号引用这一步,连接器会修改代码节数据节对每个符号的引用,使其指向运行时地址。在遇到对最终位置不确定的引用的时候,会生成一个重定位条目。代码的这部分在.rel.txt之中。计算公式省略
5.6 hello的执行流程
加载程序:

call ld-2.23.so!_dl_start

ld-2.23.so!_dl_init

hello!__libc_start_main@plt

运行:

Main

Hello!puts@plt

终止:
5.7 Hello的动态链接分析
PLT:
PLT是数组其中每个条目是16字节代码。PLT[0]是一个特殊条目,它跳转到动态链接器中。每个被可执行程序调用的库函数都有它自己的PLT条目。每个条目都负责调用一个具体的函数。
GOT:
GOT是数组,每个条目是8字节。和PLT联合使用时,GOT[O]和GOT[1]包含动态链接器在解析函数地址时会使用的信息。GOT[2]是动态链接器在1d-linux.so模块中的入口点。其余的每个条目对应于一个被调用的函数,其地址需要在运行时被解析。每个条目都有一个相匹配的PLT条目。
取得.got
以及.got.plt位置
我们可以发现在这个操作以后动态链接的库信息被填入对应的位置
从elf文件中可以发现,got起始地址为0x404000.

5.8 本章小结
连接器对可重定向目标文件执行了连接操作,向我们的hello文件中加入了外部调用的库函数,将相对偏移量修改为偏移地址寻址,并为每条指令定义了运行时地址。
链接器首先在生成可执行目标文件时链接了部分外部函数,在运行时还可以通过编辑GOT表动态链接库文件,使用各种静态动态库,使得可执行目标文件的大小大幅缩减,并大大提高了程序的内存利用率 。

第6章 hello进程管理
6.1 进程的概念与作用
概念:一个执行中程序的实例,每个进程都有对应的地址空间。一般情 况下,包括文本区域、数据区域、和堆栈。文本区域存储处理器执行的代码;数 据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储区着活动 过程调用的指令和本地变量。
作用:给用户提供了两个假象:一个是程序仿佛是系统中执行的唯一程序,第二个是处理器仿佛在无间断的执行程序
6.2 简述壳Shell-bash的作用与处理流程
shell:一个交互型的应用级程序,代表用户运行其他程序,基本作用便是解释运行用户的指令
流程:
shell会解析用户传入的命令行
1、对于空命令行直接返回
2、对于内置命令则直接执行对应操作
3、若不是内置命令,则在当前路径和PATH中寻找对应的应用程序,若找到,则创建一个新的进程并加载相关程序运行
6.3 Hello的fork进程创建过程
fork:创建子进程,父进程返回子进程pid,子进程返回0
hello在被调用时候,shell调用fprk创建一个完全相同的子进程,并在子进程中执行调用hello的操作
6.4 Hello的execve过程
作用:在当前进程中载入并行陈旭,覆盖当前的代码,数据栈,同时继承打开的文件描述以及信号的上下文。
shell中fork()子进程先阻断信号再设置进程pid,成功便可以加载execve并允许文件,返回则说明失败了。当shell已调用fork为hello创建一个独立的子进程后,在子进程中调用execve函数,载入可执行文件hello,因为fork函数创建的子进程拥有与shell完全相同的写时复制内存映像,被覆盖的部分将经由写时复制的规则,申请一块新的内存并映射,这样便创建了一个运行着hello的子进程,并将其pid经由fork函数返回至shell的主进程
6.5 Hello的进程执行
一个进程可以将他理解为是一个逻辑控制流,计算机同时运行着不同的进程,我们可以将这些进程称为是并发的。对于并发进程来说,他们是没有交集的,因为一个核心在一个时间内只能执行一个进程。为了营造这种假象,计算机使用上下文切换来实现多任务的情况。
内核会为每个进程都维护上下文,上下文是内核重新启动一个之前被抢占的进程所需要的状态。
hello进程在执行以后,可能会挂起,此时系统调度其他的进程或者异常处理程序执行,同时保存他的上下文,一直到hello这个进程下次再被系统调用,或者从异常处理之中返回。循环以上步骤一直到hello进程结束

6.6 hello的异常与信号处理
1,异常类型原因,属性以及他们的返回行为:

2,键盘上输入crtl-z

可以发现并未回收,而是运行在后台,根据使用ps指令可以看到,可以再次调用使其继续运行打印剩下的info

3,输入crtl c
使用ps可以发现没有hello进程,该指令会导致内核发送信号到前台的每个进程

4,乱按,并不会影响输出

没有影响
6.7本章小结
本章从进程角度解释了当hello被执行时shell经过怎样的过程执行和维护hello进程,解释了进程运行时的调度和上下文机制,说明了可能引起的异常和对应的信号处理。

第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址是指在计算机体系结构中是指应用程序角度看到的内存单元、存储单元、网络主机的地址。
线性地址是逻辑地址到物理地址变换之间的中间层。在分段部件中逻辑地址是段中的偏移地址,然后加上基地址就是线性地址。
虚拟地址是Windows程序时运行在保护模式下,这样程序访问存储器所使用的逻辑地址称为虚拟地址,与实地址模式下的分段地址类似,虚拟地址也可以写为“段:偏移量”的形式,这里的段是指段选择器。
在存储器里以字节为单位存储信息,为正确地存放或取得信息,每一个字节单元给以一个唯一的存储器地址,称为物理地址,又叫实际地址或绝对地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
逻辑地址由两部分组成:段标识符以及段内偏移量。前者由16位长字段组成-段选择符。前十三位为索引号-段描述符。一些全局段描述符,就放在放在GDT之中,局部的就放在LDT中。使用这两个中的某个由T1字段决定,0则为GDT,1则为LDT。
首先,给定一个完整的逻辑地址[段选择符:段内偏移地址],
1、根据段选择符的T1选择当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。
2、取出段选择符前13位,可以查找到对应的段描述符,得到其基地址。
3、Base + offset是要转换的线性地址。
7.3 Hello的线性地址到物理地址的变换-页式管理
CPU的页式内存管理单元,负责将一个线性地址,最终翻译为物理地址。从管理和效率的角度出发,线性地址被分为以固定长度为单位的组-页(page),举例:一个32位的机器,线性地址最大可为4G,可以用4KB为一个页来划分,这页,整个线性地址就被划分为一个tatol_page[2^20]的大数组,共有2的20个次方个页。这个大数组我们称之为页目录。目录中的每一个目录项,就是一个地址。
分页地址中,页目录唯一,地址在寄存器中是地址转换的开始。每一个进程都有对应的虚拟内存以及独立的页目录地址。运行进程需要将页目录地址放在寄存器之中。每个线性地址被分为三部分分别是。面目录索引,页表索引,偏移
7.4 TLB与四级页表支持下的VA到PA的变换
36位的vpn被划分为4个9位的片,每个片被用作页表偏移量。CR3寄存器包含了L1页表的物理地址。VPN1提供一个L1 PET偏移量,该pet包含L2页表基地址。VPN2提供一个L2PTE的偏移量
7.5 三级Cache支持下的物理内存访问
结构:
1,组选择
它的组选择与直接映射高速缓存的组选择一样,组索引位标识组。
2,行匹配与字选择
组相联高速缓存中的行匹配比直接映射高速缓存中的更复杂,因为它必须检查多个行的标记位和有效位,以确定所请求的字是否在集合中。传统的内存是-一个值的数组,以地址作为输人,并返回存储在那个地址的值。另一方面,相联存储器是一个(key, value)对的数组,以key为输入,返回与输人的key相匹配的(key. value)对中的 value 值。因此,我们可以把组相联高速缓存中的每个组都看成一个小的相联存储器,key 是标记和有效位,而value就是块的内容。
3,组相联高速缓存中不命中时的行替换
如果CPU请求的字不在组的任何一行中,那么就是缓存不命中,高速缓存必须从内存中取出包含这个字的块。不过,一旦高速缓存取出了这个块,该替换哪个行呢?当然,如果有一个空行,那它就是个很好的候选。但是如果该组中没有空行,那么我们必须从中选择一个非空的行,希望CPU不会很快引用这个被替换的行。
最简单的替换策略是随机选择要替换的行。其他更复杂的策略利用了局部性原理,以使在比较近的将来引用被替换的行的概率最小。例如,最不常使用(Least-Frequently Used,LFU)策略会替换在过去某个时间窗口内引用次数最少的那一行。最近最少使用(Least-Recently-Used, LRU)策略会替换最后一次访问时间最久远的那一行。所有这些策略都需要额外的时间和硬件。但是,越往存储器层次结构下面走,远离CPU,一次不命中的开销就会更加昂贵,用更好的替换策略使得不命中最少也变得更加值得了。
7.6 hello进程fork时的内存映射
当fork函数被当前进程调用时,内核为新进程创建数据结构,并分配一个唯一PID。为了给新进程创建虚拟内存,它创建了当前进程的mm_struct,区城结构和页表的原样副本。它将两个进程中的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虛拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间的抽象概念。
7.7 hello进程execve时的内存映射
Execve调用时进行如下操作:
1)删除已存在的用户区域
2)映射私有区域
3)映射共享区域
4)设置程序计数器PC
继续进行该进程时,他将从设置的程序计数器作为入口点开始执行,linux根据需要换入代码和数据页面
7.8 缺页故障与缺页中断处理
缺页中断的处理:处理器生成一个虚拟地址,并将其传送给MMU。MMU生成PTE地址,并从高速缓存/主存申请得到它。高速缓存/主存向MMU返回PTE。PTE中的有效位是零,所以MMU触发了一次异常,传递CPU中的控制到操作系统内核中的缺页异常处理程序。缺页处理程序确定出物理内存中的牺牲页,如果这个页面已经被修改了,则把它换出到磁盘。缺页处理程序页面调人新的页面,并更新内存中的PTE。缺页处理程序返回到原来的进程,再次执行导致缺页的指令。CPU将引起缺页的虚拟地址重新发送给MMU。命中后,主存将所请求字返回给处理器。
7.9动态存储分配管理
动态内存分配器维护着一个进程的虚拟内存空间,称为堆。系统之间细节不同,但是不失通用性,,假设堆是一个请求二进制零的区域,他紧接在未初始化的数据区域后开始,并向上生长,,对于每一个进程,内核维护着一个变量brk,他指向堆的顶部,分配器将堆视为一组不同大小的块的集合来维护。

每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式的保留为供应用程序使用。空闲的块可用来分配。空闲块保持空闲,直到它显式的被应用所分配,。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。
分配器有两种不同风格。两种风格都要求应用显式的分配块,他们的不同在于由哪个实体来负责释放已分配的块。
Malloc返回一个指针,指向大小为size字节的内存块,这个块会为可能包含在这个块内的任何数据对象类型做对齐。在32位模式中,malloc块返回的地址总是8的倍数。如果malloc遇到问题,那么他就返回NULL,并设置errno。
动态内存分配器,使用mmap和munmap,显式的分配和释放堆内存,或者还可以使用sbrk函数,sbrk函数通过将内核的brk指针增加incr来扩展和收缩堆,如果成功,他就返回brk的旧值。如果incr为零,那么sbrk就返回brk的当前值。用一个负的incr来调用sbrk是合法的,而且很巧妙,因为返回值(brk的旧值)指向距新堆顶向上abs(incr)字节处。程序通过调用free来释放已分配的堆块
一个块是由一个字的头部,有效载荷,以及可能的一些额外填充组成的,头部编码了这个块的大小(包括头部和所有的填充),以及这个块是已分配的还是空闲的。如果我们强加一个双字的对齐约束条件,那么块大小就是8的倍数,且块大小的最第三位总是0。我们就可以将剩余的三位用来编码其他信息。在这种情况中,我们用其中的最低位来指明这个块是已分配的还是空闲的。头部后面就是应用调用malloc时请求的有效载荷。有效载荷的后面是一片不使用的填充块,其大小可以是任意的。需要填充有很多原因。比如填充可能是分配策略的一部分,用来对付外部碎片,或者也需要用它来满足对其要求,我们可以组织一个连续的已分配块和空闲块的序列,它类似一个单向链表,我们称其为隐式空闲链表,空闲块通过头部中的大小字段隐含的连接着,分配器可以通过遍历堆中所有的块,从而间接的遍历整个空闲块的集合。同时我们使用一个特殊标记了的结束块以标记堆的结尾,即设置一个已分配的长度为零的块。
隐式空闲链表为我们提供了一种介绍一些基本分配器概念的简单方法。然而,因为块的分配与堆块的总数呈线性关系,所以对于通用的分配器,隐式空闲链表是不合适的,一种更好的方法时将空闲块组织成某种形式的显式数据结构。因为根据定义,程序不需要一个空闲块的主体,所以实现这个数据结构的指针可以存放在这些空闲块的主体里面。例如,堆可以组织成一个双向空闲链表,在每个空闲块中,都包含一个pred和succ指针,使用双向链表而不是隐式空闲链表,使首次适配的分配时间从块的总数的线性时间减少到了空闲块数量的线性时间,不过,释放一个块的时间可以是线性的,也可能是个常数,这取决于我们选择的空闲链表的排序策略。
7.10本章小结
对linux运行是进行内粗分配的实现以后,可以发现,内存分配的基层实现是特别复杂的,地址发出以后,翻译申请命中以及不命中或者缺页替换,最终加载到ALU之中,这些过程都十分值得学习,思考

第8章 hello的IO管理
8.1 Linux的IO设备管理方法
(以下格式自行编排,编辑时删除)
设备的模型化:文件
设备管理:unix io接口
Linux文件相当于一个m字节的序列。所有io设备被模型化为文件,所有输出输入都被当成相应文件的读写执行。这样就允许linux的内核引入一个简单低级的应用接口。
1)打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备。内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件。内核记录有关这个打开文件的所有信息。应用程序只需记住这个描述符。Linux shell创建的每个进程开始时都有三个打开的文件:标准输入(描述符为0)、标准输出(描述符为1)和标准错误(描述符为2)。头文件< unistd.h> 定义了常量STDIN_FILENO、STDOUT FILENO和STDERR FILENO,它们可用来代替显式的描述符值。
2)改变当前的文件位置。对于每个打开的文件,内核保持着一个文件位置k,初始为0。这个文件位置是从文件开头起始的字节偏移量。应用程序能够通过执行seek操作,显式地设置文件的当前位置为k.
3)读写文件。一个读操作就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n.给定一个大小为m字节的文件,当k≥m时执行读操作会触发一个称为EOF的条件,应用程序能检测到这个条件。在文件结尾处并没有明确的“EOF符号"。写操作就是从内存复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k.
4)关闭文件。当应用完成了对文件的访问之后,它就通知内核关闭这个文件。作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。无论一个进程因为何种原因终止时,内核都会关闭所有打开的文件并释放它们的内存资源。
8.2 简述Unix IO接口及其函数
打开:open()
关闭:close()
读:read()
写:write()
8.3 printf的实现分析
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
printf接受的规格化命令,将参数格式化输出。vprintf返回一个要打印的字符串的长度,实际上相当于将printf输出的参数格式化,之后输出给write,然后write调用系统中断门,实现系统服务,最后调用sys_call实现输出。对于控制台输出,linux使用do_console
字符显示驱动子程序
功能:指定位置用指定颜色,显示一个字符串以0结尾
参数:dh=行号,dl=列号,cl=颜色,ds:si指向的字符串的首地址

8.4 getchar的实现分析
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
分析:读取键盘输入缓冲区的第一个字符返回给程序
8.5本章小结
介绍了linux系统的io接口,以及printf,getchar这些函数的实现,帮助了解了一个hello程序的系统及io程序。

结论
用计算机系统的语言,逐条总结hello所经历的过程。
你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
hello.c在经过编译器预处理为.i文件,之后生成汇编码的.s文件,最后生成可重定位目标文件hello.o,最后连接器将.o文件与其他金太苦产生的文件链接,修改重定位的条目,最后生成一个可执行目标文件。
随后shell创建子程序hello,调用fork以及execue函数创建进程将可执行文件加载到了允许部分,之后我们测试了键盘输入的若干指令,并用ps观察后台的状态。
最后,进入了内存管理以及io接口部分,了解了MMU管理的内存映射,以及从cpu产生逻辑地址到指定位置取出字的过程。之后了解了printf以及getchar的实现。这整个过程加深了我对这门课程的理解,将之前零碎的知识贯穿起来

附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.i:代码文件
hello.s:汇编码文件
hello.o:可重定位目标文件
hello:可执行文件

参考文献
为完成本次大作业你翻阅的书籍与网站等
[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.
[7] 深入理解计算机系统(第三版) Randal E Bryant&David R. O’Hallaron
[8]https://baike.baidu.com/item/%E8%99%9A%E6%8B%9F%E5%9C%B0%E5%9D%80/1329947?fr=aladdin
[9] https://www.bilibili.com/read/cv4260090
[10]https://baike.baidu.com/item/%E9%80%BB%E8%BE%91%E5%9C%B0%E5%9D%80/3283849?fr=aladdin

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值