2025春哈工大计算机系统大作业

计算机系统

大作业

题     目  程序人生-Hellos P2P  

专       业           计算机与电子通信类             

学     号            2023111734            

班   级           23L0507             

学       生              王腾远    

指 导 教 师             史先俊        

计算机科学与技术学院

20255

摘  要

本文从hello.c这一简单程序出发,依次研究了它的预处理、编译、汇编、链接、加载、运行和终止的过程。结合《CSAPP》课程中所学内容,以hello为例子,在linux系统下,对程序的预处理、编译、汇编、链接、进程管理和内存管理的观察和分析,研究了计算机系统各个部分的知识,将计算机系统各部分生动组织在一起

关键词:计算机系统;程序处理;进程管理;内存管理

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

目  录

第1章 概述

1.1 Hello简介

1.2 环境与工具

1.3 中间结果

1.4 本章小结

第2章 预处理

2.1 预处理的概念与作用

2.2在Ubuntu下预处理的命令

2.3 Hello的预处理结果解析

2.4 本章小结

第3章 编译

3.1 编译的概念与作用

3.2 在Ubuntu下编译的命令

3.3 Hello的编译结果解析

3.4 本章小结

第4章 汇编

4.1 汇编的概念与作用

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

4.4 Hello.o的结果解析

4.5 本章小结

第5章 链接

5.1 链接的概念与作用

5.2 在Ubuntu下链接的命令

5.3 可执行目标文件hello的格式

5.4 hello的虚拟地址空间

5.5 链接的重定位过程分析

5.6 hello的执行流程

5.7 Hello的动态链接分析

5.8 本章小结

第6章 hello进程管理

6.1 进程的概念与作用

6.2 简述壳Shell-bash的作用与处理流程

6.3 Hello的fork进程创建过程

6.4 Hello的execve过程

6.5 Hello的进程执行

6.6 hello的异常与信号处理

6.7本章小结

第7章 hello的存储管理

7.1 hello的存储器地址空间

7.2 Intel逻辑地址到线性地址的变换-段式管理

7.3 Hello的线性地址到物理地址的变换-页式管理

7.4 TLB与四级页表支持下的VA到PA的变换

7.5 三级Cache支持下的物理内存访问

7.6 hello进程fork时的内存映射

7.7 hello进程execve时的内存映射

7.8 缺页故障与缺页中断处理

7.9动态存储分配管理

7.10本章小结

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

8.2 简述Unix IO接口及其函数

8.3 printf的实现分析

8.4 getchar的实现分析

8.5本章小结

结论

附件

参考文献

第1章 概述

1.1 Hello简介

Hello的P2P:P2P指的是From Program to Process,即从可执行程序变成运行进程的过程。首先Hello.c通过C预处理器(即cpp)生成文本文件hello.i。之后hello.i文件再由C编译器(即ccl)编译,生成汇编语言文件hello.s。再将hello.s文件通过汇编器翻译成机器语言指令,打包生成可重定位目标文件hello.o。最后通过链接器链接,生成可执行程序hello。通过fork产生一个子进程,再使用execve函数,P2P就被完成。

Hello的020:020指的是From Zero to Zero。最开始时,内存之中并无hello相关文件,即从零开始(From Zero)。运行时,可以在shell中调用execve函数,hello相关文件会被加载到内存中,进行执行。程序运行结束后,hello的进程会被回收,再由内核删除hello相关内容,即最后归于零(to Zero)。

1.2 环境与工具

列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。

硬件:12th Gen Intel(R) Core(TM) i9-12900H   2.50 GHz

16.0 GB RAM

软件:Windows 11 64位

VMWare

Ubuntu 20.04.4 64位

开发工具:gcc,gdb,edb,objdump

1.3 中间结果

文件名

说明

hello.i

C预处理器生成的文本文件

hello.s

C编译器编译生成的汇编语言文件

hello.o

汇编器生成的可重定位目标文件

hello

可以执行的文件

                                                      表1 中间结果

1.4 本章小结

