计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 人工智能方向(2+x)
学 号 2022111659
班 级 22WL021
学 生 刘仲轩
指 导 教 师 刘宏伟
计算机科学与技术学院
2024年5月
本文通过对hello.c程序在Ubuntu系统下的整个运行过程分析,结合《深入了解计算机系统》一书以及课上相关内容讲解,对其从原始程序,到预处理,再到编译,汇编,链接,进程加载,运行,终止,回收的过程进行了分析。通过对hello.c程序整个生命周期的深入研究,成功梳理了计算机系统相关知识,在实践中加深了对知识的印象,做到学以致用。
关键词:计算机系统;程序生命周期;编译原理;
目 录
本章主要介绍了linux下的汇编相关命令及汇编作用,以及生成的.o文件类型。还对可重定位目标elf格式进行了逐段分析,最后对比了.s文件与反汇编文件的区别。... - 17 -
6.2 简述壳Shell-bash的作用与处理流程... - 28 -
6.3 Hello的fork进程创建过程... - 28 -
7.2 Intel逻辑地址到线性地址的变换-段式管理... - 34 -
7.3 Hello的线性地址到物理地址的变换-页式管理... - 35 -
7.4 TLB与四级页表支持下的VA到PA的变换... - 35 -
7.5 三级Cache支持下的物理内存访问... - 36 -
7.6 hello进程fork时的内存映射... - 37 -
7.7 hello进程execve时的内存映射... - 37 -
第1章 概述
1.1 Hello简介
Hello的P2P是指hello.c从可执行程序(Program)通过进程管理变成进程(Process)的过程。Linux核心的系统中,hello文件经过c预处理器(cpp,C-Pre-Processor)的预处理,得到hello.i。hello.i经过编译,得到hello.s;再经过汇编得到hello.o,经由链接器(ld)生成可执行目标程序hello。最后在shell中运行可执行文件,shell调用fork函数为其创建子进程。
Hello的020是指程序从无到有再到无的全过程,即shell接受命令,fork创建子进程,execve函数加载进程,执行时利用虚拟地址访存及处理缺页异常和中断等,然后载入物理内存,链接器链接printf所在的动态库。进入main函数执行目标代码,CPU为执行文件hello分配时间周期,执行逻辑控制流,每条指令在流水线上取值、译码、执行、访存、写回、更新PC。hello运行完毕后,产生sigchld信号,程序运行结束后shell父进程负责回收hello进程,进行资源释放。
1.2 环境与工具
硬件环境:Intel Core i7-12700H;DDR5 16G;1T+512GSSD
软件环境:Windows 11 64位;Ubuntu 22.04.4 64位
开发工具:Visual Studio2022;Devc++;vi/vim/gedit+gcc
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
hello.c:源程序文本文件
hello.i:hello.c预处理后生成的文本文件
hello.s:hello.i编译后生成的汇编语言文件
hello.o:hello.s汇编后得到的可重定位目标文件
hello:hello.o经过链接生成的可执行文件
1.4 本章小结
本章对hello的P2P与020过程进行了介绍,交代了编写论文过程中用到的软硬件环境和开发工具,并介绍了整个过程中的文件生成结果。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
预处理(Preprocessing)是指在编译程序之前对源代码进行的一系列处理步骤,以便为后续的编译过程做好准备。预处理通常包括宏替换、文件包含、条件编译等操作。预处理器是负责执行这些操作的工具,它在编译器之前运行。预处理在许多编程语言中都有应用,但在 C 和 C++ 中尤为常见。
预处理的作用包括:代码重用和模块化,提高代码可读性和可维护性,减少代码冗余,支持调试和开发以及提高编译效率等。
2.2在Ubuntu下预处理的命令
2.3 Hello的预处理结果解析
hello.c经过预处理后,拓展为3092行。
首先是包含的各种头文件信息(13-71行):
然后是头文件中typedef定义的各种变量(74-334行):
接下来是这些头文件的内容主题(341-3061行):
最后是main函数(3078-3092行):
2.4 本章小结
本章主要介绍了预处理的概念,作用和命令,以及预处理后生成的.i文件内容。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译(Compilation)是指从 .i 到 .s 的过程,即预处理后的文件到生成汇编语言程序。
编译主要有以下几个作用:将高级语言转换为汇编语言,语法和语义检查,代码优化,提供跨平台支持以及代码安全性检查等。
3.2 在Ubuntu下编译的命令
3.3 Hello的编译结果解析
3.3.1数据存储
字符串常量(储存在.rodata数据段中)
其中中文字符串被编码为UTF-8格式,一个汉字占3个字节。
整数
argc传入main函数
这两部分分别通过movl %edi, -20(%rbp)存储参数argc的值,通过movq %rsi, -32(%rbp)存储参数argv的值。
3.3.2判别式
以上步骤实现argc!=5
根据movl $0, -4(%rbp) 可以看出编译器将i存到了-4(%rbp) 中,且占4个字节。与addl $1, -4(%rbp)和cmpl $7, -4(%rbp)实现循环结构for(i=0;i<10;i++)。
3.3.3 算术操作
如addl $1, -4(%rbp)
3.3.4 控制转移
如:
或者
3.3.5 函数调用
调用printf函数:
3.4 本章小结
本章介绍了Ubuntu下编译的命令,以及生成的.s文件的内容,和.s文件中各个汇编指令对应实现的功能,如赋值,运算等。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
汇编(Assembly)是将汇编语言代码转换为机器代码的过程。汇编语言是一种低级编程语言,它使用助记符(mnemonics)和标签来表示机器指令和内存地址,便于人类阅读和编写。汇编器(Assembler)是执行这一转换的工具。
汇编的作用:将汇编代码转换为机器代码,提供对硬件的直接控制以及优化性能。
4.2 在Ubuntu下汇编的命令
4.3 可重定位目标elf格式
elf头:
包含以下信息:标识信息,文件类型,系统架构,节表偏移,程序头表偏移,头表大小等。
节头部表:
节头部表中对ELF文件中不同节的信息进行了描述,包括节名称,类型,偏移,大小等,以及标志标记等。
符号表:
符号表对所有定义的符号进行罗列,在链接与调试中起重要作用。
重定位表:
重定位表包含代码中使用的外部变量信息,在汇编程序生成目标模块时为最终位置未知的目标参照生成重定位条目,链接时链接器根据重定位节的信息对于外部变量符号决定选择何种方法计算正确的地址。
动态节区信息:
hello中没有动态节区。
4.4 Hello.o的结果解析
hello.o的反汇编与第三章hello.s对比,不同之处在于:
1.反汇编生成的指令前有十六进制代码段
2..s文件中call后接函数名,如call puts@PLT;而反汇编文件中call后接地址字段起始位置。
3..s文件中跳转指令后接代码段名字,而反汇编文件中跳转指令后接代码地址。
机器语言常由操作码和操作数构成,而指令格式定义了操作码和操作数在指令中的位置和长度。通常情况下,汇编语言和机器语言一一对应。
4.5 本章小结
本章主要介绍了linux下的汇编相关命令及汇编作用,以及生成的.o文件类型。还对可重定位目标elf格式进行了逐段分析,最后对比了.s文件与反汇编文件的区别。
(第4章1分)
第5章 链接
5.1 链接的概念与作用
链接(Linking)是将多个目标文件(Object Files)和库文件(Libraries)组合在一起,生成一个可执行文件(Executable File)的过程。链接器(Linker)是执行这一过程的工具。链接的主要作用是:解决符号引用、合并代码和数据段、以及生成最终的可执行文件。
5.2 在Ubuntu下链接的命令
5.3 可执行目标文件hello的格式
ELF头:
ELF头包含以下信息:标识信息,文件类型,系统架构,入口点地址,程序头起点等,描述了文件的总体格式。与第四章中的相比,这次的hello.elf类型,程序头大小和节头数量增加,且获得了入口地址。
节头:
节头部表中对ELF文件中不同节的信息进行了描述,包括节类型,位置,偏移,大小等,以及标志标记等。与第四章中的相比,链接后的内容更丰富。
程序头:
程序头部分描述了系统准备程序执行所需的段或其他信息。
动态节区:
符号表:
符号表中包含定位,重定位程序中符号定义和引用的信息,在其中声明引用的符号。
5.4 hello的虚拟地址空间
使用edb加载hello,可查看本进程的虚拟地址空间各段信息,能看出虚拟地址空间起始地址为0x401000
从edb和readelf中可看出标记的虚拟地址相同。例如,对于.text节:
readelf:
edb:
可以看出地址均为0x004010f0
5.5 链接的重定位过程分析
对hello反汇编:
与hello.o反汇编生成的文件对比:
不同之处:
- 链接后增加了.plt,puts@plt,printf@plt等函数(动态链接库链接后加入了库函数)
- 反汇编地址有所不同:hello的反汇编地址从401000开始,而hello.o的反汇编地址从0开始
hello:
hello.o:
- 跳转指令的参数变化:hello.o后接相对地址,hello后接绝对地址
hello.o:
hello:
重定位过程:重定位(Relocation)是链接过程中的一个关键步骤,涉及调整目标文件中代码和数据的地址引用。目标文件中的地址通常是相对地址或未解析的符号地址,链接器通过重定位将这些地址转换为最终可执行文件中的绝对地址。
重定位一般分为以下几步:
1.解析符号:链接器首先解析所有目标文件中的符号,确定每个符号的定义位置和地址。
2.计算地址:链接器根据符号表计算每个符号的最终地址。
3.调整地址引用:链接器根据重定位项目调整目标文件中的地址引用,使其指向正确的绝对地址。
5.6 hello的执行流程
子程序名 | 程序地址 |
_init | 0x401000 |
_start | 0x4010d0 |
_libc_csu_init | 0x401190 |
main | 0x401105 |
puts@plt | 0x401080 |
exit@plt | 0x4010b0 |
_fini | 0x401208 |
5.7 Hello的动态链接分析
由于编译器不能预测函数运行时的地址,因此动态链接器使用.got与.plt实现函数动态链接,GOT中存放函数目标地址,PLT用GOT中地址跳转目标函数(重定位过程由动态链接器完成)
可知.got.plt起始地址为:0x00404000。dl_init执行后可见两个地址0x7ff339ccc190和0x7ff339af5cc0,是动态链接后函数的最终地址。
5.8 本章小结
本章对链接的概念及作用进行了简单介绍,对hello的可执行文件ELF格式文进行了分析,比较hello.o与hello反汇编后的不同之处。此外,还对hello的虚拟地址空间进行了分析,以及分步研究了hello的执行流程,观察了链接前后hello地址变化。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
进程是一个正在执行的程序的实例,包括程序代码、数据、堆栈和进程控制块等。它是操作系统分配资源和调度执行的基本单位。
作用:操作系统通过进程来管理和分配资源(如CPU时间、内存、I/O设备等)。通过多进程机制,操作系统能够并发执行多个程序,提高系统的效率和响应速度。进程之间的内存空间是相互隔离的,防止一个进程的错误影响其他进程。这种隔离机制提高了系统的稳定性和安全性。通过进程调度算法也可以决定哪个进程在何时运行。程之间可以通过进程间通信(IPC, Inter-Process Communication)机制进行数据交换和协作。
6.2 简述壳Shell-bash的作用与处理流程
Shell 是操作系统中的一个命令解释器,它提供了用户与操作系统之间的接口。Bash(Bourne Again Shell)是 Linux 和 Unix 系统中最常用的 Shell 之一。
shell的作用:
1.Shell 接受用户输入的命令,并将这些命令解释为操作系统能够执行的操作。
2.Shell 允许用户编写脚本,以自动化执行一系列命令。
3.Shell 可以启动和管理其他程序或进程。
4.Shell 管理用户会话的环境变量,这些变量影响命令的执行和程序的运行环境。
5.Shell 提供了命令来浏览和操作文件系统,如 cd(更改目录)、ls(列出目录内容)、cp(复制文件)等。
6.Shell 支持将命令的输入和输出重定向到文件或其他命令,这使得数据处理更加灵活。
处理流程:读取输入->解析命令->变量替换->通配符展开->命令替换->分词与分析->执行命令->输入输出重定向->等待命令完成->显示结果
6.3 Hello的fork进程创建过程
对于fork进程的创建,首先,执行hello,父进程通过fork创建一个新运行子进程hello。子进程与父进程用户级虚拟地址空间相同,包括代码和数据段、堆、共享库以及用户栈。父进程和子进程之间的最大区别是他们有不同的PID。在父进程中,fork返回子进程的PID;在子进程中,fork返回0。子进程结束时,父进程如果仍存在则由它执行对子进程回收,否则init进程回收。
6.4 Hello的execve过程
调用fork创建新的子进程后子进程可以调用execve函数,在当前进程的上下文中加载并运行hello。一般情况下execve函数不返回,除非有错误。
函数格式如下:int execve(const char *filename, const char *argv[], const char envp[]);
新程序启动后,用户栈组织结构如下:
6.5 Hello的进程执行
进程通过独立逻辑控制流执行,若hello不被占用则正常执行,若有占用,则通过上下文切换进行并发运行。进程为每个程序提供一种假象,好像它独占地使用系统地址空间。
运行应用程序的代码的进程开始处于用户模式中。当异常发生时,控制传递到异常处理程序,处理器将模式从用户模式改为内核模式。处理程序运行在内核模式中,当它返回到应用程序代码时,处理器就把模式改回用户模式。
进程调度是操作系统中管理多个进程(程序实例)执行的机制。它决定了在任何给定时间,哪个进程可以使用 CPU 资源。进程调度的主要目标是确保系统资源的高效利用,提供公平的服务,并优化系统性能指标如响应时间、吞吐量和等待时间。
6.6 hello的异常与信号处理
异常(Exception)是指程序在运行过程中发生的各种意外情况或错误事件。根据不同的分类标准,异常可以分为多种类型。
硬件异常:
1.中断(Interrupts):由外部设备(如键盘、硬盘、网络设备等)产生的信号,用于通知 CPU 某个事件的发生。中断通常是异步的。
2.陷阱(Traps):由程序主动触发的异常,例如系统调用或断点调试。陷阱通常是同步的。
3.故障(Faults):由程序错误引起的异常,例如除零错误、非法内存访问等。故障通常是同步的,且可能会被修复(如页面缺失)。
4.终止(Aborts):由严重错误或硬件故障引起的异常,通常无法恢复,例如硬件故障或内存损坏。
软件异常:
1.运行时异常(Runtime Exceptions):在程序运行期间发生的异常,例如空指针引用、数组越界等。这类异常通常是由编程错误引起的。
2.逻辑异常(Logic Exceptions):程序逻辑错误导致的异常,例如除零错误、类型转换错误等。
3.系统异常(System Exceptions):操作系统或运行环境引发的异常,例如文件未找到、权限不足等。
信号格式如下:
- 正常运行:
- ctrl+c:hello前台运行时输入ctrl+c终止进程
- ctrl+z:hello前台运行输入ctrl+z发送SIGTSTP信号挂起进程
- fg:将第一个后台变回前台
- 乱按:乱按内容在程序结束后作为命令输入。
- ps
- jobs
- pstree
- kill
6.7本章小结
本章介绍了进程管理的相关概念,以及shell-bash的作用处理流程,hello的进程创建过程以及execve过程,并分析了hello的进程执行内在步骤,最后对hello运行过程中可能出现的异常和不同输入产生的结果进行了分析。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
1.逻辑地址
逻辑地址是由程序生成的地址,通常是在编译时或运行时由编译器和链接器生成的。对于hello来说,逻辑地址是指程序中变量和指令的地址。在编译hello时,编译器会生成机器代码,并为每条指令和变量分配逻辑地址。这些逻辑地址被用于程序的编译和链接过程。
2.线性地址
线性地址是逻辑地址经过段选择器(Segment Selector)转换后的地址。在基于段的内存管理模型中,逻辑地址由段选择器和段内偏移量组成。CPU 使用段选择器查找段描述符,段描述符包含段的基地址,然后将基地址与段内偏移量相加得到线性地址。
在现代 x86 处理器中,段选择器通常被设置为 0,段基址为 0,因此逻辑地址和线性地址通常是相同的。hello程序被加载到内存中,并且段选择器为 0,那么程序中指令和变量的逻辑地址和线性地址是相同的。
3.虚拟地址
虚拟地址是操作系统提供的一个抽象层,使每个进程都认为自己有一个完整的内存空间。虚拟地址空间是由操作系统的内存管理单元(MMU)管理的。
4.物理地址
物理地址是实际的内存地址,是 CPU 通过地址总线访问的地址。操作系统和 MMU 将虚拟地址映射到物理地址。这个映射通常通过页表(Page Table)进行。
7.2 Intel逻辑地址到线性地址的变换-段式管理
Intel处理器通过段式管理实现逻辑地址到线性地址的变换。段式管理(Segmentation)是一种内存管理技术,用于将内存划分为不同的段(Segments),每个段代表一个逻辑单元,如代码段、数据段、堆栈段等。每个段都有自己的基地址和长度,段式管理可以提供更灵活的内存分配和保护机制,在 Intel x86 架构中得到了广泛应用。
以printf为例,在编译和链接后,CPU解析CS段选择器,确定使用GDT,然后从CS中提取索引和TI。根据索引从GDT中查找段描述符,线性地址等于段基地址+偏移地址。通过这种方式,逻辑地址被转换为线性地址。然后,线性地址可以进一步通过页表转换为物理地址。
7.3 Hello的线性地址到物理地址的变换-页式管理
页式管理(Paging)是一种内存管理技术,用于将物理内存和逻辑地址空间划分成固定大小的页(Pages)和页框(Page Frames)。每个页可以单独映射到物理内存中的任意页框,从而实现灵活的内存分配和虚拟内存管理。页式管理在现代操作系统中得到了广泛应用。页表是一个数据结构,用于存储逻辑地址到物理地址的映射关系。每个进程都有自己的页表。页目录是用于管理页表的更高层次的数据结构,包含页表的基地址。
页命中时,处理器生成一个虚拟地址,并将其传送给MMU;MMU生成PTE地址(PTEA),并从高速缓存/主存请求得到PTE(Page Table Entry 页表条目、页表项);高速缓存/主存向MMU返回PTE;MMU 将物理地址传送给高速缓存/主存;高速缓存/主存返回所请求的数据字给处理器。
7.4 TLB与四级页表支持下的VA到PA的变换
当是四级页表,Core i7时,虚拟地址空间有48位,物理地址空间有52位,TLB四路十六组相连,页表大小4KB,PTE大小8字节。
可知VPN有36位,被划分为9位的片。PTBR包含L1页表物理地址,VPN1提供L1 PTE的偏移量,其中包含L2页的基地址。VPN2,3,4同上。
7.5 三级Cache支持下的物理内存访问
通用的高速缓存存储器架构如下所示。
主存地址如图所示:
一般读高速缓存分为以下几步:定位组,检查集合中的任何行是否有匹配的标记,如果是且行有效则命中,然后定位从偏移开始的数据。如果不命中,则从存储器层次结构下一层取出被请求的块,然后将新块存在对应高速缓存行中。
7.6 hello进程fork时的内存映射
1.创建子进程:
操作系统创建一个新的进程控制块(PCB)来表示子进程。子进程的 PCB 包含与父进程相同的内存映射信息,包括代码段、数据段、堆栈段等。
2.共享内存页:
父进程和子进程共享相同的物理内存页。页表项中的引用计数增加,表示这些页被多个进程共享。
3.设置页表项为只读:
为了实现写时复制,操作系统将父进程和子进程的页表项设置为只读。如果父进程或子进程尝试写入这些页,将触发页面错误(Page Fault)。
4.写时复制:
当父进程或子进程尝试写入共享的只读页时,操作系统捕获页面错误。之后操作系统为尝试写入的进程分配一个新的物理页,并将原始页的内容复制到新页。然后更新页表项,使其指向新的物理页,并将该页设置为可写。这样,只有在需要写入时才进行内存复制,从而节省了内存和提高了效率。
写时复制是一种优化技术,允许父进程和子进程在 fork() 后共享相同的物理内存页,直到其中一个进程尝试修改这些页。这种技术减少了不必要的内存复制,提高了系统性能。
7.7 hello进程execve时的内存映射
1.加载新程序:
操作系统读取新程序可执行文件,并将其加载到内存中。可执行文件包含 echo 程序的代码段、数据段、堆栈段等。
2.清空当前进程的地址空间:
当前进程的地址空间被清空,所有与 hello 相关的内存映射被移除。
3.建立新的内存映射:
操作系统为新程序建立新的内存映射。代码段映射到新的可执行文件中的代码部分。数据段映射到新的可执行文件中的数据部分。堆栈段重新初始化。
4.设置程序入口点:
操作系统将程序计数器(PC)设置为新程序(echo)的入口点。
5.执行新程序:
程序开始执行,从入口点开始运行。程序打印到标准输出。
7.8 缺页故障与缺页中断处理
缺页异常处理:
1) 处理器生成一个虚拟地址,并将其传送给MMU
2) MMU生成PTE地址(PTEA),并从高速缓存/主存请求得到PTE
3) 高速缓存/主存向MMU返回PTE
4) PTE的有效位为零, 因此 MMU 触发缺页异常
5) 缺页处理程序确定物理内存中的牺牲页 (若页面被修改,则换出到磁盘——写回策略)
6) 缺页处理程序调入新的页面,并更新内存中的PTE
7) 缺页处理程序返回到原来进程,再次执行导致缺页的指令
7.9动态存储分配管理
动态内存分配器维护着称为堆的进程虚拟内存区域,对每个进程,内核维护一个变量brk,指向堆的顶部。分配器将堆视作一组不同大小的块的集合,每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。分配器有两种基本风格:显式分配器和隐式分配器。显式分配器要求应用显式地释放任何已分配的块,隐式分配器要求分配器检测一个已分配块何时不再被程序所用,那么就释放这个块。隐式分配器又叫垃圾收集器,而自动释放未使用的已分配的块的过程叫做垃圾收集。
malloc的基本过程如下:
1.请求内存:
程序调用 malloc 并请求一定大小的内存。例如,void *ptr = malloc(size); 其中 size 是请求的字节数。
2.查找合适的空闲块:
malloc 在内部维护一个空闲链表或其他数据结构来管理可用内存块。使用不同的分配策略(如首次适配、最佳适配等)查找一个足够大的空闲块。
3.分配内存:
如果找到合适的空闲块,则从该块中分配所需大小的内存。更新空闲链表或数据结构,记录分配后的剩余空闲块。
4.返回指针:
返回指向分配内存的指针。如果没有足够的内存可用,返回 NULL。
7.10本章小结
本章简单介绍了hello存储管理的相关内容。首先对hello的存储器地址空间进行了简介,然后分别介绍了段式管理和页式管理,以及虚拟地址和物理地址之间的转换。之后,研究了三级cache支持下的物理内存访问。最后讲述了hello在fork和execve时的内存映射,以及缺页中断处理和动态存储分配管理方法。
(第7章 2分)
结论
hello程序首先从hello.c开始,依次经过
预处理:生成hello.i文件;
编译:通过词法语法分析将合法指令翻译成汇编代码,生成hello.s汇编文件;
汇编:将汇编代码翻译成机器语言,并把这些指令打包成可重定位目标程序,生成hello.o文件;
链接:将文件与动态链接库链接,生成可执行文件hello;
shell接受命令:通过读取用户键盘输入命令行获取参数,调用fork创建子进程hello,通过execve加载代码及数据(缺页处理,虚拟地址翻译为物理地址,三级内存访问等)程序开始执行。
shell回收:运行结束后父进程进行hello的回收以及资源释放。
通过研究hello从头至尾的运行过程,我对计算机系统的设计与实现有了更深刻的理解。计算机系统是一个高度复杂且分层的系统,每一层都起着举足轻重的作用。从预处理到链接,每一种机制都在完成它的功能,资源管理,并行处理,每一个都在保证安全可靠性的同时提升系统性能。
创新方面,我认为可以通过机器学习算法进行智能资源管理,动态调整内存分配策略;或者通过开发基于AI的工具,自动分析代码中的潜在安全漏洞来提供修复建议。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
名称 | 作用 |
hello.c | 源代码文本文件 |
hello.i | hello.c预处理后得到的源程序文本文件 |
hello.s | hello.i编译后得到的汇编文件 |
hello.o | hello.s汇编后得到的可重定位目标文件 |
hello | hello.o链接后得到的可执行目标程序 |
hello_o.asm | hello.o反汇编得到的文件 |
hello2.asm | hello反汇编得到的文件 |
(附件0分,缺失 -1分)
参考文献
为完成本次大作业你翻阅的书籍与网站等
[1] Randal E.Bryant David R.O’Hallaron. 深入理解计算机系统(第三版). 机械工业出版社,2016.
[2]sunny-II, 【Linux】GDB保姆级调试指南(什么是GDB?GDB如何使用?)[EB/OL].[2023-11-15][2024-06-14]https://blog.csdn.net/weixin_45031801/article/details/134399664
(参考文献0分,缺失 -1分)