栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

hello的一生

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

hello的一生

计算机系统

大作业

题 目 程序人生-Hello’s P2P
专 业 计算机科学与技术
学   号 120L021517
班   级 2003012
学 生 朱加昊    
指 导 教 师 郑贵滨

计算机科学与技术学院
2021年5月
摘 要
每一个程序员在学习一个新语言的时候,往往都会以hello程序作为一个好的开始。但尽管是像hello这样简短的程序,也需要通过诸多流程,才能将hello输出到屏幕上为我们所见。本文将详细叙述hello程序从文本文件开始,如何通过一步步最终生成可执行文件,再被shell执行到回收的全过程,从而更好地帮助我们理解一个程序在计算机中的底层实现。

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

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

目 录

第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的预处理结果解析 - 6 -
2.4 本章小结 - 6 -
第3章 编译 - 7 -
3.1 编译的概念与作用 - 7 -
3.2 在Ubuntu下编译的命令 - 7 -
3.3 Hello的编译结果解析 - 9 -
3.4 本章小结 - 13 -
第4章 汇编 - 14 -
4.1 汇编的概念与作用 - 14 -
4.2 在Ubuntu下汇编的命令 - 14 -
4.3 可重定位目标elf格式 - 15 -
4.4 Hello.o的结果解析 - 15 -
4.5 本章小结 - 17 -
第5章 链接 - 19 -
5.1 链接的概念与作用 - 19 -
5.2 在Ubuntu下链接的命令 - 19 -
5.3 可执行目标文件hello的格式 - 19 -
5.4 hello的虚拟地址空间 - 21 -
5.5 链接的重定位过程分析 - 21 -
5.6 hello的执行流程 - 23 -
5.7 Hello的动态链接分析 - 23 -
5.8 本章小结 - 24 -
第6章 hello进程管理 - 25 -
6.1 进程的概念与作用 - 25 -
6.2 简述壳Shell-bash的作用与处理流程 - 25 -
6.3 Hello的fork进程创建过程 - 25 -
6.4 Hello的execve过程 - 25 -
6.5 Hello的进程执行 - 26 -
6.6 hello的异常与信号处理 - 26 -
6.7本章小结 - 29 -
第7章 hello的存储管理 - 30 -
7.1 hello的存储器地址空间 - 30 -
7.2 Intel逻辑地址到线性地址的变换-段式管理 - 30 -
7.3 Hello的线性地址到物理地址的变换-页式管理 - 30 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 31 -
7.5 三级Cache支持下的物理内存访问 - 31 -
7.6 hello进程fork时的内存映射 - 32 -
7.7 hello进程execve时的内存映射 - 32 -
7.8 缺页故障与缺页中断处理 - 32 -
7.9动态存储分配管理 - 33 -
7.10本章小结 - 33 -
第8章 hello的IO管理 - 35 -
8.1 Linux的IO设备管理方法 - 35 -
8.2 简述Unix IO接口及其函数 - 35 -
8.3 printf的实现分析 - 35 -
8.4 getchar的实现分析 - 35 -
8.5本章小结 - 36 -
结论 - 36 -
附件 - 37 -
参考文献 - 38 -

第1章 概述
1.1 Hello简介
1)P2P过程:P2P过程本质上是文本文件到可执行文件的过程。
首先,hello.c经过预处理器生成hello.i,然后hello.i经过编译器生成hello.s,也就是汇编语言,接着hello.s经过汇编器生成hello.o,也就是二进制代码,最后经过连接器生成可执行程序hello,以及通过shell fork产生子进程,并在子进程中用execve函数执行。
2)020过程:020过程本质上是可执行文件hello从执行到回收的过程。
首先,shell fork产生子进程,并在子进程中调用execve函数执行hello,然后将虚拟内存映射到物理内存中,接着从程序入口进入hello,运行main函数,最后程序运行结束,父进程回收子进程。
1.2 环境与工具
硬件环境:处理器i7-10875H CPU 内存16G 刷新率2.3GHz
软件环境:Windows10 64位 Ubuntu-20.04.4
开发与调试工具:Visual Studio 2022 gcc/edb objdump
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
hello.i:hello.c经过预处理后得到的文本文件
hello.s:hello.i经过编译器后得到的汇编文件
hello.o:hello.s经过汇编器后得到的可重定位目标文件
hello:hello.o经过连接器后得到的可执行文件
hello_objdump:hello的反汇编代码
hello.o_objdump:hello.o的反汇编代码
1.4 本章小结
本章简要叙述了hello的P2P和020过程,列出了实验环境与工作和中间结果。
(第1章0.5分)

