ToB企服应用市场:ToB评测及商务社交产业平台

标题: 计算机体系大作业 步伐人生HIT [打印本页]

作者: 没腿的鸟    时间: 2024-11-25 05:11
标题: 计算机体系大作业 步伐人生HIT




计算机体系


大作业



题     目  步伐人生-Hellos P2P 
专       业   人工智能2+x    
学     号   2021111938     
班   级    21WL021      
学       生            
指 导 教 师      史先俊          





计算机科学与技术学院
20234

摘  要

本文遍历了hello.c在Linux下生命周期,借助Linux下系列开发工具,通过对其预处理处罚、编译、汇编等过程的分步解读及对比来学习各个过程在Linux下实现机制及原因。并由操作体系举行历程管理、存储管理和I/O管理的全过程。以此将CSAPP课程中的内容举行全面地总结和梳理,加深对计算机体系的明白。
                   
关键词:1.编译体系;2.Hello步伐;3.历程;4.信号与非常;5.虚拟内存;6.I/O管理。                               









目  录


第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简介

1.1.1 P2P: From Program to Process
P2P,即从步伐到历程。用户利用高级语言C语言编写hello.c源步伐,颠末cpp预处理处罚形成hello.i,再颠末ccl编译形成汇编语言步伐hello.s,然后颠末as转换为机器语言指令,形成可重定位目的步伐hello.o,末了通过ld与库函数链接并符号解析与重定位,形成可执行目的文件hello。而后可执行文件hello通过shell加载,fork产生子历程,颠末以上步骤hello步伐(program)酿成了hello历程(process)。
1.1.2 020: From Zero-0 to Zero -0
020,即从运行到结束。初始时内存中没有hello文件干系的内容,通过fork产生hello子历程后,通过execve举行加载,先删除当前虚拟地址已存在的数据结构,为hello的代码、数据、bss等创建地区,然后映射共享地区,设置步伐计数器,进入main函数,CPU分配时间片执行逻辑控制流。执行过程中,虚拟内存为历程提供独立的空间;存储结构层层递进,让数据从磁盘传输到CPU中;TLB、分级页表等也为数据的高效访问提供保障;I/O装备通过描述符与接口实现了hello的输入输出。多方面合作共同之下,hello完成执行。然后,shell接纳hello历程,删除hello的所有痕迹,释放运行中占用的内存空间。至此,hello从运行到结束,完成020过程。
1.2 情况与工具

1.2.1 硬件情况

X64 CPU;2GHz;2G RAM;256GHD Disk



1.2.2 软件情况

Windows 10 64位;Vmware-workstation-16;Ubuntu 20.04 LTS 64位



1.2.3 开发与调试工具

Codeblocks 64位;vi/vim/gedit+gcc

1.3 中间结果

列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
hello.c

源代码

hello.i

预处理处罚后的文本文件

hello.s

编译后的汇编文件

hello.o

汇编后的可重定位目的文件

hello

链接后的可执行文件

hello.elf

用readelf读取hello.o的ELF格式信息

hello.asm

反汇编hello.o的反汇编文件

hello2.elf

由hello可执行文件生成的.elf文件

hello2.asm

反汇编hello可执行文件得到的反汇编文件
1.4 本章小结

本章重要介绍了hello的P2P和020过程,从总体上简要论述了hello的一生,给出了论文研究时的情况与工具以及中间生成的文件信息。



第2章 预处理处罚

2.1 预处理处罚的概念与作用

2.1.1 预处理处罚的概念
预处理处罚步骤是指步伐开始运行时,预处理处罚器(cpp)根据以字符#开头的命令,修改原始的C步伐的过程。

