计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 人工智能模块
学 号 202211308
班 级 wl026
学 生 韩喆
指 导 教 师 郑贵滨
计算机科学与技术学院
2024年5月
Hello.c是平凡但不平庸的程序,它是我学习C语言第一个编写的程序。从当时对C语言的懵懵懂懂到现在深入了解计算机系统,开始学习汇编这种更基础的语言。Hello.c依然值得我继续深究,在本次论文中,将会详细分析hello.c的生命历程,继续当时未曾了解过的内容,从.c,.i,.s,.o,到hello,从fork到execve,在到MUM,探索hello.c到hello world的每一步。
关键词:Linux;hello.c;汇编;计算机进程;
(摘要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的预处理结果解析.................... - 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.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简介
P2P是指From Program to Process,即从项目到进程的过程。在完成C语言hello.c之后,hello.c首先通过预处理器cpp后变为修改后的源程序hello.i;再经过编译器ccl变为汇编文件hello.s;再经过汇编器as之后得到可重定位目标文件hello.o;最后经过连接器ld便可变为可执行程序hello。此刻完成了所有program 的过程。在运行该可执行程序时,操作系统将其读取并且加载到内存上,之后使用fork函数创建一个子进程,在创建的子进程中调用execve()函数,在子进程上下文中执行hello。此时process进程完成。
020是指hello在运行之前和结束之后所占的资源均是0,当hello开始运行时,操作系统分配内存和运算资源给hello,当hello结束之后所有资源将被父进程,shell回收。
以上便是P2P和020的整个过程。
1.2 环境与工具
使用的软件环境:VMware虚拟机 Ubuntu22.04 Windows 11
硬件环境:Intel i5 13500H处理器
开发工具:gcc,edb,gdb,VS2022
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
文件名 | 作用 |
hello.c | hello.c源文件 |
hello.i | hello.c的预处理文件 |
hello.s | hello.i生成的汇编文件 |
hello.o | hello.s生成的可重定向文件 |
hello | hello.o生成的可执行文件 |
hellogdb | 用于gdb调试的可执行文件 |
hello.dump | hello.o生成的反汇编文件 |
hello.asm | hello生成的反汇编文件 |
hellohead.txt | hello.o生成的elf头 |
helloheaddk.txt | hello生成的elf文件 |
hellorelo.txt | hello.o生成的重定位头 |
hellosection.txt | hello.o生成的节头部表 |
hellosym.txt | hello.o生成的符号表 |
表1.1 中间结果
1.4 本章小结
主要介绍了hello 的基础内容,以及开发环境和中间结果。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
预处理的概念:预处理是代码执行前的一个阶段,负责对源代码进行宏替换,条件编译等操作。
预处理的作用:1.将源文件中以”include”格式包含的文件复制到编译的源文件中。
2.用实际值替换用”#define”定义的字符串。
3.根据”#if”后面的条件决定需要编译的代码。
4.删除所有的注释。
5.添加行号和文件标识符
2.2在Ubuntu下预处理的命令
预处理命令gcc -E hello.c -o hello
图2.1 预处理命令
2.3 Hello的预处理结果解析
预处理器会打开头文件,并将其加到当前程序中,同时进行了许多宏展开,且源程序被保留在了最后。
打开的头文件(部分):
图2.2 被打开的头文件
宏展开(部分):
图2.3 宏展开部分
原代码(保留在最后):
图2.4 保留的源代码
2.4 本章小结
完成了从源程序hello.c到预处理文件hello.i的步骤,解析了hello.i的内容,看到了被打开的头文件,宏展开和源代码。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译的概念:是将预处理文件hello.i翻译成汇编文件hello.s的过程,编译器ccl通过一系列的语法分析,词法分析,以及优化等级,将原C语言进行翻译成汇编语言并根据优化等级进行优化。
编译的作用:1.词法分析:扫描器将源代码中的字符分割成一系列的记号
2.语法分析:语法分析器将记号产生语法树,使用工具实现语法 分析。
3.代码优化:根据优化等级提升代码性能。
3.2 在Ubuntu下编译的命令
gcc -S hello.i -o hello.s
图3.1 编译命令
3.3 Hello的编译结果解析
3.3.1常量
1.字符串常量
在hello.c中存在字符串常量
图3.2 字符串常量
首先通过.section .rodata声明了只读段,其中存了两个字符串常量.LC0与.LC1,分别对于了hello.c程序中printf()里面的内容。
2.数字常量
在程序中出现的数据则都在.text段中的汇编代码中。如
图3.3 数字常量
hello.c中if条件判断中出现的5和9
3.3.2变量
在hello.c中用到的变量为i,由于i的作用是数组寻址,可得如图
图3.4 变量
由此实现访问数组的功能
3.3.3全局变量
图3.5 全局变量
由该段可知,编译器将main声明为了一个全局函数变量
3.3.4类型转换
在hello.c中,向sleep函数传递的参数为int类型,而实际传入了string类型,所以需要进行转换,直接通过调用库中函数atoi完成类型转换,如图
图3.6 类型转换
3.3.5赋值操作
赋值操作在hello.s中使用的次数很多,在变量变换,访问地址时均会用到赋值操作。如
图3.7 赋值操作
分别在运算和访问地址时进行了赋值操作。
3.3.6算数操作
如图的addl指令实现了hello.c中的i++命令
3.3.7关系操作
在hello.c中,if和for循环里各有一次关系运算。
图3.9 关系操作
分别对应了if条件和for循环的循环条件,判断argc是否等于5与i是否等于9,cmpl均为不等则跳转。
3.3.8数组指针的操作
主要是通过3.3.6与3.3.7中的操作完成数组指针的访问。而具体是通过.L4段和.L3段的一部分实现的。
图3.10 指针操作
3.3.9控制转移
图3.11 控制转移
通过cmpl判断与jle实现控制转移。
3.3.9函数操作
在hello.c中,主要调用了sleep,atoi,printf,getchar,exit函数,在汇编中,是通过call指令完成函数的跳转,如图。
图3.12 函数操作
而函数调用的前六个参数存放在%rdi,%rsi,%rdx,%rcx,%r8,%r9中,其他的参数则是存在栈中,函数的返回值则存在%rax中。
3.4 本章小结
执行了从预处理文件hello.i到汇编文件hello.s的过程,分析了hello.s中一系列操作步骤。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
汇编的概念:使用汇编器,完成从.s文件到.o文件的转换,即生成可重定位文件。
汇编的作用:将汇编语言生成机器二进制文件,生成可重定位目标文件,为下一步作做准备。
4.2 在Ubuntu下汇编的命令
使用gcc hello.s -c -o hello.o命令
图4.1 汇编指令
4.3 可重定位目标elf格式
使用readelf命令分别读出hello.o的elf头,符号表,节头部表,重定位节的信息。
Hello.o的elf头:
图4.2 elf头指令
elf头中的Magic保存了许多信息,它是一个十六字节序列,前四个字节确定文件类型,第五个字节表示系统位数,第六个字节表示是大端序还是小端序,第七个字节表示ELF文件版本,由于可重定位文件不会进行执行,没有程序头表故剩余字节用00填充,该序列描述生成文件的大多数信息。文件本身也描述了很多文件相关信息,如Start of section headers表示节头部表的偏移量。
图4.3 elf头
符号表:
图4.4 符号表指令
符号表中保存着hello.s中定义和引用的全局变量和函数的相关信息。
图4.4 符号表
在表中,Num表示序号,value表示该符号距离起始地址的偏移量,Size表示大小Type表示数据类型,Bind表示符号来源,Ndx表示到节头部表的索引,Name表示符号名称。
节头部表:
图4.5 节头部表指令
节头部表位于重定向文件中的最后面,表中包含来了如名称,类型,地址,偏移量,大小,旗标,信息,对齐等信息,由于没有重定向,故全都是0。
图4.6 节头部表
重定位节:
图4.7 重定位节指令
重定位节中包含了偏移量:相对text节,信息,类型,符号值,符号名称+加数信息,有该表可以确定重定位的具体地址和信息。
图4.8重定位节
4.4 Hello.o的结果解析
使用objdump -d -r hello.o >hello.dump命令生成hello.o的反汇编文件hello.dump
图4.9 反汇编指令
反汇编结果如图:
图4.10 反汇编结果
- 操作数表示不同:
在hello.s中,立即数使用十进制数来表示,在hello.dump中,立即数都用16进制表示。
Hello.s:
图4.11 hello.s 中的立即数
Hello.dump:
图4.12 hello.dump 中的立即数
- 跳转标识不同:
hello.s中call指令的跳转是跳转到标识符,hello.dump的call指令则可以直接根据main函数的相对地址进行跳转。
图4.13 hello.s中的跳转
图4.14 hello.dump中的跳转
上图分别是hello.s与hello.dump的跳转指令。
- 可视内容不同
hello.dump中可以看到指令的机器代码。如图
图4.15 hello.dump中的机器代码
- 函数调用差异
hello.s中函数调用使用函数名,hello.dump则使用函数的真实地址。
图4.16 hello.s的函数调用
图4.17 hello.dump中的函数调用
上述分别是hello.s与hello.dump的函数调用方式。
4.5 本章小结
执行了从汇编文件hello.s到可重定位文件hello.o的步骤,查看并分析了hello.o文件的elf头,符号表,节头部表,可重定位节的信息,并反汇编了汇编文件hello.dump,与汇编文件hello.s进行了比较
(第4章1分)
第5章 链接
5.1 链接的概念与作用
链接的概念:将可重定向文件中的各种代码和数据片段链接形成可执行文件的过程,它可以在内存中被直接加载运行。同时链接可分为,编译时链接,加载时链接和运行时链接。其中运行时链接的内存占用率最低,使用也更广泛。
链接的作用:对可执行文件完成符号解析和重定位,使文件可以被直接加载运行。
5.2 在Ubuntu下链接的命令
直接使用指令ld hell.o -o hello时会报错
图5.1 链接指令
是因为没有链接可执行程序中需要的依赖。
通过gcc -v hello.o -o hello查询在链接时需要的库。
图5.2 gcc链接
从中可以查看到链接hello.o函数时需要的库有
liblto_plugin.s ld-linux-x86-64.so.2 crt1.o crtbeginS.o hello.o crtendS.o crtn.o
找到在系统目录下的库有
linux-x86-64.so.2 Scrt1.o crti.o hello.o libc.so crtn.o
使用ld命令ld-ohello-dynamic-linker/lib64/ld-linux-x86-64.so.2/usr/lib/x86_64-linux-gnu/Scrt1.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可得到可执行文件hello
图5.3 ld链接指令
5.3 可执行目标文件hello的格式
使用readlef指令readelf -a hello > helloheadk.txt
图5.4 elf指令
5.3.1节头部表内容:
图5.5 elf头
在hello.o即可重定向文件的elf头中,可以看到入口点地址为0x0,而在hello
的elf头中,入口点地址为0x4010f0。
5.3.2节头
图5.6 节头
在hello的节头中,由于已经完成了链接,每个节的地址均为实际地址,在节
头中给出了类型,地址,偏移量,节大小,旗标,连接信息,对齐方式的信
息。
5.3.3程序头
图5.7 程序头
程序头的内容可以分为很多段,其中重要的段:
INTERP段是对程序解释器的位置描述;
LOAD段是代码段与数据段内容;
DYNAMIC段是动态段,包含了动态链接时需要的信息;
NOTE段提供了一些与系统相关的信息;
5.3.4段节
图5.8 段节
段节中存放了关于段与共享库的一些信息
3.3.5重定位节
重定位节包含了便宜量,信息,类型,符号之,符号名称+加数信息,可以看
到,和hello.o的重定位节相比,在链接后hello重定位节中的加数全部变为0,
而偏移量和信息相对应的也发生了许多变化。
3.3.6符号表
图5.10 符号表
对比hello.o的符号表,可以看出链接之后多出了一些符号,推测可能是在链
接过程中解析出来的新符号添加在了符号表中。
5.4 hello的虚拟地址空间
edb中的虚拟地址空间:
可以看到,虚拟地址从9e545000开始到9e545ff0结束
5.5 链接的重定位过程分析
使用objdump -d -r hello > hello.asm反汇编
图5.12 反汇编 hello.asm
观察hello.asm可以看到call指令的地址全部变成了虚拟地址,所以链接过程中完成了对hello.o的重定位工作。而且hello.asm中多出了一些函数,这些函数是动态库的函数,在链接时也被重定位。
重定位可以分为两步组成:重定位节和符号定义与重定位节的符号引用。第一步中,链接器将所有相同类型的节合并为同一类型的聚合节,然后链接器将运行时的内存地址赋值给新的聚合节;第二步中,链接器修改代码节中和数据节中对每个符号的引用,使他们指向正确的运行时地址。
5.6 hello的执行流程
由gdb执行hello得到
start 程序地址:0x555555555100
init 程序地址:0x555555555000
main 程序地址:0x5555555551e9
printf@plt 程序地址:0x5555555550b0
puts@plt 程序地址:0x5555555550a0
exit@plt 程序地址:0x5555555550e0
sleep@plt 程序地址:0x5555555550f0
atio@plt 程序地址:0x5555555550d0
_finl@plt 程序地址:0x5555555550b0
getchar@plt 程序地址:0x5555555550c0
5.7 Hello的动态链接分析
在代码段中,编译器创建了一个PLT表,PLT包括多个表项,每个表项包括几条指令,PPLT[0]和PLT[1]是内置的表项,PLT[0]用于调用动态链接器,然后重定位某个函数引用的地址,PLT[1]用于跳转到__libc_start_main函数执行初始化工作,这个函数调用了用户程序的main函数,从PLT[2]开始为函数引用的表项,有多少个函数引用就有多少个表项,例如PLT[2]为第一个函数引用的表项,PLT[3]为第三个函数引用的表项,以此类推。
在数据段中,编译器创建为了GOT表,GOT表包括多个表项,每个表项里存储一个地址,GOT[0]~GOT[3]是内置的表项,从GOT[4]开始,每个函数一个表项,例如GOT[4]是第一个函数引用的表项,GOT[5]为第二个函数引用的表项,编译器生成可执行文件时,GOT[4]中存储的地址总是指向了PLT[2]表项的第二条指令,GOT[5]中存储的地址总是指向了PLT[3]表项的第二条指令,因此类推。
edb中运行之前
edb之后
图5.14 edb运行之后
5.8 本章小结
介绍了hello链接的步骤,查看了hello生成的elf文件,反汇编文件,并且与hello.o的生成文件进行了对比。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
进程的概念:进程就是执行中的程序实例,是操作系统进行资源分配的基本单元。系统中的每个程序都运行在某个进程的上下文中,上下文是由程序运行的正确状态组成的。
进程的作用:进程提供了一种假象,即程序是系统当前唯一运行的程序一样,运行的程序好像独自占用和使用内存和处理器,处理器就好像无间断的一条条执行我们的指令,我们的代码和数据好像是内存中的唯一对象。
6.2 简述壳Shell-bash的作用与处理流程
Shell-bash的作用:shell是命令行界面的解析器,能够为用户提供操作解码,提供内核服务,可以执行一系列的读,写操作。读操作读取来自用户的一个命令行,求值操作解析命令并且运行程序。
Shell的处理流程:
1.读取用户输入
2.解析用户输入
3.如果解析结果是执行内部命令则直接执行
4.如果解析结果不是内部命令,则假设是一个可执行程序的路径,会在一个进程的上下文加载并运行,如果参数中有&,则后台运行,反之前台运行。
创建过程:
1.命令行输入./hello执行hello程序
2.由于hello不是内部命令,所以shell会fork一个子进程
3.在子进程中加载并执行hello程序
使用fork()创建进程:
1.创建的子进程拥有和父进程一样的用户级虚拟地址空间和文件描述符
2.fork调用一次返回两次父进程的pid为0,子进程的pid非0
3.子进程和父进程是并发运行的独立进程
6.4 Hello的execve过程
execve函数调用时传入的参数为filename,argv,envp。其中filename是可执行文件,argv是参数列表,envp是环境变量列表。execve函数会在当前进程的上下文加载并运行可执行程序filename,覆盖当前进程的代码,栈,数据;成功调用hello则返回hello,没有成功调用则什么也不返回。
6.5 Hello的进程执行
上下文:上下文是指一个进程想要被内核重新调用所需要的一系列信息。
进程时间片:一个进程执行它的控制流的一部分的每一段时间。
进程的调度过程:
图6.1 进程切换
进程的调度过程就是内核处理process之间切换的过程,当从processA切换至processB时,需要先保留processA的上下文信息,然后切换到processB,下次切换的时候,先保留processB的上下文,之后读取processA保留的上下文,接着运行processA。在从processA切换到processB的过程中有内核模式和用户模式之间的转化,因为内核模式有着更大的权限,可以访问内存中的任何区域,所以在切换进程时存在用户模式和内核模式的切换。
6.6 hello的异常与信号处理
异常种类:中断,陷阱,故障,终止。
陷阱:陷阱是有意的,是执行指令的结果,且发生时间是可预知的,在hello执行的过程中在执行sleep函数时就是陷阱。陷阱的处理方式如下图:
图6.2 陷阱处理
中断:中断主要包括了I/O设备发出的中断与时钟中断。在hello执行的过程中是通过键盘发出的ctrl-z,ctrl-c信号。中断的处理方式如下图:
图6.3 中断处理
故障:故障是一种非有意的,但是可以被修复的异常,如果不能被修复则终止。在hello的执行过程中,可能存在缺页故障,故障的处理方式如下:
图6.4 故障处理
终止:终止是非有意的致命的异常,会中止当前进程。在hello中可能是硬件异常的情况。终止的处理方式如下:
hello的运行:
1.正常运行:
图6.6 正常运行
2.ctrl-c发送SIGINT信号
图6.7 SIGINT
3.ctrl-z发送SIGTSTP信号
图6.8 SIGTSYP
4.停止后运行ps
图6.9 ps指令
5.停止后运行jobs
图6.10 jobs指令
6.停止后运行pstree
图6.11 pstree指令
7.使用fg将后台转为前台运行
图6.12 fg指令
8.使用kill命令发送信号
6.7本章小结
介绍了hello进程的诞生(fork,execve),以及对信号的处理。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:编译器进行编译时,产生的汇编代码和数据都有自己的逻辑地址,逻辑地址等于段基地址加偏移量。
线性地址:在加载程序时,会为程序分配内存,分配的内存分为代码段和数据段,代码段基地址在CS中,数据段在DS中,线性地址等于偏移量加逻辑地址的段基址。
虚拟地址:虚拟地址是逻辑地址在进过映射之后的得到的地址。
物理地址:实际的物理内存地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
段式管理的全称是段页式内存管理,这种方式是将逻辑地址转换为线性地址,再由线性地址转换为物理地址。逻辑地址对应于逻辑空间,逻辑空间则划分成了长短不一的不同段。首先通过段选择符找到对应的段描述符,而段描述符中存储着描述一个段的详细信息,再根据段描述符获取到基地址,基地址加偏移量即可得到线性地址。值得说的是,线性地址和逻辑地址是一致的。
7.3 Hello的线性地址到物理地址的变换-页式管理
Hello的线性地址到物理地址的变换时通过页式管理实现的,页式管理是将内存划分为固定大小的页框,并将逻辑空间划分为了相同大小的页,一般大小为4k,每个页都有唯一的页号且存在页表中。通过页表,既可以访问到需要的物理地址空间。
在hello执行的过程中,当使用线性地址访问内存时,会先进行页表的查找和地址变换,将线性地址转换为物理地址。这样可以将逻辑地址空间划分为一系列大小相同的页,而不需要连续的物理内存。页式管理提供了更灵活的内存分配和管理方式,同时可以实现虚拟内存的功能,允许将进程的逻辑地址空间映射到物理内存或磁盘上的页面。这样,程序可以使用连续的逻辑地址空间,而不必关心物理内存的实际分布情况。同时,页式管理也引入了额外的管理开销,如页表的维护和地址变换的开销。
7.4 TLB与四级页表支持下的VA到PA的变换
TLB的作用是缓存最近使用过的页表项,减少对多级页表的访问次数,从而提高效率,从而当处理器发出内存访问时,首先应该使用TLB,如果TLB中没有需要的信息,再访问四级页表。
VA到PA的过程如下:
1. 当程序生成虚拟地址时,先将虚拟地址划分为四部分:页目录索引、页表索引、页内偏移和页内偏移的页面偏移。
2. 首先,从处理器的TLB中查找页表项。如果TLB中存在对应的页表项,则将页的物理地址与页内偏移的页面偏移相加,得到最终的物理地址,将物理地址用于访问内存中的数据。。如果TLB中没有对应的页表项,那么需要进行四级页表的访问。
3. 使用页目录索引从页目录中获取页表的物理地址。
4. 使用页表索引从页表中获取页的物理地址。
5. 将页的物理地址与页内偏移的页面偏移相加,得到最终的物理地址。
6. 将物理地址用于访问内存中的数据。
7. 如果需要更新TLB,将刚才查找到的页表项加载到TLB中,以便下次访问时加速地址转换。
7.5 三级Cache支持下的物理内存访问
当处理器得到需要访问的物理内存时,首先检查第一级缓存,如果命中则进行读写操作,如果没有命中则继续检查第二级缓存,如果命中则进行读写操作,如果没有命中则继续检查第三级缓存,如果命中则进行读写操作,如果没有命中则访问内存,如果内存没有缺页则进行读写操作,如果缺页则访问磁盘。
7.6 hello进程fork时的内存映射
当hello进程执行fork调用时,会创建一个新的子进程,该子进程与父进程共享相同的内存映射。
当fork调用完成后,父进程和子进程拥有相同的代码段和数据段,但是并不是完全一样,在堆和栈方面还是存在一些差异,子进程虽然会复制父进程的堆和栈的内容,但是他们相互独立,在修改一个进程的堆或栈时不会影响到另一个进程。同时,虽然父进程和子进程共享相同的内存映射,但它们拥有各自独立的虚拟地址空间。这意味着它们在使用相同的内存地址时,实际上指向不同的物理内存。
7.7 hello进程execve时的内存映射
在hello进程中调用execve函数时,内存映射过程中包括了清除原由的内存映射,加载新的可执行程序,设置新的内存映射这三个步骤。首先是清除原有的内存映射:在执行execve函数之前,旧的内存映射会被清除,包括代码段、数据段、堆和栈等。这样旧的可执行文件的内容会替换为新的可执行文件内容;之后加载新的可执行文件:execve函数会根据传入的可执行文件路径,加载新的可执行文件到内存中。这包括将可执行文件的代码段、数据段和其他段加载到适当的内存位置;最后设置新的内存映射:执行execve后,操作系统会根据新的可执行文件的格式和需要的内存资源,为该进程创建新的内存映射。这包括代码段、数据段、堆、栈以及其他可能的段。
7.8 缺页故障与缺页中断处理
缺页故障:当引用一个地址时,在页表中无法找到,则会触发一次缺页异常。
缺页中断处理一般流程:
1.当操作系统发现缺页中断时,尝试发现需要哪个虚拟页面。
2.一旦知道了发生缺页中断的虚拟地址,操作系统会检查地址是否有效,并检查读写是否与保护权限一致,不过不一致,则向进程发一个信号或者杀死该进程。如果是有效地址并且没有保护错误发生则系统检查是否有空闲页框。如果没有,则执行页面置换算法淘汰页面。
3.如果选择的页框脏了,则将该页写回磁盘,并发生一次上下文切换,挂起产生缺页中断的进程让其他进程运行直到写入磁盘结束。且回写的页框必须标记为忙,以免其他原因被其他进程占用。
4.一旦页框干净后,操作系统查找所需页面在磁盘上的地址,通过磁盘操作将其装入,当页面被装入后,产生缺页中断的进程仍然被挂起,并且如果有其他可运行的用户进程,则选择另一用户进程运行。
5.当磁盘中断发生时,表明该页已经被装入,页表已经更新可以反映他的位置,页框也标记位正常状态。
7.9动态存储分配管理
动态内存管理的基本方法与策略:
1. 放置已分配块:当应用程序请求分配一块内存时,动态内存分配器会搜索空闲链表,寻找一个足够大的空闲块来放置所请求的内存块。
2. 分割空闲块:当找到一个足够大的空闲块时,需要决定将其分割成多大的块来满足请求。可以选择将整个空闲块分配给请求,但这会导致内部碎片。另一种策略是将空闲块分割为两部分,一部分用于分配给请求,剩余部分变成新的空闲块。
3. 获取额外的堆内存:如果分配器无法找到足够大的空闲块来满足请求,它会通过调用sbrk函数向内核请求额外的堆内存。分配器会将这个额外的内存转换为一个大的空闲块,并将其插入到空闲链表中,然后将请求的内存放置在这个新的空闲块中。
4. 合并空闲块:分配器释放一个已分配的内存块时,它会合并相邻的空闲块。合并的时机可以选择立即合并或推迟合并。合并过程将当前空闲块与前面和后面的空闲块合并成一个更大的空闲块,以减少内存碎片。
7.10本章小结
从Intel逻辑地址到线性地址的变换-段式管理;Hello的线性地址到物理地址的变换-页式管理;TLB与四级页表支持下的VA到PA的变换;三级Cache支持下的物理内存访问;hello进程fork时的内存映射;hello进程execve时的内存映射;缺页故障与缺页中断处理;动态存储分配管理;这八个维度介绍了hello被载入内存后,需要的数据是如何访问分配的一系列步骤。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:文件
设备管理:unix io接口
Linux采用文件系统管理硬件设备,所有的设备都看成是特殊的文件,从而将硬件设备的特性及管理细节对用户隐藏起来,实现设备无关性。这允许Linux以unix io接口执行输入和输出的操作。
8.2 简述Unix IO接口及其函数
open函数:打开文件或者创建文件,将文件名转换为文件描述符,如果成功运行则返回描述符,运行失败则返回-1.
close函数:关闭一个文件,如果成功运行则返回0,运行失败则返回-1.
read函数:从文件中读取数据,将字节复制到指定的内存位置buf,并返回复制的字节数。
write函数:向文件中写入数据,将最多n个字节从内存位置buf复制到文件返回写入的字节数。
Unix io接口:是Unix-like操作系统中常用的文件操作接口,被广泛引用与Linux系统和类Unix系统中。
8.3 printf的实现分析
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
查看得到printf函数的定义:
图8.1 printf函数
Printf函数是一个变参数函数,函数中的va_list就是typedef后的char字符串。va_list arg 就是得到了后面所有参数中的第一个量。之后printf调用vsprintf函数。vsprintf函数会将需要输出的字符串格式化并把内容存放在buf中。并返回要输出的字符个数i。然后调用系统函数write来在屏幕上打印buf中的前i个字符,也就是我们要输出的格式串。调用write系统函数后,程序进入到陷阱,系统调用 int 0x80或syscall指令等,通过字符驱动子程序打印printf中的内容,最后返回i。
8.4 getchar的实现分析
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
查看得到getchar函数的定义:
图8.2 getchar函数
getchar函数使用了系统函数read来从标准输入读取数据。
在用户按下键盘时,键盘接口生成键盘扫描码,并产生一个中断请求。操作系统响应这个中断请求后,调用相应的中断处理程序。键盘中断子程序会从键盘接口获取键盘扫描码,并将其转换成对应的ASCII码。然后,它会将ASCII码存储到系统的键盘缓冲区中。getchar函数会从键盘缓冲区中读取一个字符,并将其返回。
如果读取失败或没有可用字符,则返回EOF,表示读取结束或出现错误。
8.5本章小结
以linux下的IO管理为基础,介绍了可执行程序hello的IO管理。
(第8章1分)
结论
hello的前身是hello.c,在hello.c经过预处理,其头文件被展开,宏定义被存入到hello.i中,之后,编译器对其进行处理,生成低级汇编语言hello.s汇编代码,之后汇编器对齐进行处理,使其成为可重定向文件,此时其已经含有机器代码,之后,链接器对其进行符号解析和重定位,终于hello.c最终转化成为可执行文件hello。
hello是一串二进制代码。当程序员键入./hello时,其被调入到内存中,shell解析其命令,为其分配上下文,进行资源调度,访问内存等一系列步骤后,hell被成功执行。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
文件名 | 作用 |
hello.c | hello.c源文件 |
hello.i | hello.c的预处理文件 |
hello.s | hello.i生成的汇编文件 |
hello.o | hello.s生成的可重定向文件 |
hello | hello.o生成的可执行文件 |
hellogdb | 用于gdb调试的可执行文件 |
hello.dump | hello.o生成的反汇编文件 |
hello.asm | hello生成的反汇编文件 |
hellohead.txt | hello.o生成的elf头 |
helloheaddk.txt | hello生成的elf文件 |
hellorelo.txt | hello.o生成的重定位头 |
hellosection.txt | hello.o生成的节头部表 |
hellosym.txt | 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分)