【ICS大作业】

2023-11-12

零、摘要

本文对给定的hello程序的生命周期进行了系统性分析,程序经预处理生成hello.i、编译生成hello.s,汇编生成hello.o;最后链接成可执行目标文件hello。Shell收到./hello的指令,调用fork函数创建进程,execve将hello加载内存,由CPU进行控制,最后结束进程并由父进程回收。

关键词:预处理;编译;汇编;链接;进程;IO管理

一、概述

1.1 Hello简介

程序员通过编辑器创建并保存成文本文件,文件名为hello.c。此程序通过字节序列存储在文件中。

GCC编译器驱动程序读取源程序文件,并经下面4个阶段将其翻译成可执行目标文件hello。

预处理器根据#开头的命令修改原始C程序。

编译器将文本文件hello.i翻译成文本文件hello.s,即汇编语言程序。

汇编器将hello.s翻译成机器语言指令,并将这些指令打包成一种可重定位目标程序,并将结果保存在目标文件hello.o中。

链接器将printf等函数的printf.o等预编译文件合并到hello.o程序中,结果得到hello文件——可执行目标文件。

可执行目标文件被存储在磁盘上,等待程序运行。

程序加载时,机器指令被复制到主存;程序运行时,机器指令又被复制到处理器。在信息传递的过程中,使用高速缓存的机制来提高程序性能。

程序运行过程中,同时有着许多进程在运行,他们之间通过上下文切换来实现并发运行。

通过shell输入./shell,shell通过fork函数创建了一个新的进程,之后调用execve映射虚拟内存,通过mmap为hello程序开辟空间。

CPU从虚拟内存中的.text,.data节取代码和数据,调度器为进程规划时间片,有异常时触发异常处理子程序。

程序运行结束时,父进程回收hello进程和它创建的子进程,内核删除相关数据结构。

1.2 环境与工具

硬件环境:X64CPU;1.80GHz/2.30GHz;16GB RAM

软件环境:Windows 10 64位;VMware;Ubuntu 64位

开发工具:Visual Studio 2022 64位;Code Blocks 64位;gedit + gcc;edb

1.3 中间结果

hello.c 源程序

hello.i 预处理后的文件

hello.s 编译后的文件

hello.o 汇编后的可重定位目标执行文件

hello 链接后的可执行目标文件

hello.elf hello.o的elf格式文件

hello1.elf hello的elf格式文件

1.4 本章小结

本章简单介绍了Hello程序的生命周期,并介绍了所需软硬件环境和开发工具的基本信息。

二、预处理

2.1 预处理的概念与作用

预处理一般是指在程序源代码被翻译成为目标代码的过程中,生成二进制代码之前的过程。这个过程并不对程序的源代码进行解析,但它把源代码分割或处理成为特定的单位——预处理记号用来支持语言特性。

预处理会展开以#起始的行,试图解释为预处理指令,使源代码在不同的执行环境中被方便地修改或编译。hello.c生成hello.i。

2.2在Ubuntu下预处理的命令

gcc -E hello.c -o hello.i

 

 

 (生成了hello.i文件)

2.3 Hello的预处理结果解析

 预处理将头文件展开,程序变成了3060行。其中仍为C语言代码,说明预处理还未解析。头文件中包含了函数声明、结构体定义、变量定义、宏定义等内容。

2.4 本章小结

本章借助hello程序预处理的过程来展示了预处理的概念和作用,查看hello.i文件后发现对头文件展开了。

三、编译

3.1 编译的概念与作用

编译器将hello.i转换成hello.s文件,其中包含汇编语言程序。

这个工作过程中包括:词法分析、语法分析、语义检查和中间代码生成、代码优化、目标代码生成。其中词法语法检查出错,则编译器会给出提示信息。

3.2 在Ubuntu下编译的命令

 

gcc -S hello.i -o hello.s

生成了hello.s文件

3.3 Hello的编译结果解析

3.3.1汇编初始部分

.file 声明为源文件

.text 代码节