第2章 预处理
2.1 预处理的概念与作用
预处理是在程序编译前需要进行的准备工作,其主要任务是执行预处理指令。预处理指令以“#”开头,到“#”后第一个换行符结束,其指令类型包括条件包含、源文件包含、宏替换、行控制、抛错、杂注和控指令。
其中,源文件包含的作用是搜索指定的文件,并将其内容包含进来。例如“#include”等头文件,预处理指令会将其头文件中定义的内容加载到输出文件中,便于程序的编译。
宏替换的作用是把一个标识符(预处理记号)指定为其他一些称为替换列表的预处理记号,当这个标识符出现在后面的文本中时,将用对应的替换列表把它替换掉。例如“#define a b”,预编译时会把后续所有的a用b替换,另外“#undef”则会取消一对宏替换,即后续出现被替换字符不会被替换。
条件包含的作用是根据条件有选择性地保留或者放弃源文件中的某些内容,例如“#ifdef, #ifndef, #else, #elif, #endif”等,通过引入这些伪指令,预处理器可以决定编译程序对哪些代码进行处理,并且过滤不必要的代码。
2.2在Ubuntu下预处理的命令

图1 使用cpp对hello.c进行预处理

图2 预处理结果
2.3 Hello的预处理结果解析
经过预处理,hello.c中使用的库函数被直接添加到了c文件中,而main函数中的主体内容并没有改变。而库函数的内容占了hello.i文件的一大半,可见简单的一个hello程序引用的库函数也非常庞大。
2.4 本章小结
综述了预处理的概念和作用,并对hello.c进行了预处理,对其预处理结果进行了分析。

(第2章0.5分)

第3章 编译
3.1 编译的概念与作用
编译的概念就是指把预处理得到的.i文件,经过编译器后得到汇编语言程序的过程。
汇编的作用有以下两项:
1)检查代码的准确性,即包括代码的规范性、代码是否存在语法错误。
2)在代码准确无误的前提下,编译器将hello.i文件编译为hello.s文件,也就是从高级语言翻译为汇编语言。
3.2 在Ubuntu下编译的命令

图3 Ubuntu下使用gcc对hello.i进行编译

图4 Ubuntu下hello.s编译结果
3.3 Hello的编译结果解析
3.3.1 数据
hello.s中c语言的数据类型有全局变量,局部变量,指针数组,字符串。
1)hello.s中存在局部变量,即循环变量i。编译器将i存放在堆栈中,具体地址为-4(%rbp),占用4字节的栈空间。

图5  hello.s中局部变量的存储
2)字符串:程序中有2个字符串,“347224250346263225: Hello345255246345217267  345247223345220215  347247222346225260357274201”、“Hello %s %sn”,均为printf()函数的参数。

图6 hello.s中的字符串
3)main()函数的函数名为全局变量,存放在数据段。 main()函数有两个参数:int argc和char *argv[]:int argc是参数的个数,存放在寄存器%edi中;char *argv[]是一个存放用户在shell中输入的命令行的数组,存放在寄存器%rsi中。

图7 hello.s中main函数及其传参的存储
3.3.2 控制转移
main()函数中有一个条件判断语句,判断的内容是argc是否等于4,编译的结果是:cmpl $4, -20(%rbp);je .L2;如果cmpl的结果不会改变变量的值,只会改变标志位的值,je指令通过标志位判断是否跳转至.L2。
main()函数中有一个循环结构,循环条件为:for(i=0;i<8;i++)。循环变量的存储已经在3.3.1中说明,判断循环终止条件的编译结果是:cmpl $7, -4(%rbp);jle .L4,含义同上。

