计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 人工智能领域
学 号 2022110562
班 级 22WL026
学 生 吴建锋
指 导 教 师 郑贵滨
计算机科学与技术学院
2024年5月
hello程序作为每个人接触的第一个程序,简单的几条语句背后是整个计算机系统协同工作的结果。从平凡的01串到屏幕上输出的结果,程序需要经过编译、汇编、链接、创建进程、内存分配、IO管理等多个过程。本文将从hello程序实现的过程分析各个过程里计算机系统的工作原理与实现途径,体会人类智慧的结晶。
关键词:计算机系统;汇编;链接;hello程序
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目 录
第1章 概述
1.1 Hello简介
Hello World!
这个几乎每个程序员最开始踏上码农道路所实现的第一个程序,我们曾为这简简单单的一句话欣喜若狂,标志了我们开创了一个新的世界,并且拥有掌握这个世界的能力。但小小的hello.c的运行并不天马行空,在我们看不见的背后隐藏了计算机系统的所有奥秘。
从hello.c(Program)到计算机执行的程序(Process),一般由编译器驱动程序完成,将原文件转变为可执行文件。这个从无到有的过程开创世界的过程主要可分为五个步骤:完善规则、解释规则、打造基石、九九归一。
首先是完善规则:就像是“神说要有光!”但很明显除了光世界上还有很多事物神没有直说,hello.c文件也一样那个printf只是你对这个程序的‘多余’要求,而其他要素便在#开头的命令中,所以第一步便是直接将#命令修改为C语言命令,这个过程叫预编译(cpp),产生hello.i文件。
其次是解释规则:作为一个神、你现在使用的世界制造机(CPU)并不是那么智能,你说的话它无法直接理解,所以你只能先将你的想法转换为它所能识别的语言(汇编语言)再喂给这个笨机器。而这个过程就是编译,产生编译代码文件hello.s。
然后在打造基石这一过程,那个笨机器终于可以进行工作了,他们根据你的汇编指令转化为这个世界的真实规则,如将太阳点亮。而这些真实规则便是机器语言,是最终机器识别并执行的语言,而这一步就是汇编,并将机器指令保存在hello.o文件中。
最后便是九九归一,形成一个新的世界。你创造的点亮太阳的指令,和一代代老创世神所积累的基石按照一定链接联系到一起,这个时候你才能真正看见这个世界。而与程序来说,这便是链接。将有关的目标文件彼此相连接,使得所有的目标文件成为一个能够被操作系统载入执行的统一整体,即hello。
到此为止,世界设计或者说目标文件hello诞生了,这便是这一小条语句背后的完整秘密,这就是p2p (From Program to Process)。
当然这个程序执行的过程中也存在着内存相关操作,首先将程序代码存入内存中,而后在shell的子进程映射虚拟内存,在处理器等操作下运行到代码结束,回收进程,删除内存中的相关代码以及数据,一切清零。这个过程就是hello的020(From Zero-0 to Zero-0)过程。
1.2 环境与工具
1.2.1 硬件环境
Windows系统:
图1.2.1
Linux系统:
图1.2.2
1.2.2 软件环境
图1.2.3
1.2.3 开发工具
Visual Studio 2022
1.3 中间结果
hello.i: ASCII码的中间文件(预处理器产生),用于分析预处理过程。
hello.s: ASCII汇编语言文件(预处理器产生),用于分析编译的过程。
hello.o:可重定位目标程序(汇编器产生),用于分析汇编的过程。
hello:可执行目标文件(链接器产生),用于分析链接的过程。
hello.txt:hello可执行文件反汇编文件。
dump.s:hello.o反汇编文件。
ELF.txt:hello可执行文件ELF头。
1.4 本章小结
在第一章中主要讲述Hello程序过程的简要概述,基本简述后续章节的内容。其次介绍本次程序人生的环境与工具,与中间结果文件及其作用。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
预处理也称预编译,是c语言编译器在对源代码编译之前,将其中的伪指令进行处理,进而得到.i结尾的c语言文件,其本质上仍为高级语言文件,可以较轻松地阅读与理解。
预处理的主要作用包括下面3种类型:
- 将所有的#define删除,并且展开所有宏定义;
- 处理条件编译指令,如:#if、#elif、#else、#endif等;
- 处理头文件包含指令,如#include,将被包换的文件插入到该预编译指令的位置;
2.2在Ubuntu下预处理的命令
在泰山服务器终端输入 gcc -E hello.c -o hello.s
图2.2.1
2.3 Hello的预处理结果解析图2.3.1
展示hello.i部分代码,具体可看附件hello.i,可以看出hello主函数的基本结构没有发生变化,但是代码上下新增大量代码,是将#include所引用头文件的内容加入到当前程序中,其次代码段中的注释也被全部删除。源代码在经过预处理后,输出(.i文件)被直接交给编译器,编译器检查程序是否有错误,并经程序翻译为目标代码。
2.4 本章小结
第二章主要介绍程序执行过程中的预处理阶段,简述了预处理的概念与其基本操作类型。最后将hello.c文件在linux系统下预处理为hello.i文件,并叙述了在hello.i中的实际操作。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译的概念是编译器(ccl)将预处理完的文本文件hello.i进行一系列的词法分析、语法分析、语义分析和优化,翻译成文本文件hello.s。汇编语言以一种文本格式描述了低级机器语言指令。汇编语言是非常有用的,因为它为不同高级语言的不同编译器提供了通用的输出语言。
编译过程可分为6步:词法分析、语法分析、语义分析、源代码优化、代码生成、目标代码优化。
词法分析:扫描器(Scanner)将源代的字符序列分割成一系列的记号(Token)。 语法分析:语法分析器将记号(Token)产生语法树(Syntax Tree)。
语义分析:包括静态语义(在编译器可以确定的语义)与动态语义(只能在运行期才能确定的语义)。
源代码优化:源代码优化器(Source Code Optimizer),将整个语法书转化为中间代码(Intermediate Code)(中间代码是与目标机器和运行环境无关的)。
目标代码生成:代码生成器(Code Generator).
目标代码优化:目标代码优化器(Target Code Optimizer)。
3.2 在Ubuntu下编译的命令
在泰山服务器终端输入 gcc -S hello.i -o hello.s
图3.2.1
hello.s内容如下:
.file "hello.c"
.text
.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 \346\211\213\346\234\272\345\217\267 \347\247\222\346\225\260\357\274\201"
.LC1:
.string "Hello %s %s %s\n"
.text
.globl main
.type main, @function
main:
.LFB6:
.cfi_startproc
endbr64
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 $5, -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 $24, %rax
movq (%rax), %rcx
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
movq -32(%rbp), %rax
addq $32, %rax
movq (%rax), %rax
movq %rax, %rdi
call atoi@PLT
movl %eax, %edi
call sleep@PLT
addl $1, -4(%rbp)
.L3:
cmpl $9, -4(%rbp)
jle .L4
call getchar@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size main, .-main
.ident "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
3.3 Hello的编译结果解析
main源代码内容如图3.3.1:
图3.3.1
通过对比源文件与编译文件,可以分析出编译器是怎么处理C语言的各个数据类型以及各类操作。
3.3.1 数据
1.常量:在源文件中出现的常量为字符串:“用法: Hello 学号 姓名 秒数! \n”和“Hello %s %s\n”。
在汇编文件中存在LC0与LC1字段中:
LC0:
.string "\347\224\250\346\263\225: Hello \345\255\246\345\217\267 \345\247\223\345\220\215 \346\211\213\346\234\272\345\217\267 \347\247\222\346\225\260\357\274\201"
.LC1:
.string "Hello %s %s %s\n"
2.变量:main.c函数中局部变量i,根据相关知识可知,局部变量存储 在程序的栈中,在hello.s中相关代码为
.L2:
movl $0, -4(%rbp)
jmp .L3
还有main函数参数argc与argv也存放在栈中
movl %edi, -20(%rbp)
movq %rsi, -32(%rbp)
3.3.2操作
1.赋值操作:源文件赋值操作为i=0,而由上文可知,i为局部变量存在栈中, 所以只需更改栈中数值即可。
.L2:
movl $0, -4(%rbp)
jmp .L3
2.算术操作:在循环中进行计时器i++操作,则如赋值操作相同进行add操作。
addl $1, -4(%rbp)
3.条件转移操作:
1)if(argc != 5)利用比较代码如下:
cmpl $5, -20(%rbp)
je .L2
检查argc是否为5,如果相等则跳转到L2
- for(i=0;i<10;i++)代码如下:
cmpl $9, -4(%rbp)
jle .L4
通过比较i与9判断是否继续循环。
4.数组操作:
对数组操作主要是变址寻址,如对argv数组进行操作时:
movq (%rax), %rcx
movq -32(%rbp), %rax
addq $16, %rax
movq (%rax), %rdx
movq -32(%rbp), %rax
addq $8, %rax
movq (%rax), %rax
movq %rax, %rsi
通过依靠对栈帧寄存器偏移量实现数组操作。
5.函数操作:
源文件调用了puts,exit,printf,sleep,atoi,getchar函数。在编译文件中使用call指令进行函数调用,如:
call exit@PLT
3.4 本章小结
本章主要分析程序的编译过程,在linux系统下生成汇编文件hello.s。并依据源C文件内容实例对汇编文件进行语句分析,并逐一解释了常量、变量、赋值操作、关系跳转操作、数组操作与函数操作等汇编代码。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
汇编是汇编器(as)将编译完的汇编代码文件hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中,hello.o是一个二进制文件。
4.2 在Ubuntu下汇编的命令
在泰山服务器终端输入 gcc hello.s -c -o hello.o
图4.2.1
4.3 可重定位目标elf格式
4.3.1 ELF
在linux系统下用命令 readelf -h hello.o 查看可重定位文件ELF(如图4.3.1)
图4.3.1
分析elf格式可以得到以下信息:
hello.o是64位文件;数据的存储形式为补码、小端序存储;文件的类型为REL(可重定位文件),入口点地址为0x0,程序头起点为0,节头表的起始位置为1264;文件共有14节.
4.3.2头节表
在linux系统下用命令 readelf -S hello.o 查看头节表(如图4.4.2)
图4.3.2
通过头节表我们可以知道.o文件中各节的字节大小,地址,偏移量,访问方式等信息。
4.3.3符号表
在linux系统下用命令 readelf -s hello.o 查看头节表(如图4.4.3)
图4.3.3
在符号表中存储着本程序使用的符号及其信息:类型、全局符号、字节大小等信息。
4.3.4重定位节
在linux系统下用命令 readelf -r hello.o 查看头节表(如图4.4.4)
图4.4.4
在重定位字段中显示了本程序需要重定位的符号及信息。
4.4 Hello.o的结果解析
在linux系统下用命令 objdump -d -r hello.o >dump.s 可以查看hello.o的反汇编信息,并将其存入至dump.s中,内容如下:
hello.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 83 ec 20 sub $0x20,%rsp
c: 89 7d ec mov %edi,-0x14(%rbp)
f: 48 89 75 e0 mov %rsi,-0x20(%rbp)
13: 83 7d ec 05 cmpl $0x5,-0x14(%rbp)
17: 74 16 je 2f <main+0x2f>
19: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 20 <main+0x20>
1c: R_X86_64_PC32 .rodata-0x4
20: e8 00 00 00 00 callq 25 <main+0x25>
21: R_X86_64_PLT32 puts-0x4
25: bf 01 00 00 00 mov $0x1,%edi
2a: e8 00 00 00 00 callq 2f <main+0x2f>
2b: R_X86_64_PLT32 exit-0x4
2f: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
36: eb 53 jmp 8b <main+0x8b>
38: 48 8b 45 e0 mov -0x20(%rbp),%rax
3c: 48 83 c0 18 add $0x18,%rax
40: 48 8b 08 mov (%rax),%rcx
43: 48 8b 45 e0 mov -0x20(%rbp),%rax
47: 48 83 c0 10 add $0x10,%rax
4b: 48 8b 10 mov (%rax),%rdx
4e: 48 8b 45 e0 mov -0x20(%rbp),%rax
52: 48 83 c0 08 add $0x8,%rax
56: 48 8b 00 mov (%rax),%rax
59: 48 89 c6 mov %rax,%rsi
5c: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 63 <main+0x63>
5f: R_X86_64_PC32 .rodata+0x2c
63: b8 00 00 00 00 mov $0x0,%eax
68: e8 00 00 00 00 callq 6d <main+0x6d>
69: R_X86_64_PLT32 printf-0x4
6d: 48 8b 45 e0 mov -0x20(%rbp),%rax
71: 48 83 c0 20 add $0x20,%rax
75: 48 8b 00 mov (%rax),%rax
78: 48 89 c7 mov %rax,%rdi
7b: e8 00 00 00 00 callq 80 <main+0x80>
7c: R_X86_64_PLT32 atoi-0x4
80: 89 c7 mov %eax,%edi
82: e8 00 00 00 00 callq 87 <main+0x87>
83: R_X86_64_PLT32 sleep-0x4
87: 83 45 fc 01 addl $0x1,-0x4(%rbp)
8b: 83 7d fc 09 cmpl $0x9,-0x4(%rbp)
8f: 7e a7 jle 38 <main+0x38>
91: e8 00 00 00 00 callq 96 <main+0x96>
92: R_X86_64_PLT32 getchar-0x4
96: b8 00 00 00 00 mov $0x0,%eax
9b: c9 leaveq
9c: c3 retq
通过对比编译文件与反汇编文件可以看出两者大体相同,但有一些差别的地方:
- 分支跳转:hello.s的跳转指令是通过段名字进行跳转的,而反汇编文件是直接给出跳转地址实现的。
- 函数调用:hello.s中函数调用是通过call指令加函数名称实现的,而反汇编文件call指令后加的是指令的地址。
- 全局变量访问:hello.s中全局变量访问是段的名称+寄存器,反汇编文件中全局变量访问是0+寄存器,因为全局变量数据的地址在运行时已经确定,访问也需要重定位。
这些不同说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。
4.5 本章小结
本章介绍了程序的汇编过程,并在linux下生成汇编文件hello.o。并对汇编文件ELF,符号表,重定位表进行了导出与分析。最后反汇编.o文件,并对比分析了反汇编文件与编译文件(hello.s)的不同及原因。
(以下格式自行编排,编辑时删除)
(第4章1分)
第5章 链接
5.1 链接的概念与作用
链接概念:链接(linking)是由叫链接器(linker)的程序自动执行的将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时(compile time),也就是在源代码被翻译成机器代码时;也可以执行于加载时(load time),也就是在程序被加载器(loader)加载到内存并执行时;甚至执行于运行时(run time),也就是由应用程序来执行。
原hello程序调用了一系列编译器已经准备好的函数。以printf函数举例,它存在于一个名为printf.o的单独的预编译好了的目标文件中,连接器(ld)负责把这个文件合并到我们的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/crti.o hello.o /usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o
图5.2.1
5.3 可执行目标文件hello的格式
5.3.1ELF头
在linux系统下用命令 readelf -a hello > ELF.txt 查看程序文件ELF头信息(如图5.3.1)
图5.3.1
图5.3.1截出ELF中ELF头信息,在其中可以知道关于本程序信息:
hello以补码、小端序形式存储,入口点地址为0x4010f0,程序头节点气质为64,节头表起始地址为14208,对比可重定位文件表明文件已经重定位完成。
5.3.2节头表
图5.3.2
在节头表中记录了hello中各节信息,如大小、类型等。对比可重定位文件中的节头表,可以看出节的数目变多,其次在可重定位文件中各节的地址全为零,而在hello节头表中的各节都有具体地址,说明已经重定位完成。
5.3.3符号表
图5.3.3
在符号表中记录了hello程序所引用和定义的符号,并且指明了此符号的大小、类型、距离所在节的偏移量等信息。
5.4 hello的虚拟地址空间
在linux系统下使用edb运行hello程序,运行结果如图5.4.1。
图5.4.1
通过图中信息可以得出,程序被加载到虚拟地址0x401000到0x402000中,各节地址等信息与可执行文件ELF中记录的信息一致。
5.5 链接的重定位过程分析
在泰山服务器终端输入 objdump -d -r hello > hello.txt如图5.5.1,记录反汇码代码。
图5.5.1
部分结果如下(图5.5.2、图5.5.3、图5.5.4):
图5.5.2
图5.5.3
相比较hello.o反汇编文件多了一些链接库中的函数,这是通过重定位垒放到程序代码段的,并且每个重定位符号都有确切地址。
图5.5.4
在main函数段中,跳转指令发生变化,连接过程重定位各函数地址,计算相对距离,修改了跳转地址,形成了完整的反汇编代码。
hello的重定位方式是将各可重定位文件与库文件中的各节按照一定规矩从高地址到低地址垒放在一起并计算各符号的相对位置,进行重定位代码替换,最终形成完整的程序文件。
5.6 hello的执行流程
使用gdb/edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程(主要函数)。请列出其调用与跳转的各个子程序名或程序地址。
_dl_start、_dl_init
_start
_libc_start_main
_main
_printf
_exit
_sleep_getchar
_dl_runtime_resolve_xsave
_dl_fixup
_dl_lookup_symbol_x
Exit
ld-2.27.so!_dl_start
ld-2.27.so!_dl_init
hello!_start
lib-2.27.so!__libc_start_main
hello!puts@plt
5.7 Hello的动态链接分析
动态链接是指编译器在程序加载之前为共享库函数生成一条重定位记录,然后动态链接器在加载程序时再解析此记录。GNU使用延迟绑定将过程地址的绑定推迟到第一次调用时绑定。延迟绑定由GOT与PLT组成。其中GOT是一个8字节地址数组,GOT[O]和GOT[1]包含动态链接器在解析函数地址时会使用的信息。GOT[2]是动态链接器在1d-linux.so模块中的入口点。其余的每个条目对应于一个被调用的函数,其地址需要在运行时被解析。每个条目都有一个相匹配的PLT条目。而PLT是一个16字节代码数组,PLT[0]是一个特殊条目,它跳转到动态链接器中。每个被可执行程序调用的库函数都有它自己的PLT条目。每个条目都负责调用一个具体的函数。
在程序文件中.got节用于动态链接,其中包含所有符号的地址表,当动态链接时,会更新这个表,以便程序正确访问各符号。.got.plt节与.got节类似,只有在函数第一次调用时才会进行符号的重定位并更新.goy.plt中的符号地址。
5.8 本章小结
本章主要介绍了程序的链接过程,并在linux下链接生成可执行目标文件hello。并分析了程序文件ELF内容并与第四章内容进行对比,分析了重定位的具体操作,最后根据hello反汇编文件与hello.o反汇编文件的不同分析了动态链接的实现。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
进程是计算机系统中正在运行的程序的实例。它是操作系统对程序执行的基本单位,可以看作是程序在执行过程中的动态表现。每个进程都有自己的内存空间、代码、数据和执行状态。多个进程可以同时运行在计算机系统中。进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
作用:进程可以解决在一个系统可以并发执行多个任务的问题,提高程序的执行效率。
6.2 简述壳Shell-bash的作用与处理流程
Shell是Linux下的命令行解释器。将用户的命令翻译给内核来处理命将最后的处理结果反馈给用户,避免了用户与核的直接沟通,保护内核不被误处理。
处理流程:
- 读取用户输入的命令
- 对命令进行分析与处理
- 建立一个子进程,子进程进行处理或出现异常后报告给父进程
- 等待子进程结束并进行回收
6.3 Hello的fork进程创建过程
首先父进程新建一个子进程,然后在内核中分配一个PCB,将其挂在PCB表上。子进程获得与父进程相同的虚拟地址空间,复制它的父进程的环境并为其分配资源。最后复制父进程地址空间里的内容,如:代码共享等。将进程置成就绪状态,并将其放入就绪队列,等待CPU调度。
6.4 Hello的execve过程
hello通过fork()新建子程序后,如果想让子程序调用新的可执行文件则需要子程序运行exceve()函数来加载并启动新的程序。内核在调用exceve时,这个函数先打开目标映像文件并读入其ELF文件头,然后调用另一个函数search_binary_handler()搜索Linux支持的可执行文件类型队列进行匹配,然后调用load_binary函数指针所指向的处理函数来处理目标映像文件,加载可执行文件。
6.5 Hello的进程执行
6.5.1进程上下文信息
进程上下文实际上是进程执行活动全过程的静态描述。 我们把已执行过的进程指令和数据在相关 寄存器 与 堆栈 中的内容称为上文,把正在执行的指令和数据在寄存器和堆栈中的内容称为正文,把待执行的指令和数据在寄存器与堆栈中的内容称为下文。内核通过控制上下文切换机制来控制跳转到新的进程。
6.5.2进程时间片
时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,即该进程允许运行的时间,使各个程序从表面上看是同时进行的。如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。而不会造成CPU资源浪费。由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,每个程序轮流执行。
6.5.3进程调度过程
进程调度的过程主要进行以下步骤:所有准备好执行的进程都在就绪队列中等待。CPU根据特定的算法选择一个进程来执行。进行上下切换,保存当前运行进程的状态,并加载所选进程的上下文。执行被选中的进程。当进程终止时,从系统中移除。
6.5.4用户态与核心态转换
内核态和用户态的切换主要通过中断和系统调用来实现.当用户态的进程需要执行内核态的代码时,通常会通过系统调用陷入到内核态。这个过程涉及到保存用户态的上下文到内核态,然后加载新的内核态的上下文开始执行。 当内核态的代码执行完毕后,它会将控制权返回给用户态,并恢复原来的上下文,完成从内核态到用户态的切换。
6.6 hello的异常与信号处理
程序的异常控制流主要分为以下四种:异常、中断、陷阱与故障。异常是指为了响应某个事件而将控制转移给操作系统内核的情况。异常处理类似于过程调用,异常根据发生的时钟周期可以分为同步异常与异步异常。而中断是异步发生的,由外部I/O设备产生,处理终端的程序称为中断处理程序。陷阱是一种人为设置的异常,系统调用提供了用户程序与内核之间的接口。故障是意外发生但可能被修复的异常,例如缺页、浮点异常等。
- 对于hello正常运行情况如图6.6.1:
图6.6.1
- 输入ctrl +Z(如图6.6.2)
图6.6.2
- 通过ps命令查看进程发现有hello进程(图6.6.3)没有终止,被暂时挂起。
图6.6.3
- 使用jobs命令查看(图6.6.4)
图6.6.4
- 使用pstree命令查看hello进程的位置(图6.6.5)
图6.6.5
- 输入fg 1将后台进程转到前台(图6.6.6)
图6.6.6
- 输入ctrl+C命令终止进程(图6.6.7-6.6.8)
图6.6.7 图6.6.8
6.7本章小结
本章主要介绍了linux的壳Shell的作用与工作流程,进而引出了进程的概念与进程调用的相关函数等。最后依据理论知识分析了hello程序的进程实现与相关命令的现象,介绍了hello异常与信号处理流程。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
1.逻辑地址:编译器生成机器指令时所带有的地址,即程序中数据与函数相对段的偏移量。
2.线性地址:指令的逻辑地址加上段基址形成线性地址,整个程序代码线性排列在内存空间中。
3.虚拟地址:在页式管理机制中程序映射到虚拟内存,其地址为虚拟地址。
4.物理地址:在页式管理机制中程序映射到物理内存,其地址为物理地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
段式管理就是段基地址加偏移地址,最终覆盖一维空间。
(以下格式自行编排,编辑时删除)
7.3 Hello的线性地址到物理地址的变换-页式管理
页式管理是将各进程的虚拟空间划分为若干长度相等的页。内存空间按页的大小建立页表。页结构体由页号、页面偏移组成,用来分配物理内存运行程序。其工作方式类似cache全相联映射。
7.4 TLB与四级页表支持下的VA到PA的变换
多级页表虚拟地址被划分成为k个VPN和1个VPO。每个VPN i都是一个到第i级页表的索引,其中1≤i≤k。第j级页表中的每个PTE,1≤j≤k-1,都指向第j+1级的某个页表的基址。属于四级页表的一到三级页表中存放的数据是指向下一级页表的首地址,而不是物理页号。逐步访问到第四级页表,第四级页表中装的就是物理页号,通过第四级页表读出的物理页号链接上虚拟地址中的VPO获得物理地址。
7.5 三级Cache支持下的物理内存访问
CPU提供的地址w,从其中间抽取出6位组索引。这些位被解释个一个对应于一个组号的无符号整数。接下来需要确定是否有字w的一个副本存储在组i的一个高速缓存行中,只有标记位相匹配且有效位为1的时候才缓存命中,否则缓存不命中。如果缓存命中,根据提供的块偏移位,我们就可以将其取出 并返还给CPU。如果缓存不命中,那么它需要从存储器层次结构中的下一层取出被请求的块,然后将新的块存储到组索引位指示的组中的一个高速缓存行中。如果组中都是有效高速缓存,那么必须驱逐出一个现有的行,可以采用最近最少使用策略LFU进行替换。
7.6 hello进程fork时的内存映射
当fork函数被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID。为了给这个新进程创建虚拟内存,它创建了当前进程的mm_struct、区域结构和页表的原样副本。它将两个进程中的每个页面都标记为只读,并且把两个进程中的每个区域结构都标记为私有的写时复制。
当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个进程的任一个后来进行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间的抽象概念。
7.7 hello进程execve时的内存映射
execve在当前进程中加载并运行新程序主要为以下几个步骤:
- 删除已存在的用户区域
- 映射私有区域,为新程序的代码、数据等创建新的区域结构,都是私有的写时复制的。
- 映射共享区域,把动态链接库映射到共享区域
- 设置程序计数器(PC),使之指向代码区域的入口点。
7.8 缺页故障与缺页中断处理
缺页故障即进程在执行一条指令时,如果发现他要访问的页没有在内存中,那么停止该指令的执行,并产生一个页不存在的异常,对应的故障处理程序可通过从外存加载该页的方法来排除故障,之后,原先引起的异常的指令就可以继续执行,而不再产生异常。
缺页中断处理:中断处理函数为arch/arm/mm/fault.c文件中的do_page_fault函数。
7.9动态存储分配管理
动态存储分配主要在堆中进行。brk指向堆的顶部,分配器将堆视为一组不同大小的块的集合来维护,每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。
printf调用malloc函数,会先访问堆中是否有合适大小的空闲块,如果有相应的空闲块则分配这个块给Hello,返回一个指针指向这个分配块的首地址。如果没有合适的空闲块,系统会拓展堆空间,改变brk的值来保证有合适的空闲块。
7.10本章小结
本章主要解释了程序如何加载到虚拟内存与物理内存并运行的过程,引出页结构完成对应页的映射关系,并简介hello在运行时内存的使用情况包括fork等进程函数的调用与堆的动态内存空间管理等。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
Linux的设备管理的主要任务是控制设备完成输入输出操作。它的任务是把各种设备硬件的复杂物理特性的细节屏蔽起来,提供一个对各种不同设备使用统一方式进行操作的接口。Linux把设备看作是特殊的文件,系统通过处理文件的接口—虚拟文件系统VFS来管理和控制各种设备。
设备的模型化:文件
设备管理:unix io接口
8.2 简述Unix IO接口及其函数
unixI/O:将设备映射为文件的方式,允许Unix内核引出一个简单、低级的应用接口。 Linux/unix IO的系统调用有5个函数:open (打开)、close (关闭)、read (读)、write (写)、lseek (定位)。
8.3 printf的实现分析
vsprintf的作用就是格式化。它接受确定输出格式的格式字符串fmt。用格式字符串对个数变化的参数进行格式化,产生格式化输出。 代码中的vsprintf只实现了对16进制的格式化。
Write系统函数的功能就是不断的打印出字符,直到遇到'\0'。
syscall将字符串中的字节“Hello 2022110562 吴建锋 11111111111 1”从寄存器中通过总线复制到显卡的显存中,显存中存储的是字符的ASCII码。
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
字符显示驱动子程序将通过ASCII码在字模库中找到点阵信息将点阵信息存储到vram中。
显示芯片会按照一定的刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
于是我们的打印字符串“Hello 2022110562 吴建锋 11111111111 1”就显示在了屏幕上。
8.4 getchar的实现分析
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章主要介绍了linux系统的IO设备管理方法,包括Unix IO接口与函数,最后分析了printf与getchar函数实现系统与外设工作的相关内容。
(第8章1分)
结论
计算机的产生是人类历史上的巨大飞跃,人类从使用规则开拓到创造规则的领域,并可以随着各种奇思妙想进一步发展、进化。简简单单的hello程序运行:从编译到汇编、从链接到内存分配,其背后确实整个人类智慧的结晶。在逐步分析的过程中我可以感受到前辈们遇山开路、遇水架桥的精神,也正是这种精神推动着整个计算机系统的蓬勃发展。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.i: ASCII码的中间文件(预处理器产生),用于分析预处理过程。
hello.s: ASCII汇编语言文件(预处理器产生),用于分析编译的过程。
hello.o:可重定位目标程序(汇编器产生),用于分析汇编的过程。
hello:可执行目标文件(链接器产生),用于分析链接的过程。
hello.txt:hello可执行文件反汇编文件。
dump.s:hello.o反汇编文件。
ELF.txt: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分)
转载自CSDN-专业IT技术社区
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_71906766/article/details/139371995