.section .rodata 只读数据段
.globl 声明全局变量
.type 声明符号为函数类型或数据类型
.string 声明一个字符串

.align 声明对指令或者数据的存放地址进行对齐的方式

3.3.2 数据类型

 

3.3.2.1字符串

程序中的含有两个字符串,均在上图中展示,即为.string后引号中的字符串。这两个字符串是作为printf函数的参数来进行调用的。

3.3.2.2局部变量i

main函数仅声明了一个局部变量i,存放在了-4(%rbp)。

 

3.3.2.3参数argc

 

argc存放在了-20(%rbp),作为main函数第一个参数,参与了main函数中选择条件判断。

3.3.2.4数组argv[]

以指针形式作为main函数的第二个函数,数组中每个元素都是一个指向字符类型的指针。如上图,数组起始地址存放在-32(%rbp)位置,下图中为main函数的两次调用。

 3.3.2.5立即数

在汇编代码中带有$标志的即为立即数。

3.3.3类型转换

 调用了atoi函数,将字符串类型转换为整型。

3.3.4操作

3.3.4.1赋值操作

汇编代码中使用mov指令实现赋值操作,常见的普通mov类指令有:

movb 1字节

movw 2字节

movl 4字节

movq 8字节

3.3.4.2算数操作

汇编代码中出现了最常见的addq与subq,分别代表加法与减法。

3.3.4.3关系操作

包括cmp等条件判断指令,之后常常进行j跳转。

比较argc与4,为选择条件判断。

此处为循环判断条件,i<8。

3.3.4.4控制转移

 

此处je为相等则跳转。

 

此处jmp为直接跳转。

 

此处jle为小于等于则跳转。

3.3.5函数操作

3.3.5.1全局函数

在汇编初始部分将main函数声明为全局函数。

3.3.5.2参数传递

main函数参数为argc和*argv[]

printf函数参数为argv[1], argv[2]

exit函数参数为1

sleep函数参数为atoi(argv[3])

getchar函数无参数。

3.3.5.3函数调用

用call指令来实现。例如:

 

3.4 本章小结

本章主要分析了编译后的汇编指令,将其与C语言中的数据和操作联系了起来。

 四、汇编

4.1 汇编的概念与作用

汇编器将hello.s翻译成机器语言指令,这些指令打包成可重定位目标程序,并保存在目标文件hello.o中。hello.o为二进制文件,包含了main函数中指令的机器语言格式。

4.2 在Ubuntu下汇编的命令

命令:gcc -c -o hello.o hello.s

4.3 可重定位目标elf格式

命令:readelf -a hello.o > hello.elf

 

分析:

1 ELF头

 

包括Magic:一个16个字节的序列,描述了生成该文件的系统的字的大小和字节顺序和可供链接器识别的有关信息:文件类别、数据类型、版本号、系统类型、ABI版本、文件类型、系统架构、节头部表的文件偏移、节头部表中数据的大小和数量。

2 节头

节头记录了各节的名称、类型、地址、偏移量、大小、全体大小、旗标、链接、信息、对齐。其中旗标部分在下面有解释部分。

3 重定位节

 

.rela.text即.rel.text为一个.text节中位置的列表,当链接器把这个目标文件和其它文件组合时,需要修改这些位置。一般而言,任何调用外部函数或者引用全局变量的指令都需要修改。另一方面,调用本地函数的指令则不需要修改。重定位信息通常省略。

4 符号表

 

符号表存放在程序中定义和引用的函数和全局变量的信息。每个可重定位目标文件在.symtab中都有一张符号表(除非用STRIP命令去掉它)。但和编译器中的符号表不同,.symtab符号表不包含局部变量的条目。

4.4 Hello.o的结果解析

命令:objdump -d -r hello.o

 

与hello.s存在部分差异:

1 转移指令

 

 

.L3变为了80 <main+0x80>,由原先的段地址变为了现在的确定的相对地址。

2 函数调用

 

 