图8 hello.s中的控制转移
3.3.3 函数调用
main()函数中调用的函数有:printf1()、atoi()、sleep()()等,编译器结果为:call call printf@PLT;call atoi@PLT;call sleep@PLT;等。

图9 hello.s中的函数调用
3.3.4 函数返回
main函数在运行结束时需要返回到shell,使用的是leave指令。在抵达return 0后,编译结果为:movl $0, %eax;leave;其中leave指令包含了对%rbp的移动用以平衡栈帧。

图10 hello.s中的函数返回

3.4 本章小结
本章阐述了编译的概念和作用,并使用gcc编译hello.i生成hello.s。然后在数据存储、控制转移、函数调用、函数返回四个方面,对hello.s编译成果进行了分析。到此,原本的hello.c已经实现了又高级语言到汇编语言的转变。
(第3章2分)

第4章 汇编
4.1 汇编的概念与作用
汇编的概念:汇编器将汇编语言(.s文件)转化为机器指令的过程就是汇编。
汇编的作用:例如在本次实验中,汇编器将hello.s汇编生成hello.o。hello.o是二进制文件,包含main函数在内的二进制代码。
4.2 在Ubuntu下汇编的命令

图11 Ubuntu下汇编的命令

图12 Ubuntu下hello.o汇编结果
4.3 可重定位目标elf格式

图13 hello.o的ELF格式的ELF头
ELF头以一个16字节的Magic序列开始,描述的是生成该文件的系统的字的大小和字节顺序。剩下的部分包含帮助链接器分析语法和解释目标文件的信息,其中包含ELF头大小、目标文件的类型、机器类型、字节头部表的文件偏移。

图14 hello.o的ELF格式的节头表
节头表描述了不同节的位置和大小,其中目标文件中每个节都有一个固定大小的条目。具体的描述包括节的名称、类型、地址和偏移量等。

图15 hello.o的ELF格式的重定位节
重定位节中有4列,第一列是需要重定向文件在.text或者.data中的偏移量;第二、三列包含了符号和类型,符号是重定位目标在symtab中的偏移量,类型是重定位的类型;第四列是重定向的目标名称。

图16 hello.o的ELF格式的符号表
4.4 Hello.o的结果分析

图17 hello.o的反汇编代码
分析比较hello.o的反汇编代码和hello.s的汇编代码,可以得到一下结论:
1)立即数部分,hello.s中的立即数是十进制数,hello.o的反汇编代码中的立即数是十六进制数。
2)函数调用部分,hello.s中的的函数调用方法是“call<函数名>”,而反汇编代码中函数调用直接跳转到函数的首地址。
3)分支部分,hello.s中使用.Lx在hello.o的反汇编代码中以跳转地址的方式呈现。
4.5 本章小结
本章对于汇编的概念和功能进行了阐述,对hello.s文件进行了汇编处理得到了hello.o文件,然后观察了elf中的代码内容并进行了分析,最后使用objdump查看了hello.o的反汇编代码,与hello.s的汇编代码进行对比,分析了汇编过程中对于代码的改变和调整。

(第4章1分)

第5章 链接
5.1 链接的概念与作用
链接的概念:链接是将可重定位目标文件经过解析和重定位步骤合成可执行目标文件的过程,也就是链接器将.o文件生成可执行文件的过程。
链接的作用:链接的作用主要有两个,一是模块化,程序可以编写为一个较小的源文件的集合;二是效率高,时间上分开编译,更改一个源文件,编译,然后重新链接,不需要重新编译其他源文件。空间上可以将公共函数聚合为单个文件,而可执行文件和运行内存映像只包含它们实际使用的函数的代码。
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

图18 使用ld指令链接hello.o生成hello可执行文件
5.3 可执行目标文件hello的格式

