锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

哈工大计算机系统大作业论文:Hello的一生

时间:2023-07-19 09:07:00 612gdj211bg传感器

计算机系统

大作业


题 目
专 业
学 号
班 级
学 生
指 导 教 师


计算机科学与技术学院
2021年5月
摘 要
生活是一个人从生到死的过程。虽然程序不属于生命的范畴,但也有这样一个过程。本论文将结合一学期的知识进行更完整的阐述Hello从预处理到编译、汇编到链接到可执行文件的生命周期。同时,本文还将分析我们的计算机系统如何见证Hello诞生、成长和谢幕。
关键词:Hello,预处理、编译、汇编、链接、流程管理、存储管理、IO管理

目 录

第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.5 三级Cache支持的物理内存访问 - 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缩写,即从程序到过程的过程。首先,初始hello.c要经过cpp的预处理。cpp不要试图理解hello.c,相反,它通过文本替换转换成文本替换hello.i。在这之后,hello.i通过编译器ccl编译,它会hello.i转化成机器代码hello.s。然后,hello.s经过汇编器as汇编转化为可重定位文件hello.o。最后,hello.o经由链接器ld将链接转化为可执行文件hello.out。在shell输入相应的命令行指令后,shell通过fork到目前为止,产生一个子过程P2P完成。
020:
即zero to zero缩写是从运行过程到回收过程的过程。shell执行execve、在子过程中加载指令hello。进入程序入口后,输入物理内存,然后输入main函数执行代码。在这个过程中,TLB、Cache为hello保证快速运行,CPU为运行的hello分配时间片执行逻辑控制流。Hello通过IO管理输入输出。当hello运行结束,shell中hello父亲过程回收僵死hello到目前为止,020已经完成了内核删除相关信息和数据结构的过程。
1.2 环境与工具
硬件环境:I64 CPU;2.6GHz;32G RAM;512GHD Disk
软件环境:Windows11;Ubuntu-20.04.4
开发工具:GDB,OBJDUMP,GCC,EDB,Code Blocks等
1.3 中间结果
文件名 文件说明
hello.i hello.c预处理获得的文本文件
hello.s hello.i编译的机器代码文件
hello.o hello.s汇编后获得的可重定位文件
hello.out hello.o链接获得的可执行文件
hello.txt hello.o反汇编后获得的文本文件
hello.c 源代码


1.4 本章小结
本章从总体出发,简要总结Hello.c的一生:p2p以及020,并说明了写本论文所用的软硬件环境和工具,以及所有中间结果文件。


第2章 预处理
2.1 预处理的概念和作用
概念:预处理是在编译程序源代码之前,由预处理器处理程序源代码。

功能:预处理不分析程序源代码的语法,而是将源代码分割或处理成特定符号,以便进一步编译。
2.2在Ubuntu下一个预处理命令

gcc -E hello.c -o hello.i
或cpp hello.c > hello.i

2.3 Hello预处理结果分析
打开hello.i,相比之下可以发现hello.c有更多的预处理头文件stdio.h,stdlib.h等内容。将hello.i下拉,可以找到hello.c的内容被完整整地复制了下来,可见预处理并不解析源代码的语法。


2.4 本章小结
本章结合图片,阐述了预处理的概念、作用及相关的指令,并对预处理生成的hello.i文件进行了简要的分析。

第3章 编译
3.1 编译的概念与作用
概念:由编译器(ccl)将后缀名为“.i”的文本文件翻译成可被机器执行的机器级代码(后缀名为“.s”)的过程。

作用:先检查代码的正确性,进行代码的词法分析、语法分析、语义分析,检查无误后将源代码翻译为汇编代码。        
3.2 在Ubuntu下编译的命令
gcc -S hello.c -o hello.s


3.3 Hello的编译结果解析
3.3.1 数据类型
(1)字符串类型
汇编文件hello.s中共有两个字符串:"用法: Hello 学号 姓名 秒数!\n"和"Hello %s %s\n",都存储在只读数据段中,均作为printf函数的参数。可以看出其对汉字进行utf-8编码。汉字以\347\开头,每个中文字符占三个字符。

(2)整型
        main中定义了整型数i,存储在栈中-4(%rbp),编译器为其分配了4个字节。对于局部变量,只有在程序开始使用它(初始化)的时候才会申请一个内存空间,使用后释放。

(3)数组
程序中有一个数组argv[ ]。argv[1]、argv[2]作为for循环中printf函数的参数。程序对数组的引用体现在下图中:


