计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 信息安全
学 号 2022112525
班 级 2203201
学 生 熊锋
指 导 教 师 史先俊
计算机科学与技术学院
2024年5月
摘 要
本文档是关于《程序人生-Hello’s P2P》的大作业报告,针对信息安全专业学生在计算机系统课程中的学习成果进行了展示。报告详细记录了从Hello程序的预处理、编译、汇编到链接的整个流程,以及进程管理、存储管理和IO管理的相关概念与操作。通过对这些计算机系统底层操作的深入解析,展示了程序从源代码到可执行文件,再到运行时的完整生命周期
**关键词:**预处理;编译;汇编;链接;进程管理;存储管理;IO管理;ELF格式;重定位;虚拟地址空间;页式管理;写时复制;Unix I/O接口
**
**
目 录
6.2 简述壳Shell-bash的作用与处理流程 - 10 -
7.2 Intel逻辑地址到线性地址的变换-段式管理 - 11 -
7.3 Hello的线性地址到物理地址的变换-页式管理 - 11 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 11 -
7.7 hello进程execve时的内存映射 - 11 -
第1章 概述
1.1 Hello简介
hello的C语言程序经过预处理,编译,汇编和链接等操作之后,转变为可操作程序。之后,在操作系统管理下,通过fork和execve系统调用并运行了新的进程,在为其分配时间片之后通过CPU执行该程序。操作系统和MMU共同管理虚拟内存和物理内存的映射,利用多级表,TLB和缓存机制来提高内存访问效率。同时,操作系统通过设备驱动和信号处理机制与硬件设备进行交互,确保程序正确输入输出。最后,操作系统回收其占用的所有资源完成020的循环。
1.2 环境与工具
Intel CORE i7 10th GEN
X64 CPU; 2GHz; 16G RAM; 256G HD Disk
Windows11
VMware Workstation pro2022
Ubuntu20.04
Visual Stdio 2019; ClodeBlocks; gedit+gcc;gdb
1.3 中间结果
文件名称 作用
hello.c 储存hello程序源代码
hello.i 源代码经过预处理产生的文件(包含头文件等工作)
hello.s hello程序对应的汇编语言文件
hello.o 可重定位目标文件
hello 二进制可执行文件
1.4 本章小结
简要介绍了hello程序的“一生”,并介绍了实验环境,是总体大纲。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
预处理阶段是第一个阶段,在正式的编译阶段之前进行。预处理阶段将根据已放置在文件中的预处理指令来修改源文件的内容。如#include指令就是一个预处理指令,它把头文件的内容添加到.cpp文件中。
这个在编译之前修改源文件的方式提供了很大的灵活性,以适应不同的计算机和操作系统环境的限制。因为一个环境需要的代码跟另一个环境所需的代码可能有所不同。在许多情况下,可以把用于不同环境的代码放在同一个文件中,再在预处理阶段修改代码,使之适应当前的环境。
2.2在Ubuntu下预处理的命令
用gcc指令即可
2.3 Hello的预处理结果解析
这里预处理出的文本文件,在原c文件的基础上增加了信息,原程序可以在最后几行找到。
预处理会将具体引用的库插入文件中,这里可以看到有stdio.h的基本库都在这里被引用了。后续还能发现头文件中定义的所有宏,数据类型以及函数声明,这里不一一展开说明。
2.4 本章小结
预处理作为第一个处理阶段,对代码的初步转化主要集中在对预处理指令或者是调用的库插入中。这里处理hello.c时因为没有预处理指令所以只进行了对头文件的解释插入。经过这一步,我们得到的是一个优化的源代码,便于进行下一步的编译。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译是指将预处理之后的文件通过词法分析和语法分析,在确保所有的指令符合语法规则的同时将其翻译为等价的中间代码或汇编代码。
主要作用是翻译并优化代码,得到目标机器指令。
3.2 在Ubuntu下编译的命令
3.3 Hello的编译结果解析
3.3.1 int数据类型
在汇编代码中,整型变量在栈上用subq $32, %rsp分配空间
整型变量的值通过 movl 指令进行加载和存储(例如 movl $0, -4(%rbp) 初始化 i 为 0)。
3.3.2 char*数据类型
指针参数 argv 作为 main 函数的第二个参数(%rsi),指向命令行参数数组的首地址。
使用addq $x, %rax计算偏移量和movq (%rax), %rcx内存引用来访问数组中的字符串。
3.3.3 字符串常量:
字符串常量(如 “用法: Hello 学号 姓名 手机号 秒数!” 和 “Hello %s %s %s\n”)被存储在只读数据段(.rodata)中。
使用 leaq .LC0(%rip), %rdi 之类的指令将字符串常量的地址加载到寄存器中。
3.3.4 控制转移
cmpl $5, -20(%rbp) 比较 argc(存储在 -20(%rbp))与 5 是否相等。
je .L2 如果相等,则跳转到 .L2 标签处执行。
3.3.5 函数操作
使用 call 指令调用函数(如 puts@PLT)。
函数参数通过寄存器(如 %rdi、%rsi)或栈上的空间传递。
3.3.6 逻辑循环操作
for 循环通过 cmpl $9, -4(%rbp) 和 jle .L4 指令实现循环条件的判断。
循环体内部多次调用printf 和 sleep
以及通过addl $1, -4(%rbp)更新循环变量
3.3.7 指针操作
本程序使用 movq、movl 等指令加载和存储内存数据。
用计算数组元素或结构体的偏移量来实现指针运算(如 addq $x, %rax)。
3.3.8 类型转换
用atoi 函数将字符串(argv[4])转换为整数。
3.4 本章小结
编译操作遵循C语言语义语法,能正确处理数据类型,实现源代码中例如循环,打印字符串的操作。具体执行这些汇编代码就能模拟出真正所需C语言程序的功能,但是,还需要下一步的汇编使得代码进一步成为可重定位目标文件。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
汇编将汇编语言代码转化为机器语言目标代码,汇编语言作为一种低级编程语言,需要通过汇编这一步来转换为机器可以理解的硬件操作指令。
汇编语言由于允许程序员直接操作访问且接近机器代码,因此汇编在实际运用中通常被用于编写底层系统代码。
4.2 在Ubuntu下汇编的命令
应截图,展示汇编过程!
4.3 可重定位目标elf格式
用readelf -h hello.o查看ELF头
ELF格式以一个magic序列开始,这个序列的内容表明了系统的字大小和使用的存储法。ELF剩下的内容则是其他信息,包括ELFA的大小,数据的格式以及所使用的汇编语言版本等。
用命令readelf -S hello.o查看节头
这里出现的每个节头名称都有自己对应的意义
- .text:包含程序的执行代码。
- .data:包含已初始化的全局和静态变量。
- .bss:为未初始化的全局和静态变量保留空间(在目标文件中不占用空间,但在内存中会分配)。
- .rela.text:包含与 .text 节中的符号相关的重定位信息。
- .comment:包含有关文件的注释信息(通常是编译器版本和编译时间戳)。
- .symtab 和 .strtab:分别包含符号表和与符号相关的字符串表,用于调试和链接。
用命令readelf -s hello.o查看符号表
这个符号表提供了关于 hello.o 中定义的符号和引用的外部符号的信息。在链接过程中,链接器会使用这些信息来解析符号引用,并将所有目标文件组合成一个可执行文件或共享库。
用readelf -r hello.o查看可重定位段信息
这里的重定位段信息是文件的重定位表
这里的信息表示以下含义:
- 偏移量:该引用在目标文件中的位置
- 信息:这是一个包含类型(Type)和符号表索引(Symbol Table Index)的字段。类型描述了重定位的类型,而符号表索引则指向符号表中该符号的条目。
- 符号值(Symbol Value):在链接过程中,这个值将被替换为符号的实际地址。在目标文件中,它通常是一个占位符或默认值。
- 加数(Addend):在某些类型的重定位中,可能需要一个额外的加数值来调整符号的最终地址。
4.4 Hello.o的结果解析
4.4.1操作数
在汇编语言中,我们经常使用符号名(如变量名、函数名等)来引用数据或地址。但在机器语言中,这些符号名被替换为实际的内存地址或立即数值。例如,在hello.s中,puts和exit等函数调用通过符号名引用,但在机器代码中,它们被替换为相对于当前指令指针(RIP)的偏移地址(如R_X86_64_PLT32 puts-0x4)。
4.4.2分支转移
分支转移指令(如je、jle等)用于改变程序的控制流。在hello.s中,我们可以看到使用je指令进行条件跳转(如果等于则跳转)。在机器代码中,这个指令被编码为一个特定的操作码,并后跟一个偏移量,该偏移量指定了跳转的目标地址。
4.4.3函数调用
函数调用在汇编语言中通过call指令实现。在机器代码中,call指令被编码为一个操作码,并后跟一个目标地址(或者是一个相对于当前指令指针的偏移量)。这个地址指向了被调用函数的入口点。此外,为了支持动态链接和重定位,hello.o中的函数调用地址可能是相对于某个基准点的偏移量(如R_X86_64_PLT32),这些偏移量将在链接过程中被解析为实际的函数地址。
4.4.4重定位
在hello.o这样的目标文件中,很多地址和符号引用都是相对于某个基准点的。这些引用在链接阶段需要被解析为实际的内存地址。重定位信息(如R_X86_64_PC32和R_X86_64_PLT32)提供了必要的元数据,以便链接器可以正确地解析这些引用。
4.5 本章小结
通过对照分析hello.s和hello.o的反汇编输出,我们可以更深入地理解汇编语言与机器语言之间的映射关系,以及接下来的链接过程中如何处理符号引用和重定位信息。
(第4章1分)
第5章 链接
5.1 链接的概念与作用
链接是将多个单独编译的对象文件组合成一个可执行文件的过程,由连接器执行。
最重要的作用也自然是组合多个对象文件,生成可执行文件了,这个过程中连接器读取可重定位目标文件里的符号表的相对地址从而将其装换为可执行文件中的绝对地址,还要解析每个文件中的符号引用,使其能够正确地执行程序。
还有类似于优化代码之类的作用,主要通过删除不用的代码和数据实现。
5.2 在Ubuntu下链接的命令
5.3 可执行目标文件hello的格式
在ELF头中明显发生了变化,变成了EXEC,节头部数量也增加到了27个
27个节头部都有了声明,并且这里计算出的位置是最终确定的位置。确保能够正确引用这个表中的每个节。
符号表此时包括了许多其他库函数的符号,这是链接的作用之一。
、最后查看重定位段信息
5.4 hello的虚拟地址空间
用edb查看虚拟空间地址,5.3相对应的空间可以查找到init节对应的虚拟空间位置,起始空间为0x401000有对应。
5.5 链接的重定位过程分析
当链接器将hello.o和其他必要的文件(如C库)链接成hello时,它会解析这些符号引用,并将它们替换为实际的地址。这就是链接过程中的“重定位”部分。因此hello与hello.o的主要区别在于,hello包含了所有必要的符号解析和重定位信息,使得程序能够直接执行。而hello.o则可能包含对外部符号的引用,这些引用在链接过程中被解析和重定位。链接器通过更新.plt和其他相关结构中的指令和数据,将hello.o中的外部符号引用替换为实际的地址,从而生成了可执行的hello文件。5.6 hello的执行流程
一.程序启动(_start):
地址: 0x4010f0
当程序被操作系统加载并准备执行时,首先会跳转到_start符号处。这是程序的入口点,通常由编译器和链接器自动设置。在_start中,程序会进行基本的设置,如初始化堆栈和调用_libc_start_main。
二.C运行时库初始化(_libc_start_main):
地址: 0x7ffff7de2f90
_libc_start_main是C运行时库提供的函数,用于在调用main函数之前执行一些必要的初始化工作。这包括设置全局变量、调用初始化函数(如__libc_csu_init)以及处理程序的终止(如调用__GI___cxa_atexit注册退出函数)。
三.全局构造函数和C++运行时初始化(__libc_csu_init):
地址: 0x4011c0
__libc_csu_init通常用于调用全局构造函数和C++运行时库的初始化函数。这是程序启动过程中的一个重要步骤,因为它确保了在main函数执行之前,所有全局变量和对象都已正确初始化。
四.主函数(main):
地址: 0x401125
main函数是C和C++程序的入口点。在这里,程序执行用户定义的代码,包括变量声明、函数调用和循环等。main函数的返回值通常用于指示程序的退出状态。
五.其他函数(如strcmp):
这些函数(如strcmp)是标准库函数,用于执行常见的任务,如字符串比较。它们被程序在需要时调用,以简化代码并提高可重用性。
六.程序终止:
当main函数返回时,C运行时库会负责执行清理工作,如调用在_libc_start_main中注册的退出函数以及释放分配的资源。然后,控制权会返回到操作系统,程序终止执行。
5.7 Hello的动态链接分析
静态连接就是把上面绝对地址跳转和相对地址跳转全部写死,整个程序作为一台精密的机器在运行。
而动态连接目的就是无论A程序的代码加载到哪个地址。B,C,D…程序都能访问到它。(我们把A程序 叫做共享对象,B,C,D程序叫做可执行程序。
用ldd ./hello查看动态库
5.8 本章小结
本章解释了链接的过程,从重定位到动态链接,完成了p2p的过程。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
进程是计算机中程序关于某数据集合上的一次运行活动。
进程的作用有分配资源,执行程序。
6.2 简述壳Shell-bash的作用与处理流程
Shell-bash是用户与操作系统之间交互的桥梁,允许用户通过输入命令来执行各种操作。
bash Shell读取用户输入的命令,解析命令内容,查找并执行相应的程序,然后将结果返回给用户并显示在终端上。
6.3 Hello的fork进程创建过程
程序运行时,Shell会创建一个新的进程,并为新进程分配独立的上下文。这个新进程可以运行可执行目标文件。
在这个过程中,fork() 函数会创建一个新的进程,并返回一个整型值。在子进程中,fork 返回0,而在父进程中,fork 返回子进程的 PID。新创建的进程和父进程几乎相同,但有一些细微的差别。
子进程获得了父进程的虚拟地址空间的独立副本(包括代码、数据段、堆、共享库和用户栈),并且拥有与父进程不同的 PID。
在 Hello 程序中,fork() 函数创建了一个新进程,并为新进程分配了独立的上下文,使得新进程可以运行可执行目标文件。
6.4 Hello的execve过程
在Linux系统中,execve 函数用于在当前进程的上下文中加载并运行一个新的程序。对于Hello程序,execve 过程如下:
- 当调用 execve 函数时,它会加载并执行指定的可执行文件(在这个例子中是Hello程序)。
- execve 函数会覆盖当前进程的代码段、数据段、堆和栈,用新程序的内容替换它们。
- 新程序的代码、数据、堆和栈会被映射到当前进程的虚拟地址空间中。
- execve 函数不会创建新的进程,而是用新程序替换当前进程的内容。
- 当新程序开始执行时,它会从入口点(通常是 main 函数)开始执行。
总结一下,execve 函数在Hello程序中的作用是加载并执行Hello程序,用Hello程序的内容替换当前进程的内容,并在当前进程的上下文中运行Hello程序。
6.5 Hello的进程执行
进程调度涉及创建、就绪、选中Hello程序执行,用户态-核心态切换以执行和管理。时间片机制控制CPU分配,上下文切换在进程间进行。Hello程序执行后,核心态切换至用户态并退出,释放资源。
6.6 hello的异常与信号处理
对无效输入没有反应,Ctrl-z会直接退出。
Kill进程会直接结束
Jobs指令会给出进程表
这是Linux终端(TTY)中 ps 命令的输出,显示了当前终端(pts/0)中运行的进程信息。每一行代表一个进程
pstree 是一个在 Linux 和类 Unix 系统中用来显示进程树的工具,它会展示一个树形结构,显示当前进程及其子进程之间的关系。
输入fg会让已暂停的进程继续
6.7本章小结
本章详细论述了进程的产生过程,执行的顺序,以及常见的各种面向进程的指令的作用。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
在讨论 hello 进程及其存储器地址空间时,我们需要谈到以下几个概念:
- 逻辑地址:从应用程序角度看到的内存单元或存储单元地址。这些地址是程序员在编写程序时使用的,但它们不一定直接映射到物理内存中。
- 线性地址:这是逻辑地址到物理地址变换过程中的中间层。在分段机制中,逻辑地址是段中的偏移地址,加上基地址后,得到线性地址。
- 虚拟地址:当 CPU 启动保护模式时,进程会运行在虚拟地址空间中。对于 hello 进程,它所访问的存储器地址就是虚拟地址。
- 物理地址:这是在地址总线上以电子形式存在的地址,使得数据总线可以访问主存中的特定存储单元。
当 hello 进程运行时,它会使用虚拟地址来访问存储器。这些虚拟地址经过地址翻译器或映射函数的转换,最终会映射到物理内存中的某个位置。在这个过程中,虚拟地址首先被转换为线性地址,然后再将线性地址转换为物理地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
在Intel CPU中,逻辑地址(由段选择器和偏移量组成)被转换为线性地址的过程如下:
- 使用段选择器查找 GDT/LDT 中的段描述符。
- 从段描述符中获取段的基地址。
- 将基地址与偏移量相加,得到线性地址。
这样可以确保每个进程拥有独立的地址空间,并提供内存保护机制
7.3 Hello的线性地址到物理地址的变换-页式管理
在页式内存管理中,线性地址到物理地址的变换过程如下:
- 线性地址被分为三个部分:页目录索引、页表索引和页内偏移。
- 使用页目录索引查找页目录表,找到对应的页表地址。
- 使用页表索引在页表中查找,得到物理页的起始地址。
- 将物理页的起始地址与页内偏移相加,得到最终的物理地址。
这种机制允许操作系统更灵活地管理内存,实现虚拟内存和内存保护。
7.4 TLB与四级页表支持下的VA到PA的变换
TLB (Translation Lookaside Buffer) 是一个硬件缓存,用于加速虚拟地址到物理地址的转换。在 TLB 中存储了最近使用过的虚拟地址到物理地址的映射,可以快速查找这些映射,避免每次访问内存时都需要进行完整的页表查找。
在四级页表支持下的 VA 到 PA 的变换过程如下:
- 首先,检查 TLB 中是否存在该 VA 的映射。
- 如果 TLB 中存在,直接使用 TLB 中的映射,否则需要进行页表查找。
- 在页表查找过程中,按照如下步骤进行: a. 使用 VA 的最高 10 位作为页目录索引,查找页目录表,找到对应的页表地址。 b. 使用 VA 的中间 10 位作为页表索引,在页表中查找,得到物理页的起始地址。 c. 使用 VA 的最低 12 位作为页内偏移,与物理页的起始地址相加,得到最终的 PA。
- 如果 TLB 未命中,将新的 VA 到 PA 映射存储在 TLB 中,以备后续使用。
7.5 三级Cache支持下的物理内存访问
在三级缓存(L3 Cache)支持下的物理内存访问,通常发生在以下情况下:
- 内存层次结构:计算机的内存通常分为多个层次,L1、L2 和 L3 缓存是其中的高速缓存,L3 Cache 位于处理器内部,靠近处理器核心,用于缓存那些最近和最常访问的数据。
- 缓存命中:当处理器需要访问数据时,首先会在 L3 Cache 中查找。如果数据已经存在缓存中(即缓存命中),则访问速度非常快,几乎瞬时完成,因为缓存访问速度远超内存。
- 缓存替换策略:如果 L3 Cache 中的数据被新数据替换(缓存替换),那么被替换的数据需要回写到内存中。当再次需要这些数据时,它们可能需要从内存重新加载到 L3 Cache。
- 缓存未命中:如果 L3 Cache 未找到数据,处理器会继续查询 L2 Cache,如果再未命中,会去主内存(DRAM)查找。这个过程会比缓存命中慢得多,因为内存访问速度较慢。
7.6 hello进程fork时的内存映射
当一个进程通过`fork()`创建子进程时,子进程会继承父进程的内存映射。这些映射包括文件映射和匿名映射。为了效率,子进程和父进程最初共享相同的物理内存页,这些页被标记为只读。如果任一进程尝试写入这些共享页,内核会触发页面错误,并创建该页的私有副本,这个过程称为写时复制(Copy-on-Write, COW)。这样,父子进程可以在不互相影响的情况下独立修改内存。如果通过`exec()`执行新程序,原有的内存映射会被新程序的映射取代。
7.7 hello进程execve时的内存映射
`hello`进程在执行`execve()`时,它的内存映射包括:代码段(只读)、初始化数据段、未初始化数据段、堆、栈、环境变量表和命令行参数表。这些内存区域都有自己的特定用途,并在进程执行时发挥不同的作用。在执行`execve()`函数时,原有的内存映射会被新的可执行文件的内存映射所替代。
7.8 缺页故障与缺页中断处理
当一个进程需要访问的内存页不在物理内存中时,就会发生缺页故障(Page Fault)。当缺页故障发生时,CPU会触发一个缺页中断(Page Fault Interrupt),然后控制权会转移到操作系统的内核中。
内核会保存当前进程的上下文,包括寄存器的值和程序计数器的值。内核会检查缺页请求是否合法,如果请求的内存页不在虚拟内存空间中,或者请求的内存页被保护起来,内核会返回一个错误。如果请求合法,内核会分配一个物理内存页框给进程。如果请求的内存页在磁盘上,内核会将其读入到刚刚分配的页框中。内核会修改页表,将虚拟内存页映射到物理内存页框上。内核会恢复进程的上下文,将控制权返回给进程。进程会重新执行导致缺页故障的指令,这次访问内存时不会再发生缺页故障。
7.9动态存储分配管理
动态内存管理是指在程序运行时请求和释放内存,是计算机程序设计中重要的一项技术。C语言中常用的动态内存管理函数包括malloc和free。
malloc用于动态分配一片连续的内存空间,其原型为void* malloc(size_t size),返回一个指向分配内存的指针,如果分配失败则返回NULL。使用malloc分配的内存未被初始化,其中存储的值是未定义的。
7.10本章小结
本章节我们讨论了进程存储器地址空间的几个概念,包括逻辑地址、线性地址、虚拟地址和物理地址。 TLB 和四级页表的虚拟地址到物理地址的变换,最后,我们介绍了缺页故障和缺页中断处理的过程,以及动态内存分配管理的相关函数 malloc。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
将所有I/O设备都模拟为文件,并使用统一的文件接口(Unix I/O)进行输入/输出操作。这种抽象使得应用程序开发更加简单,因为程序员可以使用一组已知的、统一的系统调用(如open、read、write、close等)来处理各种类型的I/O请求。8.2 简述Unix IO接口及其函数
Unix I/O 是 Unix/Linux 系统中进行 I/O 操作的接口,将所有 I/O 设备视为文件,提供一组简单、统一的函数来进行 I/O 操作,包括 `open`、`close`、`read`、`write`、`lseek`、`fcntl` 和 `ioctl` 等函数。这些函数可以用于控制文件描述符的属性、移动文件指针、读写文件等操作,使得应用程序开发更加简单和高效。
8.3 printf的实现分析
printf函数的实现首先通过解析格式化字符串生成要显示的信息,然后通过系统调用write将信息输出到标准输出。在内核中,数据被传递到字符显示驱动程序,将字符转换为屏幕上的像素,并存储在视频RAM中。最后,显示芯片按照刷新频率逐行读取VRAM,并通过信号线向液晶显示器传输每一个点,显示出相应的图像。8.4 getchar的实现分析
getchar函数的运作原理是基于键盘中断处理和系统调用。当用户按下键盘,引发中断后,系统会将按键的扫描码转换成ASCII码,存入键盘缓冲区。getchar函数通过调用read系统函数,持续从缓冲区读取字符,直到遇到回车键才停止并返回读取的ASCII码。这个过程允许程序接收用户的输入,并在接收到特定字符后返回给调用者。
8.5本章小结
本章介绍了 Linux 系统中的 I/O 管理方法,包括将所有 I/O 设备都模拟为文件的方法,以及使用统一的文件接口(Unix I/O)进行输入/输出操作。此外,本章还简述了 printf 和 getchar 函数的实现原理。总的来说,这一章介绍了 Linux 系统中的 I/O 管理方法,以及一些常用的 Unix I/O 函数,帮助读者更好地理解和使用 Linux 系统中的 I/O 操作。
(第8章1分)
结论
通过本次大作业的实践,我深刻体会到了计算机系统底层操作的复杂性和精细性。从源代码到可执行文件的转变,每一步都充满了挑战和学习的机会。进程管理、存储管理和IO管理作为计算机系统的重要组成部分,它们的设计和实现直接关系到程序的性能和系统的稳定性。在未来的学习和工作中,我将继续深入探索计算机系统的奥秘,不断提升自己的技术能力和创新能力。同时,我也希望能够将本次学习的成果应用到实践中,为解决实际问题贡献力量。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
文件名称 作用
hello.c 储存hello程序源代码
hello.i 源代码预处理文件
hello.s hello程序汇编语言文件
hello.o 可重定位目标文件
hello 二进制可执行文件
(附件0分,缺失 -1分)
参考文献
为完成本次大作业你翻阅的书籍与网站等
[1]预处理、编译、汇编和链接https://blog.csdn.net/Zhubingge/article/details/105202116
[2]ELF文件介绍
[3]linux动态链接
[4] Linux fork 写时复制
Linux fork 写时复制_fork写时拷贝-CSDN博客
[5] https://www.cnblogs.com/pianist/p/3315801.html
[6] 深入理解getchar函数:从键盘读取字符的底层机制-百度开发者中心 (baidu.com)
(参考文献0分,缺失 -1分)
时复制_fork写时拷贝-CSDN博客](https://blog.csdn.net/weixin_45030965/article/details/124035512?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171725485016800184127650%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=171725485016800184127650&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-2-124035512-null-null.142%5ev100%5epc_search_result_base1&utm_term=%E5%86%99%E6%97%B6%E5%A4%8D%E5%88%B6&spm=1018.2226.3001.4187)
[5] https://www.cnblogs.com/pianist/p/3315801.html
[6] 深入理解getchar函数:从键盘读取字符的底层机制-百度开发者中心 (baidu.com)
(参考文献0分,缺失 -1分)