函数调用由原先的函数名变为了具体的地址。

3 立即数

由原先的$0变为了$0x0,变为了16进制。

4.5 本章小结

本章介绍了汇编的概念和作用,并分析了ELF格式的文件,与链接联系了起来;分析了hello.o反汇编后的指令,发现了此时得到的汇编语言与机器语言联系的紧密性。

五、链接

5.1 链接的概念与作用

链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时,也就是在源代码被翻译成机器代码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;甚至执行于运行时,也就是由应用程序来执行。在早期的计算机系统中,链接是手动执行的。在现代系统中,链接是由叫做链接器的程序自动执行的。

链接器使得分离编译成为了可能。

5.2 在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

 

5.3 可执行目标文件hello的格式

命令:readelf -a hello > hello1.elf

分析:

1 ELF头

 

类型不同于hello.o的ELF格式文件,类型变为EXEC可执行文件,有27个节。

2 节头

 

3 重定位节

 

4 符号表

 

5.4 hello的虚拟地址空间

程序头:

 

LOAD地址为0x400000

 

在edb中定位此位置,则可以通过相对位置找到各个节的信息。5.3中节头表已经给予了相对地址的信息。

5.5 链接的重定位过程分析

命令:objdump -d -r hello

相比hello.o多了许多行,本质是在.text节的基础上多了.init、.plt、.plt_sec节,且原有的.text节中也多了<start>等,链接的函数增多了,可执行文件也更丰富了,其实对于机器来讲是更直观了,只需要按部就班执行语言就好。

hello重定位的过程:

(1)重定位节和符号定义链接器将所有类型相同的节合并在一起后,这个节就作为可执行目标文件的节。然后链接器把运行时的内存地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号,当这一步完成时,程序中每条指令和全局变量都有唯一运行时的地址。

(2)重定位节中的符号引用这一步中,连接器修改代码节和数据节中对每个符号的引用,使他们指向正确的运行时地址。执行这一步,链接器依赖于可重定位目标模块中称为的重定位条目的数据结构。

(3)重定位条目当编译器遇到对最终位置未知的目标引用时,它就会生成一个重定位条目,代码的重定位条目放在.rel.txt中。

(4)重定位地址计算算法:

 

 

5.6 hello的执行流程

加载hello——>_start——>_libc_start_main——>_main——>_printf——>_exit——>_sleep——>_getchar——>exit

程序名

程序地址

_start

0x4010f0

_libc_start_main

0x2f12271d

_main

0x401125

_printf

0x401040

_exit

0x401070

_sleep

0x401080

_getchar

0x401050

 

5.7 Hello的动态链接分析

延迟绑定由got和plt实现,查阅ELF文件:

Got起始表位置在0x404000处。

  

调用dl_init之前,0x404008~0x404017均为0;

 

调用dl_init之后,上述地址范围被赋值,.got.plt条目已变化。

5.8 本章小结

本章介绍了链接的概念和作用,对可执行目标文件hello的ELF格式文件进行了分析,并与hello.o进行了对比,梳理了hello从加载到退出调用的函数,重点进行了重定位和动态链接的分析。

 六、hello进程管理

6.1 进程的概念与作用

概念:一个执行中程序的实例。让我们的程序好像是独占地使用处理器(一个独立的逻辑控制流)和内存(一个私有的地址空间)。

创建:父进程通过调用fork函数创建一个新的运行的子进程。子进程和父进程虚拟地址空间独立、PID不同,其它完全相同。

回收:当一个进程由于某种原因终止时,被保持在一种已终止的状态中,直到被它的父进程回收。当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已经终止的进程,从此开始,该进程就不存在了。一个僵死了但还未被会回收的进程被称为僵死进程。如果一个父进程终止了,内核会安排init进程成为它的孤儿进程的养分,init进程将会回收这些孤儿进程。使用waitpid函数来等待子进程终止或者停止。

6.2 简述壳Shell-bash的作用与处理流程