这一章主要简单描述了hello的P2P和020的过程,介绍了实验过程中的软硬件环境和使用到的中间文件。

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

预处理的概念:C处理器根据以#开头的预处理命令,对源C程序进行修改的过程,主要包括头文件包含、宏替换、条件编译等操作。如 #include stdio.h命令,就会将#include后面的系统头文件stdio.h进行读取,将其替换为对应的文本。也会将#define 的内容用具体值进行替换。处理之后得到.i文本文件。

预处理的作用:将头文件,宏定义的内容进行替换,使其生成一个对于机器更加容易处理的文件,便于后续编译器进行处理。

2.2在Ubuntu下预处理的命令

命令:gcc -E hello.c -o hello.i

图1 生成预处理文件

2.3 Hello的预处理结果解析

预处理后生成了hello.i文件,文件共有3061行,内容比起.c文件大大增加。可以看到主函数中的内容在最后的部分。之前的部分则是stdio.h unistd.h stdlib.h头文件的引入和替换以及对#define内容的替换。首先先找到头文件对应的位置,将其引入,然后删除对应的#include语句,对#define内容也进行相应替换,进行预处理。

图2 hello.i文件内容

2.4 本章小结

本章主要介绍了预处理的概念和作用,并通过实际对hello.c进行预处理生成hello.i,对预处理结果进行了分析。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译的概念:C编译器通过对.i文件的内容进行分析,将其中的合法指令转换为汇编语言,生成汇编语言文件.s.

编译的作用:将文本文件转换为汇编语言文件,便于之后生成二进制文件。

3.2 在Ubuntu下编译的命令

指令:gcc -S hello.i -o hello.s

图3 编译生成hello.s

3.3 Hello的编译结果解析

3.3.1数据:常量、变量(全局/局部/静态)、表达式、类型、宏

①int i 整型局部变量

可以看出,编译器在处理局部变量时,将其放在栈中,i这一变量在栈中占据了4字节。

图4 变量i在.s文件中

②int argc 整型变量

argc是主函数的一个参数,被保存在栈中,由寄存器传入。

图5 argc

③char *argv[] 字符型数组变量

argv也是主函数的一个参数,它的首地址被保存在栈中,由寄存器传入

图6 argv

3.3.2赋值

①int i初始未赋值,默认赋值为0。

图7 i默认赋值为0

3.3.3类型转换

①atoi(argv[4])将字符型argv[4]转换为整型。

首先从栈中取出 argv的初始地址,保存到 %rax 中。因为每个指针8个字节,因此移动32个字节到达argv[4]的地址,再将此地址传给rdi,然后调用atoi函数进行类型转换。

图8 对argv[4]进行类型转换

3.3.4算术操作

①i++,实现i=i+1。

图9 i++的实现

3.3.5 关系操作

①argc!=5

将argc和5做比较,使用了cmpl这一命令。

图10 比较argc和5

②i<10

判断i是否小于10,所以将i和9比较,同样使用了cmpl命令。

图11 比较i和9

3.3.6 数组操作

①printf("Hello %s %s %s\n",argv[1],argv[2],argv[3]);

首先将argv的首地址存入rax,再将rax加上对应偏移(8*n),找到对应的地址,再将其存在另一个寄存器中(rcx,rdx,rsi)。

图12 取出argv[1],argv[2],argv[3]地址

3.3.7 控制转移

①if(argc!=5)

将argc和5比较,je表示相等则执行,即如果argc=5就跳转到L2,否则执行下方命令,实现了控制转移。

图13 比较argc和5 实现对应跳转

②for(i=0;i<10;i++)

先将i和9进行比较,jle是不相等则执行,即如果不相等,就会跳转到L4,执行循环内容,循环末尾addl $1, -4(%rbp)实现i++,实现循环计数。

图14 进行循环条件判断

                                               图15 循环主体

3.3.8 函数操作

①exit(1);

使用call调用exit函数。

图16 调用exit

②sleep(atoi(argv[4]));

使用call调用sleep函数。

图17 调用sleep

③getchar();

使用call调用getchar函数。

图17 调用getchar

④return 0