可见数组元素的地址被保存在栈中,-0x32(%rbp)处存储的是argv[0]的地址。argv[2]是printf函数的第三个参数,而与之对应的寄存器%rdx存储的是-0x16(%rbp)处的内容,因此-0x16(%rbp)处存储的是&argv[2]。同理可得,-0x2A(%rbp)处存储的是&argv[1],-0xe(%rbp)处存储的是&argv[3]。&argv[1],&argv[2]与&argv[3]在栈中都占8个字节。
综合3个数组元素的寻址方式,可见程序对数组元素的寻址被编译器编译为基址+偏移量的寻址模式。
3.3.2各类操作
(1)赋值:程序中涉及的赋值操作有“i=0”。
i是int型,占4个字节,因此采用mov语句赋值。

(2)算术:程序中算术操作主要包括循环变量i++。
编译器将i的自加操作编译如下:

(3)控制:程序中的控制语句主要包括循环中的“i<8”与“argc!=4”。
“i<8”:当i>=8时跳出循环。编译器用cmpl语句与条件跳转语句jle实现。

“argc!=4”:编译器用cmpl语句与条件跳转语句je实现。

可见程序对循环的控制语句、条件分支语句被编译器编译成cmp+jxx的语句组合。
(4)函数调用
在执行call语句之前,汇编代码为被调用分配栈帧空间。编译器将函数的参数保存在各个寄存器或栈中。

调用函数时,调用语句被编译器编译成call xx语句。


当函数运行结束,汇编代码清除栈帧并通过ret语句返回到调用该函数的call语句的下一条语句。

3.4 本章小结
本章简要阐述了编译的概念与作用,演示了编译的过程并对汇编代码文件hello.s进行详细的解析。

第4章 汇编
4.1 汇编的概念与作用
概念:把汇编语言翻译成机器指令,并将其打包成可重定位文件的过程。
作用:将汇编指令转化成机器可逐条执行的机器指令。
    
4.2 在Ubuntu下汇编的命令
as hello.s -o hello.o           
或:gcc -c hello.s -o hello.o

4.3 可重定位目标elf格式
1.ELF Header:包含了系统信息,编码方式,ELF头大小,节的大小和数量等信息。
2.Section Headers:描述.o文件中各个节的名字、类型、地址、大小等信息。

3.Program Headers:描述.o文件中每一个段在文件中的位置、大小以及放进内存后的位置、大小等信息。

4.Relocation section:描述了不同节的重定位信息。

5.Symbol table:描述了.o文件中的函数和全局变量信息。

4.4 Hello.o的结果解析
对Hello.o反汇编的结果:


Hello.s的内容:

对比发现,对Hello.o反汇编的结果与Hello.s中的汇编代码的总体框架、语句基本一致。反汇编结果左侧标注了各条机器指令的地址,机器指令以16进制显示。主要区别在于,反汇编结果中,跳转语句的目标是具体的地址,函数调用语句call后面是具体的函数地址,而Hello.s中跳转语句的目标是L2,L3等标号,call语句后面只是函数名。
4.5 本章小结
本章简述了汇编的概念与作用,熟悉了汇编的基本指令,通过readelf语句解析了.o文件,并将其反汇编与原始的.s进行比较。


第5章 链接
5.1 链接的概念与作用
概念:将多个可重定位文件拼接成可执行文件的过程。
作用:将大型程序分解成更小、更好管理的模块,并能独立地修改、编译这些模块,降低了编程的难度。
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/5/crtbegin.o hello.o -lc /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/x86_64-linux-gnu/crtn.o -z relro -o hello


5.3 可执行目标文件hello的格式
命令:readelf -a hello > elf_hello.txt  
文件头表:type为EXEC表面这是一个可执行文件

节头部表:声明了所有节的信息(包括节的大小、地址偏移量等)

重定位表:

符号表:


5.4 hello的虚拟地址空间
可知hello的虚拟地址开始于0x00005616859c7000,结束于0x00005616859c8000

根据节头部表,可在edb中找到各个节的虚拟地址。
例如:.txt节,虚拟地址开始于0x00005616859c7550,大小为0x132.

.init节,虚拟地址开始于0x00005616859c74c0,大小为0x17

以此类推。
5.5 链接的重定位过程分析