作用:Shell是一个交互型应用级程序,为使用者提供操作界面,接收用户命令,然后调用相应的应用程序。

处理流程:

1)从终端读入输入的命令。

2)将输入字符串切分获得所有的参数。

3)如果是内置命令则立即执行。

4)否则调用相应的程序执行。

5)shell 应该接受键盘输入信号,并对这些信号进行相应处理。

6.3 Hello的fork进程创建过程

输入./hello 120L020211 yxm 1,shell调用eval函数,再调用builtin_command函数检查发现非内置命令,则通过fork创建一个子进程。

6.4 Hello的execve过程

fork创建子进程后,子进程调用execve函数加载并运行hello。

1、删除已存在的用户区域,删除之前进程在用户部分中已存在的结构。

2、创建新的代码、数据、堆和栈段。所有这些区域结构都是私有的。虚拟地址空间的代码和数据区域被映射为hello文件的.txt和.data区。bss区域是请求二进制零的,映射匿名文件,其大小包含在hello文件中。栈和堆区域也是请求二进制零的,初始长度为零。

3、映射共享区域。如果hello程序与共享对象链接,比如标准C库libc.so,那么这些对象都是动态链接到这个程序的,然后再映射到用户虚拟地址空间中的共享区域。

4、设置程序计数器(PC)。exceve做的最后一件事就是设置当前进程的上下文中的程序计数器,使之指向代码区域的入口点。

6.5 Hello的进程执行

逻辑控制流:如果想用调试器单步执行程序,我们会看到一系列的程序计数器(PC)的值,这些值唯一地对应于包含在程序的可执行目标文件中的指令,或是包含在运行时动态链接到程序的共享对象中的指令。这个PC值的序列叫做逻辑控制流,或者简称为逻辑流。

并发流:一个逻辑流的执行在时间上与另一个流重叠,称为并发流。

私有地址空间:进程为每个程序提供假象,好像它独占地使用系统地址空间。进程为每个程序提供它自己的私有地址空间。

用户模式和内核模式:处理器控制某个控制寄存器中的一个模式位来限制一个应用可以执行的指令以及它可以访问的地址空间范围。当设置了模式位时,进程就运行在内核模式中;未设置模式位时,进程就运行在用户模式中。异常发生时用户模式转为内核模式。/proc文件系统允许用户模式进程访问内核数据结构的内容。

上下文切换:一种较高层形式的异常控制流,以实现多任务。

hello进程的执行:进程调用execve函数,最初在用户模式下输出Hello 120L020211 yxm,后来进入内核模式运行信号处理程序,再回到用户模式。过程中不断切换上下文,与其它进程交替占用CPU。

6.6 hello的异常与信号处理

异常种类

类别

原因

异步/同步

返回行为

中断

来自IO设备

异步

返回到下一条指令

陷阱

有意的异常

同步

返回到下一条指令

故障

潜在错误可恢复

同步

返回当前指令或终止

终止

错误不可恢复

同步

不会返回

程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps  jobs  pstree  fg  kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。

正常运行在6.5中已经给出了结果,下面先展示按键盘的情况:

不停乱按

 

回车

 

Ctrl-C

 

Ctrl-Z

 

不停乱按和回车都被存入了stdin的缓存中,因此按回车的程序执行完毕后又执行了多次回车。

下面展示Ctrl-Z后运行指令:

 

说明Ctrl-Z后只是挂起了前台的作业,输入fg后又能继续运行。

6.7本章小结

本章先介绍了进程的概念和作用、shell的概念和处理流程,分析了建立子进程并执行所需的fork与execve,最终借助hello的执行来深化了这一章的知识。

七、hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:指的是CPU分配给每个进程的地址。逻辑地址用来指定一个操作数或者是一条指令的地址。是由一个段标识符加上一个指定段内相对地址的偏移量,表示为段标识符:段内偏移量。

线性地址:逻辑地址向物理地址转化过程中的一步,逻辑地址经过段机制后转化为线性地址,为描述符:偏移量的组合形式,分页机制中线性地址作为输入。