2.1.2 预处理处罚的作用
宏睁开:将所有的宏定义举行替换,然后删除#define
条件编译:如果源代码中包含条件预处理处罚指令(如#if),就会先判断条件,再修改源代码
头文件睁开:对文件包含命令#include,引入对应头文件,将头文件的内容(.h)插入到命令地点位置,从而把头文件和当前源文件连接成一个源文。
2.2在Ubuntu下预处理处罚的命令

预处理处罚命令:gcc -E hello.c -o hello.i

图1.预处理处罚结果
2.3 Hello的预处理处罚结果解析


图2.hello.i的代码截图
预处理处罚后,文件酿成3062行的文本文件,前面为头文件<stdio.h> <unistd.h> <stdlib.h> 的内容被复制插入进代码中,消除include宏,随后是原本的代码。可以看到,预处理处罚后的文件更加完整和清楚,也没有了多余的注释等无用的部分。
2.4 本章小结

在本章中,我们对hello.c举行了预处理处罚,生成hello.i,预处理处罚器会举行宏睁开、头文件睁开、条件编译等处理处罚,并删除注释,对函数源码并不做过多修改,hello.i文件可用于下一步的处理处罚。



第3章 编译


3.1 编译的概念与作用

3.1.1 编译的概念
编译是指预处理处罚后,编译器(ccl)将预处理处罚文件hello.i翻译成汇编语言文件hello.s。

3.1.2 编译的作用
语法查抄:查抄代码是否存在语法错误,如果有错误的话就会报错。
生成汇编代码:将步伐翻译成汇编语言,从而在下一阶段可以让汇编器翻译成机器语言指令。
代码优化:编译器会对步伐举行优化,生成效率更高的目的代码。
3.2 在Ubuntu下编译的命令


图3.编译过程
3.3 Hello的编译结果解析

3.3.1 汇编代码展示
 .file "hello.c"
 .text
 .section .rodata
 .align 8
.LC0:
 .string "\347\224\250\346\263\225: Hello \345\255\246\345\217\267 \345\247\223\345\220\215 \347\247\222\346\225\260\357\274\201"
.LC1:
 .string "Hello %s %s\n"
 .text
 .globl main
 .type main, @function
main:
.LFB6:
 .cfi_startproc
 endbr64
 pushq %rbp
 .cfi_def_cfa_offset 16
 .cfi_offset 6, -16
 movq %rsp, %rbp
 .cfi_def_cfa_register 6
 subq $32, %rsp
 movl %edi, -20(%rbp)
 movq %rsi, -32(%rbp)
 cmpl $4, -20(%rbp)
 je .L2
 leaq .LC0(%rip), %rdi
 call puts@PLT
 movl $1, %edi
 call exit@PLT
.L2:
 movl $0, -4(%rbp)
 jmp .L3
.L4:
 movq -32(%rbp), %rax
 addq $16, %rax
 movq (%rax), %rdx
 movq -32(%rbp), %rax
 addq $8, %rax
 movq (%rax), %rax
 movq %rax, %rsi
 leaq .LC1(%rip), %rdi
 movl $0, %eax
 call printf@PLT
 movq -32(%rbp), %rax
 addq $24, %rax
 movq (%rax), %rax
 movq %rax, %rdi
 call atoi@PLT
 movl %eax, %edi
 call sleep@PLT
 addl $1, -4(%rbp)
.L3:
 cmpl $8, -4(%rbp)
 jle .L4
 call getchar@PLT
 movl $0, %eax
 leave
 .cfi_def_cfa 7, 8
 ret
 .cfi_endproc
.LFE6:
 .size main, .-main
 .ident "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0"
 .section .note.GNU-stack,"",@progbits
 .section .note.gnu.property,"a"
 .align 8
 .long  1f - 0f
 .long  4f - 1f
 .long  5
0:
 .string  "GNU"
1:
 .align 8
 .long  0xc0000002
 .long  3f - 2f
2:
 .long  0x3
3:
 .align 8
4:
3.3.2 汇编文本结构分析
表3.2.2-1 hello.s文件结构

内容               寄义

.file               源文件

.text               代码段

.global             全局变量

.data        存放已经初始化的全局和静态C 变量

.section  .rodata    存放只读变量

.align              对齐方式

.type          体现是函数类型/对象类型

.size               体现巨细

.long  .string    体现是long类型/string类型
3.3.3 常量
编译时对常量举行编码,并将其存储在只读代码区的 .rodata节,在步伐运行时会直接通过寻址找到常量。

例如将hello.c中“Usage: Hello 学号 姓名 秒数!”编译为汇编代码第6行:

.string "\347\224\250\346\263\225: Hello \345\255\246\345\217\267 \345\247\223\345\220\215 \347\247\222\346\225\260\357\274\201"
3.3.4 变量
不同类型的变量在不同位置定义,初始化的全局变量和静态变量定义在只读代码区的.bss节,已初始化的全局和静态变量定义在只读代码区的.data节,局部变量在堆上举行定义和释放。

例如,局部变量int i被保存在栈上,通过机器指令对其赋值

movl $0, -4(%rbp)
3.3.5 赋值操作
对局部变量举行赋值操作,使用MOV指令,根据不同的数据巨细选择不同指令movb、movw、movl、movq等。具体见3.3.4例。

3.3.5 算术运算
表3.3.5-1 算数指令

指令           效果

leaq s,d        d=&s

inc d          d+=1

dec d          d-=1

neg d          d=-d

add s,d        d=d+s

sub s,d        d=d-s

imulq s      r[%rdx]:r[%rax]=s*r[%rax]

mulq s      r[%rdx]:r[%rax]=s*r[%rax]

idivq s      r[%rdx]=r[%rdx]:r[%rax] mod s
 r[%rax]=r[%rdx]:r[%rax] div s

divq s      r[%rdx]=r[%rdx]:r[%rax] mod s
 r[%rax]=r[%rdx]:r[%rax] div s

在hello.s中,例如,实现i++的操作:

addl $1, -4(%rbp)
开辟栈以及接纳栈:

subq $32, %rsp
3.3.6 比较和跳转操作
通过COM指令举行比较,计算两个值相减巨细,根据结果设置条件码,根据条件码来判断跳转值,也可通过跳转指令J判断有无符号。

例如,查抄argc是否不便是4。在hello.s中,使用cmpl $4,-20(%rbp),比较 argc与4的巨细并设置条件码,为下一步je利用条件码举行跳转作预备。

cmpl $4, -20(%rbp)
je .L2

指令              条件

Jmp            直接跳转

Je                相等   

Jne              不便是

Js                小于

Jns              小于便是

Jg                大于

Jge            大于便是

Ja            大于(无符号)

Jae           大于便是(无符号)

Jbe           小于便是(无符号)

3.3.7 数组/指针操作
对数组的索引相当于在第一个元素地址的基础上通过加索引值乘以数据巨细来实现。

例如,在hello.c中,存在char *argv[],根据argv首地址获得argv[1]和argv[2]必要通过加减操作:

movq -32(%rbp), %rax
addq $16, %rax
movq (%rax), %rdx
movq -32(%rbp), %rax
addq $8, %rax
movq (%rax), %rax
movq %rax, %rsi
3.3.8 函数操作
hello.c中包括main函数,printf函数,sleep函数,getchar函数,exit函数。

首先,内核shell获取命令行参数和情况变量地址,执行main函数,在main中必要调用其它函数,在main中为被调用函数分配栈空间。调用函数必要借助栈,先将返回地址压入栈中,并将PC设为被调用函数的起始地址,然后调用。返回时,先从栈中弹出返回地址,再PC设置为该返回地址。return正常返回后,leave恢复栈空间。

在hello.s中调用函数有:

call        puts@PLT

call        exit@PLT

call        printf@PLT

call        sleep@PLT

call        getchar@PLT

3.4 本章小结

本章重要探讨编译器将颠末预处理处罚阶段后的C步伐hello.i翻译成汇编语言步伐的处理处罚过程,包括对数据、算术操作、关系操作、控制转移、数组操作、函数操作的处理处罚。编译器也会在处理处罚过程中对步伐举行一些优化,最终的结果被保存在hello.s文件中,可以或许在下一阶段让汇编器翻译机器语言指令。


第4章 汇编


4.1 汇编的概念与作用


4.1.1 汇编的概念
汇编是指汇编器(as)将hello.s翻译成机器语言指令的过程,把这些指令打包成可重定位目的步伐的格式,并将结果保存在目的文件hello.o中。

4.1.2 汇编的作用
将汇编指令转换成机器可以直接读取分析的机器指令,生成hello.o文件,用于后续的链接。
4.2 在Ubuntu下汇编的命令

4.3 可重定位目的elf格式

分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特殊是重定位项目分析。
在shell中输入readelf -a hello.o > hello.elf指令获得hello.o文件的 ELF 格式:
4.3.1 ELF头

以 16字节序列 Magic 开始,其描述了生成该文件的体系的字的巨细和字节顺序,ELF 头剩下的部分包含资助链接器语法分析和解释目的文件的信息,此中包括 ELF 头巨细、目的文件类型、机器类型、节头部表的文件偏移,以及节头部表中条目的巨细和数目等干系信息。

4.3.2 节头

节头记载了各节名称及巨细、类型及全体巨细、地址及旗标、连接、信息和偏移量及对齐信息。


4.3.3 重定位节

当链接器把这个目的文件和其他文件组适时,必要修改表中的这些位置。一般,调用外部函数大概引用全局变量的指令都必要修改。

此中包括R_X86_64_PC32( PC相对地址的引用)和R_X86_64_32(绝对地址的引用)。


4.4 Hello.o的结果解析

4.4.1objdump -d -r hello.o  分析hello.o的反汇编,并请与第3章的 hello.s举行对照分析。


4.4.2 与汇编代码比较
将hello.asm和hello.s举行比较,大部分相同,重要有一下几个方面不同:

1,包含内容:
hello.s中包含.type .size .align以及.rodata只读数据段等信息,而hello.asm中只有函数的干系内容。

2.分支转移:

在hello.s中,跳转指令的目的地址直接记为段名称,如.L1,.L2等。而在反汇编得到的hello.asm中,跳转的目的为具体的地址。

3.函数调用:

在hello.s文件中,call之后直接跟着函数名称,而在反汇编得到的hello.asm中,call 的目的地址是当前指令的下一条指令。

4. 全局变量访问:

在hello.s 文件中,使用段名称+%rip访问 rodata(printf 中的字符串),而在反汇编得到的hello.asm中,使用 0+%rip举行访问。

4.4.3 机器语言的构成
机器语言是机器能直接识别的步伐语言或指令代码,无需颠末翻译,每一操作码在计算机内部都有相应的电路来完成它,或指不经翻译即可为机器直接明白和接受的步伐语言或指令代码。机器语言使用绝对地址和绝对操作码。不同的计算机都有各自的机器语言,即指令体系。从使用的角度看,机器语言是最低级的语言。

4.4.4 机器语言与汇编语言的映射关系
汇编语言和机器语言一般是逐一对应的,汇编语言是机器语言的符号体现方式。而不同类型的CPU 有不同的机器指令体系,也就有不同的汇编语言,以是,汇编语言步伐与机器有着密切的关系。以是,除了同系列、不同型号CPU 之间的汇编语言步伐有肯定程度的可移植性之外,其它不同类型(如:小型机和微机等)CPU 之间的汇编语言步伐是无法移植的,也就是说,汇编语言步伐的通用性和可移植性要比高级语言步伐低。
4.5 本章小结

本章讨论了汇编阶段将汇编代码hello.s翻译成机器语言指令hello,o的过程,等待下一步链接器的处理处罚。同时,比较了汇编代码与反汇编代码之间的不同之处。



5链接


5.1 链接的概念与作用

5.1.1 链接的概念

链接是指链接器(ld)将各种代码和数据片断网络并组合成一个单一可执行目的文件的过程。
5.1.2 链接的作用

使得分离编译成为可能,我们可以独立的修改和编译模块,当我们改变这些模块的此中一个时,只需简单的重新编译它,并重新链接应用,而不必重新编译其他文件。
5.2 在Ubuntu下链接的命令


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

输入命令:readelf -a hello查看可执行文件中的信息内容
1.ELF 头部表


hello与hello.o的ELF头大抵相同,不同之处在于hello的类型为EXEC可执行文件,表明hello是一个可执行目的文件,有25个字节。

节头部表是描述目的文件的节,各节的基本信息均在此中举行了声明,包括名称,巨细,类型,全体巨细,地址,旗标,偏移量,对齐等信息等。


5.4 hello的虚拟地址空间

    命令行输入:edb --run hello
  观察edb的Data Dump窗口。窗口显示虚拟地址由0x401000开始,到0x402000结束

5.5 链接的重定位过程分析

5.5.1 重定位概念
链接器在完成符号解析以后,就把代码中的每个符号引用和一个符号定义关联起来。此时,链接器就知道它的输入目的模块中的代码节和数据节的确切巨细。

然后就可以开始重定位步骤了,在这个步骤中,将归并输入模块,并为每个符号分配运行时的地址。在 hello 到 hello.o 中,首先是重定位节和符号定义,链接器将所有输入到 hello 中相同类型的节归并为同一类型的新的聚合节。当这一步完成时,步伐中的每条指令和全局变量都有唯一的运行时内存地址了。然后是重定位节中的符号引用,链接器会修改 hello 中的代码节和数据节中对每一个符号的引用,使得他们指向精确的运行地址。

5.5.2 hello与hello.o的不同
1.链接后函数数目增长,hello中增长了外部函数。

2.函数调用指令call的参数发生变化,hello中调用为call+函数名,hello.o中为call+相对偏移地址。

3.跳转指令参数发生变化。

4.hello.o中是相对偏移地址,hello为虚拟内存地址。
5.6 hello的执行流程
0x00007ffff7e16e20 <_init>

0x0000000000401090<_start>

0x0000000000401150<in __libc_csu_init>

0x0000000000401000<_init>

0x00000000004010c5<main>

0x0000000000401030<puts>

0x0000000000401070<exit>


5.7 Hello的动态链接分析

   延迟绑定是通过GOT和PLT实现的。GOT是数据段的一部分,而PLT是代码段的一部分。两表内容分别为:

PLT:PLT是一个数组,此中每个条目是16字节代码。PLT[0]是一个特殊条目,它跳转到动态链接器中。每个被可执行步伐调用的库函数都有它本身的PLT条目。每个条目都负责调用一个具体的函数。

GOT:GOT是一个数组,此中每个条目是8字节地址。和PLT联合使用时,GOT[O]和GOT[1]包含动态链接器在解析函数地址时会使用的信息。GOT[2]是动态链接器在1d-linux.so模块中的入口点。别的的每个条目对应于一个被调用的函数,其地址必要在运行时被解析。每个条目都有一个相匹配的PLT条目。

图5.7-1调用 dl_init 之前的全局偏移表

图5.7-2调用 dl_init 之后的全局偏移表
由上图对比可知,调用dl_init函数后,发生动态链接,GOT条目改变。
5.8 本章小结

本章介绍了链接的概念和功能,分析可执行文件hello的ELF格式及其虚拟地址空间,并对重定位、动态链接举行深入的分析。



6hello历程管理


6.1 历程的概念与作用

6.1.1 历程的概念
历程的经典定义就是一个执行中的步伐的实例。历程是计算机中的步伐关于某数据集合上的一次运行活动,是体系举行资源分配和调度的基本单位,是操作体系结构的基础。在早期面向历程计划的计算机结构中,历程是步伐的基本执行实体;在当代面向线程计划的计算机结构中,历程是线程的容器。步伐是指令、数据及其构造形式的描述,历程是步伐的实体。
6.1.2 历程的作用
每次用户通过向 shell 输入一个可执行目的文件的名字,运行步伐时, shell 就会创建一个新的历程,然后在这个新历程的上下文中运行这个可执行目的文件。应用步伐也可以或许创建新历程,而且在这个新历程的上下文中运行它们本身的代码或其他应用步伐。
6.2 简述壳Shell-bash的作用与处理处罚流程

6.2.1 Shell作用
读取输入并解析命令行;替换特殊字符,比如通配符和汗青命令符;设置管道、重定向和后台处理处罚;处理处罚信号;程式执行的干系设置。
1. 可交互,和非交互的使用shell。在交互式模式,shell从键盘吸取输入;在非交互式模式,shell从文件中获取输入。
2. shell中可以同步和异步的执行命令。在同步模式,shell要等命令执行完,才气吸取下面的输入。在异步模式,命令运行的同时,shell就可吸取其它的输入。重定向功能,可以更过细的控制命令的输入输出。别的,shell答应设置命令的运行情况。
3. shell提供了少量的内置命令,以便自身功能更加完备和高效。
4. shell除了执行命令,还提供了变量,流程控制,引用和函数等,雷同高级语言一样,能编写功能丰富的步伐。
5. shell强大的的交互性除了可编程,还体现在作业控制,命令行编辑,汗青命令,和别名等方面。
6.2.2 Shell 处理处罚流程
命令行是一串 ASCII 字符由空格分隔。字符串的第一个单词是一个可执行步伐,大概是 shell 的内置命令。命令行的别的部分是命令的参数。
如果第一个单词是内置命令,shell 会立即在当前历程中执行。否则,shell 会新建一个子历程,然后再子历程中执行步伐。新建的子历程又叫做作业。通常,作业可以由 Unix 管道连接的多个子历程构成。
如果命令行以&符号结尾,那么作业将在后台运行,这意味着在打印提示符并等待下一个命令之前,shell 不会等待作业终止。否则,作业在前台运行,这意味着 shell 在作业终止前不会执行下一条命令行。 因此,在任何时候,最多可以在一个作业中运行在前台。 但是,恣意数目的作业可以在后台运行。例如,键入命令行:sh> jobs,会让 shell 运行内置命令 jobs。键入命令行 sh> /bin/ls -l -d 会导致 shell 在前台运行 ls 步伐。根据约定,shell会执行步伐的 main 函数 int main(int argc, char *argv[]),argc 和 argv 会吸取到下面的值:
argc == 3,
argv[0] == ‘‘/bin/ls’’,
argv[1]== ‘‘-l’’,
argv[2]== ‘‘-d’’.
下面以&结尾的命令行会在后台执行 ls 步伐:
sh> /bin/ls -l -d &
Unix shell 支持作业控制的概念,答应用户在前台和后台之间来回移动作业,并更改历程的状态(运行,制止或终止)。在作业运行时,键入 ctrl-c会将 SIGINT 信号传递到前台作业中的每个历程。SIGINT 的默认动作是终止历程。雷同地,键入 ctrl-z 会导致 SIGTSTP 信号传递给所有前台历程。SIGTSTP 的默认操作是制止历程,直到它被 SIGCONT 信号唤醒为止。Unixshell 还提供支持作业控制的各种内置命令。例如:
jobs:列出运行和制止的后台作业。
bg <job>:将制止的后台作业更改为正在运行的后台作业。
fg <job>:将制止或运行的后台作业更改为在前台运行。
kill <job>:终止作业。
图6.3-1步伐执行过程
6.3 Hello的fork历程创建过程

父历程通过调用fork函数创建一个新的、处于运行状态的子历程。

函数原型:int fork(void);

调用fork函数后,子历程返回0,父历程返回子历程的PID;新创建的子历程几乎但不完全与父历程相同:子历程得到与父历程虚拟地址空间相同的(但是独立的)一份副本;子历程获得与父历程任何打开文件描述符相同的副本;但是子历程有不同于父历程的PID。fork函数:被调用一次,却返回两次!

Fork具体处理处罚hello文件过程:

首先,带参执行当前目次下的可执行文件hello,父历程会通过fork函数创建一个新的运行的子历程hello。子历程获取了与父历程的上下文,包括栈、通用寄存器、步伐计数器,情况变量和打开的文件相同的一份副本。子历程与父历程的最大区别是有着跟父历程不一样的PID,子历程可以读取父历程打开的任何文件。当子历程运行结束时,父历程如果仍然存在,则执行对子历程的接纳,否则就由init历程接纳子历程。

6.4 Hello的execve过程


调用函数fork创建新的子历程之后,子历程会调用execve函数,在当前历程的上下文中加载并运行一个新步伐hello。execve 函数从不返回,它将删除该历程的代码和地址空间内的内容并将其初始化,然后通过跳转到步伐的第一条指令或入口点来运行该步伐。将私有的地区映射进来,例如打开的文件,代码、数据段,然后将公共的地区映射进来。后面加载器跳转到步伐的入口点,即设置PC指向_start 地址。_start函数最终调用hello中的 main 函数,这样,便完成了在子历程中的加载。
6.5 Hello的历程执行

6.5.1 历程时间片
一个历程执行它的控制流的一部分的每一时间段叫做时间片。

6.5.2 上下文信息
上下文就是内核重新启动一个被抢占的历程所必要的状态,它由通用寄存器、浮点寄存器、步伐计数器、用户栈、状态寄存器、内核栈和各种内核数据结构等对象的值构成。

6.5.3 用户模式和内核模式
处理处罚器通常使用一个寄存器提供两种模式的区分,该寄存器描述了历程当前享有的特权,当没有设置模式位时,历程就处于用户模式中,用户模式的历程不答应执行特权指令,也不答应直接引用地址空间中内核区内的代码和数据;设置模式位时,历程处于内核模式,该历程可以执行指令会合的任何命令,而且可以访问体系中的任何内存位置。

6.5.4 执行过程
历程调度指在执行过程中,内核可以决定抢占当前历程,并重新开始一个先前被抢占的历程,这个过程称为调度。内存收到中断信号之后,将当前历程加入等待序列,举行上下文切换将当前的历程控制权交给其他历程,当再次收到中断信号时将hello从等待队列加入运行队列。
6.6 hello的非常与信号处理处罚

非常可以分为四类:中断(interrupt),陷阱(trap),故障(fault)和终止(abort)。

中断是异步发生的,是来自处理处罚器外部的I/O装备的信号的结果。在当前指令完成执行后,处理处罚器注意到中断引脚的电压变高,就从体系总线读取非常号,然后调用适当的中断处理处罚步伐。当处理处罚步伐返回后,它就将控制返回给下一条指令。

陷阱是有意的非常,是执行一条指令的结果。应用步伐执行一次体系调用,然后把控制传递给处理处罚步伐,陷阱处理处罚步伐运行后,返回到syscall之后的指令。

故障由错误情况引起,故障发生时处理处罚器将控制转移给故障处理处罚步伐。如果处理处罚步伐可以或许修正这个错误情况,它就将控制返回到引起故障的指令,从而重新执行它。否则,处理处罚步伐返回到内核中的abort例程,abort例程会终止引起故障的应用步伐。

终止是不可恢复的致命错误造成的结果。终止处理处罚步伐从不将控制返回给应用步伐,处理处罚步伐将控制返回给一个abort例程,该例程会终止这个应用步伐。

以下给出步伐运行过程中按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps  jobs  pstree  fg  kill 等命令的运行结果:
步伐运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps  jobs  pstree  fg  kill 等命令,请分别给出各命令及运行结截屏,说明非常与信号的处理处罚。
3、键盘上操作导致的非常:
(1)运行时输入回车:

图6-6-2 运行hello时输入回车
(2)运行时输入Ctrl+C

图6-6-3 运行hello时输入Ctrl+C


图6-6-4 运行hello时输入Ctrl+Z
输入ps,监视后台步伐

图6-6-5 输入ps

输入jobs,显示当前暂停的历程

图6-6-6 输入jobs
输入pstree,以树状图形式显示所有历程

图6-6-7 输入pstree

图6-6-8 输入pstree

图6-6-9 输入pstree
输入fg,使制止的历程收到SIGCONT信号,重新在前台运行。

图6-6-10 输入fg
输入kill,-9体现给历程9974发送9号信号,即SIGKILL,杀死历程。

图6-6-11 输入kill信号,杀死hello历程


6.7本章小结

本章简述了历程、shell的概念与作用,分析了hello步伐使用fork创建子历程的过程以及使用execve加载并运行用户步伐的过程,运用上下文切换、用户模式、内核模式、内核调度等知识,分析了hello历程的执行过程,末了分析了hello对于非常以及信号的处理处罚并举行了现实操作

7hello的存储管理


7.1 hello的存储器地址空间

1.物理地址(physical address)
用于内存芯片级的单元寻址,与处理处罚器和CPU连接的地址总线相对应。
2.逻辑地址(logical address)
逻辑地址指的是机器语言指令中,用来指定一个操作数大概是一条指令的地址。如Hello中sleepsecs这个操作数的地址。
3.线性地址(linear address)或也叫虚拟地址(virtual address)
跟逻辑地址雷同,它也是一个不真实的地址,如果逻辑地址是对应的硬件平台段式管理转换前地址的话,那么线性地址则对应了硬件页式内存的转换前地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理

段式内存管理方式就是直接将逻辑地址转换成物理地址,也就是CPU不支持分页机制。其地址的基本构成方式是段号+段内偏移地址。
在x86保护模式下,段的信息(段基线性地址、长度、权限等)即段描述符占8个字节,段信息无法直接存放在段寄存器中(段寄存器只有2字节)。Intel的计划是段描述符会合存放在GDT或LDT中,而段寄存器存放的是段描述符在GDT或LDT内的索引值(index)。

首先给定一个完整的逻辑地址[段选择符:段内偏移地址],

1.看段选择描述符中的T1字段是0还是1,可以知道当前要转换的是GDT中的段,还是LDT中的段,再根据指定的相应的寄存器,得到其地址和巨细,我们就有了一个数组了。

2.拿出段选择符中的前13位,可以在这个数组中查找到对应的段描述符,这样就有了Base,即基地址就知道了。

3.把基地址Base+Offset,就是要转换的下一个阶段的地址。

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

1.基本原理

将步伐的逻辑地址空间划分为固定巨细的页(page),而物理内存划分为同样巨细的页框(page frame)。步伐加载时,可将恣意一页放入内存中恣意一个页框,这些页框不必一连,从而实现了离散分配。该方法必要CPU的硬件支持,来实现逻辑地址和物理地址之间的映射。在页式存储管理方式中地址结构由两部构成,前一部分是虚拟页号(VPN),后一部分为虚拟页偏移量(VPO):

页式管理方式的优点是:

1)没有外碎片

