计算机系统

大作业

题 目 程序人生-Hello’s P2P
专 业 计算机类
学   号 1180300223
班   级 1803002

计算机科学与技术学院
2019年12月
摘 要
本文主要介绍了一个示例程序从编写到执行到介绍的全过程. 通过详细介绍各个过程中的具体状态和操作, 将计算机系统各个组成部分的工作有机地结合统一起来, 完成搭建知识体系、将知识融会贯通的目标. 本文主要包括示例程序的预处理、编译、汇编、链接、进程管理、存储管理、I/O管理等部分的具体操作和结果, 并最终得出系统性结论.

关键词:程序; 进程; P2P; 020; 计算机系统

(摘要0分, 缺失-1分, 根据内容精彩称都酌情加分0-1分)

目 录

第1章 概述 - 4 -
1.1 HELLO简介 - 4 -
1.2 环境与工具 - 4 -
1.3 中间结果 - 4 -
1.4 本章小结 - 5 -
第2章 预处理 - 6 -
2.1 预处理的概念与作用 - 6 -
2.2在UBUNTU下预处理的命令 - 6 -
2.3 HELLO的预处理结果解析 - 7 -
2.4 本章小结 - 7 -
第3章 编译 - 8 -
3.1 编译的概念与作用 - 8 -
3.2 在UBUNTU下编译的命令 - 8 -
3.3 HELLO的编译结果解析 - 8 -
3.4 本章小结 - 12 -
第4章 汇编 - 13 -
4.1 汇编的概念与作用 - 13 -
4.2 在UBUNTU下汇编的命令 - 13 -
4.3 可重定位目标ELF格式 - 14 -
4.4 HELLO.O的结果解析 - 15 -
4.5 本章小结 - 16 -
第5章 链接 - 17 -
5.1 链接的概念与作用 - 17 -
5.2 在UBUNTU下链接的命令 - 17 -
5.3 可执行目标文件HELLO的格式 - 18 -
5.4 HELLO的虚拟地址空间 - 19 -
5.5 链接的重定位过程分析 - 20 -
5.6 HELLO的执行流程 - 20 -
5.7 HELLO的动态链接分析 - 21 -
5.8 本章小结 - 21 -
第6章 HELLO进程管理 - 22 -
6.1 进程的概念与作用 - 22 -
6.2 简述壳SHELL-BASH的作用与处理流程 - 22 -
6.3 HELLO的FORK进程创建过程 - 22 -
6.4 HELLO的EXECVE过程 - 22 -
6.5 HELLO的进程执行 - 23 -
6.6 HELLO的异常与信号处理 - 23 -
6.7本章小结 - 26 -
第7章 HELLO的存储管理 - 27 -
7.1 HELLO的存储器地址空间 - 27 -
7.2 INTEL逻辑地址到线性地址的变换-段式管理 - 27 -
7.3 HELLO的线性地址到物理地址的变换-页式管理 - 28 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 28 -
7.5 三级CACHE支持下的物理内存访问 - 29 -
7.6 HELLO进程FORK时的内存映射 - 30 -
7.7 HELLO进程EXECVE时的内存映射 - 30 -
7.8 缺页故障与缺页中断处理 - 30 -
7.9动态存储分配管理 - 30 -
7.10本章小结 - 31 -
第8章 HELLO的IO管理 - 32 -
8.1 LINUX的IO设备管理方法 - 32 -
8.2 简述UNIX IO接口及其函数 - 32 -
8.3 PRINTF的实现分析 - 32 -
8.4 GETCHAR的实现分析 - 35 -
8.5本章小结 - 35 -
结论 - 36 -
附件 - 37 -
参考文献 - 38 -

第1章 概述
1.1 Hello简介
根据Hello的自白, 利用计算机系统的术语, 简述Hello的P2P, 020的整个过程.

  1. P2P: 指From Program to Process, 具体如下
    (1)编写源程序(Program), hello.c源程序
    (2)从.c的文本文件经过ccp预处理,生成.i文件
    (3)经过ccl, 将.i文件编译为.s的汇编程序
    (4)经过as将.s文件翻译为机器语言,生成一个二进制文件(可重定位目标程序)
    (5)通过ld与标准C库里的printf.o函数合并, 得到二进制可执行文件hello
    (6)在shell中键入启动命令
    (7)通过fork创建子程序hello
    (8)通过execve加载并执行hello

  2. 022: 指Zero(0) to Zero(0)
    进入程序入口, 加载物理内存, 进入main函数执行目标代码, CPU为运行的hello分配时间片执行逻辑控制流. 当程序运行结束后, shell父进程回收hello进程,内核删除相关数据结构,实现020
    1.2 环境与工具
    列出你为编写本论文, 折腾Hello的整个过程中, 使用的软硬件环境, 以及开发与调试工具.
    硬件环境: X64 CPU; 2.4GHz; 16GB RAM
    软件环境: Windows 10 64位; VMware Workstation 15 Player, Ubuntu 16.04 LTS 64 位
    开发工具: gcc + gedit, Codeblocks, gdb, edb, objdump
    1.3 中间结果
    列出你为编写本论文, 生成的中间结果文件的名字, 文件的作用等.
    hello.c: 源程序文件
    hello.i: 预处理后的文件
    hello.s: 编译后的汇编文件
    hello.o: 汇编后的可重定位文件
    hello: 链接后的可执行文件
    1.4 本章小结
    本章简单介绍了hello, 分析了P2P和020的过程,列出了本次大作业的实验环境和开发工具, 阐述了过程中产生的中间程序文件及其来源与作用
    (第1章0.5分)

