目 录
2.2在Ubuntu下预处理的命令.......................................................................... - 5 -
3.2 在Ubuntu下编译的命令............................................................................. - 6 -
4.2 在Ubuntu下汇编的命令............................................................................. - 7 -
5.2 在Ubuntu下链接的命令............................................................................. - 8 -
5.3 可执行目标文件hello的格式.................................................................... - 8 -
6.2 简述壳Shell-bash的作用与处理流程..................................................... - 10 -
6.3 Hello的fork进程创建过程..................................................................... - 10 -
6.6 hello的异常与信号处理............................................................................ - 10 -
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 -
8.1 Linux的IO设备管理方法.......................................................................... - 13 -
8.2 简述Unix IO接口及其函数....................................................................... - 13 -
第1章 概述
1.1 Hello简介
首先hello.c经过预处理,编译,汇编,链接,形成hello的可执行文件,在bash中,操作系统的进程管理程序为hello用fork创建新进程,用execve加载hello程序,进行虚拟内存映射,分配时间片,在上下文中运行hello程序,CPU对每一条指令取指译码执行,完成整个程序,最后bash回收运行hello的进程
1.2 环境与工具
Windows 10 / Ubuntu 18.04.2 LTS 双系统
i5-8250u 8g ram 512g disk
gcc codeblocks objdump gdb
1.3 中间结果
hello.c :源程序
hello.i:预处理文件
hello.s 根据hello.i编译得到的.s文件
hello.o:可重定位目标程序
helloelf.txt:根据hello.o得到的.elf文件
hello_elf.txt:可执行目标文件的.elf文件
1.4 本章小结
第一章首先对hello进行了简要的介绍,随后概述了本实验的环境与工具和一些中间文件的信息
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
预处理器(cpp)根据义字符#开头的命令,修改原始的c程序。比如将hello.c中第一行#include<stdio.h>命令告诉我们预处理器读取系统头文件stdio.h中的内容,并把它直接插入到程序文本中,得到了另一个C程序,以.i为文件扩展名
2.2在Ubuntu下预处理的命令
cpp hello.c > hello.i
2.3 Hello的预处理结果解析
预处理器对.c文件中以#开头的语句进行展开,对hallo.c中的stdio.h,unistd.h,stdlib.h等依次进行展开,使得hello.i中的前面部分为展开后的内容,将hello的代码保存在文件的最后
2.4 本章小结
本章介绍了预处理的概念与作用,说明了Ubuntu下的预处理命令,同时对hello的预处理结果进行了解析
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译器(ccl)将文本文件(hello.i)翻译成文本文件(hello.s),它包含一个汇编语言程序,程序中包括函数main的定义,简而言之就是将.i文件中的c代码翻译成汇编代码
3.2 在Ubuntu下编译的命令
gcc -S hello.i -o hello.s
3.3 Hello的编译结果解析
(以下格式自行编排,编辑时删除)
hello.c中源代码如下
编译后.s汇编代码如下
3.3.1:数据
hello.s中的数据包括局部变量int i,字符串,字符数组argv
其中,i被保存在-4(%rbp)内,如图
i作为for循环的迭代计数,被初始化为0,之后跳转到.L3,.L3中将i与7进行比较,如果i小于等于7则进入.L4中的循环体,否则跳出循环执行getchar
argv[1]和argv[2]都声明在.rodata只能读数据段中,并且给出了字符串的编码。图示中字符串"用法: Hello 学号 姓名 秒数!\n"和"Hello %s %s\n"分别被声明在.LC0和.LC1段中的字符串,如图所示。
3.3.2:赋值
如上节所示,利用movl $0 -4(%rbp)将立即数0传给i作为初值
其中movl传输一个32为位的字,符合int的大小
3.3.3:类型转换
hello.c中调用了函数atoi将字符串转换为int型整数,.s中汇编代码如上图所示,首先使rax指向argv[3]的首地址,再将其赋给rdi作为参数调用atoi函数,将转换后的结果保存在rax中
3.3.4:算术操作
该程序内的算数操作仅有循环内的i++
.L4内末尾最后一行的addl描述了这个操作,l代表操作数的大小为一个字
3.3.5:关系操作
程序中涉及的关系操作有:
argc!=4 :
比较存在-20(%rbp)中的argc和立即数4,若不相等跳转到.L2,否则继续运行进入if语句内
i<8 :
对于循环内的i,汇编代码中将i与立即数7进行比较,若i小于或等于7,则跳转进入循环体.L4否则跳出循环
3.3.6:数组/指针/结构操作
程序中设涉及的数组仅有argv代表的字符指针数组
这是在调用第二个printf处的汇编代码,首先将argv的首地址-32(%rbp)传给%rax,再将这个首地址加16,也就是得到了argv[2]字符指针地址的值,将这个值解引用得到字符指针的值保存在%rdx中作为第三个参数
其次又进行类似的操作,将argv首地址的值加8得到argv[1]字符指针的地址,解引用得到字符指针的值,将这个值赋给%rsi作为第二个参数
3.3.7:控制转移
见3.3.5关系操作
3.3.8:函数操作
64位程序可通过寄存器传递函数参数,hello程序中涉及的函数有
printf:两次,第一次
将字符串"用法: Hello 学号 姓名 秒数!\n"的首地址传给%rdi作为第一个也是唯一一个参数调用puts
第二次,见于3.3.6数组指针操作
exit :将立即数$1作为参数调用exit退出程序
atoi :
将argv首地址加24解引用得到argv[3]的首地址作为参数调用atoi函数,并在之后将返回值保存在%rax内
sleep : 在调用atoi函数后得到保存在%rax的返回值,将这个值作为参数调用函数sleep
3.4 本章小结
本章主要介绍了编译的原理及作用,同时对编译后得到的汇编代码中的几种对应操作进行了较为详细的解析
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在hello.o文件中,hello.o是一个二进制文件
4.2 在Ubuntu下汇编的命令
as hello.s –o hello.o或gcc –c hello.s –o hello.o
4.3 可重定位目标elf格式
分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。
分析内容:
ELF头:用于概述ELF文件各信息的段,含帮助链接器语法分析和解释条目文件的信息,总共占16个字节。从Magic开始,描述了生成该文件的的系统的大小和字节顺序,ELF头剩下的部分包含帮助链接器语法分析和解释目标文件的信息
节头:描述.o文件出现的各节的名称,类型,地址,偏移量,大小, 全体大小,旗标,链接,信息,对齐等信息
重定位节 '.rela.text':包含.text节需要进行重定位的信息,在.o生成可执行文件的时候会被修改,包括图中的八个部分
重定位节 '.rela.eh_frame':是ef_frame节的重定位信息
符号表 '.symtab':存放在程序中定义和引用的函数和全局变量信息
4.4 Hello.o的结果解析
与第三章hello.s中的不同:
1.机器语言的构成改变为行号+机器指令+汇编代码(+注释)的形式
2.操作数寻址由助记符+%rip改变为十六进制偏移量+%rip,例如hello.s中第一个字符串地址表示为.LC0(%rip),而返回编后的结果为0x0(%rip),其次对于.s文件中的十进制立即数全部改为16进制立即数
3.分支转移跳转的地址由原来的助记符改为具体的地址,例如jmp .L3改为jmp 7c <main+0x7c>
4.函数调用调用的函数地址也从函数名@plt改为了call下一条指令的具体地址,如call puts@PLT改为callq 21 <main+0x21>
4.5 本章小结
本章讲述了汇编的概念及其作用,介绍了ELF文件各部分的内容,并对汇编后生成的.o文件进行反汇编后得到的文件与原来的.s文件进行了比对,分析了它们的不同之处
(第4章1分)
第5章 链接
5.1 链接的概念与作用
链接是指程序的各模块之间传递参数和控制命令,并把它们组成一个可执行的整体的过程。c语言程序要c标准库进行链接才能使用标准库函数,将.o文件进行链接之后生成可执行文件,才能被加载在内存中被操作系统执行
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的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
readelf -a hello > helloelf.txt 后得到文件helloelf.txt
ELF文件的信息如图所示,各节的含义参照4.3章的部分
5.4 hello的虚拟地址空间
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
。。。。。。edb整不明白,第五章后面内容放弃
5.5 链接的重定位过程分析
(以下格式自行编排,编辑时删除)
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
5.6 hello的执行流程
(以下格式自行编排,编辑时删除)
使用edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。
5.7 Hello的动态链接分析
(以下格式自行编排,编辑时删除)
分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。
5.8 本章小结
(以下格式自行编排,编辑时删除)
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量
进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程
6.2 简述壳Shell-bash的作用与处理流程
shell是一个交互型的应用级程序,它代表用户运行其他程序
shell执行一系列的读/求值步骤,然后终止,读步骤读取来自用户的每一个命令行,求值步骤解析命令行,如果输入的命令是一个可执行程序,它会用fork创建一个进程,然后用execve加载并运行程序,运行完后回收该进程
6.3 Hello的fork进程创建过程
父进程shell通过调用fork函数创建一个子进程,新创建的子进程几乎但不完全与父进程相同。子进程得到与父进程用户级虚拟地址空间相同但是独立的一份副本,包括代码和数据段,堆,共享库,以及用户栈,同时子进程还获得与父进程任何打开文件描述符相同的副本,创建后hello和shell并发执行
6.4 Hello的execve过程
shell通过调用execve函数在子进程的上下文加载并运行hello
execve函数加载并运行可执行文件hello,且带参数列表argv和环境变量列表envp,函数execve调用驻留在存储器中的加载器来运行hello,加载器删除子进程现有的虚拟内存段,创建一组新的代码,数据段,堆栈段,新的堆栈段被初始化为0,通过将虚拟地址空间中的页映射到可执行文件的页大小的片,新的代码和数据段被初始化为可执行文件的内容,最后,加载器跳转到_start地址,它最终会调用应用程序的mian函数
6.5 Hello的进程执行
shell用fork创建子进程并用execve加载运行hello程序后,hello进程开始执行
hello进程运行在用户模式中,cpu通过取指译码等等步骤逐步运行hello的代码,在这个过程中hello进程可能被分为不同的时间片,在一定的时间内被暂时挂起轮到其他进程占用cpu,此时操作系统内核会进行上下文切换,用调度器重新开始一个先前被抢占的进程,包括三个步骤
1)保存当前进程的上下文
2)恢复某个先前被枪战的jincheng被保存的上下文
3)将控制传递给这个新恢复的进程
此外,在hello调用sleep函数的时候也会进行上下文切换,切换时进入信号处理程序,这个过程在内核模式下运行,调用sleep后暂时挂起进入后台,等待返回到hello进程,再次调用sleep...一共循环这个过程八次,最后main函数返回结束进程等待内核回收
6.6 hello的异常与信号处理
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
如图显示了hello进程在pstree中的位置
Ctrl+z暂停进程:父进程收到SIGSTP信号,处理是打印屏幕回显,暂时挂起hello进程。
Ctrl+c终止进程:父进程收到SIGINT信号,处理是使hello进程终止并将其回收
6.7本章小结
本章中较为详尽地讲述了hello进程从始至终被shell创建,加载运行的过程,hello从可执行程序到被加载运行再到回收,每一个步骤都是系统内核,虚拟内存等等方面软硬件共同协调配合完成的
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:在有地址变换功能的计算机中,访内指令给出的地址 (操作数) 叫逻辑地址,也叫相对地址。要经过寻址方式的计算或变换才得到内存储器中的实际有效地址,即物理地址。
线性地址:线性地址(Linear Address)是逻辑地址到物理地址变换之间的中间层。在分段部件中逻辑地址是段中的偏移地址,然后加上基地址就是线性地址。
虚拟地址:虚拟地址 (virtual address): CPU启动保护模式后,程序运行在虚拟地址空间中,这样程序访问存储器所使用的逻辑地址称为虚拟地址,CPU通过虚拟地址访问主存,在被送到内存之前通过MMU翻译成物理地址
物理地址:在存储器里以字节为单位存储信息,为正确地存放或取得信息,每一个字节单元给以一个唯一的存储器地址,称为物理地址(Physical Address),又叫实际地址或绝对地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
段式管理:逻辑地址->线性地址==虚拟地址
逻辑地址空间:段地址:偏移地址
线性地址空间:非负整数地址的有序集合
在保护模式下,段地址+偏移地址=线性地址,而Linux下的偏移地址为0x00000000,因此Linux下的逻辑地址等于线性地址
7.3 Hello的线性地址到物理地址的变换-页式管理
页式管理:虚拟地址->物理地址
线性地址通过页表查找来对应物理地址,页表是当前任务的线性地址转物理地址的查找表。
分页的机制是将内存划分成大小固定的若干单元,每个单元称为一页,每页包含4k字节的地址空间,这样每一页的起始地址都是4k字节对齐的,页表实质上就是一个虚拟页到物理页的映射,虚拟地址空间每个页在页表中一个固定的偏移量处都有一个页表头目,页表头目由一个有效位和地址字段组成,有效位表明该虚拟页是否被缓存,如果设置了有效位,地址字段就表示相应的物理页起始位置,通过这种方式完成线性地址到物理地址的转换
7.4 TLB与四级页表支持下的VA到PA的变换
TLB是一个小的,虚拟寻址的缓存,其中每一行都保存着一个由单个PTE组成的块,下述为一个从TLB中获取物理地址的过程:
1)CPU产生一个虚拟地址。
2)MMU从TLB中取出相应的PTE。
3)MMU将这个虚拟地址翻译成一个物理地址,并且将它发送到高速缓存/主存。
4)高速缓存/主存将所请求的数据字返回给CPU。
多级页表:
使用4级页表可对页表的寻址进行压缩,级数从低到高依次进行查找,虚拟地址中的VPN被划分为4部分,VPO保持不变。每个VPNi都是一个到第i级页表的索引。如果一级页表中的一个PTE为空,则不存在相应的二级页表,因此无需向后搜索
7.5 三级Cache支持下的物理内存访问
一个机器的高速缓存被组织成一个有S个高速缓存组的数组。每个组包含E个高速缓存行。每个行是由一个B字节的数据块组成的,行中的一个有效位指明这个行是否包含有意义的信息,还有t个标记位,它们唯一地标识存储在这个高速缓存行中的块。
组索引是一个无符号整数,通过组索引寻址到相应的组后检查组中的标记位,当且仅当设置了有效位并且该行的标记位与地址中的标记位相匹配时,组中的这一行才包含这个字,否则就产生一个不命中,每次不命中后,我们需要从下一级的存储结构中提取我们需要的数据块
7.6 hello进程fork时的内存映射
shell调用fork创建新进程后,为子进程分配一个唯一的PID,并为之创建新的独立的地址空间,并创建了当前进程的mm_struct、区域结构和页表的原样副本,它将两个进程中的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制
当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面。因此,也就为每个进程保持了私有地址空间的抽象概念
7.7 hello进程execve时的内存映射
加载并运行hello需要如下几个步骤
删除已存在的用户区域
映射私有区域
映射共享区域
设置程序计数器
7.8 缺页故障与缺页中断处理
DRAM缓存不命中称为缺页
地址翻译硬件从内存中读取某个虚拟地址对应的页表,从有效位推断出某个虚拟地址未被缓存,并且触发了一个缺页异常。缺页异常调用内核中的缺页异常处理程序,该程序会选择一个牺牲页,来替换为该虚拟地址所对应的物理页
当异常处理程序返回时,它会重新启动导致缺页的指令,该指令会把导致缺页的虚拟地址重发送到地址翻译硬件
7.9动态存储分配管理
动态存储可用内存空间被称为堆,它是一个进程的虚拟内存区域的一部分,由动态内存分配器维护。对于每一个进程,内核维护着一个变量brk,它指向堆的顶部
分配器将堆视为一组不同大小的块的集合来维护,每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的,已分配的显式地供程序实用,空闲块可用来分配
隐式空闲链表——带边界标签的隐式空闲链表分配器原理
假设想要释放的块为当前块。那么合并下一个空闲块很简单而且高效。当前块的头部指向下一个块的头部,可以检查这个指针以判断下一个块是否是空闲的。如果是,就将它的大小简单地加到当前块头部的大小上,这两个块在常数时间内被合并。给定一个带头的隐式空闲链表,唯一的选择将是搜索整个链表。记住前面块的位置,直到我们打到当前块。使用隐式空闲链表,这意味着每次调用free需要的时间都于堆的大小呈线性关系。即使使用更复杂精细的空闲链表组织,搜索时间也不会是常数。
边界标记技术,允许在常数时间内进行对前面块的合并,这种思想,是在每个块的结尾处添加一个脚部,其中脚部就是头部的一个副本。
如果每个块包括这样一个脚部,那么分配器就可以通过检查它的脚部,判断前面一个块的起始位置和状态,这个脚部总是在距当期块开始位置一个字的距离。
显式空闲链表是一种更好的方式是将空闲块组织为某种形式的显式数据结构。因为根据定义,程序不需要一个空闲块的主体,所以实现这个数据结构的指针可以存放在这些空闲块的主体里面。例如,堆可以组织成一个双向空闲链表,在每个空闲块中,都包含一个pred(前驱)和succ(后继)指针。
显示空闲链表使用双向链表而不是隐式空闲链表,使首次适配的分配时间从块总数的线性时间减少到了空闲块数量的线性时间。不过,释放一个块的时间可以是线性的,也可能是常数,这取决于我们所选择的空闲链表中块的排序策略。
一种方法是用后进先出(LIFO)的顺序维护链表,将新释放的块位置放在链表的开始出。另一种方法是按照地址顺序来维护链表,其中链表中每个块的地址都小于它后继的地址。
7.10本章小结
本章主要讲述了逻辑地址,线性地址,虚拟地址及物理地址之间的相互转换,描述了从线性地址到物理地址的页式管理方式,以及TLB,多级页表等方式对减小内存开支的左右。最后分析了hello进程执行过程中的内存映射和动态内存分配的情况
(第7章2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
所有的IO设备都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行,这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单低级的应用接口,称为Unix I/O。
设备的模型化:文件
设备管理:unix io接口
8.2 简述Unix IO接口及其函数
打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备,内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件,内核记录有关这个打开文件的所有信息。
函数:
进程通过open创建一个新文件或者打开一个已存在的文件,它将filename转换为一个文件描述符,并返回描述符数字。flag函参指明了进程打算如何访问这个文件。mode参数制定了新文件的访问权限位。
Shell创建的每个进程都有三个打开的文件:标准输入,标准输出,标准错误。
改变当前的文件位置:对于每个打开的文件,内核保持着一个文件位置k,初始为0,这个文件位置是从文件开头起始的字节偏移量,应用程序能够通过执行seek,显式地将改变当前文件位置k。
读写文件:一个读操作就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n,给定一个大小为m字节的而文件,当k>=m时,触发EOF。类似一个写操作就是从内存中复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k。
函数:
应用程序分别调用read和write函数来执行输入输出
关闭文件,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中去。
函数:
最后,进程通过调用close函数关闭一个已打开的文件,关闭一个已关闭的描述符时会出错
8.3 printf的实现分析
https://www.cnblogs.com/pianist/p/3315801.html
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
首先来看看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;
}
fmt是一个const char *型指针,(char*)(&fmt) + 4) 表示的是...中的第一个参数。
然后vsprintf函数的内容为:
int vsprintf(char *buf, const char *fmt, va_list args)
{
char* p;
char tmp[256];
va_list p_next_arg = args;
for (p=buf;*fmt;fmt++) {
if (*fmt != '%') {
*p++ = *fmt;
continue;
}
fmt++;
switch (*fmt) {
case 'x':
itoa(tmp, *((int*)p_next_arg));
strcpy(p, tmp);
p_next_arg += 4;
p += strlen(tmp);
break;
case 's':
break;
default:
break;
}
}
return (p - buf);
}
printf函数中有语句i = vsprintf(buf, fmt, arg);并最终printf返回i,可以想到vsprintf函数返回的就是要打印出来的字符串的长度,因此,vsprintf的作用就是格式化。它接受确定输出格式的格式字符串fmt。用格式字符串对个数变化的参数进行格式化,产生格式化输出。
最后,write函数对buf中的i个字节进行输出
8.4 getchar的实现分析
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章主要讲述了 Linux的IO设备管理方法,简述了Unix IO接口及其函数,同时对printf和getchar函数的实现进行了一定的分析
(第8章1分)
结论
用计算机系统的语言,逐条总结hello所经历的过程。
你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
hello的历程:
预处理:hello.c中所有外部的库展开合并到hello.i文件中
编译:将hello.i编译成为汇编文件hello.s,将c代码转换为汇编指令
汇编:将hello.s汇编成为可重定位目标文件hello.o,将汇编指令转换为机器级二进制指令
链接:将hello.o与可重定位目标文件和动态链接库链接成为可执行目标程序hello
加载:在shell中键入./hello 1183710218 石文杰 3后,shell调用fork函数创建子程序,再调用execve函数将hello加载到内存,并跳转到main的入口
执行:由CPU为其分配时间片,在一个时间片中,hello享有CPU资源,顺序执行自己的控制逻辑流
访存:CPU发送虚拟内存到MMU,MMU通过查询页表找到对应虚拟页映射的物理页,翻译虚拟地址为物理地址
动态内存分配:在调用printf函数时,printf函数会调用malloc函数动态分配hello进程的虚拟空间中的堆
结束:hello进程结束后会由shell回收,并删除hello进程的数据
感受:一个简单的hello程序需要经历如此复杂的过程才能完整地在计算机系统中执行,这个过程离不开精密复杂的软硬件之间的相互协作,深入理解hello的生命历程,深入理解计算机系统,驱散了我们对程序运行认识的迷雾,使我们对自己编写的程序有了更深入清晰的认识
(结论0分,缺失-1分,根据内容酌情加分)
来源:https://www.cnblogs.com/qaqbeb-2020/p/12153203.html