2)一个步伐不必一连存放。

3)便于改变步伐占用空间的巨细(重要指随着步伐运行,动态生成的数据增多,  所要求的地址空间相应增长)。

缺点是:要求步伐全部装入内存,没有足够的内存,步伐就不能执行。

2.页式管理的数据结构
在页式体系中历程建立时,操作体系为历程中所有的页分配页框。当历程撤销时收回所有分配给它的页框。在步伐的运行期间,如果答应历程动态地申请空间,操作体系还要为历程申请的空间分配物理页框。操作体系为了完成这些功能,必须记载体系内存中现实的页框使用情况。操作体系还要在历程切换时,精确地切换两个不同的历程地址空间到物理内存空间的映射。这就要求操作体系要记载每个历程页表的干系信息。为了完成上述的功能,—个页式体系中,一般要接纳如下的数据结构。
页表:页表将虚拟内存映射到物理页。每次地址翻译硬件将一个虚拟地址转换为物理地址时,都会读取页表。页表是一个页表条目(PTE)的数组。虚拟地址空间的每个页在页表中一个固定偏移量处都有一个PTE。假设每个PTE是由一个有效位和一个n位地址字段构成的。有效位表明了该虚拟页当前是否被缓存在DRAM中。如果设置了有效位,那么地址字段就体现DRAM中相应的物理页的起始位置,这个物理页中缓存了该虚拟页。如果没有设置有效位,那么一个空隙址体现这个虚拟页还未被分配。否则,这个地址就指向该虚拟页在磁盘上的起始位置。