图19 hello的ELF格式
各段信息:
. Init:定义了_init函数,程序初始化代码会调用它。
. text:已编译程序的机器代码。
. rodata:只读数据,比如printf语句中的格式串和开关语句的跳转表。
. data:已初始化的全局和静态C变量。
. bss:未初始化的全局和静态C变量。
. symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。
. debug:一个调试符号表,其条目时程序中定义的全局变量和类型定义,程序中定义和引用的全局变量,以及原始的C源文件。
. line:原始C源程序的行号和.text节中机器指令之间的映射。
. strtab:一个字符串表,其内容包括 .symtab 和 .debug节中的符号表,以及节头部中的节名字。
5.4 hello的虚拟地址空间

图20 edb打开hello可执行文件
使用edb打开hello,在Plugins->SymbolViewer里输入_start,观察到地址为0x4010f0,与ELF中.text的地址是一致的。特别的,在第4章中,ELF中的地址都是0x0,是没有分配地址的,所以是可重定位文件,而链接之后就分配了具体的虚拟内存地址,所以是可执行文件。
5.5 链接的重定位过程分析

图21 hello的反汇编代码
分析hello.o与hello的区别:
1.hello中没有hello.o的重定位条目。
2.hello中加入了main函数调用的库函数,而hello.o没有。
3.hello中函数调用地址是虚拟内存地址,而hello.o中没有分配地址。
4.hello.o中相对偏移地址是hello中的虚拟内存地址。
链接的过程就是链接器读取一组可重定位目标文件,将其链接起来形成可执行文件。文件的选择参照hello.o中的函数调用,通过对这些调用进行解析,链接器可以获取函数输入目标模块中代码节和数据节的具体大小,然后将它们重定位即可。
5.6 hello的执行流程
函数的调用与跳转流程如下:
0x401000 <_init>;
0x401090 puts@plt;
0x4010a0 printf@plt;
0x4010b0 getchar@plt;
0x4010c0 atoi@plt;
0x4010d0 exit@plt;
0x4010e0 sleep@plt;
0x4010f0 <_start>;
0x401125 ;
0x4011c0 <__libc_csu_init>;
0x401230 <__libc_csu_fini>;
0x401238 <_fini>;
5.7 Hello的动态链接分析
PLT:PLT是一个数组,其中每个条目是16字节代码。PLT[0]是一个特殊条目,跳转到动态链接器中。每个条目都负责调用一个具体的函数。PLT[[1]]调用系统启动函数 (__libc_start_main)。从PLT[[2]]开始的条目调用用户代码调用的函数。
GOT:GOT是一个数组,其中每个条目是8字节地址。和PLT联合使用时,GOT[0]和GOT[[1]]包含动态链接器在解析函数地址时会使用的信息。GOT[[2]]是动态链接器在ld-linux.so模块中的入口点。其余的每个条目对应于一个被调用的函数,其地址需要在运行时被解析。

图22 edb调试结果
5.8 本章小结
本章主要介绍了链接的概念和作用,并且阐释了在链接器工作时对于原先可重定位文件的改变,以及hello可执行文件虚拟地址的分配。展示了hello可执行文件的流程以及动态链接分析。
(第5章1分)