将0赋给eax,即0为返回值,然后退出。

图18 返回值为0

3.4 本章小结

这一章主要介绍了编译的概念与作用,将hello.i文件编译生成hello.s,并通过对hello.s文件进行分析,解释了编译器如何处理各种数据类型和各种操作,说明了hello.c中各种操作在汇编语言上的实现。

(第3章2分)

第4章 汇编

4.1 汇编的概念与作用

汇编的概念:汇编器将.s汇编语言文件中的汇编内容,转换为机器语言指令,将它们打包生成可重定位目标程序格式文件.o。

汇编的作用:将汇编语言转换为机器语言,生成可重定位目标程序.o文件,为链接做准备。

4.2 在Ubuntu下汇编的命令

命令:gcc -c hello.s -o hello.o

图19 汇编生成.o文件

4.3 可重定位目标elf格式

先通过readelf -a hello.o 生成elf格式

图20 生成elf文本文件

①ELF头

这一部分包含了magic,类别,数据,程序头起点,头大小,节头大小和数量等基本信息。

图21 elf头

②节头

这一部分展示了各个节的大小,地址,偏移量等信息

图22 节头

③重定位节

重定位节展示了需要重定位的信息,即当链接器对这些内容进行操作时,需要对位置进行修改,进行重定位。在这个程序中,需要对.rodata,put,exit.rodata + 2c,printf,atoi,sleep,getchar进行重定位。

从重定位节中,我们可以得到偏移量、信息、类型、符号值和符号名称+加数的相关信息。

图23 重定位节

④符号表

符号表中显示了重定位时使用的符号,用于记录函数、变量等符号的名称、位置与类型,是链接器链接时候的重要导航。

图24 符号表

4.4 Hello.o的结果解析

先通过objdump -d -r hello.o生成反汇编信息,与第3章的hello.s文件进行对照分析。

图25 hello.o 的反汇编信息

机器语言由操作,操作数,寄存器组成。通过它们之间的组合来实现不同的操作。

机器语言和汇编语言大部分内容相同,可以直接映射,如寄存器,一些操作名称。

差异:

①函数调用:在汇编语言中,函数调用call后直接加调用函数的名称;而在机器语言中,在callq调用函数时,后面跟着的是下一条指令的地址,而不是所要调用函数的地址。这是应为所要调用的函数的地址还不确定,需要根据重定位,等链接之后才能确定地址,因此将callq的地址偏移设置为0,也就是直接进行到下一条指令。

图26 机器语言调用函数

②分支转移:机器语言中,分支转移指令后面的是具体地址,直接跳转到具体地址执行;而在汇编语言中,则是跳到对应的段,如L2、L4等。

图27 机器语言分支转移

4.5 本章小结

这一章主要介绍了汇编的概念与作用,将hello.s文件编译生成hello.o,并通过对hello.s文件的elf格式的分析,解释了elf各部分的功能和作用,再比较hello.o的反汇编代码和hello.s的代码,了解了汇编语言和机器语言的关系和差异。

(第4章1分)

5链接

5.1 链接的概念与作用

链接的概念:链接器将多个可重定位目标.o文件链接为一个可执行程序文件。

连接的作用:使程序模块化,可以对不同的源文件分别进行修改、编译等操作,使程序的编写更加便捷。

5.2 在Ubuntu下链接的命令

命令:ld -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  /usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o  /usr/lib/gcc/x86_64-linux-gnu/9/crtend.o /usr/lib/x86_64-linux-gnu/crtn.o  hello.o  -lc    -z relro -o hello

图28 在Ubuntu下链接命令

5.3 可执行目标文件hello的格式

使用readelf -a hello生成hello的elf格式。

图29 生成hello的elf格式

①ELF头

与hello.o的ELF文件信息相比,二者的一些基本信息,如Magic、类别等信息相同。而入口点地址、程序头起点、节头大小和节头数量都被改变。说明程序获得了入口地址。

图30 ELF头

②节头

节头中显示了各个节的名称、大小、旗标地址、偏移量等信息,比.o文件中的节头多了16个节,说明其在连接之后,加入了新的节。