3.页式管理地址变换
MMU利用VPN来选择适当的PTE,将列表条目中PPN和虚拟地址中的VPO串联起来,就得到相应的物理地址。
7.4 TLB与四级页表支持下的VA到PA的变换

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

Cashe的物理访存大抵过程如下:

1.组选择取出虚拟地址的组索引位,将二进制组索引转化为一个无符号整数,找到相应的组

2.行匹配把虚拟地址的标记为拿去和相应的组中所有行的标记位举行比较,当虚拟地址的标记位和高速缓存行的标记位匹配时,而且高速缓存行的有效位是1,则高速缓存掷中。

3.字选择一旦高速缓存掷中,我们就知道我们要找的字节在这个块的某个地方。因此块偏移位提供了第一个字节的偏移。把这个字节的内容取出返回给CPU

4.不掷中如果高速缓存不掷中,那么必要从存储层次结构中的下一层取出被请求的块,然后将新的块存储在组索引位所指示的组中的一个高速缓存行中。一种简单的 放置计谋如下:如果映射到的组内有空闲块,则直接放置,否则组内都是有效块,产生冲突(evict),则接纳最近最少使用计谋 LFU 举行替换。

7.6 hello历程fork时的内存映射

当fork函数被当前历程调用时,内核为新历程创建各种数据结构,并分配给它一个唯一的PID,同时为这个新历程创建虚拟内存。它创建了当前历程的mm_struct、地区结构和页表的原样副本。它将两个历程中的每个页面都标记位只读,并将两个历程中的每个地区结构都标记为私有的写时复制。当fork在新历程中返回时,新历程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个历程中的任一个后来举行写操作时,写时复制机制就会创建新页面。因此,也就为每个历程保持了私有空间地址的抽象概念。
7.7 hello历程execve时的内存映射