第6章 hello进程管理
6.1 进程的概念与作用
进程的概念:进程的定义是一个正在运行的程序的实例,是计算机科学最深刻的概念之一,不同于“程序”或“处理器”。进程提供给应用程序两个关键抽象:
1)逻辑控制流:每个程序似乎独占使用CPU,由OS内核通过上下文切换机制实现。
2)私有地址空间:每个程序似乎独占地使用内存系统,由OS内核地虚拟内存机制实现。
进程的作用:给应用程序提供关键抽象。一个独立的逻辑控制流,他提供一个假象,好像我们的程序独占的使用处理器。一个私有的地址空间,它提供一个假象,好像我们的程序独占的使用内存系统。每次用户通过向shell 输入一个可执行目标文件的名字,运行程序时, shell 就会创建一个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件。应用程序也能够创建新进程,并且在这个新进程的上下文中运行它们自己的代码或其他应用程序。
6.2 简述壳Shell-bash的作用与处理流程
shell是一个交互型应用级程序,代表用户运行其他程序。其功能在于为用户提供了一个界面,可以访问其他程序,并对进程进行管理。
shell的处理流程大致如下:
第一步,shell终端接收用户输入。
第二步,将用户输入分块后得到输入参数。
第三步,判断用户输入是否为内置函数,如果是,则直接调用。
第四步,如果上一步中判定不是内置函数,则调用相应的程序执行。
第五步,在正常运行的同时,shell也应对键盘输入信号做出响应。
6.3 Hello的fork进程创建过程
创建进程的方法在于,父进程通过调用fork函数创建一个新的、处于运行状态的子进程,子进程返回0,父进程返回子进程的PID,新创建的子进程几乎但不完全与父进程相同。换句话说,就是fork函数被调用一次,却返回两次。
具体到hello程序,用户在命令窗口输入执行当前目录下的可执行文件hello的命令,父进程会通过fork函数创建一个新的子进程用以执行hello。接下来,如果要真的执行hello子进程还需要配合execve函数。
6.4 Hello的execve过程
当子进程调用execve函数来运行另一个程序时,这个进程的地址空间代码和数据都被新程序的代码和数据刷新替换。也就是说,execve函数被调用的时候,并不会创建新的进程,而是换了代码段的内容,调用execve后id号不会改变。
具体到hello程序,须有以下4个步骤:
1.删除已存在的用户区域
2.映射私有区域
3.映射共享区域
4.设置程序计数器(PC)
6.5 Hello的进程执行
hello的进程执行:
1 调用sleep函数之前,进程的时间片处于用户模式
2 调用sleep函数之后,它显示地请求让进程休眠,此时进程陷入内核模式,由内核决定将控制转移给其他进程
3 sleep的计时结束之后,内核重新调度hello程序,将控制转移给它,时间片又回到用户模式
4 调用getchar函数之后,getchar函数调用read,再次进入内核模式,等待键盘的输入,内核再次调度其他进程,并发地执行其他程序
5 键盘输入完成,内核重新调度hello进程。
6.6 hello的异常与信号处理
hello运行中可能出现中断、陷阱、故障和终止。
中断是指输入键盘信号,然后根据键盘输入返回hello信息,陷进是指hello的main函数里sleep和getchar函数调用,都会使hello进入内核模式然后调用别的程序。故障是指第一次调用hello程序的时候都会触发缺页故障,然后程序会自动调用缺页异常处理程序。最后终止是指return,退出程序运行。

图23 hello程序正常运行

图24 使用ctrl+c终止hello程序

图25 在hello程序执行过程中不停乱按结果
可见,在hello程序执行过程中,乱按键盘,包括回车,并不会对程序运行产生影响。

图26 在hello程序进行过程中输入ctrl+z, jobs, ps, pstree指令结果

图27 使用kill杀死hello进程
6.7本章小结
本章对于hello的进程管理进行阐述。首先介绍了进程的概念和作用,然后介绍了shell-bash的作用和流程,接着阐释了fork和execve函数相互结合调用hello程序的过程,最后,对与hello程序执行过程中的异常和信号处理。
(第6章1分)

