目 录
第1章 概述
1.1 Hello简介
P2P过程:通过键盘输入代码保存成hello.c文件。hello.c经过cpp预处理得到hello.i,然后经ccl编译得到hello.s,在经过as汇编得到hello.o,最后通过ld链接得到可执行文件hello。至此hello由program变成process,实现P2P过程。
020过程:操作系统调用execve后映射虚拟内存,先删除当前虚拟地址的数据结构并未hello创建新的区域结构,进入程序入口后载入物理内存,再进入main函数执行代码。执行完成后,父进程回收hello进程,内核删除相关数据结构。
1.2 环境与工具
AMD Ryzen 5 4600U with Radeon Graphics 2.10 GHz1.2.2
Win10 64位
虚拟机VMware Workstation Pro12.0 Ubuntu20.04
gcc ld readelf gedit objdump edb hexedit
1.3 中间结果
hello.i:预处理生成的文本文件
hello.s:.i编译后得到的汇编语言文件
hello.o:.s汇编后得到的可重定位目标文件
hello:.o经过链接生成的可执行目标文件
1.4 本章小结
本章主要介绍了P2P、020的过程并列出实验基本信息
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
概念:预处理器(cpp)根据以字符#开头的命令,修改原始的C程序,读取系统头文件的内容,并把它们直接插入程序文本中,得到.i扩展名文件。
作用:调用系统头文件,用实际值替换用#define 定义的字符串,简化了程序,使编译器翻译程序时更加方便
2.2在Ubuntu下预处理的命令
在命令行输入cpp hello.c > hello.i

图2.1-ubuntu下预处理
2.3 Hello的预处理结果解析
hello.i中有各种头文件:

图2.2-hello.i头文件
hello.i中的main:

图2.3-hello.i main
2.4 本章小结
本章介绍预处理过程的概念、作用,对预处理结果进行解析。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
概念:编译器(ccl)将文本文件hello.i翻译成文本文件hello.s, 它包含一个汇编语言程序。
作用:生成语法树并转化为目标代码,使下一步转成二进制代码更加方便。
3.2 在Ubuntu下编译的命令
在命令行输入gcc -S hello.i -o hello.s
图3.1-ubuntu下编译
3.3 Hello的编译结果解析
3.3.1 汇编指令的介绍
图3.2-Ubuntu下编译指令结果
.text 代码段
.section
.align 对齐方式
.string 字符串
.globl 全局标识符
3.3.2 数据
1.字符串

代码中的两个字符串,在数据段中
- 局部变量i
在代码中,声明了一个局部变量i,编译器酱局部变量放在堆栈中,如图,放置在-4(%rbp)的位置上,占有4个字节


图3.4、3.5-局部变量
3.数组char * argv[]
argv[]作为main函数的第二个参数,是一个字符类型指针,arg[ 0]也存放在堆栈中,-32(%rbp)

图3.6-数组
3.3.3全局变量

如上图所示,hello.c中仅有一个全局函数main,

3.3.4 赋值操作

图3.7、3.8-赋值操作在汇编代码中的体现
main函数中的赋值操作i = 0,由于i为int,占4个字节,所以由movl语句实现

3.3.5 运算操作
一、加法 add实现

二、减法 sub实现

图3.9-运算操作
3.3.6 关系操作
1、hello.c中有i < 8,在汇编代码中是通过关系操作cmp实现的:cmpl $7,-4(%rbp),计算i-7,然后通过jle和0相比较实现i < 8的判断。

图3.10-判断操作(1)
- hello.c中有argc != 3,在汇编代码中是通过cmp实现的:cmpl $3,-20(%rbp),同时这个条指令还设置条件码,根据条件码判断是否跳转。

图3.11-判断操作(2)
3.3.7 控制转移指令
在hello.c中有jmp、jle控制转移指令,二者结合完成for循环操作,首先对i赋初值0,然后跳转到条件判断L3,判断是都符合循环条件,符合直接跳转到L4,进入循环,否则call另外的函数。

图3.12-控制转移
3.3.8 函数操作
main中调用的函数有:printf、puts、exit、sleep、getchar
一、printf函数