execve 函数调用驻留在内核地区的启动加载器代码,在当前历程中加载并运 行包含在可执行目的文件 hello 中的步伐,加载并运行 hello 必要以下几个步骤:

1.删除已存在的用户地区,删除当前历程虚拟地址的用户部分中的已存 在的地区结构。

2.映射私有地区,为新步伐的代码、数据、bss 和栈地区创建新的地区结 构,所有这些新的地区都是私有的、写时复制的。代码和数据地区被映射为 hello 文件中的.text 和.data 区,bss 地区是请求二进制零的,映射到匿名 文件,其巨细包含在 hello 中,栈和堆地址也是请求二进制零的,初始长 度为零。

3.映射共享地区, hello 步伐与共享对象 libc.so 链接,libc.so 是动态链 接到这个步伐中的,然后再映射到用户虚拟地址空间中的共享地区内。

4.设置步伐计数器(PC),execve 做的末了一件变乱就是设置当前历程 上下文的步伐计数器,使之指向代码地区的入口点。

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

页面掷中完全是由硬件完成的,而处理处罚缺页是由硬件和操作体系内核协作完成的。

处理处罚流程:

处理处罚器生成一个虚拟地址,并将它传送给MMU
MMU生成PTE地址,并从高速缓存/主存请求得到它
高速缓存/主存向MMU返回PTE
PTE中的有效位是0,以是MMU出发了一次非常,传递CPU中的控制到操作体系内核中的缺页非常处理处罚步伐。
缺页处理处罚步伐确认出物理内存中的牺牲页,如果这个页已经被修改了,则把它换到磁盘。
缺页处理处罚步伐页面调入新的页面,并更新内存中的PTE
缺页处理处罚步伐返回到原来的历程,再次执行导致缺页的命令。CPU将引起缺页的虚拟地址重新发送给MMU。因为虚拟页面已经换存在物理内存中,以是就会掷中。
7.9动态存储分配管理