第2章 预处理
2.1 预处理的概念与作用
预处理指的是在编译之前进行的处理. C语言的预处理主要有三个方面的内容:宏定义、文件包含、条件编译. 预处理命令以符号“#”开头.
预处理工作也叫做宏展开, 将宏名替换为文本. 宏定义只需将符号常量替换成后面对应的文本即可. 文件包含可以在一个文件中包含另一个文件的内容, 被包含的文件称为头文件. 头文件的内容可以有函数原型、宏定义、结构体定义. 条件编译是在条件满足时才编译某些语句.
使用条件编译可以使目标程序变小, 运行时间变短. 同时有利于代码的模块化

2.2在Ubuntu下预处理的命令
命令: gcc -E hello.c -o hello.i
图 2-1 预处理

2.3 Hello的预处理结果解析
修改得到的C程序hello.i. 源文件中的宏进行了宏展开, 头文件中的内容内包含进hello.i文件中. 打开该文件之后可以发现, 文件hello.i的长度变为3042行, 而hello.c文件长度为23行. 虽然文件内容增加,但仍是可以阅读的C语言程序文本文件

图 2-2 hello.i文件部分代码

2.4 本章小结
本阶段完成了对hello.c的预处理阶段, 根据预处理命令得到了修改后的hello.i文件, 并对hello.i进行了与处理结果的分析, 实现了从.c源程序到可执行文件的第一步.

(第2章0.5分)

第3章 编译
3.1 编译的概念与作用
编译程序也称为编译器, 是指把用高级程序设计语言书写的源程序, 翻译成等价的汇编语言格式目标程序的翻译程序. 编译程序属于采用生成性实现途径实现的翻译程序. 它以高级程序设计语言书写的源程序作为输入, 而以汇编语言表示的目标程序作为输出.
编译程序的基本功能是把源程序(高级语言)翻译成目标程序. 除了基本功能之外, 编译程序还具备语法检查、调试措施、修改手段、覆盖处理、目标程序优化、不同语言合用以及人机联系等重要功能.
注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序
3.2 在Ubuntu下编译的命令
命令: gcc -S hello.i -o hello.s

图3-1 hello.i编译生成hello.s

3.3 Hello的编译结果解析
hello.s文件的开头部分:

图3-2 hello.s文件开头部分

.file: 源文件名
.align: 对齐方式
.string: 字符串
.text: 代码段

此部分是重点, 说明编译器是怎么处理C语言的各个数据类型以及各类操作的. 应分3.3.1~ 3.3.x等按照类型和操作进行分析, 只要hello.s中出现的属于大作业PPT中P4给出的参考C数据与操作, 都应解析3.3.1 数据

3.3.1 数据

  1. 参数部分有int argc, char *argv[]两个参数.在汇编代码中, 分别将其存放在栈中rbp寄存器指向地址-20和-32处. argc由%edi代表, argv[]由%rsi代表

    图3-3 对传入参数的处理

  2. int i: 局部变量, 通常保存在寄存器或栈中. i的数据类型占用4字节的栈空间

  3. 常量: 在文件中有一些如4、8的常量以立即数形式出现

3.3.2 赋值
对局部变量i的赋值: 编译器将C语言中的i = 0编译为

图3-4 对i的赋值

3.3.3 类型转换(隐式或显式)
对函数atoi(alphanumeric to integer)来说, 它的功能时将字符串转换成整型数的一个函数, 编译器将C语言中atoi(argv[3])操作编译为

图3-5 atoi函数类型转换

3.3.4 算术操作
编译器将C语言中的i++操作编译为

图3-6 i++的算术操作

3.3.5 关系操作

  1. 编译器将C语言中的i < 8的关系编译为

    图3-7 i < 8

  2. 编译器将C语言中的argc != 4操作编译为

    图3-8 argc != 4

3.3.6 数组 / 指针 / 结构操作
指针数组: char argv[], 在该数组中,arvg[0]指向输入程序的路径和名称,argv[1]和argv[2]分别表示两个字符串, char数据类型占据8字节, 由下图知得到argv[1]和argv[2]两个字符串

图3-9 指针数组argv

3.3.7 控制转移

  1. 在.c源代码中有if(argc != 4), 即当argc不等于4的时候进行跳转, 编译器将C语言中的if(argc != 4)比较和跳转编译为

    图3-10 argc的控制转移

cmpl语句比较-20(%rbp)与-4, 设置条件码, 如果最近的操作得出结果为0, 则跳转至.L2中, 否则顺序执行下一条指令
2. 在.c源代码中有for(i=0;i<8;i++), 即当i > 7时跳转,编译器将C语言中的for(i=0;i<8;i++)编译为

图3-11 for循环的控制转移