图3.13-printf函数
将两个参数分别放在寄存器%rsi、%rdx中,然后call调用printf函数,函数返回值存放在%eax中。
二、其他函数

图3.14-调用的其他函数
三、main函数
main函数如果有返回值,则返回值存放在寄存器%eax中,然后leave恢复栈的初始状态(调用函数之前的状态),再ret。

图3.15-main函数
3.4 本章小结
本章介绍了编译的概念和作用,由hello.c为例,分析了汇编代码中各个部分的内容和含义。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
概念:汇编器(as)将汇编程序翻译成机器语言指令,把这些指令打包成可重定位目标程序的格式,并将结果保存在.o 目标文件中,.o 文件是一个二进制文件,它包含程序的指令编码。
作用:汇编,将代码转成二进制。
4.2 在Ubuntu下汇编的命令
在命令行输入as hello.s -o hello.o

图4.1-Ubuntu下汇编
4.3 可重定位目标elf格式
分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。
1.ELF头
用readelf -h hello.o 命令生成ELF Header 。ELF头以16字节的Magic开始,描述了生成该文件的系统的大小和字节顺序,其余部分包括帮助连接器语法分析和解释目标文件的信息,例如目标机器类型、字节头部表的文件偏移、节头大小等等。

图4.2-汇编中elf头
2.节头
通过readelf -S hello.o生成节头Section Header
表述了.o文件中的各节的信息,包括类型、位置、偏移量等等。由于是可重定位目标文件,所以每个节都从0开始,用于重定位。在文件头中得到节头表的信息,然后再使用节头表中的字节偏移信息得到各节在文件中的起始位置,以及各节所占空间的大小,同时可以观察到,代码是可执行的,但是不能写;数据段和只读数据段都不可执行,而且只读数据段也不可写。

图4.3汇编中section header
3.符号表
通过readelf -s hello.o生成符号表.symtab
可以看到存放了全局变量和引用的函数。value是每一个符号相对于目标节的起始位置偏移,size是目标的大小,type记录是数据还是函数,bind标识是全局符号还是本地符号,最后name描述符号名字。

图4.4-汇编中符号表
4.重定位节
通过readelf -r hello.o生成重定位节 .rela.text
从命名可知,重定位节是在.text中的位置的列表,包含.text 节中需要进行重定位的信息,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。需要重定位的有所引用的所有函数,还有全局变量。

图4.4-汇编中重定位节
4.4 Hello.o的结果解析
(以下格式自行编排,编辑时删除)
objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。
说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。

图4.5-反汇编与.s对比
将objdump来的文本文件与.s相对比发现,而这并没有太大区别,反汇编来的代码除了汇编语言之外还有很多机器代码,机器语言程序的是二进制机器指令的集合,是纯粹的二进制数据表示的语言,是电脑可以真正识别的语言。通过分析可以看到二者在函数调用和跳转指令上细微的差别,这是由于汇编语言中仅仅是助记符,而在汇编成机器语言之后是确定的地址,所以二者不同;至于函数调用部分,在反汇编程序中,call之后应该是目标地址(下一条指令),但是由于调用的函数都是共享库中的函数,需要后期动态链接才能确定地址,所以在这部分就把call指令后的相对地址设为0,而.s文件中,函数调用之后直接加上函数名称,更为简单。
4.5 本章小结
在本章中,对hello.s进行了汇编,生成可重定位的目标文件,并且依次对ELF文件进行了分析,从ELF头、节头、符号表、可重定位节几个部分,还比较了.s文件与objdump来的反汇编文件的不同,学习了从汇编语言到机器语言的映射关系。
(第4章1分)
第5章 链接
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 得到可执行程序hello

图5.1-Ubuntu下链接
5.3 可执行目标文件hello的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
1.ELF头
通过readelf -a hello > hello.elf生成hello的elf文件
可以看出hello是一个可执行目标文件,有27个节。

图5.2-elf文件
2.节头
通过readelf -S hello生成节头

图5.3-节头
3.符号表
通过readelf -s hello生成重定位节