带界限标记的隐式空闲链表的每个块是由一个字的头部、有效载荷、可能的额外填充以及一个字的尾部构成的。
在隐式空闲链表中,因为空闲块是通过头部中的巨细字段隐含地连接着的。分配器可以通过遍历堆中所有的块,从而间接地遍历整个空闲块的集合。此中,一个设置了已分配的位而巨细为零的终止头部将作为特殊标记的结束块。
当一个应用请求一个k字节的块时,分配器搜刮空闲链表,查找一个足够大的可以放置所请求块的空闲块。分配器有三种放置计谋:初次适配、下一次适共同最佳适配。分配完后可以分割空闲块镌汰内部碎片。同时分配器在面临释放一个已分配块时,可以归并空闲块,此中便利用隐式空闲链表的界限标记来举行归并。

显示空闲链表:

显式空闲链表是将空闲块构造为某种形式的显式数据结构。因为根据定义,步伐不必要一个空闲块的主体,以是实现这个数据结构的指针可以存放在这些空闲块的主体里面。如,堆可以构造成一个双向链表,在每个空闲块中,都包含一个前驱与一个后继指针。
在显式空闲链表中。可以接纳后进先出的顺序维护链表,将最新释放的块放置在链表的开始处,也可以接纳按照地址顺序来维护链表,此中链表中每个块的地址都小于它的后继地址,在这种情况下,释放一个块必要线性时间的搜刮来定位符合的前驱。
7.10本章小结