for循环中cmpl语句比较-4(%rbp)(也就是之前讨论过的存放i的位置)和7, 当i 大于7时跳出循环, 否则跳转至.L4循环体内部执行命令

3.3.8 函数操作

  1. main函数
    参数传递: 传入参数argc与*argv[], 存储在寄存器%edi和%rsi中
    函数调用: 被系统启动函数调用
    函数返回: 设置%eax为0并返回, 将C语言中的return 0编译为

    图3-12 main函数反回

  2. printf函数
    参数传递:
    (1)call puts时传入字符串参数首地址

    图3-13 call puts传入参数

(2)for循环中call printf时传入argv[1]和argv[2]的地址

图3-14 call printf传入参数

  1. exit函数
    参数传递: 传入的参数为常量1, 执行退出命令
    函数调用: if条件判断满足后被调用

    图3-15 调用exit函数

  2. atoi函数
    函数传递: 传入的参数为argv[3]的地址
    函数调用: 满足for循环后, 在调用sleep函数时, 将argv[3]的字符串转换成整型数

    图3-16 调用atoi函数

  3. sleep函数
    参数传递: 传入被转换后的argv[3], 即atoi(argv[3])
    函数调用: 满足佛如循环后被调用

    图3-17 调用sleep函数

  4. getchar函数
    函数调用: 在main函数中被调用

    图3-18 调用getchar函数

3.4 本章小结
本章通过编译, 以程序hello为例, 讲述了编译器处理C语言各个数据类型的操作过程, 以及数据的算术操作及关系操作, 并对C语言中的判断语句进行解析, 分析了控制转移的代码, 还对函数的参数传递、函数调用、函数返回进行了分析. 通过对编译后的文件进行分析, 能够加深我们对C语言数据和操作的理解. 完成该阶段转换之后, 可以进行下一阶段的汇编处理
(第3章2分)

第4章 汇编
4.1 汇编的概念与作用

汇编器(as)将hello.s编译成机器语言指令, 把这些指令打包成一种叫可重定位目标的格式, 并将结果保存在一个二进制的目标文件hello.o中的过程. hello.o是一个二进制文件, 包含程序的指令编码
注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程.
4.2 在Ubuntu下汇编的命令
汇编命令: gcc -c hello.s -o hello.o

图4-1 hello.s汇编生成hello.o文件

4.3 可重定位目标elf格式
分析hello.o的ELF格式, 用readelf等列出其各节的基本信息, 特别是重定位项目分析.

  1. 使用指令readelf -h hello.o来阅读头文件信息. ELF头以一个16字节序列开始, 描述生成该文件系统字的大小和字节顺序, 并且包含帮助链接器的语法分析和解释目标文件的信息, 包括ELF文件的大小, 节头部的起始位置(此处为1160)等

    图4-2 ELF头

  2. 使用指令readelf -S hello.o来阅读节头表信息. 节头表秒速了不同界的位置和大小, 其中目标文件每个节都有一个固定大小的条目. 描述了节的名称, 类型, 地址和偏移量等数据

    图4-3 节头表

  3. 使用指令readelf -r hello.o来阅读重定位信息的详细情况. 重定位.rel .text节, 下图中有偏移量, 重定位类型, 符号值等. 当链接器将当前慕白文件与其他文件组合的时候, 需要修改这些位置. 如下图中, 修改的有puts, exit, printf, atoi, sleep, getchar等之前介绍过的函数.
    有两种最基本的重定位类型:
    R_X86_64_PC32: 重定位一个使用32位PC相对地址的引用
    R_X86_64_32: 重定位一个使用32位PC绝对地址的引用

    图4-4 重定位节

  4. 使用指令readelf -a hello.o之后, 发现了没有进行分析的部分, 分析知, .symtab是一个符号表, 存放着程序中的定义和引用的函数和全局变量的信息. 但hello.o文件中并无全局变量, 所以最后一行字说明了这个情况

    图4-5 符号表

4.4 Hello.o的结果解析
objdump -d -r hello.o 分析hello.o的反汇编, 并请与第3章的 hello.s进行对照分析.

  1. 操作数: 在hello.s中的操作数是二进制, 而在hello.o的反汇编代码中的操作舒适十六进制.
  2. 分支转移: 跳转语句后, 在hello.s中是.L2和.L3等段名称, 而在反汇编代码中跳转指令之后是相对偏移的地址
  3. 函数调用: hello.s中, call指令之后紧跟着的就是函数名称, 而反汇编代码中call指令之后的是函数的相对偏移地址, 即call的目标地址是当前下一条指令. 原因是hello.c中调用的函数是共享库中的函数, 最终需要通过动态链接器才能确定函数的运行执行的地址, 因此在.rela.text节中添加了重定位条目
  4. 全局变量访问: 在hello.s文件中, 对于.rodata等全局变量的访问, 使用段名称+%rip, 在反汇编中0+%rip, 原因是.rodata中的数据地址在运行时才能确定, 所以访问也需要重定位. 所以在汇编成机器语言时, 需要将操作数全部置0, 并添加重定位条目
    4.5 本章小结
    本章通过汇编器(as)完成了对hello.s的汇编工作, 再通过readelf查看可重定位目标程序文件, 分析了ELF文件的结构, 获取了相关数据运行时的地址和不同节\条目的大小和偏移量等信息. 通过对比hello.o文件与hello.s文件, 深入理解了二者的差别. 该阶段结束后, 就可进入下一阶段的链接.
    (以下格式自行编排, 编辑时删除)
    (第4章1分)