比较hello与hello.o的不同:
1.hello的反汇编代码比hello.o的反汇编代码多了init,.plt,puts@plt等库函数。
2.hello1的反汇编代码中函数的地址为虚拟内存地址,而hello.o的反汇编代码中的地址均为相对地址。
链接的过程:
1.符号解析:目标文件定义和引用符号,每个符号对应于一个函数、一个全局变量或一个静态变量。符号解析将每个符号引用与一个符号定义关联起来。
2.重定位:链接器通过把每个符号定义与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,使它们指向这个内存地址。
5.6 hello的执行流程
函数名    程序地址
_init    0x00005616859c74c0
_start    0x00005616859c70d0
__libc_csu_init    0x00005616859c7190
_init    0x00005616859c74c0
main    0x00005616859c7105
puts@plt    0x00005616859c7080
exit@plt    0x00005616859c70b0
_fini    0x00005616859c7208

5.7 Hello的动态链接分析
动态链接的概念:把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。虽然动态链接把链接过程推迟到了程序运行时,但是在形成可执行文件时,还是需要用到动态链接库。
调用共享库函数时,编译器无法预测这个函数的运行时地址,因为定义它的共享模块在运行时可以加载到任意位置。通常编译器为该引用生成一条重定位记录,然后动态链接器在程序加载的时候再解析它。GNU编译系统使用延迟绑定(lazybinding),将过程地址的绑定推迟到第一次调用该过程时。
延迟绑定通过GOT和PLT实现。GOT是数据段的一部分,而PLT是代码段的一部分。两表内容分别为:
PLT:一个数组,其中每个条目是16字节代码。PLT[0]是一个特殊条目,它跳转到动态链接器中。每个被可执行程序调用的库函数都有它自己的PLT条目,每个条目都负责调用一个具体的函数。
GOT:一个数组,其中每个条目是8字节地址。和PLT联合使用时,GOT[O]和GOT[1]包含动态链接器在解析函数地址时会使用的信息。GOT[2]是动态链接器在1d-linux.so模块中的入口点。其余的每个条目对应于一个被调用的函数,其地址需要在运行时被解析。每个条目都有一个相匹配的PLT条目。
以调用puts函数为例。调用puts函数时,会跳转到puts对应的PLT代码处,执行PLT的第一条指令,第一条指令通过GOT间接跳转,第一次调用时,GOT表内存的是PLT的第二条指令地址,此时会把puts函数的ID压入栈。然后通过GOT间接地把动态链接器的一个参数压入栈中。最后动态链接器使用两个栈条目来确定puts的运行时位置,用这个地址重写对应的GOT表,再运行puts函数。第二次调用puts函数时,执行PLT第一条指令,这时第一条指令通过GOT表进行间接跳转会直接跳转到puts函数。
调用puts函数,实质上是跳转到puts函数的PLT条目处。


最后,查看GOT表在运行do_init前后是否被修改。
运行前:

运行后:

可见do_init执行时修改了GOT表中的内容。经查知该GOT表条目的内容是puts函数真正的地址。

5.8 本章小结
本章简述了链接的概念与作用,熟悉了链接的指令,对可执行文件的虚拟地址空间、重定位过程、执行流程、动态链接过程进行了具体分析。


第6章 hello进程管理
6.1 进程的概念与作用
概念:进程是一个执行中的程序的实例。
作用:进程是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
6.2 简述壳Shell-bash的作用与处理流程 
作用:shell 是一个交互型应用级程序,代表用户运行其他程序。
流程:1.读取用户输入。
2.分析输入内容,获取输入参数。
3.若为内核命令,则直接执行;若为可执行程序的地址,则调用相应程序。
4.程序运行期间,shell监视键盘的输入内容并作出反应。
6.3 Hello的fork进程创建过程
指令输入后,shell对其分析发现不是内置指令,于是会调用fork函数创建一个新的子进程。新建立的子进程几乎但不完全与父进程相同,子进程得到的是与父进程虚拟地址空间相同(但是相互独立)的一份副本,与父进程有不同的PID。运行期间,父子进程任意交替,父进程会默认等待子进程结束并进行回收。
6.4 Hello的execve过程
execve函数调用内存中的启动代码,删除子进程现有的虚拟内存段,设置新的程序栈,并将控制传给Hello的主函数main。程序栈的结构如下:

6.5 Hello的进程执行
在Hello运行时,还有程序在并发地运行,称为并发流。内核采用上下文切换的方式不断地在其他进程与Hello间切换。
上下文切换:
1.保存之前进程的上下文。
2.恢复新恢复进程被保存的上下文。
3.将控制传给新的进程。
    

6.6 hello的异常与信号处理
1.Ctrl+Z:

