计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 计算机与电子通信类
学 号 2023111734
班 级 23L0507
学 生 王腾远
指 导 教 师 史先俊
计算机科学与技术学院
2025年5月
摘 要
本文从hello.c这一简单程序出发,依次研究了它的预处理、编译、汇编、链接、加载、运行和终止的过程。结合《CSAPP》课程中所学内容,以hello为例子,在linux系统下,对程序的预处理、编译、汇编、链接、进程管理和内存管理的观察和分析,研究了计算机系统各个部分的知识,将计算机系统各部分生动组织在一起
关键词:计算机系统;程序处理;进程管理;内存管理;
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目 录
第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分)
第6章 hello进程管理
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分)
第7章 hello的存储管理
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分)
第8章 hello的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.
1524

被折叠的 条评论
为什么被折叠?