第5章 链接
5.1 链接的概念与作用
概念: 链接是将各种代码和数据片段收集并组合成一个单一文件的过程, 这个文件可悲加载(复制)到内存并执行
作用: 连接器可以将程序调用的外部函数(.o文件)与当前.o文件合并, 并得到可执行目标文件. 编译可执行于编译时, 也就是在源代码被翻译成机器代码时; 也可以执行于加载时, 也就是在程序被加载到内存并执行时; 早期计算机系统中的链接是手动执行的, 在现代系统中, 链接由链接器自动执行. 连接器是的分离编译成为可能. 开发过程中无需将大型的应用程序组织成一个巨大的源文件, 而是可以把他分解为更小且更好管理的模块, 可以独立地修改和编译这些模块.
注意:这儿的链接是指从 hello.o 到hello生成过程.

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-1 使用ld链接生成可执行目标程序hello

5.3 可执行目标文件hello的格式
ELF头中给出一个16字节序列, 描述了生成该文件系统字的大小和字节顺序. 其余的为帮助链接器进行语法分析等
使用指令readelf -a hello来阅读hello的详细内容

图5-2 ELF头

节头表给出了各节的名称、大小、偏移量、地址, 如下图

图5-3 节头表

5.4 hello的虚拟地址空间
使用edb加载hello, 查看本进程的虚拟地址空间各段信息
如下图

图5-4 使用edb查看hello虚拟地址空间段信息


图5-5 程序头

PHDR: 保存程序头表
INTERP: 程序映射到内存后, 调用的解释器
LOAD: 程序目标代码和常量信息
DYMNAMIC: 保存动态链接器使用信息
NOTE: 存储辅助信息
GNU_STACK: 标志是否可执行的权限标志
GUN_RELRO: 指定重定位后的只读区域
5.5 链接的重定位过程分析
使用指令objdump -d -r hello进行反汇编
hello与hello.o的不同:

  1. 链接增加的新函数: 链接中加入了exit、printf、slepp、atoi、getchar等hello. c中用到的函数

  2. 增加的节: hello中增加了.init和.plt节

  3. 函数调用: hello\中没有hello.o中的重定位条目, 并且跳转到函数调用的地址在hello中均变为虚拟内存地址
    5.6 hello的执行流程

  4. hello在执行前, 系统为创建子进程, 从内存加载数据, 然后跳转到_start()函数

    图5-6 _start函数

  5. 子程序名或地址:
    0x401000 init
    0x401030 puts
    0x401040 printf
    0x401050 gechar
    0x401060 atoi
    0x401070 exit
    0x401080 sleep
    0x401090 _start
    0x4010c1 main
    0x401150 __libc_csu_init
    0x4011b0 __libc_csu_fini
    0x4011b4 fini

5.7 Hello的动态链接分析
hello程序的动态链接项目: global_offset表

图5-7 do_init前

图5-8 do_init后

根据上面连图分析, 在do_init前后global_offset表产生了变化, dl_init函数能给程序赋值, 所赋值为当前执行的内存地址偏移量

5.8 本章小结
本章分析了ld链接器将hello.o的链接命令, 查看了可执行文件ELF, 阅读并叙述了可执行文件的相关信息, 了解了链接过程中的具体细节. 完成该阶段后, 得到了一个可执行的二进制文件
(第5章1分)

第6章 hello进程管理
6.1 进程的概念与作用
概念: 一个具有一定独立功能的程序关于某个数据集合的一次运行活动
作用: 进程像每个程序提供了假象, 好像它在单独地使用处理器, 独占地使用内存系统