虚拟地址:指的是线性地址。

物理地址:CPU通过地址总线的寻址,找到真实的物理内存对应地址。在前端总线上传输的内存地址都是物理内存地址。

7.2 Intel逻辑地址到线性地址的变换-段式管理

逻辑地址由两部分组成,段标识符:段内偏移量。

一个进程拥有4G的虚拟空间,其中0-3G属于用户空间,这3G的空间划分为多个段,如下图所示:

 

从下往上,5个部分依次是:
代码区:存放可执行的指令,只能读,不能写
数据区:存放初始化的全局变量和静态变量
BSS区:存放未初始化的全局变量和静态变量
heap:存放malloc/new申请的变量
stack:存放临时变量,函数参数等

 linux是通过分段机制,将逻辑地址转化为线性地址。上面的变量b就属于数据段。通过数据段寄存器ds,可以找到此进程数据段所在的段描述符,再通过段描述符找到相应的线性地址。
寄存器ds中保存了16位的段选择符,段选择符格式如下:

TI:指明段描述符是在全局描述符表(GDT, TI=0)中或局部描述符表(LDT, TI=1)中

每个段都有不同的属性,如首字节的线性地址,段的访问级别(DPL)等,段描述符就保存了段的这些属性。段描述符长度为8字节,格式如下:

 

逻辑地址到线性地址的转换:

 

7.3 Hello的线性地址到物理地址的变换-页式管理

线性地址即虚拟地址(VA)到物理地址(PA)之间的转换通过分页机制完成,而分页机制是对虚拟地址内存空间进行分页。

系统将虚拟页作为进行数据传输的单元。Linux下每个虚拟页大小为4KB。物理内存也被分割为物理页, MMU(内存管理单元)负责地址翻译,MMU使用页表将虚拟页到物理页的映射,即虚拟地址到物理地址的映射。

n位的虚拟地址包含两个部分:一个p位的虚拟页面偏移(VPO),一个n-p位的虚拟页号(VPN),MMU利用VPN选择适当的PTE,根据PTE,我们知道虚拟页的信息,如果虚拟页是已缓存的,那直接将页表条目的物理页号和虚拟地址的VPO串联起来就得到一个相应的物理地址。VPO和PPO是相同的。如果虚拟页是未缓存的,会触发一个缺页故障。调用一个缺页处理子程序将磁盘的虚拟页重新加载到内存中,然后再执行这个导致缺页的指令。

7.4 TLB与四级页表支持下的VA到PA的变换

Core i7采用四级页表的层次结构。CPU产生虚拟地址VA,虚拟地址VA传送给MMU,MMU使用VPN高位作为TLBT和TLBI,向TLB中寻找匹配。如果命中,则得到物理地址PA。如果TLB中没有命中,MMU查询页表,CR3确定第一级页表的起始地址,VPN1确定在第一级页表中的偏移量,查询出PTE,以此类推,最终在第四级页表中找到PPN,与VPO组合成物理地址PA,添加到PLT。

7.5 三级Cache支持下的物理内存访问

获得物理地址之后,先取出组索引对应位,在L1中寻找对应组。如果存在,则比较标志位,相等后检查有效位是否为1.如果都满足则命中取出值传给CPU,否则按顺序对L2cache、L3cache、内存进行相同操作,直到出现命中。然后再一级一级向上传,如果有空闲块则将目标块放置到空闲块中,否则将缓存中的某个块驱逐,将目标块放到被驱逐块的位置。

7.6 hello进程fork时的内存映射

在shell输入命令行后,内核调用fork创建子进程,为hello程序的运行创建上下文,并分配一个与父进程不同的PID。通过fork创建的子进程拥有父进程相同的区域结构、页表等的一份副本,同时子进程也可以访问任何父进程已经打开的文件。当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同,当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间。

7.7 hello进程execve时的内存映射

第6章中已进行相应分析:

fork创建子进程后,子进程调用execve函数加载并运行hello。