该操作向hello进程发送一个SIGTSTP信号,将进程暂时挂起,但hello尚未被关闭。输入ps指令后可见hello进程尚存。
2.Ctrl+C

该操作向hello进程发送一个SIGINT信号,进程处理后结束hello并将其回收。可见hello进程已消失。
3.fg指令

可见fg指令可以将已经被挂起的进程转到前台继续运行。
4.jobs指令

jobs指令可以查看当前的关键命令内容。
5.pstree指令

pstree指令用进程树的方法把各个进程用树状图的方式连接起来
6.kill指令

kill指令可以向指定进程发送指定的信号。如ps -9 15534就是向PID为15534的进程发送SIGKILL信号。
6.7本章小结
本章简述了进程的概念与作用,重温了shell-bash的作用与处理流程,并具体分析了Hello的fork、execve过程,Hello的进程执行,检验了Hello的异常与信号处理过程。
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:又称相对地址,由选择符和偏移量组成。
物理地址:CPU 通过地址总线寻址,找到的真实的物理内存对应地址。
线性地址:与虚拟地址是同一概念,是经过段机制转化之后用于描述程序分页信息的地址,是对程序运行区块的一个抽象映射。
7.2 Intel逻辑地址到线性地址的变换-段式管理
逻辑地址由三部分构成:段选择符+段描述符的判别符(TI)+地址偏移量
首先判断段描述符的判别符TI:局部段描述符(ldt) or全局段描述符(gdt)
然后再将该逻辑地址组合成段描述符+地址偏移量的形式。

7.3 Hello的线性地址到物理地址的变换-页式管理
线性地址由两部分组成:虚拟页号VPN+虚拟页偏移量VPO
查找页表,找到与虚拟页号对应的物理页号PPN,最后与虚拟页偏移量VPO结合

7.4 TLB与四级页表支持下的VA到PA的变换
虚拟地址的VPN由两部分构成:TLB标记(TLBT)与TLB索引(TLBI)。TLB是一个快表,作为页表的缓存。
变换地址时,MMU首先根据TLB索引找到对应的TLB组,若有相匹配的标记则命中,找到物理地址。若不命中,VPN将被分成4个部分。MMU在页表中查询,首先从页表机制寄存器PTBR中读出第一级页表的起始地址,VPN1确定在第一级页表中的偏移量,VPN2确定第二级页表的偏移量,VPN3确定第三级页表的偏移量,VPN4确定第四级页表的偏移量,最终查询出 PPN,与VPO组合成PA,并向TLB中添加相应的条目。

7.5 三级Cache支持下的物理内存访问
通过CPU_Z查询可得,L1 Cache 是8路64组相联,块大小为64B。共有64组,因此组索引CI为6 bit;块大小为64B,因此偏移量CO为6bit。PA共52 bit,因此剩余部分CT为40 bit。物理内存访问时,MMU发送PA给L1缓存,高速缓存根据CI找到组,然后根据标记位CT寻找PPN所在的块。若命中,则根据偏移量CO直接找到数据。若不命中,则分别查找缓存L2、L3。若仍不命中,则最终从主存中读取数据。

7.6 hello进程fork时的内存映射
fork为hello进程创建虚拟内存:
1.创建hello进程的的mm_struct, vm_area_struct和页表的原样副本。
hello进程与父进程中的每个页面都标记为只读.
hello进程与父进程中的每个区域结构(vm_area_struct)都标记为私有的写时复制。
2.在新进程中返回时,新进程拥有与调用fork进程相同的虚拟内存。 
3.随后的写操作通过写时复制机制创建新页面
7.7 hello进程execve时的内存映射
execve为hello进程创建虚拟内存:
1.删除已存在的用户区域。
2.创建新的区域结构。代码和初始化数据映射到.text和.data区(目标文件提供),.bss和栈映射到匿名文件。
3.设置PC,指向代码区域的入口点。

7.8 缺页故障与缺页中断处理
1.首先,处理器将虚拟地址发送给 MMU 
2.MMU使用内存中的页表生成PTE地址 
3.有效位为零, 因此 MMU 触发缺页异常 
4.缺页处理程序确定物理内存中牺牲页 (若页面被修改,则换出到磁盘) 
5.缺页处理程序调入新的页面,并更新内存中的PTE 
6.缺页处理程序返回到原来进程,再次执行导致缺页的指令 