6.2 简述壳Shell-bash的作用与处理流程
作用: 是用户使用Linux的桥梁. Shell是一个应用程序, 为用户对操作系统内核的访问提供了一个交互界面
处理流程:

  1. 读入输入的命令

  2. 分割字符串, 获取所有的参数

  3. 若为内置命令则执行, 否则调用相应的程序为其分配子进程执行

  4. Shell接收键盘输入信号, 并对这些中断信号进行处理
    6.3 Hello的fork进程创建过程
    一个进程, 包括代码、数据和分配给进程的资源. fork函数通过系统调用创建一个与原来进程几乎完全相同的进程, 也就是两个进程可以做完全相同的事, 但如果初始参数或者传入的变量不同, 两个进程也可以做不同的事. 一个进程调用fork函数后, 系统先给新的进程分配资源, 例如存储数据和代码的空间. 然后把原来的进程的所有值都复制到新的新进程中, 只有少数值与原来的进程的值不同, 相当于克隆了一个自己. 在fork函数执行完毕后, 如果创建新进程成功, 则出现两个进程, 一个是子进程, 一个是父进程. 在子进程中, fork函数返回0, 在父进程中, fork返回新创建子进程的进程ID. 我们可以通过fork返回的值来判断当前进程是子进程还是父进程
    6.4 Hello的execve过程
    子进程创建完成之后,子进程调用execve函数在当前进程的上下文加载运行hello程序. 并创建一组新的代码、数据、堆和栈段. 新的栈和堆段被初始化为零, 通过将虚拟地址空间中的页映射到可执行文件的页大小的片, 新的代码和数据段被初始化为可执行文件中的内容, 这样hello就完成了execve的过程.
    6.5 Hello的进程执行
    新进程的创建, 首先在内存中为新进程创建一个task_struct结构, 然后将父进程的task_struct内容复制其中, 再修改部分数据. 分配新的内核堆栈、新的PID、再将task_struct 这个node添加到链表中. 然后将可执行文件装入内核的linux_binprm结构体. 进程调用execve时, 该进程执行的程序完全被替换, 新的程序从main函数开始执行. 调用execve并不创建新进程, 只是替换了当前进程的代码区、数据区、堆和栈. 在进程调用了exit之后, 该进程并非马上就消失掉, 而是留下了一个成为僵尸进程的数据结构, 记载该进程的退出状态等信息供其他进程收集, 除此之外, 僵尸进程不再占有任何内存空间.
    为了控制进程的执行, 内核必须有能力挂起正在CPU上执行的进程, 并恢复以前挂起的某个进程的执行, 这叫做进程切换. 进程上下文切换由以下4个步骤组成:
    (1)决定是否作上下文切换以及是否允许作上下文切换. 包括对进程调度原因的检查分析, 以及当前执行进程的资格和CPU执行方式的检查等. 在操作系统中, 上下文切换程序并不是每时每刻都在检查和分析是否可作上下文切换, 它们设置有适当的时机.
    (2)保存当前执行进程的上下文. 这里所说的当前执行进程, 实际上是指调用上下文切换程序之前的执行进程. 如果上下文切换不是被那个当前执行进程所调用, 且不属于该进程, 则所保存的上下文应是先前执行进程的上下文, 或称为“老”进程上下文. 显然, 上下文切换程序不能破坏“老”进程的上下文结构.
    (3)使用进程调度算法, 选择一处于就绪状态的进程.
    (4)恢复或装配所选进程的上下文, 将CPU控制权交到所选进程手中.
    6.6 hello的异常与信号处理

  5. 异常类型
    类别 原因 异步 / 同步 返回行为
    中断 来自I/O设备信号 异步 总是返回到下一条指令
    陷进 有意的异常 同步 总是返回到下一条指令
    故障 潜在可恢复错误 同步 可能返回到当前指令
    终止 不可恢复错误 同步 不返回

  6. 运行时各命令情况
    (1)运行时无输入. 程序执行完后, 进程被回收, 此时按回车结束

    图6-1 正常运行hello

(2)运行时输入随机无意义命令

图6-2运行时输入随机无意义命令

(3)运行过程中输入CTRL+c, 父进程收到SIGINT信号, 终止hello并回收

图6-3 运行时输入CTRL+c

(4) 运行过程中输入CTRL+z后执行ps命令, 父进程收到SIGTSTP信号, 将hello挂起

图6-4 运行过程中输入CTRL+z后执行ps指令

(5) 运行过程中输入CTRL+z后执行jobs命令

图6-5 运行过程中输入CTRL+z后执行jobs命令

(6) 运行过程中输入CTRL+z后执行pstree命令

图6-6 运行过程中输入CTRL+z后执行pstree命令部分截图

(7)使用fg命令将进程调到前台

图6-7 fg命令

(8)kill发送信号给一个或多个进程, 使用命令kill -9 67681杀死hello进程

图6-8 kill命令

6.7本章小结
本章了解了hello进程的执行过程, 了解了shell共夺得处理流程,和与系统相关的函数及其功能, 分析了计算机硬软件及操作系统之间的配合协作的方式
(第6章1分)

第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址: 包含在机器语言中用来指定一个操作数或一条指令的地址. 每一个逻辑地址都由一个段(segment)和偏移量(offset)组成, 偏移量指明了从段开始的地方到实际地址之间的距离.
线性地址: 是逻辑地址到物理地址变换之间的中间层. 在分段部件中逻辑地址是段中的偏移地址,然后加上基地址就是线性地址
虚拟地址: CPU在寻址时, 按照虚拟地址来寻址, 然后通过MMU将虚拟地址转译为物理地址
物理地址: 放在寻址总线上的地址. 放在寻址总线上,如果是读,电路根据这个地址每位的值就将相应地址的物理内存中的数据放到数据总线中传输. 如果是写,电路根据这个地址每位的值就在相应地址的物理内存中放入数据总线上的内容. 物理内存是以字节(8位)为单位编址的

7.2 Intel逻辑地址到线性地址的变换-段式管理
一个逻辑地址由两部份组成,段标识符、段内偏移量. 段标识符是由一个16位长的字段组成,称为段选择符. 其中前13位是一个索引号. 后面3位包含一些硬件细节