1、删除已存在的用户区域,删除之前进程在用户部分中已存在的结构。

2、创建新的代码、数据、堆和栈段。所有这些区域结构都是私有的。虚拟地址空间的代码和数据区域被映射为hello文件的.txt和.data区。bss区域是请求二进制零的,映射匿名文件,其大小包含在hello文件中。栈和堆区域也是请求二进制零的,初始长度为零。

3、映射共享区域。如果hello程序与共享对象链接,比如标准C库libc.so,那么这些对象都是动态链接到这个程序的,然后再映射到用户虚拟地址空间中的共享区域。

4、设置程序计数器(PC)。exceve做的最后一件事就是设置当前进程的上下文中的程序计数器,使之指向代码区域的入口点。

 

7.8 缺页故障与缺页中断处理

7.9动态存储分配管理

动态内存分配器维护着一个进程的虚拟内存区域,称为堆,系统之间细节不同,但是不失通用性,假设堆是一个请求二进制零的区域,它紧接在未初始化的数据区域后开始,并向上生长(向更高地址)。对于每个进程,内核维护着一个变量brk指针,它指向堆的顶部。

 

分配器将堆视为一组不同大小的块的集合来维护。每个块就是一个个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显示地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显示地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显示执行的。要么是内存分配器自身隐式执行的。分配器有两种基本风格。两种风格都要求应用显示地分配块。他们的不同之处在于由哪个实体来负责释放已分配的块。

1.显示分配器:要求应用显示的释放任何已分配的块。例如C标准库malloc和free。

2.隐式分配器:要求分配器检测一个已分配块何时不再被程序使用,那么就释放这个块。例如Java、ML、Lisp等高级语言中的垃圾收集。

7.10本章小结

本章主要介绍了hello的存储器地址空间、intel的段式管理、hello的页式管理,以intel Core7在指定环境下介绍了虚拟地址VA到物理地址PA的转换、物理内存访问,分析了hello进程fork时的内存映射,hello进程execve时的内存映射、缺页故障与缺页中断处理和动态存储分配管理。

八、hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:均模型化为文件,所有输入输出均当作读写处理。

设备管理:Unix IO接口使得输入输出都能以一种统一且一致的方式进行。

8.2 简述Unix IO接口及其函数

接口就是连接CPU与外设之间的部件,它完成CPU与外界的信息传送。还包括辅助CPU工作的外围电路,如中断控制器、DMA控制器、定时器、高速CACHE等。

函数:

打开和关闭文件:open()和close()

读写文件:read()和write()

在文件的指定位置赋值: ssize_tread()。

8.3 printf的实现分析

printf代码:

int printf(const char *fmt, ...)

{

int i;

char buf[256];

va_list arg = (va_list)((char*)(&fmt) + 4);

i = vsprintf(buf, fmt, arg);

write(buf, i);

return i;

}

printf生成格式化后的字符串,并返回字符串的长度。

vsprintf的函数体:

vsprintf函数将所有的参数内容格式化之后存入buf,返回格式化数组的长度。write函数将buf中的i个元素写到终端。从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等。字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

8.4 getchar的实现分析

异步异常-键盘中断的处理:当用户按键时,键盘接口会得到一个代表该按键的键盘扫描码,同时产生一个中断请求,中断请求抢占当前进程运行键盘中断子程序,键盘中断子程序接受按键扫描码转成ASCII码,保存到系统的键盘缓冲区。

getchar调用了read系统函数,通过系统调用读取按键ASCII码,直到接受到回车键才返回。

8.5本章小结

本章介绍了Linux的IO设备管理方法,Unix IO接口及其函数,分析了printf函数和getchar函数的实现。

 

结论

输入:从键盘输入hello.c文本文件。

预处理:预处理hello.c,生成hello.i。

编译:hello.i生成汇编语言文件hello.s。

汇编:hello.s翻译成可重定位目标文件hello.o。

链接:将hello.o与动态链接库链接成为可执行目标程序hello。