图31 节头

③程序头

是操作系统用来加载和运行程序的索引,向系统说明将哪段文件内容加载到哪段内存,以何种权限执行。

图32 程序头

④动态段

描述了程序运行时所需的动态链接信息。比如程序需要加载哪些共享库、符号表的位置、重定位表的位置等。

图33 动态段

⑤重定位节

从重定位节中,我们可以得到偏移量、信息、类型、符号值和符号名称+加数的相关信息。说明了在链接之后,这些函数的偏移量,在调用时可以使用。

图34 重定位节

⑥符号表

符号表中显示了重定位时使用的符号,用于记录函数、变量等符号的名称、位置与类型,是链接器链接时候的重要导航。

图35 符号表(未完全列出)

5.4 hello的虚拟地址空间

从图中可以看出,程序被加载到地址0x401000-0x402000,每一节的地址都和5.3中节头表的地址一致。

图36 hello的虚拟地址空间

5.5 链接的重定位过程分析

使用objdump -d -r hello,生成hello的反汇编代码。

图37 生成hello反汇编代码(部分)

不同:

①函数数量增加:与hello.o相比,hello的部分多了很多函数,如.plt,puts@plt,sleep@plt等函数的反汇编代码,说明链接器hello将hello中所用到的函数和.o文件链接了起来。

图38 生成hello反汇编代码(部分)

②函数调用:callq后面跟着的是对应函数的地址,说明在连接时进行了重定位,将相对地址转换为在内存中真实地址。

图39 hello反汇编代码函数调用部分

③条件转移:链接之后,跳转指令,会跳转到对应语句在内存中的地址,进行执行。

图40 hello反汇编代码条件转移部分

链接的过程:首先将地址进行重定位,之后对代码和数据进行合并,之后再将库链接。

5.6 hello的执行流程

使用gdb/edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程(主要函数)。请列出其调用与跳转的各个子程序名或程序地址。

子程序名

地址

hello!start

0x4010f0

hello!_init

0x401000

hello!main

0x4011d6

hello!puts@plt

0x401030

hello!exit@plt

0x401060

hello!printf_chk@plt

0x401050

hello!sleep@plt

0x401070

hello!getc@plt

0x401080

表2子程序名或程序地址

5.7 Hello的动态链接分析

函数在运行时经过动态链接器处理,通过延时绑定,避免修改运行代码段。通过PLT和GOT表就可以实现对函数进行动态链接,加载时动态链接器重定位GOT表中的内容,就可以得到函数的正确地址。

以exit函数为例来说明。首先通过objdump -R hello查看GOT表。得到exit的GOT表地址0x404030。

图41 GOT表

然后gdb运行hello,并在主函数处设置断点后运行。

图42 设置断点

这时查看0x404030地址处对应的内容,可以发现内容为0x401060,这说明此时还没有解析真实地址,指向的是.plt表中的跳板。此时尚未解析,需要调用动态链接器。

图43 查看GOT表

之后在exit处设置断点,continue执行,再次查看0x404030地址处对应的内容,得到0x00007ffff7e05a70,说明exit的GOT项已经被替换为 libc.so 中真正的exit地址,动态链接器完成绑定。

图44 再次查看GOT表

5.8 本章小结

这一章主要介绍了链接的概念与作用,将hello.o文件链接生成可执行程序hello,并通过对hello文件的elf格式的分析,解释了链接的功能和作用,再比较hello的反汇编代码和hello.s的代码,了解了重定位和动态链接的过程,知道了重定位和动态链接的重要性。

(第5章1分)

6hello进程管理

6.1 进程的概念与作用

进程的概念:进程是程序的基本执行实例,是系统进行资源分配的基本单位。系统的程序都运行在某个进程的上下文之中,每个进程拥有自己的独立地址空间,内存等。

进程的作用:为程序提供独立的运行和空间,使程序可以“独占”处理器和内存,使不同程序并行进行。

6.2 简述壳Shell-bash的作用与处理流程