图5.4-符号表
4.重定位节
通过readelf -r hello生成重定位节

图5.5-重定位节
5.4 hello的虚拟地址空间
使用edb加载hello,查看hello的虚拟地址空间,可以发现,虚拟地址空间从0x7dedd000到0x7deddff0

图5.6-hello的虚拟空间
5.5 链接的重定位过程分析
(以下格式自行编排,编辑时删除)
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
采用objdump 而来的反汇编代码已经有了确定的虚拟地址,即完成了反汇编,而hello.o中的代码的虚拟地址均为0;另外反汇编代码多了很多节:
.interp:保存ld.so的路径
.gnu.hash:GNU拓展的符号的哈希表
.dynsym:运行时/动态符号表
.rela.dyn:运行时/动态重定位表
.plt:动态链接-过程链接表
.rela.plt:.plt节的重定位条目
.init:程序初始化需要执行的代码
.fini:当程序正常终止时需要执行的代码

图5.7-重定位(1)

图5.8-重定位(2)
5.6 hello的执行流程
依次执行下列函数:
_dl_start:0x7ffff7de3630

dl-init.:0x7ffff7fe0c20

_start:0x555555555060

__libc_start_main:0x7ffff7de4f90

_setjm:0x7ffff7e03c80

main:0x555555555149

exit:0x7ffff7e07a40

图5.9-各个主要函数的地址
5.7 Hello的动态链接分析
分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。
动态链接就是把程序拆分,在运行的时候才链接到一起。对于动态共享链接库中PIC函数,编译器没有办法预测函数的运行时地址,所以需要添加重定位记录,等待动态链接器处理,然后动态链接器在程序加载的时候再解析它。GNU编译系统使用延迟绑定(lazybinding),将过程地址的绑定推迟到第一次调用该过程时。这一过程是通过GOT和PLT实现的,GOT负责存放函数目标地址,PLT使用GOT中地址跳转到目标函数。
如图,GOT起始表位置是0x404000

图5.10-动态链接项目
5.8 本章小结
在本章中介绍了链接的概念与作用,详细介绍了hello.o的ELF格式和各个节的含义,并且用edb分析了hello的虚拟地址空间、重定位过程、执行流程、动态链接过程。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
概念:进程是程序执行时期的实例,系统中的每个程序都运行在某个进程的上下文中。上下文是由程序正确运行所需的状态组成的。包括存放在内存中的程序的代码和数据等。
作用:进程作为一种抽象,给我们提供假象:程序好像是系统中当前运行的唯一程序一样,而且好像是独占的使用处理器和内存。
6.2 简述壳Shell-bash的作用与处理流程
作用:为用户提供了一个界面以访问操作系统内核的服务。是一个交互型应用级程序。
处理流程:
1.读取输入的命令
2.对输入内容进行分割,获取参数
3.判断是否是内置命令
4.如果不是内置命令,就调用fork函数,新建一个子进程,然后execve运行程序
5.时刻监控键盘的输入信号,并对其进行相应的处理
6.3 Hello的fork进程创建过程
shell通过fork()新建一个子进程,这个子进程与父进程除了PID以外,子进程代码段、段、数据段、共享库、用户栈、任何打开文件描述符与父进程完全相同,但是同时又是独立于父进程的。他们是并发运行的独立进程。
当运行hello.c的时候,键盘输入 ./hello 120L022225 刘慧新 1,shell按照上述处理流程,解析命令,分析到hello不是内置命令,就fork子进程,然后调用execve运行hello。
6.4 Hello的execve过程
当命令行的指令不是内置命令,shell就把它当作一个需要execve运行的程序,会调用execve在当前子进程的上下文加载并运行一个新程序。创建一组新的代码、数据、堆和栈段。