第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:由程序产生的与段相关的偏移地址部分,又称绝对地址。例如在C语言指针编程中能读取指针变量的值(‘&’操作),这个值就是逻辑地址,它是相对于当前进程数据段的地址。只有在Intel实模式下逻辑地址才和物理地址相等。
线性地址:是逻辑地址到物理地址变换之间的中间层。程序代码会产生逻辑地址,或者说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。如果启用了分页机制,那么线性地址能再经变换以产生一个物理地址。若没有启用分页机制,那么线性地址就是物理地址。
虚拟地址:现代处理器使用一种称为虚拟寻址(virtual addressing)的寻址形式。使用虚拟寻址,CPU通过生成一个虚拟地址(Virtual Address,VA)来访问主存,这个虚拟地址在被送到内存之前先转换成适当的物理地址。
物理地址:目前CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果。如果启用了分页机制,那么线性地址会使用页目录和页表中的项变换成物理地址。如果没有启用分页机制,那么线性地址直接就是物理地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
在段式管理系统中,整个进程的地址空间是二维的,即线性地址由段好+段内偏移地址两部分组成,为了完成进程逻辑地址到物理地址的映射,处理器到内存中查找段表,用段号匹配具体条目从而得到段的首地址,加上段内地址得到实际的物理地址,这个过程由处理器硬件完成,每个进程都有属于自己的段表。
7.3 Hello的线性地址到物理地址的变换-页式管理
虚拟地址由虚拟页号和虚拟页偏移量组成,如下图:

图28 虚拟页号与虚拟页偏移
物理地址由物理页号和物理页偏移量组成,如下图:

图29 物理页号与物理页偏移
在读取虚拟地址时,根据VPN(虚拟页号),在页表中找到对应的物理页号(PPN),而虚拟页偏VPO和物理页偏移PPO相同。
7.4 TLB与四级页表支持下的VA到PA的变换

图30 Core i7地址翻译
读取VA时,利用VPN得出TLBT,然后从TLB中获取物理页号,从而取得物理地址。特别的,TLB充当页表的缓存,可以加速虚拟地址到物理地址的变换过程。
7.5 三级Cache支持下的物理内存访问
三级Cache支持下的物理内存访问流程如下:
1.首先获取物理地址VA,使用物理地址的CI进行组索引(8路组相联),对8 路的块分别与缓存标记CT进行标志位匹配。若匹配成功且块的valid标志位为1则命中。然后根据块偏移CO取出数据并返回。
2.若未到相匹配的块或者标志位为0,则miss。一级cache向下逐级cache,   即二级cache甚至是三级cache中寻找查询数据。然后向上逐级写入cache。
3.在更新cache的时候,需要判断是否有空闲块。如果有空闲块(即有效位为0)则写入;如果不存在则驱逐一个块(LRU策略,Least Recently Used,即最近最少使用)。
7.6 hello进程fork时的内存映射
当shell调用fork时,创建当前进程的的mm_struct, vm_area_struct和页表的原样副本。两个进程中的每个页面都标记为只读,两个进程中的每个区域结构(vm_area_struct)都标记为私有的写时复制(COW),新进程拥有与调用fork进程相同的虚拟内存。
7.7 hello进程execve时的内存映射

图31 execve的内存映射
7.8 缺页故障与缺页中断处理

图32 缺页故障与缺页中断处理
具体步骤如下:
1.处理器将虚拟地址发送给MMU
2.MMU使用内存中的页表生成PTE地址
3.同2
4.有效位为零,因此MMU触发缺页异常
5.缺页处理程序确定物理内存中牺牲页(若页面被修改,则换出到磁盘)
6.缺页处理程序调入新的页面,并更新内存中的PTE
7.缺页处理程序返回到原来进程,再次执行缺页的指令
7.9动态存储分配管理
堆:动态存储的分配管理是由动态内存分配器完成的,动态内存分配器维护着一个进程的虚拟内存区域(称为堆),堆是请求二进制零的区域,它季节再未初始化的数据区域后面,并向上增长。对于每个进程,内核维护一个brk变量,它指向堆顶。
堆结构:分配器把堆看成一组大小不同的块的集合来维护。其中每一个块就是一个连续虚拟内存片,它的状态有两种,已分配或者空闲。已分配块显式地保留为应用程序所使用,空闲块可被分配,保持空闲直到显式地被应用程序分配。已分配块保持分配状态,直到被释放。
分配器的实现:
1.分配器的分类:
显示分配器:要求应用显式地释放任何已分配块
隐式分配器:要求分配器检测一个已分配块何时不再被程序使用,自动地释 放已分配块
2.隐式空闲链表:
带边界标签的隐式空闲链表将这些信息嵌入块本身,在块的前四个字节的头 部和最后四个字节的尾部中嵌入表示块状态的信息,最低位用来表示块的分配 状态,1表示已分配,0表示未分 配;整个四字节的值用来表示块的大小;在 空闲块进行合并时,通过前一个块的脚部和后一个块的尾部就能知道新释 放的空闲块该如何合并。
3.显式空闲链表:
将堆组织成一个双向空闲链表,在每一个空闲块中,都包含一个pred(前驱) 和succ(后继)指针,释放一个块可以采用后进先出(LIFO)的顺序或者按 照地址顺序来维护链表,前者释放块可以在常数时间内完成,后者需要线性的 时间,但具有更高的内存利用率。
7.10本章小结
本章首先阐述了hello程序的虚拟地址空间,然后简要分析了虚拟地址到物理地址空间的段式管理和页式管理,接着介绍了VA到PA的翻译方式,以及基于TLB和四级页表的翻译模式还有缺页的处理方式,然后是fork函数和execve函数的内存映射,最后介绍了动态内存的分配策略。
(第7章 2分)