Shell的作用:是用户与操作系统之间的命令解释器,它的作用是接收用户输入的命令,解释并调用内核或程序执行相应操作,是用户控制系统的主要接口。

Shell的处理流程:用户输入命令—>shell对输入的命令进行拆分—>对命令的内容进行解析—>创建子进程(fork)—>执行命令(execve)。

6.3 Hello的fork进程创建过程

输入./hello 2023111734 王腾远 19804541470  2(电话号%5=0,不好操作,这里使用2)。

图45 程序正常执行

首先,系统执行可执行文件hello,父进程通过fork()创建子进程hello,子进程拥有与父进程不同的pid,操作系统将父进程的上下文拷贝给子进程,父进程和子进程再并发运行。当子进程结束时,会由父进程进行回收;如果此时父进程已经结束,则由init回收。

6.4 Hello的execve过程

调用fork函数产生子进程后,子进程会调用execve函数,使在当前上下文中加载并执行新的hello。之后清除当前进程的代码和地址空间,进行初始化,然后跳转到新程序的入口地址开始执行。随后加载器跳转至程序入口,即将程序计数器(PC)设置为 _start 的地址。_start 函数最终调用hello中的 main 函数,从而完成在子进程中的程序加载过程。

6.5 Hello的进程执行

程序运行时hello 程序作为一个用户态进程被调度执行。如果hello在某次调度中用完了它的时间片(如打印字符串时间过长),则会被抢占,调度器会将 CPU 切换给其他进程。系统的调度器会根据时间片将CPU时间分配给各个进程。当程序调用sleep()时,sleep向内核发出请求,程序进入内核态,将hello挂起,然后切换上下文,执行其他的进程。Hello加入等待队列,sleep时间结束后,sleep发起一个中断请求,重新调用sleep,再次获得时间片调度,由内核态切换为用户态,继续执行进程。

6.6 hello的异常与信号处理

①不停乱按:在程序执行时,乱按不会影响程序的正常执行,而当getchar读到以回车结尾的字符串后,会将输入缓存,在程序运行结束后作为命令输出。

图46 不停乱按

②回车:输入回车对程序正常执行无影响。也只会在程序运行结束后在shell命令行输入回车。

图47 按回车

③Ctrl+C:按Ctrl+C后,进程得到中断信号SIGINT,终止了收到SIGINT信号的hello进程。

图48 按Ctrl+C

④Ctrl+Z:按Ctrl+Z之后,进程收到信号SIGTSTP,程序被挂起,暂停执行,但没有被终止。


图49 按Ctrl+Z

这时输入ps,可以看见PID为6663的进程是我们的hello。再输入jobs查看被挂起的进程,可以看见hello被暂停,而并未被结束。


图50 输入ps                             

图51 输入jobs

输入pstree,可以看见进程以树状图形式展示出来

图52 进程树状图(部分)

输入kill不同的选项可以有不同的操作,如 -9会强制终止进程。Kill后输入ps,发现hello确实已终止运行。


图53 kill命令

输入fg %对应的工作号,可以将被挂起的进程恢复到前台继续执行,hello从被挂起处继续运行,打印剩下的语句,程序正常结束。


图54 fg命令

6.7本章小结

这一章主要介绍了进程的概念与作用,和Shell-bash的作用和处理流程。本章以hello为例,研究了fork,execve函数的原理和执行过程,了解了进程调度的过程。还学习了进程执行过程中的各种异常状况和对应的信号,以及通过命令来管理进程。

(第6章2分)

7hello的存储管理

7.1 hello的存储器地址空间

结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。

①逻辑地址:是由程序生成的偏移地址,一般由段寄存器和偏移量组成,是CPU在执行指令或访问数据时最初使用的地址。逻辑地址不是一个完整的物理地址,需要通过段机制转换为线性地址。

在hello中,当你在程序中调用 printf函数时,编译器会将 printf 的调用地址编译为一个逻辑地址,即“段内偏移”,在程序运行时,CPU 会将这个逻辑地址通过段表翻译为线性地址。