本章重要介绍了hello存储器的地址空间;虚拟地址到物理地址的转换;cache的物理内存访问;历程 fork、execve 时的内存映射、缺页故障与缺页中断处理处罚;动态存储分配管理。
(第7章 2分)


8hello的IO管理


8.1 Linux的IO装备管理方法

一个Linux文件就是一个m字节的序列。所有的IO装备都被模型化为文件,所有的输入输出都被当做对相应文件的读和写来执行。这答应Linux内核引出一个简单、低级的应用结构,使得所有的输入输出可以或许以一种统一且一致的方式来执行。
装备的模型化:文件
装备管理:unix io接口
8.2 简述Unix IO接口及其函数

8.2.1 接口
打开文件:通过内核代开文件,内核返回非负整数,成为描述符。描述符体现这个文件。内核记载有关文件的所有信息。
文件位置。每个打开的文件,内核保持一个文件位置k,体现从文件开头起始的字节偏移量。
读写文件。举行复制操作并改变文件位置k的值。
关闭文件。内核释放相应数据结构,将描述符恢复到可用的描述符池中。
8.2.2 函数
    int open(char *filename, int flags, mode_t mode)
    将filename转换为文件描述符,返回描述符数字,总返回历程中没有打开的最小描述符。
    int close(fd)
    关闭一个打开的文件。
    ssize_t read(int fd, void *buf, size_t n);
    从fd复制至多n个字节到buf
    ssize_t write(int fd, const void *buf, size_t n);
    从buf复制至多n个字节到fd
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;
}
此中vsprintf(buf, fmt, arg)函数可以或许返回我们想要打印的字符串的长度并对我们的格式化字符串举行解析。当获取到字符串的长度后,我们便可以或许将字符串打印出来。
从vsprintf生成显示信息,到write体系函数,到陷阱-体系调用 int 0x80或syscall等.
字符显示驱动子步伐:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。