第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:将输入输出设备模型化为文件,然后对于IO设备的管理可以看作文件的读和写。
设备管理:允许Linux内核引出一个简单、低级的应用接口,将设备优雅地映射为文件的方式称为Unix I/O,这使得所有的输人和输出都能以一种统一且一致的方式来执行。
8.2 简述Unix IO接口及其函数
Unix IO有以下接口和函数:
read和write,简单的读写函数
readn和writen,原子性读写函数
recvfrom和sendto,增加了目标地址和地址结构长度的参数
recv和send,允许从进程到内核传递标志
readv和writev,允许指定往其中输入数据或从其中输出数据的缓冲区
recvmsg和sendmsg,结合了其他IO函数的所有特性,并具备接受和发送辅助数据的能力
8.3 printf的实现分析
https://www.cnblogs.com/pianist/p/3315801.html
print函数调用vsprint和write函数实现字符的格式化和显示:
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
调用getchar可以分为如下两个步骤:
1.用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止
2.当用户键入回车之后,getchar从stdin流中每次读入一个字符
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章首先介绍了linux的IO设备管理方法,然后简述了Unix IO的接口和方法,然后对于printf和getchar函数的实现进行了分析。
(第8章1分)
结论
用计算机系统的语言,逐条总结hello所经历的过程。
1.首先使用高级语言编程完成hello.c
2.预处理后将hello.c文件转变为hello.i文件
3.hello.i文件通过编译得到hello.s文件
4.hello.s文件通过汇编得到hello.o文件
5.hello.o文件通过链接得到hello可执行文件
6.在shell中输入命令行指令运行hello
7.Shell调用fork函数创建子进程,子进程中执行execve函数运行hello程序
8.执行hello程序过程中对于异常和信号输入做出反应
9.hello被shell回收
在一个学期的学习后,我从原来只会敲代码开始逐渐明白了计算机在运行一个程序的过程中所需要完成的步骤,明白了CPU、内存和操作系统在程序运行中的工作,我对于今后面向过程的程序编写有了更高维度的认识。
在计算机的迭代更新过程中,我认为如今的计算机已经发展到了一种非常高的境界,GPU的出现加强了对于浮点数的计算,可以说计算机的功能已经可以覆盖我们的日常需求。在计算机便利化的过程中,如果要做一个好的程序员,我们还是要回到计算机的本质上,对于每一条命令,每一个寄存器或者每一个二进制操作有自己的理解,这样产生的程序才能充分发挥计算机的全部实力。
(结论0分,缺失 -1分,根据内容酌情加分)

附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.i:hello.c经过预处理后得到的文本文件
hello.s:hello.i经过编译器后得到的汇编文件
hello.o:hello.s经过汇编器后得到的可重定位目标文件
hello:hello.o经过连接器后得到的可执行文件
hello_objdump:hello的反汇编代码
hello.o_objdump:hello.o的反汇编代码
(附件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分)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/887805.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号