7.9动态存储分配管理
动态内存分配器维护着一个进程的虚拟内存区域,称为堆。分配器将堆视为一组不同大小的块的集合来维护,每个块要么是已分配的,要么是空闲的。

分配器的类型:1.显式分配器,要求应用显式第释放已分配的块,如C语言中的malloc与free。2.隐式分配器,应用检测到已分配块不再被程序所使用,就释放 
这个块。如java的垃圾收集器。
以malloc为例:
malloc(size_t size)每次声明内存空间都要保证至少分配size_t大小的内存,保证双字对齐,每次必须从空闲块中分配空间,在申请空间的时候将空闲的空间碎片合并以尽量减少浪费。
记录空闲块的方法:

7.10本章小结
本章主要分析了hello进程的内存管理过程,重温了虚拟地址到物理地址的转换、缓存支持的物理内存访问、虚拟内存映射、缺页处理、动态内存分配的内容。

第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:所有的 IO 设备都被模型化为文件,而所有的输入和输出都被 当做对相应文件的读和写来执行,这种将设备映射为文件的方式,允许 Linux 内核引出一个简单低级的应用接口,称为 Unix I/O。

文件的类型:
1.普通文件(regular file):包含任意数据的文件。
2.目录(directory):包含一组链接的文件,每个链接都将一个文件名映射到一个文件(他还有另一个名字叫做“文件夹”)。
3.套接字(socket):用来与另一个进程进行跨网络通信的文件
4.命名通道
5.符号链接
6.字符和块设备

设备管理:unix io接口
1.打开和关闭文件
2.读取和写入文件
3.改变当前文件的位置
8.2 简述Unix IO接口及其函数
1.open函数:打开一个已存在的文件或创建新文件。打开失败则返回-1。

2.close函数:关闭一个已打开的文件。

3.read函数:从文件中读取字节到指定的内存地址。

4.write函数:往文件中写入字节。若返回值为负数则写入出错。

8.3 printf的实现分析
printf的代码如下:

printf接受一个格式化的命令,并把指定的匹配的参数格式化输出。
观察printf的代码,可见其调用了两个函数vsprintf和write。


vsprintf的代码如下:


可见viprintf将所有的参数内容格式化之后存入buf,然后返回格式化数组的长度。
write则是将buf中的元素写到终端。
因此可得printf的运行流程:
1.vsprintf生成显示信息并将其传送到write系统函数。
2.write函数陷阱-系统调用 int 0x80或syscall.字符显示驱动子程序。
3.显示芯片按照刷新频率逐行读取vram(存储每一个点的RGB颜色信息),并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析


getchar的代码如上图。getchar调用read函数,将缓冲区中用户的输入读入buf数组中。n为read函数的返回值,即缓冲区的输入。若n大于等于1则getchar返回buf数组的第一个元素,否则返回EOF。
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章简要阐述了linux的IO设备管理方法,Unix接口及其函数。同时具体地分析了printf与getchar的实现。
结论
让我们最后来回顾下hello短暂的一生。hello最开始是.c文件,首先经过预处理转成.i文件,之后经过编译转成.s文件,再经过汇编转成.o文件,最后经过链接转成.out文件,也就是可执行文件。我们输入./指令,然后shell创建一个hello子进程。之后我们通过键盘向其传送sigint、sigtstp信号,并输入kill、fg等命令。最后hello运行终止,被父进程回收。
感想:hello程序虽然很简单,但其背后蕴含的计算机系统知识可谓丰富多彩。通过撰写这一篇论文,我得以重新回顾课本知识,学习edb等工具的使用,并加深了对程序运行历程的理解,可谓受益颇丰。
                              附件
hello.c:hello的源代码。
  hello.i:hello.c经过预处理后的产物。
hello.s:hello.i经过编译后的产物。
hello.o:hello.s经过汇编后的产物
  hello.out:hello.o经过链接后的产物。
hello.txt:hello.o反汇编的结果。
参考文献
[1] Advanced Micro Devices, Inc. Software Optimization Guide for AMD64 Processors,
2005.Publication Number 25112
[2] S.Borkar. Thousand cor chips: A tchnology perspective. In Proceedings of the 44th Design Automation Conference, pages 746-749.ACM, 2007
[3]龚奕利. 深入理解计算机系统.北京:机械工业出版社,2016.
[4] printf函数实现的深入剖析:
https://www.cnblogs.com/pianist/p/3315801.html
[5]逻辑地址、线性地址和物理地址之间的转换:
https://blog.csdn.net/gdj0001/article/details/80135196

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章