【无标题】HIT-盘算机体系-程序人生-Hello’s P2P

打印 上一主题 下一主题

主题 874|帖子 874|积分 2622


摘  要

本文具体介绍了hello程序在Linux操纵体系上的预处置惩罚、编译、汇编、链接和运行过程。起首概述了hello程序的整个生命周期,然后渐渐分析了各个阶段的操纵下令及其作用,具体解析了每个阶段产生的中间结果和终极结果。通过对hello程序的深入剖析,展示了从源代码到可执行文件的整个流程,并探讨了进程管理、存储管理和IO管理等关键技术细节。本文体系地回首了盘算机体系课程中的内容。

关键词:盘算机体系、hello、进程、可执行文件

(择要0分,缺失-1分,根据内容出色称都酌情加分0-1分









目  录


第1章 概述................................................................................... - 4 -
1.1 Hello简介............................................................................ - 4 -
1.2 情况与工具........................................................................... - 4 -
1.3 中间结果............................................................................... - 4 -
1.4 本章小结............................................................................... - 4 -
第2章 预处置惩罚............................................................................... - 5 -
2.1 预处置惩罚的概念与作用........................................................... - 5 -
2.2在Ubuntu下预处置惩罚的下令................................................ - 5 -
2.3 Hello的预处置惩罚结果解析.................................................... - 5 -
2.4 本章小结............................................................................... - 5 -
第3章 编译................................................................................... - 6 -
3.1 编译的概念与作用............................................................... - 6 -
3.2 在Ubuntu下编译的下令.................................................... - 6 -
3.3 Hello的编译结果解析........................................................ - 6 -
3.4 本章小结............................................................................... - 6 -
第4章 汇编................................................................................... - 7 -
4.1 汇编的概念与作用............................................................... - 7 -
4.2 在Ubuntu下汇编的下令.................................................... - 7 -
4.3 可重定位目标elf格式........................................................ - 7 -
4.4 Hello.o的结果解析............................................................. - 7 -
4.5 本章小结............................................................................... - 7 -
第5章 链接................................................................................... - 8 -
5.1 链接的概念与作用............................................................... - 8 -
5.2 在Ubuntu下链接的下令.................................................... - 8 -
5.3 可执行目标文件hello的格式........................................... - 8 -
5.4 hello的虚拟地点空间......................................................... - 8 -
5.5 链接的重定位过程分析....................................................... - 8 -
5.6 hello的执行流程................................................................. - 8 -
5.7 Hello的动态链接分析........................................................ - 8 -
5.8 本章小结............................................................................... - 9 -
第6章 hello进程管理.......................................................... - 10 -
6.1 进程的概念与作用............................................................. - 10 -
6.2 简述壳Shell-bash的作用与处置惩罚流程........................... - 10 -
6.3 Hello的fork进程创建过程............................................ - 10 -
6.4 Hello的execve过程........................................................ - 10 -
6.5 Hello的进程执行.............................................................. - 10 -
6.6 hello的异常与信号处置惩罚................................................... - 10 -
6.7本章小结.............................................................................. - 10 -
第7章 hello的存储管理...................................................... - 11 -
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 -
7.9动态存储分配管理.............................................................. - 11 -
7.10本章小结............................................................................ - 12 -
第8章 hello的IO管理....................................................... - 13 -
8.1 Linux的IO装备管理方法................................................. - 13 -
8.2 简述Unix IO接口及其函数.............................................. - 13 -
8.3 printf的实现分析.............................................................. - 13 -
8.4 getchar的实现分析.......................................................... - 13 -
8.5本章小结.............................................................................. - 13 -
结论............................................................................................... - 14 -
附件............................................................................................... - 15 -
参考文献....................................................................................... - 16 -



第1章 概述

1.1 Hello简介

在盘算机体系中,“从程序到进程”(From Program to Process)形貌了一个C程序从编写到在机器上运行的转换过程。这个过程包括以下几个阶段:即hello.c源文件经过cpp预处置惩罚生成hello.i,再经过cc1编译生成hello.s汇编语言程序,之后再由as生成 hello.o二进制的可重定位目标程序,再由毗连器ld链接生成二进制的可执行目标程序 hello,最后通过加载器将程序加载到内存中并运行,这样就完成了hello.c程序从program到process的转换。上述过程可用下图表示:

020过程(From Zero to Zero)形貌的是一个程序从启动到结束的整个生命周期。当用户运行一个程序,比方hello.c,起首体系会检查该下令是否为内置下令。如果不是,体系通过fork函数创建一个新的子进程。这个子进程不会直接执行原有程序,而是通过execve体系调用来加载和运行可执行文件,这个过程中,子进程原有的虚拟内存被清空,并创建新的代码段、数据段、堆和栈段。

此时,操纵体系将可执行文件的各个部分映射到新创建的虚拟内存空间中,随后程序开始执行。在执行过程中,通过异常控制流机制,程序所需的数据从磁盘读取到内存中。程序完成使命后,进程将终止,此时控制权返回到操纵体系,它将接纳子进程使用的资源,包括内存和其他体系资源,使得体系资源的使用情况再次回到了最初的“零”状态。

整个过程概括了一个程序从不存在到执行再到结束的全过程,终极资源被完全接纳,体系回归到初始状态。

1.2 情况与工具

硬件情况:Intel Core i7 13700H处置惩罚器
软件情况:Windows11 64位专业版/VMware 17 Pro/Ubuntu 22.04 LTS 64位
开辟与调试工具:VS code
1.3 中间结果

hello.i(hello.c预处置惩罚后的文件)
hello.s(hello.i编译后的文件)
hello.o(hello.s汇编后得到文件)
hello(hello.o经过链接获得的可执行目标文件)
hello.o.asm(hello.o反汇编得到的文件)
hello.asm(hello反汇编得到的文件)
其中,readelf检察的信息均已给出指令,并未生存到文件。
1.4 本章小结

本章紧张讨论了hello.c程序的编译和执行过程,涵盖了P2P(程序到进程)和020(从无到有再到无)两个关键过程。还列出了实验过程中所依靠的情况和工具,最后列出了完本钱次作业过程中所生成的中间文件。
(第1章0.5分)




第2章 预处置惩罚

2.1 预处置惩罚的概念与作用

预处置惩罚概念:预处置惩罚是C程序编译的第一步,它直接操纵源代码文件的文本,执行如去除解释、宏替换、条件编译和头文件展开等操纵。预处置惩罚不涉及代码逻辑的分析,仅对代码举行文本级的处置惩罚,并生成后缀为.i的预处置惩罚后的文件。
预处置惩罚作用:
美满代码:预处置惩罚通过展开头文件(如使用#include指令)将必要的声明和定义直接插入到源文件中,这有助于美满程序的功能。虽然这使得单个源文件大概失去了独立完整性,但却极大地便利了程序的编写和管理。
帮助编译:预处置惩罚器通过宏定义(#define)答应程序员定义编译时替换的符号或代码块,以及通过条件编译(#ifdef, #ifndef等指令)仅编译符合特定条件的代码段。这些功能简化了源代码的复杂性,使得编译过程更为高效。
代码简化:预处置惩罚器去除所有解释和多余的空白,只留下有效代码和指令,从而简化了编译器处置惩罚的文本量,提高了编译的速度和效率。
2.2在Ubuntu下预处置惩罚的下令

Ubuntu下预处置惩罚下令:gcc -E -o hello.i hello.c

2.3 Hello的预处置惩罚结果解析

2.3.1 预处置惩罚文件代码与原文件代码比力
原始的C语言程序包含18行代码,但经过预处置惩罚后,代码量扩展到了3092行。这种显著的增加紧张是由于源文件中包含的头文件内容被展开。在扩展后的文件中,最后部分是源程序中main函数的代码。在main函数代码之前的部分紧张由几个关键构成部分构成:包括的头文件路径、数据类型的重定义、外部函数的声明,以及枚举类型定义,这些枚举通常用于定义常量值。这样的布局展示了预处置惩罚如何通过包含头文件和展开宏定义来增加程序代码的行数,并为编译准备完整的代码布局。



2.3.2 头文件路径
头文件的引用通常位于C源文件的开头部分,确保全文可以访问所需的定义和声明。然而,根据代码的特定需求,有时也会在文件的其他部分引用头文件。在这些头文件中,重定义数据类型的段落将标准的C数据类型与在头文件内定义的类型关联起来。除此之外,大部分内容涉及到外部函数的声明,这是代码插入中最为广泛的一部分。别的,很大一部分头文件也涉及到为常用的库函数定义的常量,这些常量通常通过枚举类型举行组织和分类。


2.3.3 数据类型的重定义
数据类型重定义涉及给已存在的数据类型赋予新的名称,或调整其属性以适应特定的应用需求。


2.3.4 外部函数的声明
这种做法使得函数可以跨文件或模块使用,有助于代码的模块化和重用。

2.3.5 枚举类型定义

2.4 本章小结

本章分析了hello.i的生成过程,表明确预处置惩罚的作用和实现的功能,最后具体地分析了预处置惩罚的结果,讨论了预处置惩罚后文件各个部分的意义。
(第2章0.5分)


第3章 编译

3.1 编译的概念与作用

编译的概念:编译器将预处置惩罚后的代码转换成汇编语言。在这一步中,编译器也举行语法和语义分析,确保代码服从C语言的规则,并且逻辑上也是可行的。
编译的作用:编译不但使得以高级语言编写的程序可以或许在特定的盘算机硬件上运行,而且还通过编译时的优化提高程序的运行效率和性能,同时在编译阶段举行的错误检查也有助于提高代码的质量和稳定性。
3.2 在Ubuntu下编译的下令

Ubuntu下编译下令:gcc -S hello.i -o hello.s

3.3 Hello的编译结果解析

本节将从数据存储、赋值操纵、算术操纵与比力操纵、参数传递、函数调用与返回和循环操纵这四个方面对hello的实现举行分析。

3.3.1 数据存储
字符串:.LC0: 这是一个 UTF-8 编码的字符串。.LC1: 格式字符串 "Hello %s %s %s\n",用于 printf 函数。


后续访问时,使用的是rip+段偏移量间接寻址。


       整数:整数紧张用于局部变量存储和传递参数。




       在main函数中定义了一个未赋值的局部变量int i,储存在栈中,其生命周期与main函数自身雷同,rsp向下移动了32个字节,其中就有给int i预留的空间。具体如下图所示:

       数组:argv 是一个指针数组,其存储方式如下:

数组的操纵一样平常都是通过首地点加上偏移量得到的。movq -32(%rbp), %rax: 加载 argv 指针。addq $24, %rax: 访问数组中的第4个元素(假设每个元素占用8个字节)。movq (%rax), %rcx: 加载数组元素到寄存器。因此通过加8、加16、加24分别可以得到main()函数的第2、3、4个参数的指针。
运行时栈布局如下图所示:

3.3.2 赋值操纵、算数操纵与比力操纵
在汇编语言下,赋值操纵使用movl或者movq指令。
加法操纵:加法操纵在代码中多次出现,紧张用于指针运算和循环计数器的增加。


       其中,在hello.c中出现了算术操纵“++”,这一操纵通过addl $1, -4(%rbp)来完成,在汇编代码中对应于第56行。
       关系操纵:比力操纵用于条件判断,以控制程序的执行流。


!=在汇编语言中,使用cmp和je的组合实现。cmp 指令举行比力并设置标记位,而 je 指令根据标记位的状态判断是否跳转。
<在汇编语言中,使用cmp和jle的组合实现。同理,通过 cmp 指令举行比力,然后通过 jle 指令根据比力结果举行相应的跳转操纵,从而实现小于或等于的条件判断。

3.3.3 参数传递、函数调用与返回
在 hello.c 中,函数调用出现了 7 次:main(), printf(), exit(), printf(), sleep(), atoi(), 和 getchar()。在 x86-64 架构中,函数参数优先通过寄存器传递,当参数数量超过 6 个时,通过栈传递。本例中,各个函数的参数都是通过寄存器传递的,寄存器传递参数的顺序为:%rdi, %rsi, %rdx, %rcx, %r8, %r9(假设参数为 64 位)。
在进入main()后,参数argc和argv已经分别存入到寄存器%edi和%rsi中,之后又赋值到栈中。对于除main()之外的其它函数,都有相应的传参语句,比如sleep和atoi的传参,如下图所示:

       两次printf的调用如下所示:

 


第一次调用时,只有一个参数(字符串)通过寄存器%rdi传入,并转化成puts函数输出。第二次调用时,共有字符串、argv[0]、argv[1]共三个参数,分别通过寄存器%rdi、%rsi、%rdx传入。
       将1作为参数传给%rdi,从而调用了exit函数。

函数返回前的操纵通常包括:设置返回值(如果有)。恢复栈帧,使栈指针和基指针恢复到进入函数时的状态。使用 ret 指令返回到调用函数的下一条指令。在本例中,如下图所示:

3.3.4 循环操纵
基于比力操纵下的比力和代码跳转举行实现。


在标签 .L2 处,将循环计数器初始化为 0。在标签 .L3 处,比力循环计数器与 9,如果小于或等于 9,则继续执行循环体。在标签 .L4 处,逐个加载 argv 参数并传递给 printf 函数,调用 printf 输出参数,调用 atoi 将参数转换为整数,再调用 sleep 函数休眠指定秒数。最后,增加循环计数器。
3.4 本章小结

本章介绍了编译的概念与作用,并给出了在Ubuntu下的编译下令。之后具体的对hello.c的汇编代码举行了分析,从从数据存储、赋值操纵、算术操纵与比力操纵、参数传递、函数调用与返回和循环操纵这四个方面对hello的实现举行了分析。
(第32分)


第4章 汇编

4.1 汇编的概念与作用

汇编的概念:汇编器将hello.s翻译成机器语言指令,并将结果生存在机器可以读懂的二进制文件即目标文件hello.o中。
汇编的作用:汇编语言生成的机器代码通常比高级语言生成的代码更紧凑和高效,操纵体系内核、装备驱动程序等低级体系软件常常需要用汇编语言编写,以实现对硬件的直接控制和高效操纵。
4.2 在Ubuntu下汇编的下令

Ubuntu下汇编下令:as hello.s -o hello.o

4.3 可重定位目标elf格式

4.3.1 elf头

ELF头信息表现,该文件的Magic Number是0x7f 45 4c 46,即“ELF”标记。文件类型为REL(可重定位文件),它采用了ELF64格式,表示这是一个64位的文件,并且是小端序。文件的OS/ABI为UNIX - System V,表示其兼容UNIX体系。目标体系布局是Advanced Micro Devices X86-64,表明这是一个针对AMD的x86-64架构的文件。
别的,ELF头还表现入口点地点为0x0,程序头表偏移为0(即没有程序头表),节头表偏移为1088字节,节头表包含14个节,每个节头大小为64字节。最后,节头字符串表的索引为13,用于定位节头字符串表中的字符串。
4.3.2 elf的section头
    使用readelf -S --wide hello.o指令检察。

       该文件包含14个节,每一列分别表明确各个节的名称、类型、地点、偏移量大小、实际大小、标记、链接、信息和对齐。
       比方,.text节包含可执行的程序代码,类型为PROGBITS,大小为0x00b3,具有读取和执行权限。.note.gnu.property节:包含GNU特定的属性信息,类型为NOTE,大小为0x0020。.symtab节:包含符号表,类型为SYMTAB,大小为0x00b0,具有信息标记等等。
4.3.3 重定位节
       使用readelf -a hello.o指令检察以下内容。

    在.rela.text段中,有8个重定位条目,每个条目包含偏移量、信息、类型、符号值和符号名称+加数等信息。这些条目使用R_X86_64_PLT32类型,表示针对x86-64架构的PLT(Procedure Linkage Table)重定位,目标符号如.rodata、puts等,需要在程序运行时解析其实际地点。而在.rela.eh_frame段中,只有一个重定位条目,指向.text段的起始位置。
4.3.4 符号表

第一个符号是一个文件符号hello.c,表示源文件。第二个和第三个符号是段符号,分别表示.text和.rodata段。第四个符号main是一个全局函数,位于.text段。别的符号,如puts、exit、printf等,都是未定义的全局符号,需要在程序运行时解析它们的地点。
4.4 Hello.o的结果解析

使用objdump -dr hello.o > hello.o.asm指令检察。

机器语言由一系列二进制指令构成,每条指令包括操纵码(opcode)和操纵数(operand)。操纵码表示要执行的操纵,而操纵数提供操纵的数据或地点。
与汇编语言的映射关系如下:
机器语言的每条指令在汇编语言中都有对应的操纵码和操纵数。比方,push %rbp 在机器语言中对应 55,mov %rsp, %rbp 对应 48 89 e5。
内存操纵在机器语言中通过操纵码和内存地点表示。比方,mov %edi, -0x14(%rbp) 对应 89 7d ec,其中 89 是操纵码,7d ec 是偏移地点。
条件跳转指令在机器语言中通过操纵码和目标地点表示。比方,je 32 <main+0x32> 对应 74 19,其中 74 是条件跳转指令,19 是偏移地点。无条件跳转指令如 jmp 在机器语言中也有相应的操纵码和偏移地点。比方,jmp 91 <main+0x91> 对应 eb 56。
函数调用指令在机器语言中通过 call 指令和目标地点表示。比方,call 28 <main+0x28> 对应 e8 00 00 00 00,其中 e8 是调用指令,00 00 00 00 是偏移地点。
立即数操纵在机器语言中直接编码在指令中。比方,mov $0x1, %edi 对应 bf 01 00 00 00,其中 bf 是操纵码,01 00 00 00 是立即数。
4.5 本章小结

本章简要介绍了汇编语言的概念和作用,并给出了在Ubuntu体系下使用的汇编下令。之后分析了ELF格式的布局,包括ELF头、符号表、重定位条目和个section节等。最后比力了hello.s汇编文件与hello.o文件反汇编得到的代码,给出了二者之间的不同。
(第41分)


第5章 链接

5.1 链接的概念与作用

链接的概念:链接是盘算机程序开辟过程中将多个目标文件和库文件组合成一个单一可执行文件、共享库或静态库的关键步骤。这个过程包括符号解析、重定位和终极生成可执行文件或库文件。链接可以分为静态链接和动态链接两种方式。
链接的作用:链接将编译后的各个模块组合成一个完整的程序,从而确保程序的各部分可以或许正确协同工作。同时,链接也使分离编译成为大概,这样在开辟大型软件中可以将源文件分解为更小、更好管理的模块,可以独立地修改和编译这些模块,从而提高开辟和维护效率。
5.2 在Ubuntu下链接的下令

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的格式

5.3.1 elf头
使用readelf -h hello指令检察。

对比分析hello.o的elf头可知,hello.o作为一个可重定位文件,其布局较为简朴,没有程序头表和入口点,因为它仅包含代码和数据片断,等待链接器将其整合到终极的可执行文件中。而hello文件则包含了更多的信息,如程序头表和入口点地点,使其可以在操纵体系中直接运行。可执行文件包含了程序运行所需的所有指令和数据,链接器在将多个可重定位文件合并时,会处置惩罚符号解析和重定位,生成终极的可执行文件。
5.3.2 elf的section头

       发现hello共有27个节,对比分析hello.o的section节可知,因为此时已不需要举行重定位,所以已不存在记载重定位信息的‘.rela.text’和‘.rela.eh_frame’。
       并且,hello文件中出现了更多与动态链接和运行时相关的节,如.interp, .dynamic, .dynsym, .dynstr, .gnu.version, .gnu.version_r等。
5.3.3 程序头
       使用readelf -l hello指令检察以下内容。

       每个程序头记载了一个内存段的信息或用于准备程序执行的内存详情。需要留意的是,ELF文件的节和内存段之间并非逐一对应关系,一个内存段大概包含一个或多个节。程序头紧张对可执行文件和共享库文件有意义,而对于其他类型的目标文件,程序头的信息通常可以忽略。
5.4 hello的虚拟地点空间

5.3.1虚拟内存信息
使用edb加载hello,检察本进程的虚拟地点空间各段信息,并与5.3对照分析阐明。
在加载器执行加载操纵时,会在代码段、数据段和虚拟内存之间创建映射关系。具体来说,这些映射关系包括.init段、.text段、.rodata段、.data段、.bss段以及其他动态链接部分。


       通过检察ELF文件中的节头表,可以确定各个节的起始地点。使用edb的书签功能,可以对这些地点举行查找,从而检察各个节对应的汇编代码内容。


       .interp节的起始地点为0x4002e0。
5.4.2虚拟内存中.text节


.text节的起始地点为0x4010f0。
5.4.3虚拟内存中.data节


.data节的起始地点为0x404048。
5.5 链接的重定位过程分析

       使用objdump -d hello > hello.asm指令将可执行程序hello反汇编后得到信息导出到hello.aso文件中。
链接器将目标文件中的符号解析为具体的内存地点。比方,puts、printf等函数的调用在链接后被解析为具体的地点。链接器使用重定位表将所有符号引用替换为正确的内存地点。重定位表包含了所有需要重定位的地点和对应的符号信息。
在目标文件hello.o中,外部函数调用如puts、printf等都是未解析的,需要在链接阶段通过重定位表来解析。在可执行文件hello中,链接器已经解析了这些外部函数的地点,并将代码中的符号引用替换为具体的地点。比方,call 401090 <puts@plt>表示链接器将对puts函数的调用解析为具体的地点401090。


       如上图,hello.o反汇编中的重定位条目在 hello反汇编中替换为虚拟内存地点。
5.6 hello的执行流程



使用edb将断点打在0x4010f0(_start)处,开始调试,检察程序执行的流程。
Ld-2.27.so! _dl_start
Ld-2.27.so! _dl_init
_start
_libc_start_main
_GI__cxa_atexit
_new_exitfn
_setjmp
__sigsetjmp
__sigjmp_save
_libc_csu_int
Init
Main
Puts
Printf
Atoi
Sleep
Getchar
__GI_IO_puts
__strlen_avx2
_IO_new_file_xsputn
IO_valodate_vtable
Exit
__GI_exit
__run_exit_handlers
5.7 Hello的动态链接分析

 ELF采用了一种称为延迟绑定的技术,当程序调用共享库中的函数时,只有在首次使用该函数时才会绑定。hello程序通过该技术以及PLT和GOT举举措态链接。GOT存储函数量标地点,而PLT则使用GOT中的地点跳转到目标函数。

       起首通过前面的节头部表获得got的地点,.got的位置是0x403ff0。在执行 _dl_init 之前,.got 表是全0的,在执行 _dl_init 之后,.got 表则变成了相应的值,从而使这些被调用的函数链接到动态库。


下图为_dl_init前条目:


5.8 本章小结

本章介绍了链接的概念及其作用,并提供了在 Ubuntu 下使用的链接下令。随后,对可执行文件 hello 的布局举行了具体阐明,并对 hello 的虚拟地点空间举行了形貌。接着,深入分析了链接的重定位过程以及 hello 的执行流程,最后对 hello 的动态链接过程举行了简要分析。
(第51分)



第6章 hello进程管理

6.1 进程的概念与作用

进程的概念:进程是盘算机中正在运行的程序的实例,它不但包括可执行程序的代码,还包含程序计数器、寄存器和变量等信息。每个进程在操纵体系中都是一个独立的实体,有本身的地点空间和资源。在多使命操纵体系中,进程是资源分配和使命调治的基本单元,它们通过进程控制块(PCB)来管理和生存自身的状态信息。
进程的作用:进程提供了程序执行的基本情况,使得程序可以或许顺遂运行。并且通过独立的地点空间和资源管理,确保不同程序之间的隔离性和安全性。操纵体系通过调治和管理多个进程,实现多使命处置惩罚,从而提高体系的资源利用率和响应速度。别的,进程还支持并发执行,答应多个程序同时运行,为用户提供了高效的盘算体验。
6.2 简述壳Shell-bash的作用与处置惩罚流程

Shell-Bash的作用:将用户输入的下令翻译成操纵体系可以或许理解和执行的操纵。并且提供脚本语言的功能,使用户可以编写脚本来主动执行一系列下令,从而简化重复性使命。还能提供下令用于创建、删除、移动和修改文件和目次。也能答应用户启动和管理进程,包括前台和后台进程的控制。最后管理用户情况变量和体系设置,影响下令的执行方式和输出。
Shell-Bash的处置惩罚流程:
Bash从用户输入装备(如键盘)或脚本文件中读取下令行输入。然后将输入的下令行举行词法分析和语法分析,分解成下令、选项和参数。根据用户输入,查找下令的路径。内置下令直接执行,外部下令则通过查找路径(PATH情况变量)定位可执行文件。
执行下令:直接在Shell中执行。当为外部下令时,创建子进程并执行找到的可执行文件。之后,根据下令中的重定向符号(如>, <, |),重定向标准输入、输出和错误输出。返回执行结果和状态码。乐成执行通常返回0,错误执行返回非零状态码。
在下令执行完毕后,Bash再次表现提示符,等待用户输入下一条下令。
6.3 Hello的fork进程创建过程

在shell中输入下令行./hello 2022112632 sbr 18246154537 7后,shell将下令行转存到参数列表并举行解析。

       起首判断hello不是一个内置的shell下令,然后调用fork函数创建一个子进程。fork函数的原型为pid_t fork(void); 新创建的子进程中fork函数返回的PID为0,而在父进程中fork函数返回的为子进程的PID。除此之外,其拥有和父进程完全雷同的虚拟地点空间副本,相对父进程来说是独立的进程。二者有雷同的代码段和数据段、堆,共享库和用户栈。
       父进程调用 fork 时,子进程继续了父进程的所有打开的文件形貌符,因此子进程可以读写父进程已打开的任何文件。
6.4 Hello的execve过程

在使用fork创建新进程之后,shell调用execve函数在当前的进程上下文中加载并运行hello。总共有4步:
删除已存在的用户地区:现有的用户地点空间被扫除,以确保新程序可以在一个干净的情况中运行。
映射私有地区:为 hello 程序的代码段、数据段、.bss 段和栈段创建新的地区布局,确保这些段在子进程的地点空间中正确映射。
动态链接 hello 程序:将 hello 程序需要的共享库映射到共享地区,并解析所有动态链接所需的符号,以确保程序可以正确执行。
设置程序计数器 (PC):将程序计数器设置为 _start 地点,这样当子进程开始执行时,会从 hello 程序的入口点开始运行。


       此时,argc = 5。
6.5 Hello的进程执行

联合进程上下文信息、进程时间片,叙述进程调治的过程,用户态与核心态转换等等。
进程上下文信息:进程上下文包含了进程在CPU上执行时的所有状态信息,包括程序计数器、通用寄存器、堆栈指针、段寄存器和进程状态字等。在进程切换时,操纵体系必须生存当进步程的上下文信息,以便在下次调治该进程时可以或许恢复其运行状态。
进程时间片:时间片是操纵体系分配给每个进程在CPU上执行的时间单元。时间片调治策略是常用的一种调治算法。在这种策略下,每个进程被分配一个固定的时间片,其时间片用完时,操纵体系会强制进程停止运行并调治下一个进程。
进程调治的过程:起首先确定调治点,可以是进程主动放弃CPU(比方,调用了阻塞操纵或进入等待状态)。或者是进程的时间片用完。以及有更高优先级的进程需要执行。也大概时是当进步程终止。然后调治器根据调治算法(如最短作业优先、多级队列调治等)选择下一个要执行的进程。如果当前正在执行的进程没有完成,其上下文信息需要生存到进程控制块(PCB)中。在这之后,调治器重新进程的PCB中恢复其上下文信息。最后操纵体系将CPU控制权交给新进程,新进程开始执行。
用户态与内核态转换:在用户态下,进程的权限受限,只能访问本身内存空间中的资源,不能直接访问硬件或举行体系级操纵。在内核态下,进程具有完全的访问权限,可以执行体系调用,直接访问硬件装备,举行内存管理等操纵。在体系调用、中断处置惩罚、异常处置惩罚时都会发生用户态与内核态的转换。
在执行 ./hello 2022112632 sbr 18246154537 7时,操纵体系创建一个新的进程来执行 hello 程序。调治器将新创建的进程调治到CPU上执行。hello 进程在用户态下开始执行其代码,解析传递的参数,并执行相应的功能。然后会从用户态转变为内核状态并执行体系调用程序,执行结束时会检查并处置惩罚信号,然后从内核态转变为用户态,继续执行用户程序。最后,通过体系调用(如 exit)通知操纵体系,该进程终止,释放资源。
6.6 hello的异常与信号处置惩罚

6.6.1出现的异常
在执行 hello 程序的过程中,大概会产生以下多种信号:比如通过键盘输入 Ctrl+C 可以向前台进程发送 SIGINT 信号。通过键盘输入 Ctrl+Z 可以向前台进程发送 SIGTSTP 信号。当一个子进程终止时,内核会向其父进程发送 SIGCHLD 信号。fg向被挂起的进程发送SIGCONT 信号。kill发送SIGKILL 信号。
6.6.2不绝乱按与回车

在没有输入回车的情况下,输入的字符串被缓存到缓冲区,没有任何反应。输入回车后,回车同样被存储到缓冲区。

6.6.3 Ctrl+C

6.6.4 Ctrl+Z

6.6.4 Ctrl+Z 后 fg


    使用 fg 下令可以将最近挂起的进程或指定的后台进程恢复到前台。Ctrl+Z发送 SIGTSTP 信号后,输入 fg,fg 下令会向被挂起的进程发送 SIGCONT 信号,通知进程继续执行。
当进程收到 SIGCONT 信号后,操纵体系会恢复该进程的上下文信息,将其状态从停止状态改为运行状态。进程在用户态中恢复执行,从它被暂停的地方继续运行。
6.6.5 Ctrl+Z 后 jobs


jobs 下令,表现当前终端中的所有后台和挂起的作业。
6.6.6 Ctrl+Z 后 ps

ps 下令,表现当前终端会话中的所有进程,可以检察进程的PID、TTY、执行时间和下令。只管 hello 进程处于挂起状态,但 ps 下令仍旧列出了它的信息(PID 7334),表明它仍旧在体系中存在,但暂停执行。
6.6.6 Ctrl+Z 后 pstree

6.6.6 Ctrl+Z 后 kill

输入 kill -9 7334后,向进程 hello发送SIGKILL信号(信号编号9)。SIGKILL 信号的默认举动是立即终止进程,并且无法被捕捉、阻塞或忽略。
hello 进程收到 SIGKILL 信号后,操纵体系立即终止该进程,释放其所有资源。hello 进程被标记为已终止,表现为“已杀死”。
6.7本章小结

本章介绍了进程的概念与作用以及shell的作用以及处置惩罚流程,然后聪慧hello程序的fork进程创建过程、execve过程、hello的进程执行做出了具体的分析,最后,针对hello的异常与信号处置惩罚,用了各种指令和信号做出测试。
(第61分)


第7章 hello的存储管理

7.1 hello的存储器地点空间

逻辑地点:逻辑地点是程序员在源代码中看到的地点,在程序编译时,这些逻辑地点会被转换为相应的线性地点。比方,程序中的 argv 是一个指针数组,存储了下令行参数的地点。argv 是一个逻辑地点,表示在程序的地点空间中的位置。
线性地点:线性地点是逻辑地点经过段偏移转换后的地点。在x86架构中,逻辑地点由段选择子和段内偏移构成,CPU通过段寄存器和段形貌符表将逻辑地点转换为线性地点。线性地点是一个扁平的32位或64位地点空间。假设 argv 指向的数据段基址是 0x400000,则 argv 的逻辑地点会被转换为线性地点。
虚拟地点:虚拟地点是操纵体系提供的一个抽象条理,使每个进程都认为本身在使用整个内存空间。虚拟地点通过页表机制被映射到物理内存地点。每个进程有独立的虚拟地点空间,可以避免进程之间的相互干扰。
物理地点:物理地点是实际的内存芯片上的地点。虚拟地点通过页表映射到物理地点,从而实现程序代码和数据的访问。
7.2 Intel逻辑地点到线性地点的变换-段式管理

段式管理涉及段选择子和段内偏移的使用,联合段寄存器和段形貌符表(如GDT或LDT)举行地点转换。
段选择子是一个16位的值,包含三个部分。索引:指向段形貌符表中的一个段形貌符。TI(Table Indicator):指示使用GDT(全局形貌符表)照旧LDT(局部形貌符表)。RPL:请求的特权级别。段内偏移是从段基址开始的偏移量,表示具体地点在段内的位置。
先从逻辑地点中提取段选择子。根据段选择子的索引部分定位到GDT或LDT中的相应段形貌符。如果TI=0,段选择子指向GDT;如果TI=1,段选择子指向LDT。将段形貌符中的基址和逻辑地点中的段内偏移量相加,得到线性地点。

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

虚拟内存被划分为固定大小的块,称为虚拟页。物理内存也被划分为雷同大小的块,称为物理页。每个进程都有一个页表,用于存储虚拟页号到物理页号的映射关系。页表项包括虚拟页号、物理页号以及一些控制信息(如有效位、修改位等)。

先使用虚拟地点的虚拟页号 (VPN) 通过页表基址寄存器 (PTBR) 找到页表项。然后检查页表项中的有效位。如果有效位为1,则该页在内存中,可以继续转换过程;如果有效位为0,则需要从外存中加载该页。之后从页表项中读取物理页号 (PPN)。终极的物理地点由物理页号 (PPN) 和虚拟页偏移量 (VPO) 构成。
7.4 TLB与四级页表支持下的VA到PA的变换

7.4.1 TLB加快地点翻译
TLB是一个小型的、快速的硬件缓存,存储了最近使用的虚拟页号到物理页号的映射(即页表项的副本)。当处置惩罚器需要将虚拟地点转换为物理地点时,它起首在TLB中查找。

如果TLB掷中,则可以直接从TLB中获取物理页号,无需访问主存中的页表。否则,则需要访问主存中的页表来获取物理页号。此时,处置惩罚器将该映射加载到TLB中,以便未来的访问可以更快。
7.4.2 TLB与四级页表支持下的VA到PA的变换

在当代盘算机体系中,虚拟地点(VA)到物理地点(PA)的转换是通过页表(page table)完成的。以四级页表(four-level page table)为例,它包括四级页目次:页全局目次(PGD)、页上级目次(PUD)、页中间目次(PMD)和页表(PTE)。
当CPU访问内存时,起首检查TLB是否掷中。若掷中,则直接使用TLB中的映射完成地点转换;若未掷中,则需要访问四级页表完成转换,并将结果存入TLB,以加快后续雷同地点的访问。
访问四级页表,起首使用虚拟地点的最高位部分索引页全局目次(PGD),找到对应的页全局目次项(PDE)。然后,使用虚拟地点的次高位部分索引页上级目次(PUD),找到对应的页上级目次项(PDE)。接着,使用虚拟地点的中间位部分索引页中间目次(PMD),找到对应的页中间目次项(PMDE)。最后,使用虚拟地点的低位部分索引终极的页表项(PTE),从而找到实际的物理页框(PFN)。此时,通过将PFN与虚拟地点的页内偏移(offset)联合,得到终极的物理地点(PA)。
7.5 三级Cache支持下的物理内存访问

7.5.1三级Cache介绍
在当代盘算机体系布局中,为了优化物理内存访问,通常采用多级缓存(Cache)架构,其中三级缓存(L1、L2、L3)是常见的筹划。
L1缓存是最快速的缓存,通常分为指令缓存(L1I)和数据缓存(L1D),每个核心各自独立拥有本身的L1缓存。L1缓存的容量较小(通常在32KB到64KB之间),但其访问延迟极低,通常在几个时钟周期内。
L2缓存的容量较大(通常在256KB到512KB之间),但访问速度稍慢于L1缓存。L2缓存通常也是每个核心独立拥有,紧张用于存储那些未能在L1缓存中掷中的数据和指令。
L3缓存的容量最大(通常在几MB到几十MB之间),并且通常是共享的,即多个核心共用一个L3缓存。L3缓存紧张用于存储L1和L2缓存未掷中的数据,以减少对主存的访问频率。L3缓存的访问延迟较高(几百个时钟周期),但仍显著低于主存访问延迟。
7.5.2三级Cache支持下的物理内存访问
在物理内存访问的过程中,当CPU需要访问某个物理地点时,先将物理地点分为 CT(标记)+CI(索引)+CO(偏移量),然后检查L1缓存是否掷中。如果掷中,数据会立即被读取;如果未掷中,则检查L2缓存。如果L2缓存也未掷中,则检查L3缓存。只有当L3缓存也未掷中时,才会访问主存。找到之后则加入 cache,并返回结果。受益与局部性,通常掷中的概率比力大,平均读取时间较快。

7.6 hello进程fork时的内存映射

当fork函数被当进步程调用时,内核为新进程创建各种数据布局,并分配给它一个唯一的PID。为了给这个新进程创建虚拟内存,它创建了当进步程的mmstruct、地区布局和页表的原样副本。它将两个进程中的每个页面都标记为只读,并将两个进程中的每个地区布局都标记为私有的写时复制。

当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存雷同。当这两个进程中的任一个后来举行写操纵时,写时复制机制就会创建新页面。
7.7 hello进程execve时的内存映射

execve函数在当进步程中加载并运行包含在可执行目标文件hello中的程序并替换了当出息序。需要以下几个步骤:
删除已存在的用户地区。删除当进步程虚拟地点的用户部分中的已存在的地区布局。映射私有地区。
为新程序的代码、数据、bss和栈地区创建新的地区布局。所有这些新的地区都是私有的、写时复制的。代码和数据地区被映射为文件中的.text和.data区。bss地区是请求二进制零的,映射到名文件,其大小包含在hello中。栈和堆地区也是请求二进制零的,初始长度为零。

映射共享地区。hello程序与共享对象链接,比如标准C库1ibcso,那么这些对象都是动态链接到这个程序的,然后再映射到用户虚拟地点空间中的共享地区内。
设置程序计数器(PC)。execve做的最后一件事情就是设置当进步程上下文中的程序计数器,使之指向代码地区的入口点。
7.8 缺页故障与缺页中断处置惩罚

当CPU访问一个虚拟地点时,它起首通过地点转换机制(页表或TLB)将虚拟地点转换为物理地点。如果所访问的虚拟地点在当前页表中未找到对应的物理地点映射,体系就会产生一个缺页故障。这种情况大概是因为该页面从未加载到内存中,或者页面被交换到磁盘上。

       缺页故障会触发一个中断信号,通知操纵体系处置惩罚这个异常。当缺页中断发生时,CPU会生存当前的上下文,操纵体系根据缺页中断的地点,从页表或相关数据布局中查找对应的页面信息。如果页面不在内存中,操纵体系会分配一个物理内存框架(Page Frame)来加载该页面。如果内存已满,大概需要执行页面置换算法(如LRU或FIFO)来释放一个页面。这之后,操纵体系将所需页面从磁盘或其他存储装备加载到分配的物理内存框架中。并更新页表,恢复之前的上下文。
7.9动态存储分配管理

动态内存管理是盘算机程序在运行时分配和释放内存空间的一种方式。动态内存分配器维护着一个进程的虚拟内存地区,称为堆。系假设堆是一个请求二进制零的地区,它紧接在未初始化的地区后开始,并向上生长。对于每个进程,内核维护着变量brk,它指向堆的顶部。

分配器有两种基本风格。两种风格都要求应用表现地分配块。它们的不同之处在于由哪个实体来负责释放已分配的块。表现分配器要求引用表现的释放任何已分配的块,malloc函数分配一个块,并调用free函数释放一个块。隐士分配器也叫垃圾收集器,主动释放已使用的分配的块。
隐式空闲链表分配中,内存块的基本布局如下:

当malloc函数分配内存时,它会返回一个指向有效载荷起始处的指针,而在这之前的头部包含了该块的元数据。头部的块大小字段包含了整个内存块的大小(包括有效载荷和填充),而分配标记则指示该内存块是否已分配。
通过这样的内存管理机制,操纵体系或运行时库可以或许高效地管理和利用内存,确保体系运行的稳定性和性能。
7.10本章小结

本章具体探讨了“hello”程序的存储管理机制,涵盖了存储器地点空间布局、逻辑地点到线性地点的段式管理、线性地点到物理地点的页式管理、TLB加快地点翻译、四级页表支持下的VA到PA转换、三级Cache支持下的物理内存访问、进程fork和execve时的内存映射、缺页故障与中断处置惩罚,以及动态存储分配管理,全面解析了当代盘算机体系的内存管理策略。
(第7 2分)


第8章 hello的IO管理

8.1 Linux的IO装备管理方法

装备的模型化:文件。在Linux体系中,所有装备(包括硬件装备和虚拟装备)都被抽象为文件。这个文件可以是字符装备文件、块装备文件或网络装备文件。通过将装备抽象为文件,Linux提供了一种同一的访问和管理装备的接口,使得用户和应用程序可以通过标准的文件操纵来访问和控制装备。
字符装备通常位于/dev目次下,常见的有/dev/tty、/dev/console等。块装备同样位于/dev目次下,常见的有/dev/sda、/dev/nvme0n1等。网络装备通常不直接映射为文件,但可以通过文件体系接口举行操纵,比方通过/proc或/sys文件体系检察和设置网络装备。
装备管理:Unix IO接口。Linux体系采用Unix IO接口来管理装备,通过一组体系调用实现对装备的控制和数据传输。比如,ioctl():用于装备的特定控制操纵,答应用户程序直接与装备驱动程序举行交互。
通过这些Unix IO接口,Linux体系提供了一种同一且灵活的装备管理机制。同时,装备驱动程序负责具体装备的操纵细节,屏蔽了硬件差异,简化了上层应用的开辟。
8.2 简述Unix IO接口及其函数

open():用于打开一个文件或者装备,返回一个文件形貌符,用于后续的I/O操纵。原型:int open(const char *pathname, int flags, mode_t mode)。参数: pathname为要打开的文件路径。flags为打开文件的方式,如只读、只写、读写等。mode为文件的权限,只有在创建新文件时才使用。
flags可以为O_RDONLY 只读,O_WRONLY 只写,O_RDWR 可读只写
close():关闭一个文件形貌符,使其不再指向任何文件。原型:int close(int fd)。其中,fd为要关闭的文件形貌符。
read():从文件形貌符读取数据。原型:ssize_t read(int fd, void *buf, size_t count)。参数:buf为存储读取数据的缓冲区。count为要读取的字节数。
write():向文件形貌符写入数据。原型:ssize_t write(int fd, const void *buf, size_t count)。
dup()和dup2():复制文件形貌符。原型:int dup2(int oldfd, int newfd)。其中,oldfd为要复制的文件形貌符。newfd为新的文件形貌符。
8.3 printf的实现分析


观察printf代码发现调用printf时,参数会被传递给vsprintf举行处置惩罚。vsprintf 是一个变参函数,用于将格式化的字符串存储在一个缓冲区中。其实现包括解析格式字符串和参数,并将生成的字符串写入指定的缓冲区。下面为vsprintf的代码。

格式化后的字符串通过 write 体系调用写入到标准输出。write 函数通过体系调用接口进入内核态。常见的是使用 int 0x80 或 syscall 指令。终端驱动程序吸收内核传递的数据,将其转换为字符,并表现在屏幕上。具体实现包括字符编码转换(如ASCII到字模),然后将字符的点阵信息写入显存(VRAM)。VRAM 是用于存储表现数据的内存,每个像素点的颜色信息(RGB值)存储在显存中。
表现芯片按照设定的刷新率从VRAM逐行读取数据,并传输到表现器。表现器吸收信号,并将每个像素点的RGB信息转换为实际的光信号表现出来。
8.4 getchar的实现分析

getchar 函数是一个标准C库函数,用于从标准输入读取一个字符。若第一次调用getchar()时,输了多个字符,以后的getchar()再执行时就会直接从缓冲区中读取。下面为其原代码。

异步异常-键盘中断的处置惩罚:键盘中断处置惩罚子程序。接受按键扫描码转成ascii码,生存到体系的键盘缓冲区。
getchar 函数终极调用体系的 read 函数,从键盘缓冲区中读取数据。但read 函数会阻塞,直到有数据可读(即用户按下一个键)。当用户按下回车键时,read 函数会返回读取的字符。
8.5本章小结

本章介绍了Linux的IO装备管理方法,涵盖装备分类、装备文件和驱动程序。简述了Unix IO接口及其函数,具体分析了printf和getchar的实现过程,包括从格式化字符串生成到体系调用处置惩罚的全过程。
(第81分)

结论

用盘算机体系的语言,逐条总结hello所经历的过程。
       预处置惩罚:使用预处置惩罚器将hello.c中的宏展开、头文件包含处置惩罚、条件编译等预处置惩罚指令处置惩罚完成。从而得到hello.i文件。
       编译:hello.i通过编译转化为汇编指令,得到hello.s文件。
       汇编:将hello.s中的汇编代码转化为机器代码,得到hello.o文件。
       链接:hello.o在链接过程中举行符号解析和重定位,得到可执行文件hello。
       加载:操纵体系加载可执行文件hello到内存,初始化进程地点空间,包括代码段、数据段、堆栈等等,并准备好进程执行情况。
       执行:通过execve体系调用执行可执行文件hello,操纵体系调治器将CPU控制权交给进程,开始顺序执行代码。
IO管理:程序通过标准库函数举行输入输出操纵,底层通过体系调用与内核交互,终极可以或许与硬件交互,从而实现了真正的IO操纵。
       进程管理:操纵体系负责进程的创建、调治、切换及终止,包管各个进程的独立运行及资源的公道分配。
       存储管理:虚拟内存的使用。
       通过对hello程序的渐渐分析,我深刻领会到盘算机体系的各个条理协同工作的紧张性。每一个环节都有其不可替换的作用。操纵体系通过体系调用桥接用户程序与硬件装备,驱动程序则具体实现对硬件的控制。这些机制保障了盘算机体系的稳定、高效运行。
       至于创新理念与新的筹划与实现方法,我认为可以异构盘算资源管理,在多核及异构盘算情况下,优化使命调治策略,充分利用不同处置惩罚器的特性,提高盘算性能。
(结论0分,缺失 -1分,根据内容酌情加分)


附件

hello.i(hello.c预处置惩罚后的文件)
hello.s(hello.i编译后的文件)
hello.o(hello.s汇编后得到文件)
hello(hello.o经过链接获得的可执行目标文件)
hello.o.asm(hello.o反汇编得到的文件)
hello.asm(hello反汇编得到的文件)
其中,readelf检察的信息均已给出指令,并未生存到文件。
(附件0分,缺失 -1分)




参考文献


  •  Randal E.Bryant等.深入理解盘算机体系(原书第3版)[M]. 北京:机械工业出版社 2016.7:2.
  •  X86汇编调用框架浅析与CFI简介_cfi directive-CSDN博客 X86汇编调用框架浅析与CFI简介
  •  逻辑地点、物理地点、虚拟地点_虚拟地点 逻辑地点-CSDN博客逻辑地点、物理地点、虚拟地点
  • 预处置惩罚、编译、汇编和链接_已知hello.h和hello.c两个文件,按所需下令写在下划线上-CSDN博客 预处置惩罚、编译、汇编和链接
  • 段页式访存——逻辑地点到线性地点的转换_某采用段页式管理体系中,一操纵数的逻辑地点为9976h,若逻辑地点格式为段号(3-CSDN博客 段页式访存——逻辑地点到线性地点的转换
  • https://www.cnblogs.com/pianist/p/3315801.html printf函数实现的深入剖析
  • 理解链接之链接的基本概念 - 简书 理解链接之链接的基本概念
  • https://zhuanlan.zhihu.com/p/367223273 浅析 Linux 中的 I/O 管理
(参考文献0分,缺失 -1分)



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

汕尾海湾

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表