运行:在shell中输入./hello 120L020211 yxm 1。

创建子进程:非内置命令,shell调用fork生成子进程。

加载程序:shell调用execve函数,启动加载器,映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入main函数。

执行指令:CPU为每一个进程分配时间片,在一个时间片中hello“独享”CPU资源,顺序执行逻辑控制流。

访问内存:虚拟内存地址通过页表映射成物理地址。

动态申请内存:printf调用malloc动态申请内存。

信号管理:当程序在运行的时候我们输入Ctrl+C,内核会发送SIGINT信号给进程并终止前台作业。当输入Ctrl+Z时,内核会发送SIGTSTP信号给进程,并将前台作业停止挂起。

终止:子进程执行完毕,内核安排父进程收回子进程,将子进程的退出状态传递给父进程。内核删除为这个进程创建的所有数据结构。

你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法:

计算机系统的设计浓缩了无数前人的结晶,一个简单的hello程序也有着丰富的内涵。每一步都耗费了不知多少前辈日日夜夜的努力。我们应当不停学习计算机系统知识,努力追赶前人,在积累了一定的知识之后,可以尝试进行改进甚至创新新的方法。但现在本人学术不精,还未能实现创新。

附件

hello.c:源文件

hello.i:预处理后的文件

hello.o:编译后的文件

hello.s:汇编后的可重定位目标文件

hello:链接后的可执行文件

hello.elf:hello.o的ELF格式

hello1.elf:hello的ELF格式

参考文献

[1]  《深入理解计算机系统》[J].科学中国人,2018,(09):69.

[2]  预处理_百度百科 (baidu.com)

[3]  https://blog.csdn.net/qq_44491553/article/details/110317066

[4]  http://www.xcjxzjg.com/article-22-35623-0.html

[5]  https://www.bbsmax.com/A/MyJxPZZAJn/

[6]  百度安全验证

[7]    详解:链接中的重定位 - 知乎

[8]  https://blog.csdn.net/sy06106/article/details/118274118

[9]    https://blog.csdn.net/morixinguan/article/details/121623620

[10]  https://www.cnblogs.com/pianist/p/3315801.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【ICS大作业】 的相关文章