显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

8.4 getchar的实现分析

getchar可以或许读取stdin,然后获取输入的字符。
异步非常-键盘中断的处理处罚:键盘中断处理处罚子步伐。接受按键扫描码转成ascii码,保存到体系的键盘缓冲区。
getchar等调用read体系函数,通过体系调用读取按键ascii码,直到接受到回车键才返回
8.5本章小结

本章简述了Linux的IO装备管理方法以及UNIX IO接口及其函数,简单解释了printf以及getchar函数实现的基本原理。
结论

hello 步伐终于完成了它“艰辛”的一生。hello 的一生大事记如下:
1.颠末预处理处罚器 cpp 的预处理处罚,处理处罚以#开头的行,得到 hello.i。
2.编译器 ccl 将得到的 hello.i 编译成汇编文件 hello.s。
3.汇编器 as 又将 hello.s 翻译成机器语言指令得到可重定位目的文件 hello.o
4. 链接器 ld 将 hello.o 与动态链接库链接生成可执行目的文件 hello,至 此,hello 本身已经脱胎换骨成了一个可以运行的步伐。然而它的运行还要靠操作体系提供资助。
5. 在 shell 中输入./hello 2021112810 肖芩芩 2000,shell 为 hello fork 一个子历程,并在子历程中调用 execve,加载运行 hello。
6. CPU 为 hello 分配内存空间,hello 从磁盘被加载到内存。
7. 当 CPU 访问 hello 时,请求一个虚拟地址,MMU 把虚拟地址转换成物理地址并通过三级 cache 访存。
8. hello 运行过程中可能遇到各种信号,shell 为其提供了各种信号处理处罚步伐。
9. Unix I/O 资助 hello 实现了输出到屏幕和从键盘输入的功能。
10. 末了 hello 执行 return 0;结束了本身的一生。
我的感想:
计算机体系真的是一个庞大而又精细的构造,即使是一个简单的 hello.c也必要操作体系提供如此多的支持,而且每一步都颠末了计划者的深思熟虑,在有限的 硬件程度下把步伐的时间和空间性能都做到了近乎美满,比如存储器的层次结构,比如虚拟内存的多级页表和 TLB……


附件

文件名称                     文件作用

hello.i                       预处理处罚之后文本文件

hello.s                       编译之后的汇编文件

hello.o                      汇编之后的可重定位目的执行

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

helloo_objdmp                 Hello.o 的反汇编代码

helloo.elf                     Hello.o 的 ELF 格式

hello_objdmp                  Hello 的反汇编代码

hello.elf                       Hellode ELF 格式

参考文献


[1]  兰德尔·E·布莱恩特,大卫·R·奥哈拉伦著;深入明白计算机体系[M].北京:机械工业出版社,2016.7.

[2] C library – C++ Reference: [http://www.cplusplus.com/reference].

[3] printf函数实现的深入剖析 :[http://www.cnblogs.com/pianist/p/3315801.html].

[4]  ELF 构造:https://www.cs.stevens.edu/~jschauma/631/elf.html

[5] argc argv:argc argv_百度百科

[6] printf 函数实现的深入剖析:https://www.cnblogs.com/pianist/p/3315801.html


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




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4