②线性地址:是逻辑地址经过段机制转换后得到的地址。是段机制处理完毕地址。在不使用分页机制时,线性地址就是物理地址。线性地址空间是连续的、统一的地址空间。

在 hello 程序中,函数调用、变量访问等所有内存访问动作,都会在段寄存器与偏移量的配合下,先被转换为线性地址。

③虚拟地址:在一般情况下,虚拟地址即为线性地址。

④物理地址:CPU最终用来读写数据的地址。每一次内存访问都依赖于虚拟地址到物理地址的映射,操作系统通过页表将虚拟地址转化为对应的物理地址。

7.2 Intel逻辑地址到线性地址的变换-段式管理

Intel通过段式管理来实现逻辑地址到线性地址的变换。逻辑地址由两个部分组成:段选择符和段内偏移量。段选择符又分为三部分:索引:13位,指明描述符在GDT或LDT表中的位置。TI:1位,指示使用哪张表:0为全局描述符表GDT,1为局部描述符表LDT。RPL:2位用于判断重要程度,00为最重要的内核,11为用户层。

首先根据 TI 位判断使用的是 GDT(全局描述符表) 还是 LDT(局部描述符表)。接着用索引×8(即描述符大小),加上GDT/LDT的基地址,定位到段描述符在内存中的位置。之后从描述符中提取出 32 位的段基起始地址。最终,将段基地址与段内偏移量相加,得到32位线性地址。

7.3 Hello的线性地址到物理地址的变换-页式管理

Intel通过页式管理来实现线性地址到物理地址的变换。CPU的内存管理单元(MMU)而是将其线性地址分段解释,通过查表找到对应的物理页帧。

线性地址可以分为虚拟页号VPN和虚拟页偏移量VPO两部分,先通过VPN访问相对应的页表中的某个页表项,若有效位为1,就可以从这里得到物理页号PPN,而物理偏移量PPO和VPO相等,所以将两者组合就可以得到物理地址。

若有效位为0,说明对应的虚拟页没有被缓存,发生缺页中断,这时需要调用内核中的缺页处理程序,查看是否有空白页,有空白页则可以对应页面写入;否则需要先找到牺牲页,再将对应页面写入,得到PPN。

7.4 TLB与四级页表支持下的VA到PA的变换

以Intel Core i7 CPU为例进行说明。

CPU首先产生虚拟地址VA,VA由36位的VPN加12位的VPO组成,MMU首先通过VPN得到32位的TLBT和4位的TLBI,并根据此来在TLB中查找,若查找命中,就会得到PPN,由7.3知,PPO=VPO,将两者组合就可以得到物理地址PA。

若TLB未命中,MMU会去查询页表,此时将VPN分为四部分,通过VPN1得到在一级页表项中的偏移量,这个页表项中包含二级页表的基地址。如果有效,就会进入二级页表,再根据VPN2进行查找,以此类推,最终可以在四级页表中得到PPN,再和PPO结合,得到PA。同时将此VA到PA的映射添加到TLB中,便于后续访问。

7.5 三级Cache支持下的物理内存访问

CPU向主存请求数据后,首先访问L1 Cache,若所需的数据位于L1 Cache中,则直接得到数据。若不在L1 Cache中,则会向L2 Cache进行查找,若所需数据在L2 Cache中,则将L2 Cache对应块中数据放入L1 Cache,方便下次访问。若不在L2 Cache,则对L3 Cache进行访问,命中则将对应块写入L1、L2;否则需要去主存中找到对应数据,在将对应块写入L1、L2、L3 Cache。

在将块写入Cache时,优先向映射到的组的空白块中写入。若没有空白块,则根据LFU策略选择某一块进行替换

7.6 hello进程fork时的内存映射

hello调用fork时,内核为进程创建各种数据结构,并分配一个PID。子进程会复制父进程的内存映射,即将父进程的虚拟地址空间和页表都复制到对应的子进程,此时父进程和子进程的堆、栈、代码段和数据段都相同。

而当父进程或子进程想要修改某一页时,系统就会创建一个新页面,并将修改后的结果复制到其中,因此每个进程就都拥有自己的独立地址空间。

