HIT-CSAPP大作业
计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 *
学 号 *
班 级 *
学 生 *
指 导 教 师 *
计算机科学与技术学院
2020年3月
摘 要
本文通过hello程序实例对《深入了解计算机系统》知识主线进行总结梳理,阐释分析了Linux系统下hello.c程序从 Program到Process,从无到有再到无的过程。运用了多种调试手段和工具,从计算机系统的角度解释了Linux系统对典型C语言程序的处理机制,有助于加深初学者对于计算机软硬件系统的初步认识。
关键词:计算机系统;Linux;程序生命周期;
目 录
2.2在Ubuntu下预处理的命令............................................................................. - 5 -
5.3 可执行目标文件hello的格式........................................................................ - 8 -
6.2 简述壳Shell-bash的作用与处理流程........................................................ - 10 -
6.3 Hello的fork进程创建过程......................................................................... - 10 -
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 -
8.2 简述Unix IO接口及其函数.......................................................................... - 13 -
Hello是一个简单的C语言程序(Program),在接收到正确的参数后,将会输出“Hello 学号 姓名 秒数” 。从程序运行到结束,大致有如下过程:
P2P(From Program to Process):
在Linux系统(本次采用Ubuntu系统)下在终端中输入命令:
gcc -m64 -Og -no-pie -fno-PIC hello.c -o hello
hello.c将通过编译器驱动程序,经历预处理器、编译、汇编、链接四个过程,得到可执行文件hello。hello运行时由OS调用fork函数生成进程。以上就是从Program到Process的过程,即P2P。
O2O(From Zero-0 to Zero-0):
在上述过程之后, shell用execve函数调用函数运行程序为hello映射虚拟内存以及载入物理内存。在进入main函数后cpu为我分配时钟周期从而执行我的控制流。程序结束之后shell让父进程来回收我。删除我在linux内核中的数据。
再通过段式管理、页式管理以及各种存储器联动,CPU为hello分配时间周期,执行控制流。程序结束时,将由shell回收hello的进程,之后hello就如同“轻轻地我走了,正如我轻轻地来,挥一挥手,不带走一片云彩”,完成从无到有,再到无的过程,即O2O。
硬件环境:Intel(R) Core(TM) i5-8300H X64 CPU;2.30GHz;8.00GB RAM;256GHD Disk以上
软件环境:Windows10 64位;VirtualBox/Vmware 11以上;Ubuntu 16.04 LTS 64位
开发工具:CodeBlocks;vi/vim/gpedit+gcc;Visual Studio 2010 64位以上;GDB/OBJDUMP;DDD/EDB等
-
- 中间结果
中间结果文件 |
文件的作用 |
hello.i |
预处理后的文本文件 |
hello.s |
编译后汇编程序文本文件 |
hello.o |
汇编后的可重定位目标程序 |
hello |
链接后的可执行目标文件 |
hello_elf.txt |
hello.o的ELF格式 |
hello_elf_2.txt |
hello的ELF格式 |
hello_objdump.txt |
hello.o的反汇编代码 |
hello_objdump_2.txt |
hello的反汇编代码 |
本章简要介绍了大作业的背景——针对hello程序的探究。简述了探究的流程:P2P和O2O,并介绍了大作业的环境和工具,以及中间结果文件。
概念:预处理是C语言程序从源代码变成可执行程序的第一步,是在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。由预处理器(preprocessor) 对程序源代码进行处理,再由编译器进一步编译。
作用:预处理把源代码分割或处理成为特定的单位——预处理记号(preprocessing token)用来支持语言特性(如C/C++的宏调用)。
预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。下面是部分预处理指令:
指令 用途
# 空指令,无任何效果
#include 包含一个源代码文件
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前#if条件不为真,当前条件为真,则编译下面代码,其实就是else if的简写
#endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息
hello.c经预处理后得到中间结果hello.i
在Ubuntu系统终端中输入命令:
gcc -m64 -no-pie -fno-PIC -E -o hello.i hello.c
将hello.c预处理生成hello.i
查看预处理生成文件hello.i,发现该文件相对于hello.c,修改的是程序开头以#开头的预处理命令,对于hello.c,即
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
本章介绍了预处理的概念、作用以及在Ubuntu下通过gcc指令进行预处理的方法,通过对hello.c进行预处理,将hello.c与hello.i进行对比说明了预处理阶段做了怎样的处理。
概念:编译是将高级语言转换为汇编语言的过程。程序编译之前,C语言编译器会进行词法分析、语法分析(-fsyntax-only),接着会把源代码翻译成中间语言,即汇编语言。
作用:编译后生成的.s文本文件是汇编语言程序,更容易让计算机理解,编译是将程序转换为机器指令的中间过程。
在Ubuntu系统终端中输入命令:
gcc -m64 -no-pie -fno-PIC -S -o hello.s hello.i
将hello.i编译生成hello.s
查看编译生成的hello.s,针对其对于各个数据类型以及各类操作的处理进行分析:
3.3.1 数据
从hello.c程序代码可知,源文件中的数据有
字符串常量:
"用法: Hello 学号 姓名秒数!\n"
"Hello %s %s\n"
变量:
i、argc、argv
i在栈中使用,地址为 -4(%rbp)
argc是传入参数,存放在寄存器%edi中
3.3.2 数组操作
数组:main()函数的传入参数中的argv[]
由汇编代码可知:argv地址存放在%rsi中
3.3.3 函数操作
由程序源代码可知,存在对printf(),exit()以及getchar()的调用
exit():
getchar():
printf():
3.3.4 运算操作
由程序源代码可知,在for(i=0;i<8;i++)对i进行运算操作
汇编语句中采用了addl和cmpl指令进行操作
3.3.5 赋值操作
由程序源代码可知,赋值操作出现了1次,在for循环中
for(i=0;i<8;i++)
汇编语句中采用了movl指令进行操作
3.3.6 关系判断
由程序源代码可知,关系判断出现了2次,分别在if(argc!=4)和for(i=0;i<8;i++)
if(argc!=4):
for(i=0;i<8;i++):
3.3.7控制转移
由程序源代码可知,控制转移出现在if语句以及for循环中
if语句:
for循环:
本章介绍了编译的概念、作用以及在Ubuntu下通过gcc指令对.i文件进行编译的方法,通过对hello.i进行预处理得到hello.s,将hello.c与hello.s进行对比,选取hello.c中C语言的典型数据类型以及各类操作说明了编译阶段做了怎样的处理。
概念:是把作为中间结果的汇编代码翻译成机器代码,即目标代码的过程,由.s文件得到.o文件。
作用:从汇编指令进一步得到CPU可以执行的机器指令
在Ubuntu系统终端中输入命令:
gcc -m64 -no-pie -fno-PIC -c -o hello.o hello.s
将hello.s编译生成hello.o
在Ubuntu系统终端中输入命令:
readelf -a hello.o>hello_elf.txt
可查看hello.o的ELF格式
可知有如下部分:
4.3.1 ELF头
文件头的结构定义如下所示:
typedef struct {
unsigned char e_ident[16]; /* ELF魔数,ELF字长,字节序,ELF文件版本等 */
Elf32_Half e_type; /*ELF文件类型,REL, 可执行文件,共享目标文件等 */
Elf32_Half e_machine; /* ELF的CPU平台属性 */
Elf32_Word e_version; /* ELF版本号 */
Elf32_Addr e_entry; /* ELF程序的入口虚拟地址,REL一般没有入口地址为0 */
Elf32_Off e_phoff;
Elf32_Off e_shoff; /* 段表在文件中的偏移 */
Elf32_Word e_flags; /* 用于标识ELF文件平台相关的属性 */
Elf32_Half e_ehsize; /* 本文件头的长度 */
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize; /* 段表描述符的大小 */
Elf32_Half e_shnum; /* 段表描述符的数量 */
Elf32_Half e_shstrndx; /* 段表字符串表所在的段在段表中的下标 */
} Elf32_Ehdr;
4.3.2节头
ELF文件中有很多段,节头就是保存这些段的基本属性的结构。段表是ELF文件中出文件头以为最重要的结构,它描述了ELF的各个段的信息,如每个段的名字,段的长度,在文件中的偏移,读写权限等。ELF文件的段结构就是由段表决定的,编译器,链接器和装载器都是通过段表来定位和访问各个段的属性的。
描述段的结构如下所示:
typedef struct
{
Elf32_Word sh_name; /* 段的名字 */
Elf32_Word sh_type; /* 段的类型,代码段,数据段,符号表等 */
Elf32_Word sh_flags; /* 段在进程虚拟地址空间中的属性 */
Elf32_Addr sh_addr; /* 段的虚拟地址 */
Elf32_Off sh_offset; /* 段在文件中的偏移 */
Elf32_Word sh_size; /* 段的长度 */
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign; /* 段地址对齐 */
Elf32_Word sh_entsize;
} Elf32;
4.3.3重定位表
链接器在处理目标文件时,需要对目标文件中某些部位进行重定位,及代码段和数据段中那些对绝对地址引用的位置。这些重定位的信息都记录在ELF文件的重定位表里面,对于每个需要重定位的代码段或数据段,都会有相应的重定位表。
4.3.4符号表
ELF文件中用到了很多符号,比如段名,变量名等。因为字符串的长度往往不是固定的,故难以用固定的结构表示。常见的做法是把字符串集中起来存放到一个表,然后使用字符串在表中的偏移来引用字符串。
在Ubuntu系统终端中输入命令:
objdump -d -r hello.o
对hello.o反汇编,与hello.s对比发现
1.hello.s前没有相应的机器码,反汇编代码前面有对应的机器码
2. hello.s中列出了把每个段的段名,反汇编代码则没有
3. hello.s中,函数调用之后跟随其函数名,反汇编代码则没有
4.hello.s中操作数采用的是十进制,反汇编代码中采用的是十六进制
本章介绍了汇编的概念、作用以及在Ubuntu下通过gcc指令对.s文件进行汇编的方法,通过对hello.s进行预处理得到hello.o,分析hello.o的ELF格式,并将hello.o与hello.s进行对比说明区别。
概念:链接是处理可重定位文件,把它们的各种符号引用和符号定义转换为可执行文件中的合适信息(一般是虚拟内存地址)的过程。链接又分为静态链接和动态链接,前者是程序开发阶段程序员用ld(gcc实际上在后台调用了ld)静态链接器手动链接的过程,而动态链接则是程序运行期间系统调用动态链接器(ld-linux.so)自动链接的过程。
作用:链接将汇编语言代码彻底转化为机器代码,把可重定位目标文件和命令行参数作为输入,生成可执行目标文件。
在Ubuntu系统终端中输入命令:
ld -o hello -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 hello.o /usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o
链接生成可执行文件hello
在Ubuntu系统终端中输入命令:
readelf -S hello
查看hello的ELF格式
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
由图可知,虚拟空间从0x400000开始。
通过“左上方选项卡 - Plugins - SymbolViewer”查看本进程的虚拟地址空间各段信息,与5.3对照分析可知一致。
在Ubuntu系统终端中输入命令:
objdump -d -r hello
对可执行目标文件hello进行反汇编
对比发现,hello 和 hello.o 反汇编生成的代码完全相同,二者的区别在于:后者地址是相对偏移,前者地址是可由 CPU 直接访问的虚拟地址。
这说明链接器把 hello.o 中的偏移量加上程序在虚拟内存中的起始地址得到了可直接访问的地址。
使用edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。
0000000000400468 <_init>:
0000000000400490 <[email protected]>:
00000000004004a0 <[email protected]>:
00000000004004b0 <[email protected]>:
00000000004004c0 <[email protected]>:
00000000004004d0 <[email protected]>:
00000000004004e0 <[email protected]>:
00000000004004f0 <[email protected]>:
0000000000400500 <[email protected]>:
0000000000400510 <.plt.got>:
0000000000400520 <_start>:
000000000040054a <main>:
00000000004005e0 <__libc_csu_init>:
0000000000400650 <__libc_csu_fini>:
0000000000400654 <_fini>:
对于动态共享链接库中PIC函数,编译器添加重定位记录,等待动态链接器处理,同时,链接器采用延迟绑定的方法。
可以看出,在dl_init前, PIC函数调用的目标地址都实际指向PLT中的代码逻辑,初始时每个GOT条目都指向对应的PLT条目的第二条指令。
在dl_init后,部分数据信息发生变动。
本章介绍了链接的概念、作用以及在Ubuntu下通过ld指令进行链接的方法,通过对hello.o进行链接得到可执行目标文件hello,分析hello的ELF格式,并动态链接的实现进行分析。
作用:进程可以使CPU并行完成任务,并且高效利用内存和CPU。
-
- 简述壳Shell-bash的作用与处理流程
Shell是用户和操作系统之间完成交互式操作的一个接口程。bash是Linux操作系统的默认shell程序。
Shell是“提供使用者使用界面”的软件,是一个命令行解释器,作用是:保护内核同时帮助使用者向计算机传递交互信息。
处理流程:
1.用户输入命令或程序代码。
2.Shell解析输入信息。
3.若为内置命令,调用内置命令处理函数,否则调用fork( )创建子进程。
4.若为前台运行程序,调用wait()直至前台作业结束,如果为后台命令则,则不等待。
-
- Hello的execve过程
execve函数的作用:改变进程执行的上下文。
Shell的fork子程序调用execve函数,execve函数在当前子进程的上下文中加载运行可执行文件hello,创建一个hello的映像,该程序会覆盖子进程fork的地址空间,Shell复制的hello拥有相同的PID。只有当出现错误时,例如找不到hello,execve才会返回到调用程序。
-
- Hello的进程执行
每个进程都具有各自的虚拟地址空间。操作系统内核使用上下文切换的较高层形式的异常控制流来实现多任务:内核为每个进程维持一个上下文。
上下文切换的流程:1.保存当前进程的上下文。2.恢复某个先前被抢占的进程被保存的上下文。3.将控制传递给这个新恢复的进程。
hello 的进程执行:刚开始运行时内核为其保存一个上下文,进程在用户状态下运行。循环结束后,hello 调用 getchar 函数,之前 hello 运行在用户模式下,在调用 getchar 时进入内核模式,执行上下文切换,把控制转移给其他进程。完成键盘输入后,内核从其他进程切换回 hello 进程,最终 hello执行 return终止进程。
-
- Hello的异常与信号处理
可能出现的异常种类:1.终止, 2.故障
可能出现的信号:1.SIGINT信号 2.SIGSTP信号
按下ctrl-z后,shell父进程收到SIGSTP信号,信号处理程序将hello进程挂起,放到后台,停止hello程序
按下ctrl-c后,shell父进程收到SIGINT信号,由信号处理函数结束hello,并回收hello进程。
1.程序正常执行:
2.程序执行过程中按空格:
3.程序执行过程中按Ctrl+C:
观察到:hello进程中断。shell收到SIGINT信号,通过信号处理程序,回收子进程hello,清除hello的状态信息。
4.程序执行过程中按Ctrl+V:
观察到: 进程hello被挂起,成为后台挂起进程。shell收到SIGTSTP信号,通过信号处理程序,停止执行程序。输入PS命令,发现hello还在进程列表中。
5.键盘乱按
观察到:程序运行时乱按键盘不会打断程序运行。
本章介绍了进程的概念和作用,分析了操作系统通过shell调用fork函数以及execve函数的过程,以及hello 程序的上下文切换、异常与信号处理。
逻辑地址:访问指令给出的地址 (操作数) 叫逻辑地址,也叫相对地址,由选择符和偏移量组成。要经过寻址方式的计算或变换才得到内存储器中的物理地址。
线性地址:线性地址或也叫虚拟地址,逻辑地址经过段机制后转化为线性地址,是一个不真实的地址,线性地址对应硬件页式内存的转换前地址。
虚拟地址:虚拟地址即线性地址,虚拟地址是对物理地址的映射。
物理地址:真实的物理内存对应地址。CPU通过地址总线的寻址,找到真实的物理内存对应地址。
在实模式下,逻辑地址CS: EA通过CS * 16 + EA转换为物理地址。
在保护模式下,以段描述符作为下标,到GDT/LDT表中查表获得段地址,将段地址+偏移地址转换为线性地址。
-
- Hello的线性地址到物理地址的变换-页式管理
虚拟地址被分为虚拟页号与虚拟页偏移量,CPU取出虚拟页号,通过页表基址寄存器定位页表条目,从页表条目中取出信息物理页号,将物理页号与虚拟页号之间建立映射,进而完成线性地址到物理地址的变换。
虚拟内存系统通过将虚拟内存分割为大小固定的“虚拟页”来处理这个问题,同时物理内存也被分割为大小等同与虚拟页的“物理页”,并与“虚拟页”之间建立映射关系。CPU产生VA,VA传送给MMU,MMU使用v*n高位作为TLBT和TLBI向TLB中寻找匹配。命中则得到PA,没有命中,则由MMU查询页表,CR3确定第一级页表的起始地址,v*n1提供一个一级页表条目(PTE)的偏移量,可通过PTE前往二级页表的基地址,如此重复,可在第四级页表中组合成PA,添加到PLT,完成虚拟地址到物理地址的变换。
对于虚拟地址请求,首先去TLB寻找,若命中则在MMU获取,没有命中则根据多级页表,得到物理地址,接下去到cache中找,三级Cache一级级寻找。
当fork 函数被shell调用时,内核为进程创建各种数据结构,并分配给它一个唯一的PID。为了给进程创建虚拟内存,它创建了hello进程的mm_struct、区域结构和页表的原样副本。它将两个进程中的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。当fork 在进程中返回时,进程现在的虚拟内存刚好和调用fork 时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,也就为每个进程保持了私有地址空间的抽象概念。
execve 函数调用驻留在内核区域的启动加载器代码,在当前进程中加载并运
行包含在可执行目标文件 hello 中的程序,用 hello 程序有效地替代了当前程序。
经历了以下步骤:
1.删除已存在的用户区域。
2.映射私有区域,为新程序的代码、数据、bss 和栈区域创建新的区域结构。
3.映射共享区域。
4.设置程序计数器,设置当前进程上下文的程序计数器,使之指向代码区域的入口点。
-
- 缺页故障与缺页中断处理
动态存储分配管理由动态内存分配器完成。动态内存分配器维护着一个进程的虚拟内存区域,称为堆。堆是一个请求二进制零的区域,它紧接在未初始化的数据区后开始,并向上生长(向更高的地址)。分配器将堆视为一组不同大小的块的集合来维护。
每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可以用来分配。空闲块保持空闲,直到它显示地被应用程序所分配。
一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。
动态内存分配器从堆中获得空间,将对应的块标记为已分配,回收时将堆标记为未分配。而分配和回收的过程中,往往涉及到分割、合并等操作。
动态内存分配器的目标是在对齐块的基础上,尽可能地提高吞吐率及空间占用率,即减少因为内存分配造成的碎片。其实现常见的数据结构有隐式空闲链表、显式空闲链表、分离空闲链表,常见的放置策略有首次适配、下一次适配和最佳适配。
本章分析了hello的存储管理,主要涉及内存。介绍了Intel逻辑地址到线性地址的变换-段式管理,以及TLB与多级页表支持下的VA到PA的转换,同时对三级Cache支持下的物理内存访问做了说明。简述了hello的fork和execve内存映射,了解了缺页故障与缺页中断处理程序,介绍了动态存储分配管理。
文件包括:普通文件(包含任意数据的文件)、目录(文件夹,包含一组链接的文件,每个链接都将一个文件名映射到一个文件)、套接字(用来与另一个进程进行跨网络通信的文件)、命名通道、符号链接以及字符和块设备。
设备管理:unix io接口
操作包括:打开和关闭文件、读取和写入文件以及改变当前文件的位置。
-
- 简述Unix IO接口及其函数
1.打开文件——open():open函数将file那么转换为一个文件描述符并且返回描述符数字。返回的描述符总是在进程中当前没有打开的最小描述符。
2.关闭文件——close():当应用完成了对文件的访问之后,它就通知内核关闭这个文件。作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。无论一个进程因为何种原因终止时,内核都会关闭所有打开的文件并释放它们的内存资源。
3.读取文件——read():read函数从描述符为fd 的当前文件位置复制最多n个字节到内存位置buf。返回值一1表示一个错误,而返回值0表示EOF。否则,返回值表示的是实际传送的字节数量。
4.写入文件——write():write函数从内存位置buf复制至多n个字节到描述符fd的当前文件位置。
-
- printf的实现分析
函数printf的实现过程调用了vsprintf和write函数,接受一个格式串之后将匹配到的参数按照格式串的形式输出。
Vsprintf函数生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall。字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点。
-
- getchar的实现分析
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
函数getchar的实现过程调用了read函数,通过系统调用读取按键ascii码,直到检测到回车键才返回。getchar函数的返回值是用户输入的第一个字符的ASCII码,如出错返回-1,且将用户输入的字符回显到屏幕。
-
- 本章小结
本章介绍了Unix I/O设备管理机制及方法,Unix I/O接口及函数。同时分析了printf和getchar函数,以理解其实现。
从程序员的角度来看,从写完hello.c的那一刻起,他的任务就结束了。
但是从计算机系统的角度来看,hello(Program)的一生才刚刚开始,尽管现代计算机技术经过几代技术人员的添砖加瓦后日臻完善,hello从Program到Process的P2P过程实现只在一瞬间,hello在这一瞬间却有无比丰富的经历,他经历了:
1.预处理,对预处理指令进行解释,生成hello.i文件;
2.编译,将C语言代码转换成汇编语言代码,生成hello.s;
3.汇编,把汇编语言转换成机器代码,生成hello.o;
4.链接,将hello.o与可重定位目标文件及动态库链接,生成可执行程序hello;
5.创建进程:通过shell运行hello,调用fork为hello创建子进程;
6.运行程序:通过execve和加载器,加载hello到虚拟内存;
7.执行指令:CPU为其分配时间片,在一个时间片中,hello享有CPU资源,顺序执行自己的控制逻辑流
8.访问内存:MMU将程序中使用的虚拟内存地址通过页表映射成物理地址。
10.异常信号:如果进程途中出现异常信号,则调用信号处理函数进行停止或挂起进程的操作。
11. Unix I/O:完成程序与文件之间的交互操作
12.进程结束:程序运行结束时,父进程回收子进程,CPU删除hello进程创建以来的数据。
中间结果文件 |
文件的作用 |
hello.i |
预处理后的文本文件 |
hello.s |
编译后汇编程序文本文件 |
hello.o |
汇编后的可重定位目标程序 |
hello |
链接后的可执行目标文件 |
hello_elf.txt |
hello.o的ELF格式 |
hello_elf_2.txt |
hello的ELF格式 |
hello_objdump.txt |
hello.o的反汇编代码 |
hello_objdump_2.txt |
hello的反汇编代码 |
为完成本次大作业你翻阅的书籍与网站等
[1] https://blog.****.net/derkampf/article/details/71597684
[2] https://blog.****.net/weixin_41143631/article/details/81221777
[4] https://cloud.tencent.com/developer/article/1401978
[5] https://blog.****.net/h002399/article/details/49102903/