图7-1 选择段符
先将逻辑地址分成段选择符+段描述符的判别符(TI)+地址偏移量的形式,然后先判断TI字段,看看这个段描述符究竟是局部段描述符(ldt)还是全局段描述符(gdt),然后再将其组合成段描述符+地址偏移量的形式,这样就转换成线性地址了, 下图为具体操作

图7-2 具体操作

7.3 Hello的线性地址到物理地址的变换-页式管理
在这个转换中要用到翻译后备缓冲器(TLB),首先我们先将线性地址分为VPN(虚拟页号)+VPO(虚拟页偏移)的形式,然后再将VPN拆分成TLBT(TLB标记)+TLBI(TLB索引)然后去TLB缓存里找所对应的PPN(物理页号)如果发生缺页情况则直接查找对应的PPN,找到PPN之后,将其与VPO组合变为PPN+VPO就是生成的物理地址了
7.4 TLB与四级页表支持下的VA到PA的变换
每次cpu产生一个虚拟地址,MMU需要查询一个PTE,如果运气不好,需要从内存中取得,这需要花费很多时间,通过TLB(翻译后备缓冲器)能够消除这些开销. TLB是一个小的,虚拟寻址的缓存,在MMU里,其每一行都保存着一个单个PTE组成的块,TLB通常具有高度相联度

图7-3 虚拟地址组成

Core i7 MMU 使用四级的页表将虚拟地址翻译成物理地址. 36位VPN 被划分成四个9 位VPN,分别用于一个页表的偏移量, 结构如下图

图7-4 Core i7页表翻译

7.5 三级Cache支持下的物理内存访问
首先CPU发出一个虚拟地址,在TLB里面寻找. 如果命中,那么将PTE发送给L1Cache,否则先在页表中更新PTE. 然后再进行L1根据PTE寻找物理地址,检测是否命中的工作. 这样就能完成Cache和TLB的配合工作, 具体流程如下图

图7-5 三级Cache支持下的物理内存访问

7.6 hello进程fork时的内存映射
虚拟内存和内存映射说明了fork函数如何为每个新进程提供私有虚拟地址空间. Fork函数可为新进程创建虚拟内存. 创建当前进程的mm_struct,vm_area_struct和页表的原始副本. 两个进程中的每个页面都标记为只读,并且两个进程中的每个区域结构(vm_area_struct)都标记为私有写时复制(COW). 当返回新进程时,新进程与调用分支进程具有相同的虚拟内存,并且随后的写操作通过写时复制机制创建新页
7.7 hello进程execve时的内存映射
execve函数在当前进程中加载并运行新程序hello.out:删除现有用户区,创建新的区结构,将代码和初始化数据映射到.text和.data区(由目标文件提供) ,然后堆栈被映射到一个匿名文件,将PC设置为指向代码区域的入口点. Linux根据需要交换代码和数据页
7.8 缺页故障与缺页中断处理
DRAM 缓存不命中称为缺页,即虚拟内存中的字不在物理内存中. 缺页导致页面出错,产生缺页异常. 缺页异常处理程序选择一个牺牲页,然后将目标页加载到物理内存中. 最后让导致缺页的指令重新启动,页面命中
7.9动态存储分配管理
程序运行时,程序员使用动态内存分配器(例如malloc)获取虚拟内存. 动态内存分配器维护进程的虚拟内存区域,称为堆. 分配器将堆维护为不同大小的块的集合,并且每个块都被分配或空闲. 分配器的类型包括显式和隐式分配器. 前者要求应用程序显式释放所有分配的块,而后者则在检测到程序不再使用分配的块时释放该块.
动态内存管理的策略包括最合适,最合适和最合适. 第一个适配将从头开始搜索空闲列表,然后选择第一个合适的空闲块. 搜索时间与块总数(包括已分配和空闲块)线性相关. 小空闲块的“碎片”保留在链接列表的开头附近. 下一个适应与第一个适应类似,不同之处在于它从链接列表中上一个查询的末尾开始. 比第一次修改更快,避免了重复扫描那些无用的块. 最佳拟合将查询链接列表,选择最佳空闲块,满足拟合要求,并保留最少的可用空间. 它可以确保最小的碎片并提高内存利用率Printf会调用malloc, 请简述动态内存管理的基本方法与策略.
7.10本章小结
本章中主要介绍了程序的存储结构, 了解了从逻辑地址到线性地址的变换以及从线性地址到物理地址的变换的知识. 程序访问过程中的Cache结构和页表结构, 进程如何加载自己的虚拟内存空间. 了解了C语言中的malloc的具体执行规则.
(第7章 2分)

第8章 hello的IO管理
8.1 Linux的IO设备管理方法
(以下格式自行编排, 编辑时删除)
设备的模型化:文件
文件类型:

  1. 普通文件: 包含任意数据的文件
  2. 目录: 包含一组连接的文件, 每个链接都将一个文件名映射到一个文件
  3. 套接子: 用来与另一个进程进行跨网络通信的文件
  4. 命名通道
  5. 符号链接
  6. 字符和块设备
    设备管理:unix io接口
  7. 打开及关闭文件
  8. 读取及写入文件
  9. 改变当前文件的位置
    8.2 简述Unix IO接口及其函数
    Linux提供的IO接口及其函数
  10. read和write: 最简单的读写函数
  11. readn和writen: 原子性读写操作
  12. recvfrom和sendto: 增加了目标地址和地址结构长度的参数
  13. recv和send: 允许从进程到内核传递标志
  14. readv和writev: 允许指定往其中输入数据或从其中输出数据的参数
  15. recvmsg和sendmsg: 结合了其他IO函数的所有特性, 并具备接受和发送辅助数据的能力