7.7 hello进程execve时的内存映射

当execve被调用后,用户空间中的所有代码段、数据段、堆、栈都将被释放。文件映射区域、共享内存等全部清除。内核会保留PID等标识,但会清空页表、重新建立虚拟内存结构。在进行新的内存映射后,就会将新的hello加载到虚拟地址空间中,进程重新开始执行,将原有内存内容替换。

7.8 缺页故障与缺页中断处理

当发生缺页故障时,会触发缺页中断,调用内核的缺页处理程序。首先系统寻找空白物理页面;若没有空白页面,则需要选择牺牲页。然后系统将所需要的数据装载到所分配的物理页面之中,对页表进行更新,得到新的虚拟地址到物理地址的映射。然后返回程序,程序重新运行指令,能够命中,就可以将对应数据送入。

7.9动态存储分配管理

以下格式自行编排,编辑时删除

Printf会调用malloc,请简述动态内存管理的基本方法与策略。(此节课堂没有讲授,选做,不算分)

7.10本章小结

这一章以hello为例子,辨析了逻辑地址、线性地址、虚拟地址、物理地址的概念和他们之间的相互转化。知道了段式管理和页式管理的实现方式,还了解了从VA到PA的映射过程以及Cache下的物理内存访问。还讨论了hello进程fork、execve时的内存映射,缺页故障和缺页中断的处理。

(第7章 2分)

8hello的IO管理

8.1 Linux的IO设备管理方法

以下格式自行编排,编辑时删除

设备的模型化:文件

设备管理:unix io接口

8.2 简述Unix IO接口及其函数

以下格式自行编排,编辑时删除

8.3 printf的实现分析

以下格式自行编排,编辑时删除

[转]printf 函数实现的深入剖析 - Pianistx - 博客园

从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等.

字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。

显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

8.4 getchar的实现分析

以下格式自行编排,编辑时删除

异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。

getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

8.5本章小结

以下格式自行编排,编辑时删除

(第8章 选做 0分)

结论

Hello的根源是hello.c的c语言源文件,之后通过C处理器的预处理变成了文件hello.i。C编译器通过对hello.i文件的内容进行分析,将其中的合法指令转换为汇编语言,生成汇编语言文件hello.s。汇编器将hello.s汇编语言文件中的汇编内容,转换为机器语言指令,将它们打包生成可重定位目标程序格式文件hello.o。链接器再将hello.o和其他.o文件链接为一个可执行程序文件,可以运行的hello就此诞生。在Shell中fork创建子进程,execve把代码和数据加载入虚拟内存空间,hello开始执行。执行过程中逻辑地址最终被映射为物理地址,再通过Cache访问hello需要的数据。之后hello运行结束,等待被父进程回收,内核将hello相关的数据结构删除。

我感悟到了计算机系统是一个精妙而复杂的系统,学习过后,我对程序是如何在计算机上运行的有了更加深刻的了解,也知道了要如何去优化程序。

(结论0分,缺失-1分)


附件

列出所有的中间产物的文件名,并予以说明起作用。

文件名

说明

hello.i

C预处理器生成的文本文件

hello.s

C编译器编译生成的汇编语言文件

hello.o

汇编器生成的可重定位目标文件

hello

可以执行的文件

(附件0分,缺失 -1分)

参考文献

为完成本次大作业你翻阅的书籍与网站等

[1]Randal E.Bryant, David O'Hallaron. 深入理解计算机系统[M]. 机械工业出版社.2018.4 

[2]张春玲.可变参数函数printf调用过程的分析[J].电子制作,2014,(02):58.DOI:10.16589/j.cnki.cn11-3571/tn.2014.02.216.

[3]张涛.论计算机语言[J].电脑知识与技术,2010,6(14):3662-3663.

[4]何先波,唐宁九,吕方,等.ELF文件格式及应用[J].计算机应用研究,2001,(11):144-145+150.

[5]朱裕禄.Linux系统下的ELF文件分析[J].电脑知识与技术,2006,(26):111-113.

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值