图6.1-Ubuntu下进程的虚拟空间
6.5 Hello的进程执行
进程上下文信息:上下文就是内核重新启动一个被抢占的进程所需要的状态,它由 通用寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内 核数据结构等对象的值构成。
进程时间片:一个进程执行它的控制流的一部分的每一时间段叫做时间片。
在hello进程执行过程中,首先为hello分配了前文所提到的虚拟地址空间,并且hello的代码节和数据节已经存入其中;运行时首先处于用户模式,进行程序执行,当调用sleep函数后,进程进行上下文切换,保存原有上下文,加载即将执行的上下文,然后进入内核模式,,sleep结束后,恢复原有上下文,再度进行切换,将当前进程控制权移交给其他进程。这个过程中定时器从hello被挂起开始计时,当定时器发出中断信号时,内核执行信号处理,然后再次运行hello程序。

图6.2-上下文切换
6.6 hello的异常与信号处理
(以下格式自行编排,编辑时删除)
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
异常总共有四种,分别是中断、陷阱、鼓掌、终止:
| 同步/异步 | 原因 | 返回 | |
| 中断 | 异步 | 来自I/O设备的信号 | 下一条指令 |
| 陷阱 | 同步 | 故意的异常 | |
| 故障 | 潜在的可恢复的 | 当前指令 | |
| 终止 | 不可恢复的 | 直接终止 |
hello执行过程中可能出现:由外部I/O设备引起的中断异常;执行到sleep函数时会引起陷阱异常。
1.正常运行:

图6.3-正常运行
2.乱按键盘:
乱按只是将屏幕的输入缓存到 stdin

图6.4-乱按键盘
3.按ctrl+c
Ctrl+c会发送SIGINT信号,该信号的处理程序是终止前台作业,所以由ps看到,hello并不在前台进程组之中

图6.5-按crtl+c
4.按ctrl+z
ctrl+z会挂起前台进程,由ps看到hello仍然在进程组中

图6.6-按ctrl+z
6.7本章小结
本章介绍了进程的概念和作用、shell的作用和一般处理流程,通过对hello例程的分析,介绍了fork、execve的执行过程,还讨论了进程执行过程中可能出现的各种异常。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:程序经过编译后出现在汇编代码中的地址。逻辑地址用来指定一个操作数或者是一条指令的地址。是由一个段标识符加上一个指定段内相对地址的偏移量,表示为 [段标识符:段内偏移量]。
线性地址:也叫虚拟地址,和逻辑地址类似,也是一个不真实的地址,如果逻辑地址是对应的硬件平台段式管理转换前地址的话,那么线性地址则对应了硬件也是内存的转换前地址。
虚拟地址:也就是线性地址。
物理地址:CPU 通过地址总线的寻址,找到真实的物理内存对应地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
实模式下:逻辑地址=线性地址=物理地址
保护模式下:线性地址=段选择符+段内偏移地址
7.3 Hello的线性地址到物理地址的变换-页式管理
线性地址到物理地址需要通过页表的查询,线性地址分为两部分,虚拟页号(VPN)和虚拟页偏移量(VPO),其中VPO也是物理页便宜量,二者是相等的,现在需要将VPN通过查询转换为物理页号PPN,在结合后半部分的物理页偏移量PPO,就是物理地址。

图7.1-线性地址到物理地址
7.4 TLB与四级页表支持下的VA到PA的变换
假设前提:虚拟地址空间48位,物理地址空间52位,页表大小4KB,4级页表。TLB4路16组相联。由一个页表大小4KB,一个PTE条目8B,共512个条目,使 用 9 位二进制索引,一共 4 个页表共使用 36 位二进制索引,所以VPN共36位,因为VA共 48位,所以VPO占12位;因为TLB共16组,所以 TLBI 需 4 位,因为 VPN共36 位,所以TLBT占32位。
变换地址时,CPU产生一个虚拟地址,MMU从TLB中取出相应的PTE。如果命中,则得到对应的物理地址。如果不命中,VPN会被分成4个部分。MMU向页表中查询,CR3确定第一级页表的起始地址,VPN1确定在第一级页表中的偏移量,查询出 PTE,如果在物理内存中且权限符合,确定第二级页表的起始地址,以此类推,最终在第四级页表中查询到PPN,与VPO组合成 PA,并且向TLB中添加条目。