8.3 printf的实现分析
函数代码如下图

图8-1 printf函数代码

printf函数主要调用了vsprintf和write函数
首先介绍vsprintf(buf, fmt, arg)函数

图8-2 vsprintf函数

vsprintf的作用就是格式化. 它接受确定输出格式的格式字符串fmt. 用格式字符串对个数变化的参数进行格式化,产生格式化输出.
之后追踪write

图8-3 write函数

一个int INT_VECTOR_SYS_CALL表示要通过系统来调用sys_call这个函数
sys_call的实现

图8-4 sys_call
syscall 将字符串中的字节“Hello ”从寄存器中通过总线复
制到显卡的显存中,显存中存储的是字符的 ASCII 码. 字符显示驱动子程序将通过 ASCII 码在字模库中找到点阵信息将点阵信息存 储到 vram 中. 显示芯片会按照一定的刷新频率逐行读取 vram,并通过信号线向液晶显示器 传输每一个点(RGB 分量). 于是我们的打印字符串“Hello i”就显示在了屏幕上
https://www.cnblogs.com/pianist/p/3315801.html
从vsprintf生成显示信息, 到write系统函数, 到陷阱-系统调用 int 0x80或syscall.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息).
显示芯片按照刷新频率逐行读取vram, 并通过信号线向液晶显示器传输每一个点(RGB分量).
8.4 getchar的实现分析


图8-5 getchar函数

getchar由宏实现:#define getchar() getc(stdin). getchar有一个int型的返回值. 当程序调用getchar时.程序就等着用户按键. 用户输入的字符被存放在键盘缓冲区中. 直到用户按回车为止(回车字符也放在缓冲区中). 当用户键入回车之后,getchar才开始从stdio流中每次读入一个字符. getchar函数的返回值是用户输入的字符的ASCII码,若文件结尾(End-Of-File)则返回-1(EOF),且将用户输入的字符回显到屏幕. 如用户在按回车之前输入了不止一个字符,其他字符会保留在键盘缓存区中,等待后续getchar调用读取. 也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完后,才等待用户按键.
异步异常-键盘中断的处理:键盘中断处理子程序. 接受按键扫描码转成ascii码,保存到系统的键盘缓冲区.
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回
异步异常-键盘中断的处理:键盘中断处理子程序. 接受按键扫描码转成ascii码, 保存到系统的键盘缓冲区.
getchar等调用read系统函数, 通过系统调用读取按键ascii码, 直到接受到回车键才返回.
8.5本章小结
本章介绍了Linux下IO设备的管理方法, Unix IO接口及其函数, 分析了printf函数和getchar两个函数.
(第8章1分)
结论
用计算机系统的语言, 逐条总结hello所经历的过程.
你对计算机系统的设计与实现的深切感悟, 你的创新理念, 如新的设计与实现方法.
hello所经历的过程:

  1. 编写程序: 写出hello程序的代码, 保存为hello.c文件
  2. 预处理: 对带#的指令解析, 生成hello.i文件
  3. 编译: 将C语言程序编译成汇编语言程序, 生成hello.i文件
  4. 汇编: 把汇编语言转换成机器代码, 生成重定位信息, 生成hello.o文件
  5. 链接: 与动态库链接, 生成可执行文件hello
  6. 创建进程: 在shell中利用./hello运行hello程序, 父进程通过fork函数为hello创建进程
  7. 加载程序: 通过加载器, 调用execve函数, 删除原来的进程内容, 加载我们现在进程的代码, 数据等到进程自己的虚拟内存空间
  8. 执行指令:CPU取指令, 顺序执行进程的逻辑控制流. 这里CPU会给出一个虚拟地址, 通过MMU从页表里得到物理地址, 在通过这个物理地址去cache或者内存里得到我们想要的信息
  9. 异常(信号):程序执行过程中, 如果从键盘输入Ctrl-C等命令, 会给进程发送一个信号, 然后通过信号处理函数对信号进行处理.
  10. 结束:程序执行结束后, 父进程回收子进程, 内核删除为这个进程创建的所有数据结构
    (结论0分, 缺失 -1分, 根据内容酌情加分)

附件
列出所有的中间产物的文件名, 并予以说明起作用.
hello.c: 源程序文件
hello.i: 预处理后的文件
hello.s: 编译后的汇编文件
hello.o: 汇编后的可重定位文件
hello: 链接后的可执行文件
(附件0分, 缺失 -1分)