随机推荐

  • Vue生命周期详解

    Vue的生命周期 Vue实例有一个完整的生命周期 也就是从开始创建初女台化数据 编译模板 挂载DOM 渲染一更新一渲染 卸载等一系列过程 我们称这是Vue的生命周期 在Vue官网有这样一张图 生命周期 vue实例从创建到销毁的过程 声明周期
  • android卡刷包自制rom之添加删除,添加/删除ROM内置APP的教程

    ROM闪存软件包是Android闪存中必不可少的东西删除刷机包自带软件 但是大多数ROM闪存软件包都与许多应用程序集成在一起 其中许多不是您想要的 也没有您想要的应用程序 使用Mushroom ROM Assistant快速删除和添加APK
  • 偏最小二乘法(PLS)计算流程

    根据相关文献 将PLS的计算流程整理如下 供大家参考 1 输入样本矩阵X 输出值矩阵Y分别对X Y进行中心化处理 归一化方差 得到矩阵 E0和F 2 令n 0 3 计算矩阵En FF En 计算En FF En的最大特征根对应的单位特征向量
  • AndroidStudio会在@NonNull后面自动添加@org.jetbrains.annotations.Nullable

    今天呀在AndroidStudio 4 1中一直有个问题困扰着我 那就是AndroidStudio会在 NonNull后面自动添加 org jetbrains annotations Nullable 这 自动添加就算了还报错 每次都要删除
  • 区块链搬砖实战

    前言 相信不少币友在数字货币交易的时候都发现了 不同的交易平台不同的数字货币都存在一定的差价 这时候就引出了 搬砖的概念 搬砖的概念 由于各种因素导致各平台的虚拟货币的价格有价格差 产生了套利空间 运用平台之间价格差来谋求利益的行为俗称 搬
  • vs code终端修改字体大小以及其它样式

    1 在文件 gt 首选项 gt 设置 2 用户 gt 工作台 gt 找到settings json点击进入 3 在 settings json文件里添加需要修改的样式 terminal integrated cursorBlinking t
  • 软件资源下载链接

    1 Dreamweaver DW cs5 链接 https pan baidu com s 1kVqqpqJ 密码 9hc4 DW cc2014 链接 https pan baidu com s 1skQvBCL 密码 3kd8 DW cc
  • 数据库系统——复习总结

    数据库系统
  • cvCanny检测边缘,连通重要的非连通区域

    这个函数就是使用canny边缘检测算子检测图象的边缘 在opencv下使用这个函数之前最好将图象平滑处理一下 要不然可能检测不到边缘 检测到的边缘 这些边缘大多还不是连通区域 可以通过3 3的模板将一些相近的边缘连接起来 也可以用cvDil
  • Tesseract-OCR的配置和应用

    1 百度搜索Tesseract OCR下载 Tesseract orc setup 3 02 02 exe 要记得自己的安装目录 博主的安装路径为 C Program Files x86 Tesseract OCR 等会配置环境变量要用 如
  • C# Selenium chromedriver 隐藏Devtool控制台窗口

    爬取网页信息时 使用了C Selenium WebDriver dll chromedriver Chrome 除了chromedriver控制台窗口 可以通过CDS HideCommandPromptWindow true隐藏 还有出现一
  • Health Kit基于数据提供专业方案,改善用户睡眠质量

    什么是CBT I 中国社科院等机构今年发布的 中国睡眠研究报告2023 内容显示 2022年 受访者的每晚平均睡眠时长为7 40小时 近半数受访者的每晚平均睡眠时长不足8小时 47 55 16 79 的受访者的每晚平均睡眠时长不足7小时 这
  • C++异常处理机制(超级详细)

    目录 0 异常处理机制简介 1 传统错误处理机制 通过函数返回值 2 异常处理机制语法 3 异常接口声明 4 异常类型和声明周期 4 1throw基本类型异常 int float char 4 2throw字符串类型异常 4 3throw类
  • 数据可视化(学会用matplotlib绘图)

    1 绘制简单的折线图 import matplotlib pyplot as plt squres 1 4 9 16 25 plt plot squres 把列表传给plot 这个函数尝试根据这些数字绘制出有意义的图形 plt show 打
  • 【取模软件PCtoLCD2002使用教程】

    1 打开取模软件PCtoLCD 2 左上角模式选择为字符模式 3 点击选项 4 设置如下 然后点击确定 5 以16x16汉字取模为例 字宽字高都改为16 然后在输入栏输入汉字 点击生成字模生成的字模如下 然后将字模复制到例程lcdfont
  • 距离向量算法_RIP协议及距离向量算法(上)【44】

    1 RIP协议 RIP 全称Routing Information Protocol 即路由信息协议 RIP是一种分布式的基于距离向量的路由选择协议 是因特网的协议标准 最大优点的简单 RIP协议要求网络中每一个路由器都维护从它自己到其它每
  • Centos 安装mysql8(YUM方式)

    1 执行安装命令 root localhost wget https dev mysql com get mysql80 community release el8 4 noarch rpm root localhost yum modul
  • JAVA 定义静态map并赋值

    private static final Map
  • C语言入门经典三,c语言入门经典第4版和第3版有什么区别

    问 微软的C语言和其他C语言有什么区别吗 答 不知道楼主说的是所谓 微软的c 是指什么概念 个人意见 仅供参考 1 如果是指微软推出的c语言的编译器ms c的话 其实就是c语言各个编译器之间的区别 如果你想深入了解 最好是学习下c标准的制定
  • 【ICS大作业】

    零 摘要 本文对给定的hello程序的生命周期进行了系统性分析 程序经预处理生成hello i 编译生成hello s 汇编生成hello o 最后链接成可执行目标文件hello Shell收到 hello的指令 调用fork函数创建进程