图7.2-多级页表
7.5 三级Cache支持下的物理内存访问
前提如下:L1 Cache 是8路64组相联,块大小为64B。由于有64组,所以组索引CI需要6 bit,块大小为64B故组内偏移CO需要6bit。因为PA共52 bit所以剩余部分CT共40 bit。
开始访问时,发送PA给L1,如果L1中没有,就向下查找L2、L3,如果找到就根据偏移量CO找到地址取出数据返回。

图7.3-三级cache
7.6 hello进程fork时的内存映射
当进程调用fork 函数时,内核为新进程创建各种数据结构,为了给这个新进程创建虚拟内存,它创建了当前进程的 mm_struct、区域结构和页表的原样副本。它将这两个进程的每个页面都标记为只 读,并将两个进程中的每个区域结构都标记为私有的写时复制。
7.7 hello进程execve时的内存映射
execve函数加载并运行hello的步骤如下:
1.删除当前的用户区域
2.映射私有区域:为新程序的代码、数据、bss和栈区域创建新的区域结构。
3.映射共享区域:hello程序与标准C库链接,这些对象动态链接到这个程序,然后再映射到用户虚拟地址空间中的共享区域内。
4.设置程序计数器。execve做得最后一件事情是设置当前进程上下文中的程序计数器,使之指向代码区域的入口点。

图7.4-execve内存映射
7.8 缺页故障与缺页中断处理
缺页故障:当指令引用一个相应的虚拟地址,而与改地址相应的物理页面不再内存中,会触发缺页故障。
缺页故障的时候,会首先判断该虚拟地址是否合法,在判断该访问是否合法(是否有相应权限),最后当上述都合法时,选择牺牲一个现有页面,替换目标页面。
然后控制返回给引起缺页故障的指令。当指令再次执行时,相应的物理页面已经驻留在内存中,因此指令可以没有故障的运行完成。

图7.5-缺页故障
7.9动态存储分配管理
动态内存分配器维护着一个进程的虚拟内存区域,称为堆。分配器将堆视为 一组不同大小的块的集合来维护。每个块就是一个连续的虚拟内存片,要么是已 分配的,要么是空闲的。
分配器分为两种基本风格:显式分配器、隐式分配器。
隐式分配器:要求分配器检测一个已分配块何时不再使用,那么就释放这个块,自动释放未使用的已经分配的块的过程叫做垃圾收集。
显式分配器:要求应用显式地释放任何已分配的块。
7.10本章小结
本章主要介绍了hello的存储器地址空间、段式管理和页式管理、介绍了四种地址空间的差别和地址的相互转换,介绍了VA到PA的变换、物理内存访问、三级cashe的物理内存访问以及进程fork、execve时的内存映射,缺页故障与缺页中断处理,动态存储分配管理等。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:文件
设备管理:unix io接口
所有的 IO 设备都被模型化为文件,而所有的输入和输出都被 当做对相应文件的读和写来执行,这种将设备优雅地映射为文件的方式,允许 Linux 内核引出一个简单低级的应用接口,称为 Unix I/O
文件的类型:
1.普通文件(regular file):包含任意数据的文件。
2.目录(directory):包含一组链接的文件,每个链接都将一个文件名映射到一个文件(他还有另一个名字叫做“文件夹”)。
3.套接字(socket):用来与另一个进程进行跨网络通信的文件
4.命名通道
5.符号链接
6.字符和块设备
8.2 简述Unix IO接口及其函数
Unix I/O 接口:
(1)打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想 要访问一个 I/O 设备,内核返回一个小的非负整数,叫做描述符,它在 后续对此文件的所有操作中标识这个文件,内核记录有关这个打开文 件的所有信息。
(2)Shell 创建的每个进程都有三个打开的文件:标准输入,标准输出,标 准错误。 (3)改变当前的文件位置:对于每个打开的文件,内核保持着一个文件位 置 k,初始为 0,这个文件位置是从文件开头起始的字节偏移量,应用 程序能够通过执行 seek,显式地将改变当前文件位置 k。
(4)读写文件:一个读操作就是从文件复制 n>0 个字节到内存,从当前文 件位置 k 开始,然后将 k 增加到 k+n,给定一个大小为 m 字节的而文 件,当 k>=m 时,触发 EOF。类似一个写操作就是从内存中复制 n>0 个字节到一个文件,从当前文件位置 k 开始,然后更新 k。
(5)关闭文件,内核释放文件打开时创建的数据结构,并将这个描述符恢 复到可用的描述符池中去。
Unix I/O 函数:
(1)int open(char* filename,int flags,mode_t mode) ,进程通过调用 open 函 数来打开一个存在的文件或是创建一个新文件的。 open函数将filename 转换为一个文件描述符,并且返回描述符数字。
(2)int close(fd),fd 是需要关闭的文件的描述符,close 返回操作结果。
(3) ssize_t read(int fd,void *buf,size_t n),read 函数从描述符为 fd 的当前文 件位置赋值最多 n 个字节到内存位置 buf。返回值-1 表示一个错误,0 表示 EOF,否则返回值表示的是实际传送的字节数量。
4) ssize_t wirte(int fd,const void *buf,size_t n),write 函数从内存位置 buf 复制至多 n 个字节到描述符为 fd 的当前文件位置。
8.3 printf的实现分析
printf函数:

图8.1-printf内部
write系统函数:

图8.2-write内部
syscall:

图8.3-syscall内部
在printf中调用系统函数write(buf,i)将长度为i的buf输出,在write函数中,将栈中参数放入寄存器,ecx是字符个数,ebx存放第一个字符地址,
int INT_VECTOR_SYS_CALLA代表通过系统调用syscall。syscall将字符串中的字节从寄存器中通过总线复制到显卡的显存中,显存中存储的是字符的ASCII码。
字符显示驱动子程序将通过ASCII码在字模库中找到点阵信息将点阵信息存储到vram中。
显示芯片会按照一定的刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
getchar函数:

图8.4-getchar内部
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章主要介绍了 Linux 的 IO 设备管理方法、Unix IO 接口及其函数,分析了 printf 函数和 getchar 函数的实现。
(第8章1分)
结论
首先通过键盘输入代码,生成hello.c文件
预处理:将hello.c调用的所有外部的库展开合并到一个hello.i文件中
编译:将hello.i编译成汇编文件hello.s
汇编:将hello.s会变成为可重定位目标文件hello.o
链接:链接器对hello.o进行链接得到可执行文件hello,此时hello已经被操作系统加载和执行
运行hello:在终端输入命令,shell进程调用fork为hello创建一个子进程,随后调用execve启动加载器,加映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入main函数
内存使用:MMU 将程序中使用的虚拟内存地址通过页表映射成物理地址。printf 会调用 malloc 向动态内存分配器申请堆中的内存
信号:在运行时输入Ctrl+c,内核会发送SIGINT信号给进程并终止前台程序。当输入Ctrl+z时,内核会发送SIGTSTP信号给进程,并将前台程序停止挂起。
终止:当子进程执行完成时,内核安排父进程回收子进程,将子进程的退出状态传递给父进程。内核删除为这个进程创建的所有 数据结构。
计算机系统的设计思想和实现都是基于抽象实现的。从最底层的信息的表示用二进制表示抽象开始,到实现操作系统管理硬件的抽象:进程是对处理器、主存和I/O设备的抽象。虚拟内存是对主存和磁盘设备的抽象。文件是对I/O设备的抽象。
通过对hello的一生的学习、梳理,深入理解了现代计算机操作系统各部分之间的协作、调用,对系统各部分的设计思想、处理方式有了基本的认识了解。
(结论0分,缺失 -1分,根据内容酌情加分)
本文详尽分析了从源代码hello.c到最终执行的hello进程的完整过程,涵盖预处理、编译、汇编、链接以及进程管理、存储管理和IO管理等多个阶段。通过Ubuntu系统,展示了cpp、gcc、as、ld等工具的使用,解析了hello.o的ELF格式,探讨了虚拟地址空间、重定位、动态链接、进程创建、内存映射和IO设备管理。深入理解了现代计算机操作系统如何协调硬件和软件资源,执行一个简单的程序。
1335

被折叠的 条评论
为什么被折叠?