参考文献
为完成本次大作业你翻阅的书籍与网站等
[1] 林来兴. 空间控制技术[M]. 北京:中国宇航出版社, 1992:25-42.
[2] 辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社, 1999.
[3] 赵耀东. 新时代的工业工程师[M/OL]. 台北:天下文化出版社, 1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).
[4] 谌颖. 空间交会控制理论与方法研究[D]. 哈尔滨:哈尔滨工业大学, 1992:8-13.
[5] KANAMORI H. Shaking Without Quaking[J]. Science, 1998, 279(5359):2063-2064.
[6] CHRISTINE M. Plant Physiology: Plant Biology in the Genome Era[J/OL]. Science, 1998, 281:331-332[1998-09-23]. http://www.sciencemag.org/cgi/ collection/anatmorp.
(参考文献0分, 缺失 -1分)

HIT 计算机系统 大作业 程序人生-Hello’s P2P相关推荐

  1. HIT计算机系统大作业-程序人生-Hello’s P2P

    计算机系统大作业 ** 由于采用静态部署,需要看图片详细分析的小伙伴请移步个人博客网站:** 个人博客 题目:程序人生-Hello's P2P 学号: 姓名:熊峰 摘要: hello程序作为最简单的. ...

  2. HIT 深入理解计算机系统 大作业 程序人生-Hello’s P2P

    HIT 深入理解计算机系统 大作业 程序人生-Hello's P2P 本论文旨在研究 hello 在 linux 系统下的整个生命周期.结合 CSAPP 课本, 通过 gcc 等工具进行实验,从而将课 ...

  3. 【2022】哈工大计算机系统大作业——程序人生Hello’s P2P

    2022哈工大计算机系统大作业--程序人生Hello's P2P 摘要 第1章 概述 1.1 Hello简介 1.2 环境与工具 1.3 中间结果 1.4 本章小结 第2章 预处理 2.1 预处理的概 ...

  4. 2022计算机系统大作业——程序人生-Hello’s P2P

    计算机系统 大作业 题     目 程序人生-Hello's P2P 专       业 计算机 学    号 120L021716 班    级 2003005 学       生 蔡泽栋 指 导 ...

  5. 哈工大计算机系统大作业——程序人生-Hello’s P2P

    计算机系统 大作业 题          目程序人生-Hello's P2P 专          业 计算机科学与技术 学       号120L022401 班          级 200300 ...

  6. 2021哈工大计算机系统大作业——程序人生-Hello’s P2P

    计算机系统 大作业 题     目 程序人生-Hello's P2P 计算机科学与技术学院 2021年6月 摘  要 本文介绍了hello的整个生命过程.利用gcc,gdb,edb,readelf,H ...

  7. 哈工大 计算机系统大作业 程序人生-Hello’s P2P From Program to Process

    计算机系统 大作业 题     目 程序人生-Hello's P2P 专       业 计算学部 学    号 120L020512 班    级 2003004 学       生 黄鹏程 指 导 ...

  8. 2022春 计算机系统大作业 程序人生-Hello’s P2P

    计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 计算学部 学 号 班 级 学 生 指 导 教 师 计算机科学与技术学院 2022年5月 摘 要 为深入理解计算机系统,本文以hel ...

  9. 哈工大2021春计算机系统大作业 程序人生-Hello’s P2P

          计算机系统 大作业 题     目 程序人生-Hello's P2P 专       业 计算机类 学     号 1190200613 班     级 1903004 学       生 ...

最新文章

  1. html input不可编辑
  2. BGP Soft Reset Enhancement
  3. 内核调试相关变量说明
  4. sqlite的控制台中文显示问题
  5. git推送密码_保护代码完整性(六):在 Git 上使用 PGP
  6. 启动kafka失败报内存不足(Cannot allocate memory)
  7. pytorch 归一化_用PyTorch进行语义分割
  8. 2020计算机考试系统office,2020年3月计算机二级考试,大学生office考试教材,仿真考试系统...
  9. Delphi XE10.4字体字号对应的Font Size的点或像素换算
  10. 机械键盘各种设定(品牌:黑爵等)
  11. 微信视频号认证有什么要求?
  12. 【高等数学】区间再现公式及其相关推论
  13. defaults(default是什么职位)
  14. Log4cpp: log4cpp快速使用指南
  15. java第一周_从计算机基础到流程控制语句(if_else)
  16. Python+Pycharm和 VisualStudio C++社区版使用PK及易混淆的语法问题
  17. 老司机 iOS 周报 #41 | 2018-10-29
  18. TiDB 如何在 LVS FULL NAT 模式下显示客户端真实 IP
  19. 【ibokan】SEO个人笔记+百度百科详细介绍
  20. 构建安全可靠的微服务 | Nacos 在 SaaS 平台的应用实践

热门文章

  1. 写个单片机软件工程师的简历
  2. pycharm远程调试 No such file or directory
  3. 数据分析の杜邦分析法
  4. FSMC版本:多驱动器(ILI9486L等)驱动TFTLCD屏幕
  5. 电脑上使用计算机命令行,我的电脑运行命令_我的电脑什么运行命令
  6. 3G、WiFi、WLAN、蓝牙、zigbee区别
  7. 收录批量查询 网页收录批量查询数据并导出
  8. Mac平台retina高分屏开发技术分享
  9. 解决GitHub中报错Something went really wrong and we can’t process that...
  10. 不要抱怨网速慢,只怪自己不会调快网速(一分钟解决网速问题)