HIT盘算机体系——步伐人生
https://img-blog.csdnimg.cn/direct/900fa9020bd644a4a23075ede76727b7.png
盘算机体系
大作业
题 目 步伐人生-Hello’s P2P
专 业 信息安全
学 号 2022112014
班 级 2203201
学 生 李佳熹
指 导 教 师 史先俊
盘算机科学与技能学院
2024年5月
摘 要
本文重要报告一个基本的C语言步伐履历预处理、编译、汇编、链接过程形成盘算机可以加载到内存并执行的可执行目的文件的过程和细节。接着进一步展开说明这个执行文件执行过程中依靠shell创建进程、分配存储空间、进行I/O管理的技能方法和细节。
关键词:盘算机体系,编译原理,进程,I/O管理,虚拟内存;
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目 录
第1章 概述
1.1 Hello简介
1.2 情况与工具
1.3 中心结果
1.4 本章小结
第2章 预处理
2.1 预处理的概念与作用
2.2在Ubuntu下预处理的下令
2.3 Hello的预处理结果剖析
2.4 本章小结
第3章 编译
3.1 编译的概念与作用
3.2 在Ubuntu下编译的下令
3.3 Hello的编译结果剖析
3.4 本章小结
第4章 汇编
4.1 汇编的概念与作用
4.2 在Ubuntu下汇编的下令
4.3 可重定位目的elf格式
4.4 Hello.o的结果剖析
4.5 本章小结
第5章 链接
5.1 链接的概念与作用
5.2 在Ubuntu下链接的下令
5.3 可执行目的文件hello的格式
5.4 hello的虚拟地址空间
5.5 链接的重定位过程分析
5.6 hello的执行流程
5.7 Hello的动态链接分析
5.8 本章小结
第6章 hello进程管理
6.1 进程的概念与作用
6.2 简述壳Shell-bash的作用与处理流程
6.3 Hello的fork进程创建过程
6.4 Hello的execve过程
6.5 Hello的进程执行
6.6 hello的异常与信号处理
6.7本章小结
第7章 hello的存储管理
7.1 hello的存储器地址空间
7.2 Intel逻辑地址到线性地址的变换-段式管理
7.3 Hello的线性地址到物理地址的变换-页式管理
7.4 TLB与四级页表支持下的VA到PA的变换
7.5 三级Cache支持下的物理内存访问
7.6 hello进程fork时的内存映射
7.7 hello进程execve时的内存映射
7.8 缺页故障与缺页停止处理
7.9动态存储分配管理
7.10本章小结
第8章 hello的IO管理
8.1 Linux的IO装备管理方法
8.2 简述Unix IO接口及其函数
8.3 printf的实现分析
8.4 getchar的实现分析
8.5本章小结
结论
附件
参考文献
第1章 概述
1.1 Hello简介
根据Hello的自白,利用盘算机体系的术语,简述Hello的P2P,020的整个过程。
Hello的P2P(From Program to Process)过程,即从步伐(Program)到进程(process)的过程。首先,源文件hello.c必要通过编译器进行预处理、编译、汇编和毗连等过程,天生可执行文件。然后就可以在shell中执行这个文件,进程空间由shell进行分配。
Hello的020(From Zero-0 to Zero-0)过程,即从零开始到归零的过程,代表着整个步伐进程的生命周期。最初,内存中没有Hello步伐的内容,步伐开始时,操作体系通过fork体系调用建立子进程,并利用execve体系调用将Hello步伐加载到内存中。操作体系内核的进程控制器为Hello进程分配时间片,进程得以开始执行,同时操作体系还分身着步伐的内存管理、I/O装备等。步伐竣事后,shell的父进程回收Hello进程,并在操作体系内核中删除相干的指令和数据,统统又归为零。
1.2 情况与工具
1.2.1 硬件情况
联想接济者2022,处理器:12th Gen Intel(R) Core(TM) i7-12700H
1.2.2 软件情况
Windows11 64位操作体系
1.2.3 开发工具
GCC、Visual Studio 2022、Ubuntu、Vmware
1.3 中心结果
文件名
介绍
hello.c
hello的源步伐
hello.i
源步伐预处理之后的ASCII文件
hello.s
源步伐经过编译后的汇编文件
hello.o
汇编文件汇编后的可重定位目的文件
hello
可重定向目的文件链接后的可执行目的文件
hello.elf
可重定位目的文件的ELF文件,便于检察ELF格式
hello_re.elf
可执行目的文件的ELF文件,便于检察ELF格式
hello.asm
可执行目的文件反汇编之后的汇编文件
hello_o.asm
可重定向目的文件反汇编之后的汇编文件
1.4 本章小结
本章首先对Hello步伐的整个生命周期过程依托形象的P2P和020进行了概述,然后介绍了本次大作业利用的软硬件情况,列出了步伐运行过程中天生的一系列中心文件及其作用。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
2.1.1 预处理的概念
预处理是编译过程的第一个阶段,重要负责处理源代码中的预处理指令,并天生经过预处理后的新的源代码(ASCII码文件.i)
2.1.2 预处理的作用
预处理的作用重要有四条——头文件展开、去解释、宏替换和条件编译等。
[*]头文件展开
C语言的预处理指令“#include”会包含我们运行步伐过程中必要的诸多头文件,在步伐预处理时会将头文件中的相干内容(静态库存档文件的调用内容等)复制到代码文件中,以便于后续的函数和变量调用。
[*]去解释
编译器会将我们在编写代码过程中写入的解释全部替换为空格以忽略步伐以外的信息。
[*]宏替换
在C语言步伐中我们通常会通过“#define”定义很多宏变量,预处理器会在宏变量的作用范围(要么是定义之后的整个步伐,要么是“#undef”前)内,将所有出现宏定义的指定文本替换为对应值。
[*]条件编译
一样平常情况下整个步伐除解释行外的内容都要参加编译,但是有时步伐员希望对其中一部分内容在肯定条件下才进行编译,即条件编译。而编译器在预处理阶段就可以判定利用条件编译的代码块是否满意条件,不满意条件的代码块将会被裁剪,以减少步伐员后续对代码的维护量。
2.2在Ubuntu下预处理的下令
Ubuntu下对hello.c进行预处理并天生hello.i的下令为:
gcc -E hello.c -o hello.i
-E选项告知gcc编译器只对文件hello.c进行预处理,而不进行编译、汇编和毗连的后续操作。
2.3 Hello的预处理结果剖析
在Ubuntu下利用2.2中下令行预处理步伐,在同一文件夹下天生了hello.i文件:
https://img-blog.csdnimg.cn/direct/b154304d2578412f8b8fb77cd00d546d.png
打开hello.i进行检察:
https://img-blog.csdnimg.cn/direct/1aac96e7316e489699710ea1fdd0d875.png
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps4.jpg&pos_id=1RPe9eAg
通过以上两图可以看到,hello.i文件的头部几行为步伐的基本信息,而我们本身编写的步伐体代码内容位于整个文件的末了,中心的数千行是预处理器对头文件进行展开后的展开内容,例如,我们可以找到头文件stdio.h中有关基本输出函数printf的展开内容:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps5.jpg&pos_id=OMeJh9ej
而且步伐最初的解释部分已经消失。综上可以说明预处理完成了去解释和头文件展开的功能(本步伐没有宏定义,也暂未利用条件编译)。
2.4 本章小结
本章介绍了步伐编译过程中的第一步——预处理指令的概念和作用,并结合hello.c步伐进行预处理后的结果,观察、感受和分析预处理的各个作用的实现。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
3.1 编译的概念
编译(特指从.i文件天生.s文件的过程)指的是将预处理后的源文件转换为汇编语言文件的过程,编译器将高级语言代码翻译成与特定体系结构相干的汇编语言代码(机器代码的文本表示)。
3.2 编译的作用
1、转换为汇编语言
编译器通过汇编过程将 C 语言代码转换为汇编语言代码,可以方便步伐员明白步伐的底层运行原理,以及如何在盘算机上执行。
[*]平台无关性
编译器天生的汇编代码是机器级的,不依靠于特定的平台,能够保持平台无关性,便于移植,不过后续的汇编和链接过程可以将其转换为平台依靠的可执行文件。
[*]优化
编译器具有本身的优化能力,能够对代码的逻辑顺序进行简单的排序或重组,以提高步伐效率。
[*]调试
编译天生的汇编代码可以用于调试,检察汇编代码可以使步伐员了解步伐的机器级底层逻辑,并且编译器本身能够进行语法和语义的分析和报错。有助于提前发现漏洞,提高步伐质量。
3.2 在Ubuntu下编译的下令
在Ubuntu下对Hello步伐(只)进行编译过程的下令为:
gcc -S hello.i -o hello.s
该下令通过选项“-S”告诉编译器只对预处理后的源步伐hello.i执行编译这一个操作,不进行后续操作,并天生汇编步伐hello.s。
3.3 Hello的编译结果剖析
在Ubuntu下令行输入上述3.2中的指令,天生了hello.s汇编文件:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps6.jpg&pos_id=TV2d02TF
打开汇编文件,检察汇编代码并分析。
3.3.1 汇编代码的头部信息
汇编代码中main函数开始前的文本内容(在此称为“头部信息”)如下:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps7.jpg&pos_id=T7j5JfTW
各个指令对应功能如下:
[*]./file指令指定了源文件名
[*].text指令声明了接下来的指令是代码段
[*].section ./rodata指令定义了只读数据段
[*]./align 8 指令确保了下一个数据的地址是8字节对齐的
[*]L0和L1是两个局部标签,表示“:”下面给出的对应的只读数据段(详见3.3.2数据部分)
[*].global main指定了main是全局的
[*]./type main, @function指定了main类型是函数
3.3.2 数据部分
1、常量部分
首先在汇编代码头部定义了两个只读的字符串类型局部变量L0和L1:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps8.jpg&pos_id=WDDkEzos
详细而言,L0的内容是中文“用法: Hello 学号 姓名 手机号 秒数!”;L1的内容是“Hello %s %s %s\n”,用于格式化输出。
且步伐将这两个字符串的地址分别存储到了%rdi(调用函数的第一个参数)中,方便后续调用printf函数打印:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps9.jpg&pos_id=HVN1VQyr
[*]变量部分
(1)main函数的两个参数argc和*argv[]。
argc是main函数的第一个参数,根据x86-64操作体系的寄存器规定,函数参数的存储依照%rdi、%rsi、%rdx、%rcx、%r8、%r9、堆栈的次序,因而argc作为第一个参数,必然被存储在%rdi(%edi)中,且如下图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps10.jpg&pos_id=aTsMRhRs
该参数被保存到了栈中位置-20(%rbp)。
*argv[]是main函数的第二个参数,保存在寄存器%rsi(%esi)中,且如上图所示,步伐将argv的值保存到栈中位置-32(%rbp)。
[*]main函数中的局部变量i。
源步伐可见main函数中只有一个局部变量i,用于循环控制。如图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps11.jpg&pos_id=4pw3YOXd
将立刻数$0作为初始值赋给栈中位置-4(%rbp),正好对应了源步伐中对i的初始化。
3.3.3 赋值操作
汇编代码中典范的赋值语句重要有两处,一是上述2中的将立刻数值0赋给变量i作为初值:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps12.jpg&pos_id=IoOEpoA3
第二处是在循环体中将栈顶元素取出并赋值给%rax寄存器,之后对其进行其他操作:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps13.jpg&pos_id=QltipaJj
3.3.4 算术运算
汇编步伐涉及的算术运算操作重要是循环条件部分每次将i的值加一,根据之前的汇编步伐,已经将i的值赋给了栈中位置-4(%rbp),因此易知以下操作为对i加一的对应汇编指令,利用addl指令完成:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps14.jpg&pos_id=Wo6vqttL
3.3.5 关系和控制转移
在源C语言步伐中,涉及到的关系操作及控制转移重要体现在两部分,一是判定当argc的值不为5时打印字符串L0,二是循环的终止条件i<10。
[*]分支判定
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps15.jpg&pos_id=fiTZDTux
如图所示,步伐比较-20(%rbp)位置的值(由前面的分析可知是argc)和立刻数5,如果等于5就跳到.L2,否则执行下面的语句打印字符串L0:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps16.jpg&pos_id=qEjl0E16
[*]循环终止
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps17.jpg&pos_id=iLWRj3za
如图所示,步伐比较-4(%rbp)位置的值(即i的值)和立刻数9,如果i<=9,则跳转到循环体对应的指令.L4处继续循环,否则执行以下call getchar操作,跳出循环:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps18.jpg&pos_id=AcetLr4N
3.3.6 数组操作
根据源步伐的逻辑,每次循环都必要打印argv数组下标为1、2、3的元素的值,也就是必要访问上述三个值,后续也必要调用atoi函数将argv的内容地址转换为整型数,这也必要访问argv并将其作为函数第一个参数存储在寄存器%rax中,如图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps19.jpg&pos_id=L20VIFBS
循环体的上半部分完成argv下标1、2、3元素的访问,并将其赋值给相应函数参数的寄存器,访问方式是先将栈指针赋值给%rax,对%rax中的地址做肯定的算术运算得到对应数组元素的地址,再将其赋值给响应寄存器;循环体下半部分完成对argv的访问,访问方式类似。
3.3.7 函数操作
1、参数传递
根据上述分析,已经明了在调用函数进步行的传参操作,比如main函数开始时已经将argc和argv存储在%rdi和%rsi中;调用尺度输出函数之前,已经将字符串的内容赋值给参数一寄存器%rdi,相应的变量赋值给后续参数对应的寄存器。
2、函数调用
汇编步伐的函数调用利用call指令完成,比如主函数调用printf、atoi、sleep的指令,如下图:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps20.jpg&pos_id=WF5X0NSU
3、局部变量
局部变量i存储在栈中-4(%rbp)对应位置中而非寄存器中,上述分析已经分析。
3.3.8 类型转换
步伐中重要的类型转换是利用atoi函数将argv中的地址值转换为相应的整型数,以满意sleep对参数类型的要求。
3.4 本章小结
本章重要论述步伐编译的四步过程中第二步——编译的概念和作用,并对Hello步伐执行编译操作,观察天生的汇编代码以及分析其中涉及的基本操作。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
4.1.1 汇编的概念
汇编是编译器将编译后的汇编文件(.s)转换为可重定向的目的文件(.o)的过程,天生的目的文件是机器语言的二进制表示形式。
4.1.2 汇编的作用
汇编的重要作用是将高级语言代码转换为与特定处理器架构兼容的机器语言代码。它将高级语言的抽象概念转换为底层硬件可以明白的指令集,这样编译后的目的文件就可以被进一步处理(比如链接)。
4.2 在Ubuntu下汇编的下令
在Ubuntu下进行汇编的下令为:
gcc -m64 -no-pie -fno-PIC -c hello.s -o hello.o
这条指令中:“-m64”指定天生的目的文件是64位的;“-c”指示编译器只进行编译,并天生目的文件,而不进行链接;“-no-pie”指示编译器禁用 Position Independent Executable (PIE)。PIE 是一种安全性功能,用于随机化步伐的内存布局,增加攻击者的难度,在某些情况下禁用 PIE 能够提高性能;“-fno-PIC”指示编译器禁用位置无关代码 (PIC),也能够提高步伐的性能。
在Ubuntu下运行这个指令,结果是在同一文件目次中天生了可重定向的目的文件hello.o,如图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps21.jpg&pos_id=SXXflZet
4.3 可重定位目的elf格式
在下令行中输入以下指令,可以得到hello.o文件的elf格式:
readelf -a hello.o > hello.elf
打开hello.elf检察并分析。
4.3.1 ELF头
ELF头以一个16字节的序列开始,描述了天生该文件的体系的字的大小和字节顺序。ELF头剩下的部分包含资助毗连器语法分析息争释目的文件的信息。
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps22.jpg&pos_id=X7dXdsnC
如上图所示的ELF头包含各种信息:
[*]Magic:ELF 文件的魔数,用于标识文件的格式。
[*]类别:指定了 ELF 文件的类型,这里的“ELF64”,表示这是一个 64 位的 ELF 文件。
[*]数据:指定了数据编码的方式,“2 补码,小端序”表示数据以小端序(低位字节在前)的形式存储。
[*]Version:ELF 文件的版本号,当前版本为 1。
[*]OS/ABI:指定了目的操作体系和 ABI(Application Binary Interface)。
[*]ABI 版本:指定了 ABI 的版本为0。
[*]类型:指定了 ELF 文件的类型,这里是 REL,表示这是一个可重定位文件。
[*]体系架构:指定了目的体系的架构,这里是 Advanced Micro Devices X86-64,表示目的体系是 AMD 的 64 位处理器架构。
[*]版本:指定了 ELF 文件的版本为 0x1。
[*]入口点地址:指定了步伐执行的入口点地址,这里为 0x0,表示步伐的入口点地址为 0。
[*]步伐头出发点:指定了步伐头表在文件中的偏移量,这里为 0,表示该文件没有步伐头表。
[*]Start of section headers:指定了节头表在文件中的偏移量。
[*]标志:指定了一些标志位,这里为 0x0。
[*]Size of this header:指定了 ELF 头的大小,这里为 64 字节。
[*]Size of program headers:指定了步伐头表的大小,这里为 0,表示该文件没有步伐头表。
[*]Number of program headers:指定了步伐头表中的条目数量,这里为 0,表示该文件没有步伐头表。
[*]Size of section headers:指定了节头表的大小,这里为 64 字节。
[*]Number of section headers:指定了节头表中的节的数量,这里为 14,表示 ELF 文件中共有 14 个节。
[*]Section header string table index:指定了节头表字符串表的索引,用于查找节的名称,这里为 13,表示字符串表在节头表中的索引为 13。
4.3.2 节头部表
节头部表记录各节的名称、大小、类型、全体大小、地址、偏移旗标、链接、信息、对齐等信息,如下图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps23.jpg&pos_id=qNm58XP5
4.3.3 重定位节
重定位节包含重定位条目的详细信息:
[*]偏移量字段指定了必要进行重定位的位置相对于节的起始地址的偏移量。
[*]信息提供了与重定位相干的信息,例如重定位条目的序号或其他标识。
[*]类型指定了重定位的类型。在这里,有几种不同类型的重定位,包罗 R_X86_64_PC32 和 R_X86_64_PLT32。这些类型决定了重定位的详细行为和操作。
[*]符号值指定了关联符号的值。可能是一个地址或另一个符号的偏移量。
[*]符号名称+加数指定了关联符号的名称以及可能的偏移量。例如,.rodata - 4 意味着引用 .rodata 符号的值,然后减去 4。
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps24.jpg&pos_id=IFvclTHB
.rel.text是一个.text节中位置的列表,当链接器把这个目的文件和其他文件组合时,必要修改这些位置。一样平常而言,任何调用外部函数或者引用全局变量的指令都必要修改,而调用本地函数的指令不需修改。
4.3.4 符号表
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps25.jpg&pos_id=YCyO7w0M
.symtab是一个符号表,它存放一个步伐定义和引用的全局变量和函数的信息。
4.4 Hello.o的结果剖析
(以下格式自行编排,编辑时删除)
objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。
说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。
在Ubuntu下利用下令行输入objdump -d -r hello.o,进行反汇编,得到的完整反汇编代码如下图:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps26.jpg&pos_id=wGeFjimO
观察反汇编代码与汇编代码,进行比较分析:
[*]反汇编代码的实质是对机器代码二进制表示的翻译,所以反汇编代码每一行的前面都会展示相应的二进制机器代码,以表现出机器代码和汇编代码的映射关系,二进制代码的构成重要有操作码(用于指示要执行的操作类型)和操作数(指令中的数据或地址);
[*]操作数的差异。汇编代码中操作数立刻数均为十进制,而反汇编代码中操作数立刻数都是十六进制数;
[*]分支转移的差异。汇编代码的分支转移指令的操作数为步伐的局部标签,而反汇编代码中分支转移指令的操作数为“<函数名+偏移量>”的表示方式;
[*]函数调用的差异。汇编代码的函数调用利用call+函数名的方式,而反汇编代码的call指令目的地址是当前指令的下一条指令。
4.5 本章小结
本章重要论述了汇编的概念和作用,在Ubuntu下对hello.s进行汇编,观察其elf格式并分析其中的内容,然后将可重定向的目的文件进行反汇编,观察比较其和原始汇编代码的区别和联系。
(第4章1分)
第5章 链接
5.1 链接的概念与作用
5.1.1 链接的概念
链接是指编译器将各种代码和数据片断收集并组合成为一个单一文件的过程,这个文件可以被加载到内存并执行。
5.1.2 链接的作用
[*]符号剖析
在编译过程中,编译器会为步伐中用到的函数、变量等标识符天生符号,并在对象文件中保存这些符号的信息。链接器负责将这些符号剖析为实际的内存地址或者外部符号的引用。
[*]符号重定位
链接器会根据符号剖析的结果,对各个对象文件中的符号引用进行重定位,确保步伐中的各个部分可以精确地毗连在一起。包罗修正跳转地址、调整变量引用等操作。
[*]库文件链接
除了链接步伐的各个模块外,链接器还负责将步伐所需的库文件(如尺度库、第三方库等)链接到步伐中。库文件中包含了各种常用函数的实现,步伐在运行时可以调用这些函数。
[*]天生可执行文件
链接器最终将所有的对象文件和库文件合并成一个单独的可执行文件。这个可执行文件包含了步伐的全部功能,并且可以在盘算机上运行。
[*]符号表
链接器天生一个符号表,记录了步伐中各个符号的地址和属性信息。这个符号表能够方便在运行时进行动态链接和调试。
5.2 在Ubuntu下链接的下令
在Ubuntu下对hello.o进行链接并天生可执行目的文件hello的下令为:
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.o以外,还有许多文件,在此进行解释:
[*]-dynamic-linker /lib64/ld-linux-x86-64.so.2:该选项指定了动态链接器/加载器的路径 (ld-linux-x86-64.so.2),用于动态加载步伐运行所需的共享库;
[*]/usr/lib/x86_64-linux-gnu/crt1.o、/usr/lib/x86_64-linux-gnu/crti.o:这些是启动文件,用于初始化 C 运行时情况。crt1.o 包含步伐的入口点,crti.o 包含初始化代码;
[*]/usr/lib/x86_64-linux-gnu/libc.so: C 尺度库 (libc) 的路径,用于提供尺度 C 函数的实现,比如 printf、scanf 等;
[*]/usr/lib/x86_64-linux-gnu/crtn.o:一个包含终止代码的目的文件,用于步伐执行竣事时的清算工作。
在下令行中输入上述下令,在同一目次下天生了hello可执行目的文件,如图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps27.jpg&pos_id=aK54OSwv
5.3 可执行目的文件hello的格式
在下令行中输入以下指令,可以得到hello文件的elf格式:
readelf -a hello > hello_re.elf
打开hello_re.elf检察并分析。
5.3.1 ELF头
ELF头以一个16字节的序列开始,描述了天生该文件的体系的字的大小和字节顺序。ELF头剩下的部分包含资助毗连器语法分析息争释目的文件的信息。
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps28.jpg&pos_id=i8Fv6cD8
同样包含魔数类别、数据、版本、操作体系/ABI、ABI版本、类型、体系架构、版本、入口点地址、步伐头出发点、标志、节头表偏移、ELF头大小、步伐头表大小、条目数量、节头表大小、节数量及字符串表索引等内容。
与hello.o的elf格式对比,发现类型变为了可执行文件;入口点地址从0变为了实际的虚拟地址;步伐头出发点、节头表偏移、步伐头表大小、条目数量、节数量及字符串表索引等内容均发生了改变。
5.3.2 节头部表
节头部表记录各节的名称、大小、类型、全体大小、地址、偏移旗标、链接、信息、对齐等信息,如下图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps29.jpg&pos_id=JgxtPfr9
将其与hello.o的节头部表对比,发现节头部表中的节增多,上图中红色方框标出的内容为增加的节。
5.3.3 步伐头
步伐头时hello文件较之hello.o文件新增加的段,描述了文件的不同段如何映射到内存中。详细内容如下:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps30.jpg&pos_id=BxLu09tI
步伐头段的每个条目都包含以下字段:
[*]Type: 段的类型。
[*]Offset: 文件中的偏移,指出从文件开始到该段的开始的字节数。
[*]VirtAddr: 段在内存中的虚拟地址。
[*]PhysAddr: 段在物理内存中的地址(在大多数体系上,这与虚拟地址相同)。
[*]FileSiz: 段在文件中的大小。
[*]MemSiz: 段在内存中的大小。
[*]Flags: 段的权限标志(R = 可读,W = 可写,E = 可执行)。
[*]Align: 段的对齐,在内存中的对齐需求。
该hello的ELF 文件的步伐头部详细描述了如何将步伐的不同部分加载到内存中,每个段的权限设置反映了步伐的安全和操作需求。步伐头还指明了动态链接器的位置,这对于运行时动态剖析依靠关系至关重要。别的,通过设置特定的段属性,如 GNU_STACK 和 GNU_RELRO,提高了步伐运行的安全性。
5.3.4 Dynamic(动态)段
Dynamic段也是hello文件的ELF文件新增加的段,是 ELF 格式中用于存储与动态链接和加载相干信息的部分,详细内容如下:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps31.jpg&pos_id=sVIbxHOW
每个条目由一个标记(Tag)和相应的值构成,这些值可能是地址、大小或指向其他数据的引用。其中还包含了共享库的信息。
5.3.5 重定位节
重定位节依然包含偏移量、信息、类型、符号值和符号名称+加数(详细含义已在4.3.3部分论述,在此不过多赘述),如图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps32.jpg&pos_id=EKtjHwxj
将其与hello.o的elf格式对比,发现重定位条目发生了更新。
5.3.6 符号表
符号表存放一个步伐定义和引用的全局变量和函数的信息,如图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps33.jpg&pos_id=iwxs5Qlt
与hello.o的elf格式相对比,发现在进行了链接操作后,符号表的篇幅大幅增加,重要表现在增加了很多来自外部的函数和变量。
5.4 hello的虚拟地址空间
在Ubuntu下令行中键入下令:
edb --run hello 2022112014 ljx 15694599331 1
通过edb运行hello步伐,天生了图形化界面,在图形界面中找到data dump模块,并打开symbol viewer对比观察如下:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps34.jpg&pos_id=i1WMyTQS
从上图的data dump中可以看到,整个hello步伐的虚拟地址空间从0x401000开始,检察symbol窗口发现是.init的地址,与5.3中的elf文件内容对比:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps35.jpg&pos_id=A7hQjOLy
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps36.jpg&pos_id=SKmvmtxM
结论是确实匹配上了.init和虚拟地址。
同理,我们也可以找到.text、.rodata等节的虚拟地址,并与5.3对比验证。
检察头部表可以得知.text节的虚拟地址为0x4010f0:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps37.jpg&pos_id=iLUuCyLD
但是检察symbol窗口发现位于地址0x4010f0的符号为“_start”:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps38.jpg&pos_id=1tP0k0Fr
由于.text节存储的是已经编译的步伐代码,所以可以分析出“_start”应该就是步伐实际的入口。
.rodata节的虚拟地址为0x402000:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps39.jpg&pos_id=XVIRUxSL
.rodata节是只读数据区,代表的应该是用作printf函数参数的两个固定字符串,检察data dump中对应位置,发现的确保存有两个字符串的内容:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps40.jpg&pos_id=aQV6ibBw
5.5 链接的重定位过程分析
在Ubuntu下令行中键入下令:
objdump -d hello > hello.asm
objdump -d hello.o > hello_o.asm
天生hello和hello.o的反汇编步伐,观察对比:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps41.jpg&pos_id=jGZdq0lF
发现,hello.o的反汇编文件中只有main函数的相干指令,而可执行文件hello的反汇编步伐中在main之前包含了很多其他函数的指令,这说明了毗连过程中进行了符号剖析的操作,将外部定义与hello步伐的内部符号关联起来,并链接到文件中。
对比main函数函数体的部分:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps42.jpg&pos_id=UDIeZvUr
又可以发现hello将hello.o中原有的跳转指令的地址全部为相对地址,毗连过程后全部转换增加了实际的虚拟地址,原先call函数调用指令的目的地址利用下一条指令的地址取代,而随着链接的符号剖析,这部分目的地址可以重定位到实际调用的函数的虚拟地址,是这说明了重定位(将每个符号定义与一个内存地址关联起来)操作的存在。
5.6 hello的执行流程
利用gdb执行hello步伐,对可视化界面中能看到的所有函数设置断点,一步一步运行步伐从加载hello到_start,到call main,以及步伐终止的所有过程,记录调用及跳转的各个子步伐名和地址如下:
步伐名
地址
_init
0x401000
_start
0x4010f0
__libc_csu_init
0x4011c0
main
0x401125
printf
0x4010a0
atoi
0x4010c0
sleep
0x4010e0
getchar
0x4010b0
exit
0x4010d0
_fini
0x401238
5.7 Hello的动态链接分析
动态链接器会重定位全局偏移量表(GOT)中的每一个条目,使其包含精确的绝对地址,可以通过观察.got及.got.plt节的内容变化来简单说明dl_init前后动态链接项目的变化,检察hello的elf文件的节头部表:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps43.jpg&pos_id=9p87Rbx3
可以得知.got的起始位置为0x403ff0,而.got.plt的起始位置为0x404000。
打开edb,检察相应的datadump,在运行步伐前两位置的内容如下:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps44.jpg&pos_id=2rNIcUoF
运行步伐后两位置的值如下图:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps45.jpg&pos_id=sdIFNMWw
调用后的.got和.got.plt中的条目已经改变,说明动态链接完成。
5.8 本章小结
本章重要介绍了链接的概念和作用,以及在Ubuntu下利用链接指令进行链接的方法。然后观察hello反汇编的elf文件的格式,与hello.o做对比,分析毗连过程和重定位,接着通过gdb逐步分析了hello的执行流程,最终利用edb进行了动态链接的分析。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
6.1.1 进程的概念
进程是指盘算机中正在运行的一个步伐的实例,拥有独立的内存空间和体系资源,包罗CPU时间、文件和I/O装备等。进程是操作体系进行资源分配和调度的基本单位,它使得多个步伐能够同时运行并共享体系资源。
6.1.2 进程的作用
1、并发执行
进程使得多个步伐能够同时执行,从而提高了体系的整体吞吐量和效率。操作体系通过在不同的时间段分配CPU时间片给不同的进程,实现了并发执行。
2、资源分配
操作体系通过进程来管理和分配体系资源,如内存空间、文件、I/O装备等,以确保不同的步伐能够顺利地访问和利用这些资源。
3、隔离性
每个进程都有其独立的内存空间,使得不同的步伐之间彼此隔离,互不干扰。这种隔离性提高了体系的稳定性和安全性。
4、通讯与同步
进程之间可以通过进程间通讯(Inter-Process Communication, IPC)机制进行数据交换和通讯,实现合作和协同工作。别的,进程还可以通过同步机制来和谐彼此的执行顺序,避免出现竞态条件和数据不一致等题目。
5、实现多使命
进程使得操作体系能够实现多使命功能,即同时运行多个步伐,并且让它们之间表现出同时运行的结果。这种多使命能力提高了体系的灵活性和用户体验。
6.2 简述壳Shell-bash的作用与处理流程
6.2.1 壳Shell-bash的作用
Shell 是盘算机体系中的下令解释器,它是用户与操作体系之间进行交互的接口。Bash是一种常见的 Shell,是许多 UNIX 体系的默认 Shell。Shell 重要的作用是吸收用户输入的下令,然后解释并执行这些下令,将用户与操作体系之间的交互进行毗连,提供了一个用户友好的方式来操作盘算机体系。
6.2.2 壳Shell-bash的处理流程
1、提示符(Prompt)
当用户启动 Shell 时,通常会看到一个提示符,表示 Shell 已经预备好吸收用户输入下令。
2、读取输入
用户在提示符后输入下令,并按下回车键。Shell 会读取用户输入的下令,并将其存储在内存中等待执行。
3、剖析下令
Shell 对用户输入的下令进行剖析,分析下令的语法和结构,确定下令的类型、参数以及执行路径。
4、执行下令
根据剖析得到的结果,Shell 将调用相应的体系步伐或者执行相应的 Shell 内置下令来执行用户输入的下令。执行过程中,Shell 会创建一个子进程来运行下令,而不是在当前 Shell 进程中直接执行。
5、等待下令执行完成
在下令执行过程中,Shell 会等待下令的执行完成。一旦下令执行完成,Shell 将获取下令的返回状态,并根据返回状态进行相应的处理。
6、输出结果
如果下令产生了输出结果(如打印到尺度输出),Shell 将把这些结果显示给用户。用户可以在终端上看到下令的输出结果。
7、等待下一个下令
在下令执行完成并输出结果后,Shell 会回到等待用户输入的状态,等待用户输入下一个下令。
6.3 Hello的fork进程创建过程
在下令行shell中输入“./hello 2022112014 ljx 15694599331 1”,shell经过判定得知./hello并非内置指令,然后在当前目次下找到它,利用体系调用fork函数为hello创建一个子进程,fork函数的执行过程中,操作体系会为新的子进程分配一个新的标识符(PID),然后在内核中分配一个进程控制块(PCB),将其挂在PCB表上。然后,操作体系会将父进程的情况(包罗代码和数据段、堆、共享库以及用户栈等)复制到子进程中。
子进程可以读写父进程中打开的任何文件,父进程和子进程之间的区别在于它们的PID不同。
6.4 Hello的execve过程
当我们在shell输入“./hello 2022112014 ljx 15694599331 1”来执行整个步伐时,操作体系首先会根据下令行中指定的可执行文件路径 ./hello,从磁盘中加载 hello 步伐的可执行文件到内存中,然后shell通过fork体系调用为hello创建一个新的进程并分配资源,然后操作体系会将下令行中的参数传递给新进程。
之后操作体系调用 execve 函数,将 hello 步伐加载到新创建的进程的内存空间中,并开始执行步伐的代码。execve函数加载并运行可执行目的文件filename,且带参数列表argv和情况变量envp。只有当出现错误时,例如找不到filename,execve才会返回到调用步伐。所以,与fork一次调用返回两次不同,execve调用一次并不返回。
步伐开始时用户栈的典范结构如下:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps46.jpg&pos_id=YdzKwQR2
利用指令strace -f ./hello可以跟踪hello的进程:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps47.jpg&pos_id=xsu2cgQ2
6.5 Hello的进程执行
Hello步伐的进程执行过程如下:
1、加载步伐
当用户在终端中输入 “./hello 2022112014 ljx 15694599331 1” 来执行这个步伐时,操作体系会加载 hello 步伐的可执行文件到内存中。
2、创建进程
操作体系为 hello 步伐创建一个新的进程,并分配资源,包罗内存空间、文件描述符等。
3、进程上下文信息
操作体系内核为每个进程维持一个上下文。上下文是内核重新启动一个被抢占的进程所需的状态,它由一些对象的值构成,包罗寄存器、步伐计数器、用户栈等。
进程上下文切换的过程图如下:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps48.jpg&pos_id=tqtm2FSR
操作体系会在进程控制块(PCB)中保存进程的上下文信息,包罗步伐计数器(指向下一条要执行的指令)、寄存器状态、内存映射、文件描述符表等。这些信息用于在不同的时间点规复进程的执行状态。
4、进程调度
当 hello 步伐预备好开始执行时,操作体系会将其放入就绪队列中,等待分配 CPU 时间片。
5、用户态与核心态转换
当操作体系选择 hello 进程执行时,CPU 会将执行权交给 hello 进程。此时,进程处于用户态,只能执行受限的指令和访问受限的资源。当 hello 进程必要执行一些必要特权级别的操作(如进行体系调用),CPU 会触发用户态到核心态的转换,将控制权交给操作体系内核,以便操作体系完成这些特权操作。
6、执行步伐
hello 步伐开始执行 main 函数。首先,步伐查抄下令行参数的数量是否精确,如果不精确,则输出错误信息并退出步伐。如果下令行参数数量精确,步伐进入一个循环,循环打印问候消息,并在每次循环中调用 sleep 函数来进行休眠。
进程时间片
当 hello 进程正在执行时,操作体系会根据调度算法分配给该进程一个时间片,即一段时间内答应该进程执行。当时间片用完或者发生壅闭时,操作体系会重新调度其他进程执行,并将 hello 进程暂时放回就绪队列中等待下次调度。
用户输入与壅闭
当步伐循环执行完成后,调用 getchar() 函数等待用户输入。此时,进程会进入壅闭状态,等待用户输入恣意字符。在壅闭状态下,进程不会占用 CPU 时间片,而是让出 CPU 给其他可执行的进程。
7、退出步伐
当用户输入恣意字符后,步伐继续执行,并执行 return 0; 语句退出步伐。此时,操作体系会回收 hello 进程的资源,并更新进程控制块中的信息。
6.6 hello的异常与信号处理
(以下格式自行编排,编辑时删除)
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
步伐运行过程中可以按键盘,如不绝乱按,包罗回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等下令,请分别给出各下令及运行结截屏,说明异常与信号的处理。
6.6.1 步伐可能出现的异常及处理方式
异常可以分为四类:停止、陷阱、故障和终止。
[*]停止
停止是异步发生的,来自处理器外部的IO装备的信号。硬件停止不是由任何一条专门的指令造成的,从这个意义上来说是异步的。
I/O装备通过引脚向处理器芯片发送信号,并将异常号放到体系总线上来触发停止,在当前指令完成执行后,处理器会从体系总线读取异常号,然后调用得当的停止处理步伐。当处理步伐返回时,它会将控制返回给下一条指令,如图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps49.jpg&pos_id=W8juF8j9
[*]陷阱
陷阱是有意的异常,是执行一条指令的结果。就像停止处理步伐一样,陷阱处理步伐将控制返回到下一条指令。
用户步伐经常必要向内核请求服务,为了答应对这些内核服务受控的访问,处理器提供了一条特殊的syscall指令,详细处理方式如下图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps50.jpg&pos_id=8vkOxH30
[*]故障
故障由错误情况引起,它可能能够被故障处理步伐修正。
当故障发生时,处理器将控制转移给故障处理步伐。如果故障处理步伐能够修正,它就将控制返回给引起故障的指令;否则,处理步伐返回到内核中的abort进程,终止故障步伐,如图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps51.jpg&pos_id=lEOYDu74
[*]终止
终止是不可规复的致命错误造成的结果。终止处理步伐不将控制返回给应用步伐。
6.6.2 信号
信号是关照进程体系中某一实践的消息,每种信号类型对应着某种异常事件。信号对于用户进程是不可见的,不过它具有一种机制可以关照用户进程发生了响应异常。
操作体系内核信号及其对应的事件和默认行为如下图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps52.jpg&pos_id=0ze3AswN
6.6.3 hello步伐运行异常实例
1、正常运行步伐结果
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps53.jpg&pos_id=Pa6tuSvr
步伐正常地打印10次hello信息。
2、运行过程中不绝地乱按键盘
在步伐运行过程中不绝地乱按键盘(过程中穿插回车键),shell显示如下:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps54.jpg&pos_id=loxLVnBW
观察发现,shell将我们每次输入的空格前的字符串都当作了下令,在步伐竣事之后尝试剖析下令,但是剖析失败。这说明在步伐执行过程中的输入都会缓存到stdin,在后续getchar的时候就会读出一个以“\0”结尾的字符串作为下一次的输入。
3、运行过程中按Ctrl+C
运行过程中按Ctrl+C,进程收到SIGINT信号,hello步伐终止,此时利用ps指令检察当进步程,发现没有hello进程,相应地,输入jobs显示的前台进程中也没有hello进程:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps55.jpg&pos_id=FZBdAJ08
4、运行过程中按Ctrl+Z
运行过程中按Ctrl+Z,shell周到SIGSTP信号,shell显示当进步程已被挂起:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps56.jpg&pos_id=DINxTBf9
此时利用ps下令和jobs下令检察全部进程和前台进程:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps57.jpg&pos_id=hufzJzXI
ps指令列出的进程显示hello仍然存在,jobs的输出也显示了hello进程,只不过hello进程目前是被挂起的状态。
在下令行中输入pstree下令可以检察进程的树状图:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps58.jpg&pos_id=S2TboxLy
此时再输入fg,继续执行前台进程:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps59.jpg&pos_id=twkBYaNk
Hello进程剩余内容执行完后,再检察进程表就没有hello了。
我们也可以执行杀死进程的操作,操作顺序是先运行步伐,在执行过程中按下Ctrl+Z挂起进程,然后检察ps中hello进程的PID,并利用kill指令杀死进程,末了再次检察进程表,验证杀死进程结果:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps60.jpg&pos_id=I8IPKMFC
6.7本章小结
本章首先重温了盘算机体系进程的概念和作用,介绍了壳shell-bash的作用和处理流程,接着结合hello步伐感受fork进程创建、execve过程和进程执行过程。末了针对对hello步伐运行过程中人为设置异常,领会异常处理步伐的运行结果。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
1、逻辑地址
逻辑地址是由 CPU 天生的地址,它是步伐中利用的地址空间。在步伐中,指针、变量等数据对象的地址通常就是逻辑地址。在用户步伐中,逻辑地址是相对于该步伐的起始地址而言的,是步伐员所见到的地址。
比如hello步伐中printf函数参数argv[]指针扽别表示下令行参数的地址,这些地址就是步伐中利用的逻辑地址。
2、线性地址
线性地址是逻辑地址到物理地址之间的中心层。在操作体系中,线性地址是在逻辑地址和物理地址之间的一个映射。线性地址空间是逻辑地址空间经过操作体系的地址转换机制转换之后的结果。
3、虚拟地址
虚拟地址是步伐中利用的地址,它对应于线性地址。虚拟地址空间是指步伐所能访问到的全部地址范围。在多使命操作体系中,每个进程都有本身的虚拟地址空间,使得每个进程感觉本身在独立的地址空间中运行,不受其他进程的影响。
Hello步伐中main、printf、atoi、sleep等函数的地址都是虚拟地址。操作体系会将这些地址映射到实际的物理内存。
4、物理地址
物理地址是指实际存储器中的地址,它对应于盘算机体系中的实际存储单位(如RAM)。物理地址是 CPU 访问内存时利用的地址,通过内存管理单位(Memory Management Unit,MMU)的地址转换机制,将虚拟地址转换为物理地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
段式管理是一种内存管理技能,用于将逻辑地址转换为线性地址。在段式管理中,内存被划分为多个段(segments),每个段对应着步伐的一个逻辑部分,比如代码段、数据段、堆段、栈段等。每个段都有本身的起始地址和长度,并且可以设置访问权限。
段式管理的转换过程如下图,被选中的段描述符先被送至描述符cache,每次从描述符cache中取32位段基址,与32位段内偏移量(有效地址)相加得到线性地址。
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps61.jpg&pos_id=6xMk4WVk
当步伐产生一个逻辑地址时,它会由两个部分构成:段号和偏移量。段号用于标识要访问的段,而偏移量则表示段内的详细位置。详细而言过程如下:
1、查找段描述符表
首先,操作体系会根据段号从段描述符表(Segment Descriptor Table)中找到对应的段描述符。段描述符包含了段的起始地址、长度、访问权限等信息。
2、验证访问权限
操作体系会查抄访问权限,确保步伐对该段的访问是合法的。如果权限验证失败,则抛出权限异常。
3、盘算线性地址
一旦权限验证通过,操作体系会将段描述符中的起始地址与偏移量相加,得到线性地址。这个线性地址就是步伐最终要访问的内存地址。
4、访问内存
末了,CPU 将线性地址发送到内存控制器,访问内存中对应的数据或指令。如果内存访问乐成,则步伐可以正常运行;如果发生了错误(如缺页异常),则操作体系会处理相应的异常。
7.3 Hello的线性地址到物理地址的变换-页式管理
线性地址到物理地址的映射是一个地址翻译的过程,实质上是一个虚拟地址空间(VAS)中的元素和一个物理地址空间(PAS)中元素之间的映射。
MMU利用页表来实现这种映射。CPU中的控制寄存器——页表基址寄存器(PTBR)指向当前页表。虚拟地址包含两部分:虚拟页面偏移(VPO)和虚拟页号(VPN),MMU利用VPN来选择得当的页表条目(PTE)。如下图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps62.jpg&pos_id=IE6SswG2
7.4 TLB与四级页表支持下的VA到PA的变换
翻译后备缓冲器(TLB)是一个小的、虚拟内存寻址的缓存,其中每一行都保存着一个由单个PTE构成的块。TLB有较高的相连度。当CPU必要访问内存时,首先会查抄TLB中是否有对应的映射关系。如果TLB中有,就可以直接从TLB中获取物理地址,从而加速地址转换速度;如果TLB中没有,就必要通过页表来完成地址转换,并将新的映射关系存储到TLB中,以便下次访问时可以直接利用。
四级页表是一种多级页表结构,用于管理虚拟地址空间和物理地址空间之间的映射关系。在四级页表中,虚拟地址被分成多个级别的索引,每个索引对应一个页表项。通过多级索引,可以将整个虚拟地址空间划分成多个较小的页面,从而降低页表的访问复杂度。
Core i7采用四级页表层次结构,每个进程有它本身私有的页表层次结构,下图为Core i7的地址翻译过程:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps63.jpg&pos_id=bXe8tEmN
36位VPN被划分成四个9位的片,每个片被用作到一个页表的偏移量。CR3寄存器包含L1页表的物理地址。VPN提供到相应的PTE的偏移量,这个PTE包含下一页表的基地址,以此类推。Core i7利用四级页表将虚拟地址翻译为物理地址的过程如下图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps64.jpg&pos_id=s3aFUM4e
7.5 三级Cache支持下的物理内存访问
三级Cache支持下的物理内存访问重要过程是优先访问高级cache,当高级的缓存cache中没有必要访问的元素,再依次访问低层cache,以此类推直到访问到内存位置。
盘算机通用的高速缓存存储器结构如下图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps65.jpg&pos_id=zFrTi1J3
Cache被分为S组,每一组E行,每行大小为B,而地址又被分为标记为、组索引和块偏移。当一条加载指令指示CPU从主存地址A中读取一个字时,他将地址A发送到高级cache,访问过程会进行组选择、行匹配和字选择三个步骤。通过组索引选择相应的组号,在组内遍历所有行,比较每一行的标记与标记位的值是否匹配,如果有匹配的说明缓存掷中,就可以执行字选择精准访问;如果遍历组后发现没有对应标记匹配,则缓存未掷中,必要向下级cache继续访问该数据。过程如下图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps66.jpg&pos_id=mcPkPjok
图1 组选择
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps67.jpg&pos_id=I2ruH1bh
图2 行匹配和字选择
7.6 hello进程fork时的内存映射
Fork创建的新进程与父进程共享代码段、数据段和堆段,但有独立的栈段。在fork之后,父进程和子进程会同时运行,但它们有各自独立的地址空间。
Hello父进程和子进程fork时的内存映射重要包含:
1、代码段
父进程和子进程共享相同的代码段,即hello步伐的可执行代码部分。这意味着它们执行相同的指令序列。
2、数据段
父进程和子进程共享数据段,包罗全局变量和静态变量。因此,它们可以共享相同的数据。
3、堆
父进程和子进程有各自独立的堆段。堆段存储动态分配的内存(例如利用malloc函数分配的内存),因此它们的堆段是独立的,互不影响。
4、栈
父进程和子进程有各自独立的栈段。栈段用于存储函数调用的局部变量、函数参数和返回地址等信息。fork后,子进程会复制父进程的栈段,但它们是独立的,因此对栈段的修改不会影响彼此。
私有对象利用写时复刻及技能将其映射到虚拟内存中。私有对象开始生命周期的方式基本上与共享对象相同,物理内存只保存私有对象的一份副本。两个进程共享同一个私有对象的同一个物理副本时,映射私有区域的条目会被标记为只读,并且区域结构会被标记为私有的写时复刻。如下图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps68.jpg&pos_id=QanzIAkO
当hello进程调用fork时,父进程和子进程共享代码段和数据段,但拥有各自独立的堆段和栈段。这种内存映射使得父子进程可以并行执行,同时又能够独立地操作它们的数据和堆栈。
7.7 hello进程execve时的内存映射
execve体系调用用于加载并执行一个新的步伐,替换当进步程的映像。这意味着在调用execve之后,原来的步伐映像会被新的步伐映像替换掉,新步伐开始执行。
Execve函数在当进步程中加载并运行可执行文件hello,用hello步伐有效取代当前步伐。加载并运行hello步伐必要以下步骤:
[*]删除已存在的用户区域
删除当进步程虚拟地址的用户部分中的已存在的区域结构。
[*]映射私有区域
为新步伐的代码、数据、bss和栈区域创建新的区域结构。所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为hello 文件中的.text和.data 区。bss区域是请求二进制零的,映射到匿名文件,其大小包含在hello中。栈和堆区域也是请求二进制零的,初始长度为零。图9-31概括了私有区域的不同映射。
3、映射共享区域。
如果hello步伐与共享对象(或目的)链接,比如尺度C库libc. so,那么这些对象都是动态链接到这个步伐的,然后再映射到用户虚拟地址空间中的共享区域内。
4、设置步伐计数器(PC)
execve做的末了一件事变就是设置当进步程上下文中的步伐计数器,使之指向代码区域的入口点。详细过程如图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps69.jpg&pos_id=2lLVS84a
下一次调度这个进程时,它将从这个入口点开始执行。
7.8 缺页故障与缺页停止处理
缺页故障是指当CPU尝试访问某个虚拟页面时,发现该页面并不在物理内存中,必要将该页面从磁盘加载到内存中的过程。
假设MMU在试图翻译一个虚拟地址A引发缺页异常时,缺页异常导致控制转移到内核的却也处理步伐,处理步伐执行下面的步骤:
[*]判定虚拟地址A是否合法
如果指令不合法,那么缺页处理步伐就触发一个段错误,从而终止进程。
[*]判定试图进行的内存访问是否合法
如果试图进行的访问是不合法的,那么缺页处理步伐会触发一个保护异常,从而终止这个进程。
[*]处理合法虚拟地址的合法操作的缺页
此时排除了非法的虚拟地址和操作,内核就可以处来由合法虚拟地址的合法操作造成的缺页。处理方式为捐躯一个页面,如果这个页面被修改过,那么就对交际换,换入新的页面并更新页表。
上述Linux缺页停止处理过程如图所示:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps70.jpg&pos_id=bGRNU65l
当缺页处理步伐返回时,CPU重新启动引起缺页的指令,这条指令再次发送地址A到MMU。此时就不会再出现缺页停止题目。
7.9动态存储分配管理
7.9.1 动态内存管理方法
1、动态内存分配方法
(1)malloc/calloc
malloc和calloc函数用于在堆(如下图所示)中分配指定大小的内存块。malloc分配的内存块不会被初始化,而calloc会将分配的内存块初始化为零。
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=file%3A%2F%2F%2FC%3A%5CUsers%5CLenovo%5CAppData%5CLocal%5CTemp%5Cksohtml20948%5Cwps71.jpg&pos_id=Uzm7zKtp
(2)realloc
realloc函数用于重新分配已分配内存的大小,可以扩大或缩小内存块的大小。如果无法在原地扩展,则会重新分配一块新的内存,并将原内容复制到新内存中。
2、动态内存开释方法
Free函数。Free函数用于开释通过malloc、calloc或realloc分配的动态内存。开释后的内存块可以被重新分配给其他必要的内存请求。
7.9.2 动态内存管理策略
1、先辈先出(First Fit)
在空闲内存块列表中查找第一个满意要求的内存块进行分配。这种策略简单且实现轻易,但可能会导致内存碎片化。
2、最佳适应(Best Fit)
在空闲内存块列表中查找大小最靠近要求的内存块进行分配。这种策略可以减少内存碎片,但查找过程较耗时。
3、最坏适应(Worst Fit)
在空闲内存块列表中查找大小最大的内存块进行分配。固然这种策略可以减少内存碎片,但可能会导致较大的内存浪费。
4、快速适应(Quick Fit)
维护多个不同大小的内存块链表,根据请求大小选择最靠近的链表进行分配。这种策略在维护链表方面比较复杂,但可以提高分配速度和减少内存碎片。
7.9.3 内存管理优化
1、内存池(Memory Pool)
预先分配肯定数量的内存块,并在步伐运行期间重复利用这些内存块,减少动态分配和开释的开销。
2、内存复用
尽量减少动态内存分配和开释的次数,合并相邻的空闲内存块以减少内存碎片化。
3、内存检测工具
利用内存检测工具(如Valgrind、AddressSanitizer等)来检测内存走漏、越界访问等题目,确保步伐运行期间的内存利用安全。
7.10本章小结
本章重要从技能层面分析hello的存储管理。重要分析了hello的存储地址空间、段式管理、页式管理、地址翻译、物理访存、进程fork的内存映射、进程execve的内存映射和缺页故障处理等技能内容,末了论述了动态内存分配管理的方法、策略与优化。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO装备管理方法
Linux所有的I/O装备都被模型化为文件,而所有的输入输出都被当尴尬刁难响应文件的读和写来执行。这种将装备映射为文件的方式,答应Linux内核引出一个统一的简单、低级的应用接口,称为Unix I/O,这使得应用步伐可以通过读写文件来与装备进行通讯,无需关心底层硬件的细节。
装备文件通常位于/dev目次下,每个装备都有一个对应的特殊文件。比如硬盘可以表示为/dev/sda,串口可以表示为/dev/ttyS0等。
8.2 简述Unix IO接口及其函数
Unix I/O接口是用于进行输入输出操作的一组尺度接口,它提供了一种统一的方式来处理文件和装备。这些接口包罗许多函数,用于打开、关闭、读取、写入文件以及进行其他操作。
以下是一些常用的Unix I/O函数:
[*]open: 用于打开文件或装备,并返回一个文件描述符,以后的操作都将利用该文件描述符来标识该文件或装备。
[*]close: 用于关闭一个已打开的文件描述符,开释资源并使该文件描述符不再可用。
[*]read: 用于从文件或装备中读取数据,它担当文件描述符、缓冲区和要读取的字节数作为参数,并返回实际读取的字节数。
[*]write: 用于向文件或装备中写入数据,它担当文件描述符、要写入的数据和要写入的字节数作为参数,并返回实际写入的字节数。
[*]lseek: 用于在文件中移动文件指针的位置,它担当文件描述符、偏移量和起始位置作为参数,并返回新的文件指针位置。
[*]fcntl: 用于对文件描述符进行控制操作,如设置文件描述符的属性(非壅闭、文件状态标志等)。
[*]ioctl: 用于对装备进行控制操作,它担当装备文件描述符、操作码和参数作为参数,可以用于执行各种装备特定的控制操作。
[*]dup/dup2: 用于复制文件描述符,dup将已存在的文件描述符复制到一个新的文件描述符中,而dup2可以将已存在的文件描述符复制到指定的文件描述符中。
[*]select/poll/epoll: 用于在多个文件描述符上进行I/O多路复用,它们答应应用步伐同时监视多个文件描述符的状态,以便在任何一个文件描述符就绪时进行相应的操作。
[*]stat/fstat: 检索关于文件的信息(元数据),以一个文件名为输入,并填写结构体stat中的各个成员。
[*]readdir: 用于读取文件目次的内容,以路径名为参数,返回指向目次流的指针。
[*]closedir: 与readdir相对应,作用是关闭流并开释其所有的资源。
8.3 printf的实现分析
Printf函数源步伐代码如下:
[*]int printf(const char *fmt, ...)
[*]{
[*] int i;
[*] char buf;
[*]
[*] va_list arg = (va_list)((char*)(&fmt) + 4);
[*] i = vsprintf(buf, fmt, arg);
[*] write(buf, i);
[*]
[*] return i;
[*]}
8.3.1 vsprintf的作用
在这个步伐中,vsprintf 是 printf 函数的核心,负责将可变参数列表格式化为一个字符串。这个函数遍历格式字符串 fmt,每当碰到 % 符号时,根据后续的字符(如 'x', 's' 等)从参数列表中取出相应的参数,并将其转换成字符串存储到缓冲区 buf 中。
Vsprintf函数的源步伐如下:
[*]int vsprintf(char *buf, const char *fmt, va_list args)
[*]{
[*] char* p;
[*] char tmp;
[*] 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);
[*]}
该步伐的实现逻辑如下:
[*]对于每个字符,首先查抄是否为 %,如果不是,直接复制到输出缓冲区 buf。
[*]如果是 %,则检察紧接着的字符来确定参数的类型:
[*]%x:假设要将整数以十六进制格式输出。itoa 函数被用来转换整数到十六进制字符串,然后将字符串复制到 buf。
[*]%s:(示例中未实现)通常是复制一个字符串到输出缓冲区。
[*]其他情况可能涉及不同的数据类型或格式。
8.3.2 Printf函数的流程
[*]printf 首先盘算可变参数列表的起始地址,这通过偏移 fmt 参数的地址实现。
[*]调用 vsprintf 将格式化的数据写入本地缓冲区 buf。
[*]利用 write 体系调用将缓冲区的内容发送到尺度输出(通常是终端或文件)。
8.3.3 Write体系调用到字符串显示的过程
[*]体系调用
write 是一个体系调用,它将用户空间的数据传递到内核空间,详细的处理过程可能涉及文件体系、网络或终端装备的驱动。
[*]字符装备驱动
对于终端输出,字符装备驱动负责将字符数据转换为相应的显示代码(例如,ASCII码转换为屏幕上的像素点)。
[*]显示子体系
字符或图形数据被发送到显示硬件(如GPU),经过字模库处理后转换为像素数据存储在视频内存(VRAM)中。
[*]革新显示
显示控制器定期从VRAM读取数据,按照肯定的革新率通过信号线(如HDMI、VGA等)将数据发送到显示器。
[*]显示器
吸收到信号后,显示器根据RGB分量控制每个像素的颜色输出,形成用户可见的图像。
8.4 getchar的实现分析
8.4.1 异步异常与键盘停止处理
在 Unix-like 体系中,键盘输入首先触发硬件停止。停止处理是操作体系响应外部或内部事件的机制,键盘停止是由键盘硬件触发的外部停止。
当用户按下键盘上的键时,键盘硬件产生一个停止信号发送给 CPU。这个信号告知 CPU 必要立刻处理一个特定的事件——即键盘输入。CPU 吸收到停止信号后,会根据停止向量表(Interrupt Vector Table)找到相应的停止处理步伐(Interrupt Service Routine, ISR)的地址并执行它。键盘停止处理子步伐(ISR)是一个由操作体系内核管理的步伐,用于响应键盘停止。它读取硬件端口或利用特定的 I/O 指令(如 IN 或 OUT)从键盘控制器获取按键扫描码(Scan Code)。
8.4.2 扫描码转换为ASCII码
停止处理子步伐将扫描码转换为 ASCII 码。这一转换可能涉及查找表或算法来映射扫描码到其对应的字符值。转换后的 ASCII 码被存放在体系的键盘缓冲区中。这是一个内核维护的缓冲区,用来暂存从硬件装备读入的数据,直到它们被进程请求。
8.4.3 getchar函数及read()体系调用
1、getchar函数
Getchar函数的源步伐如下:
[*]int getchar(void){
[*] static char buf;
[*] static char *bb = buf;
[*] static int n = 0;
[*] if(n == 0)
[*] {
[*] n = read(0, buf, BUFSIZ);
[*] bb = buf;
[*] }
[*] return(--n >= 0)?(unsigned char) *bb++ : EOF;
[*]}
实在现逻辑大抵如下:
[*]静态缓冲区:函数内部利用一个静态数组 buf 作为缓冲区,用来暂存从尺度输入读取的数据。
[*]缓冲区指针和计数器:bb 是一个指针,指向缓冲区中当前读取的位置。n 是一个计数器,记录缓冲区中剩余的字符数。
[*]读取和缓冲管理:
[*]当 n == 0 时,说明缓冲区为空,此时调用 read(0, buf, BUFSIZ) 从尺度输入填充缓冲区。
[*]read() 返回值被存储在 n 中,表示读入的字符数量。如果 read() 返回 0 或 -1,表示输入竣事或发生错误。
[*]函数返回缓冲区中的下一个字符,并递减 n。如果 n 为负(说明没有更多字符可读),则返回 EOF。
2、getchar函数的read体系调用
当步伐调用 getchar() 时,getchar() 最终会调用 read() 体系调用,请求从尺度输入(通常是键盘,对应文件描述符为 0)读取数据。read() 可以配置为壅闭或非壅闭模式。在壅闭模式下,read() 调用会一直等待,直到有数据可读。在 Unix 中,终端装备默认配置为“规范模式”(Canonical Mode),在这种模式下,输入是以行为单位处理的。read() 会一直等待直到用户按下回车键(产生 ASCII 码 0x0A),这时才会把整行数据一次性读入缓冲区并返回。
从缓冲区到getchar的返回过程为:read() 体系调用从键盘缓冲区读取一行数据后,getchar() 从这段数据中返回下一个可用的字符。如果缓冲区为空,getchar() 会再次调用 read() 以壅闭等待更多输入。getchar() 处理来自 read() 的数据,返回第一个字符,并在后续调用中继续从这些数据中返回字符,直到必要再次从 read() 获取数据。
8.5本章小结
本章重要讨论hello的I/O管理,以Unix I/O为例,讨论了Linux的IO装备管理方法及Unix I/O的接口和函数,接着结合printf和getchar的源步伐和I/O基本操作分析实在现过程。
(第8章1分)
结论
hello.c固然是一个逻辑简单、体量微小的步伐,但是这个步伐从编写到运行必要履历所有步伐都要履历的全部过程。
在步伐员编写完步伐,按下编译运行按钮的那一刻起,hello.c步伐会先履历编译的全过程,包含预处理,编译、汇编和链接过程,最终天生可加载到内存并执行的可执行目的文件过程。
[*]预处理
hello.c步伐在预处理阶段履历头文件展开、去解释、宏替换和条件编译等操作变成hello.i文件。
[*]编译
hello.i步伐在编译器中被翻译成与特定体系结构相干的汇编语言代码(hello.s)
[*]汇编
hello.s文件履历汇编过程,将高级语言抽象转化为底层硬件可以明白的指令集——可重定向目的文件(hello.o),这样编译后的目的文件就可以被进一步处理。
[*]链接
末了一步,hello.o在编译器中会与其他外部文件相链接,罗致外部文件中的力气(外部定义的函数),结合成为最终的可执行目的文件。
至此,hello.c这个文件已经多次进化,最终成为可以执行的文件啦,执行文件的过程必要操作体系进行进程调度、存储管理和I/O管理。
[*]进程
操作体系为hello步伐创建进程以及一系列子进程并进行资源分配,并且及时识别异常并通过信号传递控制、调度进程,使得巨大的盘算性能够高效、井井有条地执行诸多步伐。
[*]存储管理
操作体系内核必要将方便与步伐员编写和编译的虚拟逻辑地址翻译为实际的物理地址,还要负责动态内存分配,以包管进程能够得到所需的富足的执行条件。
3、I/O管理
I/O是hello步伐真正能跟我们say hello~的关键,操作体系通过I/O管理控制步伐的输入输出,结合异常控制包管步伐功能的精确、饱满执行。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
列出所有的中心产物的文件名,并予以说明起作用。
文件名
介绍
hello.c
hello的源步伐
hello.i
源步伐预处理之后的ASCII文件
hello.s
源步伐经过编译后的汇编文件
hello.o
汇编文件汇编后的可重定位目的文件
hello
可重定向目的文件链接后的可执行目的文件
hello.elf
可重定位目的文件的ELF文件,便于检察ELF格式
hello_re.elf
可执行目的文件的ELF文件,便于检察ELF格式
hello.asm
可执行目的文件反汇编之后的汇编文件
hello_o.asm
可重定向目的文件反汇编之后的汇编文件
(附件0分,缺失 -1分)
参考文献
C语言学习之预处理-CSDN博客 https://blog.csdn.net/m0_69909682/article/details/128616065?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171722719616800178592969%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171722719616800178592969&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-128616065-null-null.142^v100^pc_search_result_base1&utm_term=%E9%A2%84%E5%A4%84%E7%90%86&spm=1018.2226.3001.4187
什么是预处理-CSDN博客 https://blog.csdn.net/LMM1314521/article/details/127431524?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171722719616800178592969%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171722719616800178592969&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-4-127431524-null-null.142^v100^pc_search_result_base1&utm_term=%E9%A2%84%E5%A4%84%E7%90%86&spm=1018.2226.3001.4187
gcc编译的过程 gcc编译的过程_gcc编译c文件-CSDN博客
操作体系——链接 https://blog.csdn.net/weixin_42731928/article/details/122109311?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171734114616800225544278%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171734114616800225544278&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-2-122109311-null-null.142^v100^pc_search_result_base1&utm_term=%E9%93%BE%E6%8E%A5&spm=1018.2226.3001.4187
操作体系——进程管理 https://blog.csdn.net/weixin_43765321/article/details/123279800?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171734126916800197019470%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171734126916800197019470&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-123279800-null-null.142^v100^pc_search_result_base1&utm_term=%E8%BF%9B%E7%A8%8B%E7%AE%A1%E7%90%86&spm=1018.2226.3001.4187
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]