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

正在上传…重新上传取消

 

计算机系统

大作业

题     目  程序人生-Hello’s P2P 

专       业            计算机           

学     号          120L022407          

班     级           2003008            

学       生             孙浩          

指 导 教 师              吴锐           

计算机科学与技术学院

2021年5月

摘  要

本文主要讲述了hello.c程序在编写完成后运行在linux中的生命历程,记住相关工具分析预处理、编译、汇编、链接等各个过程在linux下实现的原理,分析了这些过程中产生的文件的相应信息和作用。并介绍了shell的内存管理、IO管理、进程管理等相关知识,了解了虚拟内存、异常信号等相关内容。

关键词:预处理;编译;汇编;链接;shell;IO管理;进程管理;虚拟内存;异常信号;                           

目  录

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

P2P:Linux中,hello.c经过cpp的预处理、ccl的编译、 as的汇编、 ld的链接, 成为目标程序hello shell 输入启动命令, shell 将生成子进程 然后hello从程序变为程序。

正在上传…重新上传取消正在上传…重新上传取消

020: fork一个子进程后,子进程便有了独立的虚拟地址空间,然后调用execve加载并运行hello程序,加载完成后处理器便开始执行该程序的指令,期间将需要的指令或数据载入物理内存,CPU为运行着的hello程序分配时间片执行逻辑控制流,运行结束后,父进程shell负责回收终止的hello子进程,之后操作系统内核会从系统中删除hello的所有痕迹,即O2O

1.2 环境与工具

硬件环境:X64 CPU2GHz2G RAM256GHD Disk 以上

软件环境:Windows10 64位以上;VirtualBox/Vmware 11以上;Ubuntu 16.04 LTS 64/优麒麟 64

开发与调试工具:gccvimedbreadelfHexEdit

1.3 中间结果

hello.c:源代码

hello.i:预处理后的文本文件

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

hello.o:汇编之后的可重定位目标执行文件

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

hello.elfhello.oELF格式

hello1.elf:helloELF格式

hello0.txthello.o反汇编代码

hello1.txt:hello的反汇编代码

1.4 本章小结

本章主要介绍了helloP2P020过程,以及进行实验时的软硬件环境及开发与调试工具和在本论文中生成的中间结果文件。

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

预处理概念:预处理器cpp根据以字符#开头的命令修改原始的C程序,将引用的所有库展开合并成为一个完整的文本文件。

预处理的作用:

1、文件包含:将引用的文件插入源程序文本中。如#include

2、条件编译:进行编译时进行有选择的挑选,注释掉一些指定的代码,以达到版本控制、防止对文件重复包含。如#if#ifndef#ifdef#endif#undef等。

3、布局控制:为编译程序提供非常规的控制流信息。如#pragma

4、宏替换:这是最常见的用法,它可以定义符号常量、函数功能、重新命名、字符串的拼接等各种功能。如#define

2.2在Ubuntu下预处理的命令

命令:gcc -E hello.c -o hello.i

正在上传…重新上传取消正在上传…重新上传取消

2.3 Hello的预处理结果解析

打开预处理生成的hello.i文件可以看到,预处理后的文件有3060行,相比于预处理之前23行的源程序文件多出了很多内容。预处理器将预处理指令#include <stdio.h>替换为了系统头文件stdio,h中的内容,同理也将unistd.hstdlib.h系统头文件里的内容插入到了源程序文本中。

正在上传…重新上传取消正在上传…重新上传取消

2.4 本章小结

本章主要介绍了预处理的概念及作用,在linux下对hello.c源文件进行了预处理操作,并对预处理的结果进行了解析。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译的概念:编译即编译器(ccl)将文本文件hello.i翻译成文本文件hello.s的过程,得到的hello.s文件包含一个汇编语言程序。

编译的作用:编译器首先检查代码的规范性、是否有语法错误等,并确定代码实际要做的工作,在检查无误后,将高级语言程序翻译成汇编语言程序。除此基本功能外,编译器还具有目标程序优化等功能。

3.2 在Ubuntu下编译的命令

命令:gcc -S hello.i -o hello.s

正在上传…重新上传取消

3.3 Hello的编译结果解析

正在上传…重新上传取消正在上传…重新上传取消

3.3.1 对各数据类型的处理

1.全局变量

hello.c中定义了一个main如图3-3-1-1.

正在上传…重新上传取消正在上传…重新上传取消

3-3-1-1全局变量main

2.局部变量

局部变量一般存储在栈中或寄存器中,hello.c源文件的main函数里定义了一个局部变量int i,查看hello.s文件可以看到,i在栈中-4(%rbp)的地方,如图3-3-1-2

正在上传…重新上传取消正在上传…重新上传取消

3-3-1-2局部变量i的位置

3.字符串

程序中共出现了两个字符串,均为printf函数的格式控制字符串,如图3-3-3.

正在上传…重新上传取消正在上传…重新上传取消

3-3-1-3程序中的字符串

3.3.2 赋值操作

hello.c中有一条赋值语句i=0,赋值在汇编语言中通过mov指令实现,如图3-3-2-1,根据操作数类型的不同,可分为

movb:传送字节

movw:传送字

movl:传送双字

movq:传送四字

正在上传…重新上传取消正在上传…重新上传取消

3-3-2-1汇编中的赋值语句

3.3.3算术操作

hello.c中出现的算术操作为for循环中的i++,汇编中用addl指令实现,如

正在上传…重新上传取消正在上传…重新上传取消

3-3-3

3-3-4 i++的汇编指令

3.3.4 关系操作

1.if语句中出现的argc!=4,在汇编中用cmpl指令实现,该指令计算argc-4的值,并根据结果设置条件码,后续根据条件码中的零标志ZF判断argc0是否相等,并决定是否跳转。

正在上传…重新上传取消正在上传…重新上传取消

3-3-4-1 关系操作!=的汇编指令

2.for循环的循环条件i<8,在汇编中用cmpl指令实现,此处计算i-7的值,并根据结果设置标志位,后续根据符号标志SF和零标志ZF判断i是否<=7,并决定是否跳转。

正在上传…重新上传取消正在上传…重新上传取消

3-3-4-2 关系操作<的汇编指令

3.3.5 数组/指针

main函数的第二个参数argv[]是一个字符串数组,数组内容为指向字符类型的指针。由汇编代码可以看出argv的首地址存放在栈中的-32(%rbp)

正在上传…重新上传取消正在上传…重新上传取消

3-3-5-1 argv首地址在栈中的位置

3-3-5-2中前三行所表示的指令为对argv[2]的访问,11-13行为对argv[1]的访问,均是通过取出数组首地址,再加上相应的偏移量来访问数组中的特定元素。

正在上传…重新上传取消正在上传…重新上传取消

3-3-5-2 argv的访问

3.3.6 控制转移

for(i=0;i<8;i++)i被赋初值0后,程序无条件跳转到.L3.L3中先用cmpl指令计算i-7的值,并根据结果设置标志位,若i<=7,则跳转到.L4执行循环体,.L4结束时将i++并执行.L3,以此规则重复循环。

3-3-6-1 控制转移之if/else

正在上传…重新上传取消

3-3-6-2 控制转移之for循环

3.3.7 函数操作

main函数的调用过程:

1.参数传递:第一个参数argc保存在%rdi中,第二个参数argv保存在%rsi中,若有更多参数,依次保存在%rdx%rcx%r8%r9中,若有更多参数则保存在栈中。

2.栈帧的分配和释放:将%rbp 作为栈底指针,栈顶指针%rsp下移,为被调用者开辟栈帧。程序结束时,调用leave 指令,恢复栈空间为调用之前的状态,然后通过ret返回。

3.4 本章小结

本章主要讲述了编译阶段中编译器如何处理各种数据和操作,以及c语言中各种类型和操作所对应的的汇编代码。通过理解了这些编译器编译的机制,我们可以很容易的将汇编语言翻译成c语言。

(第32分)

第4章 汇编

4.1 汇编的概念与作用

汇编的概念:汇编是指汇编器(as)将.s文件翻译成机器语言指令,并把这些指令打包成可重定位目标程序的格式,并将结果保存在.o文件中的过程。其中.o文件是一个二进制文件,包含程序代码和数据的机器指令编码。

汇编的作用:将汇编代码转换为机器指令,并将其打包为重定位目标程序的格式,生成.o文件,为程序的链接与运行做准备。

4.2 在Ubuntu下汇编的命令

命令:gcc -c hello.s -o hello.o

正在上传…重新上传取消正在上传…重新上传取消

4.3 可重定位目标elf格式

1.ELF头:该部分包含了ELF头的大小、目标文件的类型、机器类型、节头部表的文件偏移,以及节头部表中条目的大小和数量,其中Magic描述了生成该文件的系统的字的大小和字节顺序。

正在上传…重新上传取消正在上传…重新上传取消

2.节头部表:描述了各节的名称、类型、地址、偏移量、大小、对齐要求等信息

正在上传…重新上传取消

3.重定位节:一个.text 节中位置的列表,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。节中的8个重定位条目分别是对.L0puts 函数、exit 函数、.L1printf 函数。

其中各项的含义如下:

偏移量Offset:是需要被修改的引用的节偏移。

符号名称Symbol:标识被修改引用应该指向的符号。

类型Type:告知连接器如何修改新的引用。

加数Addend:一个有符号常数,一些类型的重定位需使用它对被修改引用的值做偏移调整。

正在上传…重新上传取消正在上传…重新上传取消

4. 符号表:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息,一些程序员错误地认为必须通过-g选项来编译一个程序,才能得到符号表信息。实际上每个可重定位目标文件在.symtab中都有一张符号表(除非程序员特意用STRIP命令去掉它)。然而,和编译器中的符号表不同,.symtab符号表不包含局部变量的条目。

正在上传…重新上传取消正在上传…重新上传取消

4.4 Hello.o的结果解析

1. 分支转移:hello.s中直接跳转的跳转目标是用.L2.L3.L4等标号表示的,而hello.o的反汇编中跳转目标main+偏移量来表示的。

2. 函数调用:hello.scall指令后跟的是函数名称,hello.o的反汇编中call指令的目标地址是main+偏移量(定位到call的下一条指令),汇编器会在.rela.text 节中为其添加重定位条目,待链接时确定函数的运行时地址,然后更新call指令的编码。

正在上传…重新上传取消正在上传…重新上传取消

4.5 本章小结

概括了汇编的概念和作用,分析了ELF文件的内容,另外比较了重定位前汇编程序和重定位后反汇编的差别,了解从汇编语言翻译成机器语言的转换处理和机器语言和汇编语言的映射关系。

(第41分)

5章 链接

5.1 链接的概念与作用

链接的概念:链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载到内存并执行。链接可以执行于编译时,也将就是在源代码被翻译成机器代码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;甚至执行于运行时,也就是有应用程序来执行。

链接的作用:链接器在软件开发中扮演着一个关键的角色,因为它们使得分离编译成为可能。我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其他文件。

5.2 在Ubuntu下链接的命令

命令:ld -o hello -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o hello.o /usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o

正在上传…重新上传取消正在上传…重新上传取消

生成文件:

正在上传…重新上传取消正在上传…重新上传取消

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

1. ELF头:

正在上传…重新上传取消正在上传…重新上传取消

2.节头:

正在上传…重新上传取消

5.4 hello的虚拟地址空间

从程序头部表可以看出,代码段从虚拟地址空间的0x400000处开始,用edb加载hello,可以看出程序确实从0x400000处开始,第一部分为ELF头。

正在上传…重新上传取消正在上传…重新上传取消  

5.5 链接的重定位过程分析

1. hello的反汇编代码有确定的运行时地址,说明已经完成了重定位,而hello.o反汇编代码中涉及到运行时地址的地方均标记为0,

正在上传…重新上传取消正在上传…重新上传取消

2. hello的反汇编代码增加了.plt,.init,.fini节。与hello.o链接的库函数的代码都已经插入到了程序中

正在上传…重新上传取消正在上传…重新上传取消

重定位过程分析:

1. 重定位节和符号定义:在这一步中,链接器将所有相同类型的节合并为同一类型的新的聚合节,然后连接器将运行时地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号。这一步完成后,程序中每条指令和全局变量都有了唯一的运行时内存地址。

2. 重定位节中的符号引用:在这一步中,连接器修改代码节和数据节中对每个符号的引用,使他们指向正确的运行时地址。要执行这一步,链接器依赖于可重定位目标模块中称为的重定位条目的数据结构,hello.o的重定位条目如图5-5-3。

偏移量Offset:是需要被修改的引用的节偏移。

符号名称Symbol:标识被修改引用应该指向的符号。

类型Type:告知连接器如何修改新的引用。

加数Addend:一个有符号常数,一些类型的重定位需使用它对被修改引用的值做偏移调整。

正在上传…重新上传取消正在上传…重新上传取消

5.6 hello的执行流程

开始执行:_start、_libc_start_main

执行main:_main、_printf、_exit、_sleep、_getchar

退出:exit

程序名 程序地址

_start 0x4010f0

_libc_start_main 0x2f12271d

main 0x401125

_printf 0x401040

_exit 0x401070

_sleep 0x401080

_getchar 0x401050

5.7 Hello的动态链接分析

 动态链接的基本思想是,根据模块分离程序,当程序运行时,不像静态链接那样将所有程序模块连接到可执行文件,而是连接到可执行文件 。虽然动态链接在程序执行时延迟了链接,但在创建可执行文件时(制作可执行文件和制作可执行程序时注意不同的概念),应该使用动态链接库。例如,在创建可执行程序时, 发现引用了外部函数,此时会检查动态链接库,并发现该函数名为动态链接符号。 在这种情况下,可执行程序将不再重置此符号,而是当装载此符号时再进行 。

GNU 编译系统使用延迟绑定(lazybinding) , 直到您第一次调用程序地址绑定 。延迟绑定是通过GOT和PLT完成。GOT是数据区块的一部分,PLT是代码区块的一部分。两票的内容如下。

  PLT:PLT是拥有16字节代码的组合。PLT[0]是动态链接器移动的特殊项目。调用到可执行程序的所有库函数都有PLT项目 。各项负责呼叫具体函数。

GOT:GOT是有8字节地址的组合。与PLT协同使用时,GOT[O]和GOT[1]包含动态链接解释函数地址时所使用的信息。GOT[2) 是1d-linux.so模块中的动态链接入口处。其余各项目对应于运行中处理的函数。每个项目都有匹配的PLT项目 。

根据Hello ELF文件,GOT起始表位置为0x404000 如图 :

正在上传…重新上传取消正在上传…重新上传取消

5.8 本章小结

本章介绍了链接的概念和作用,详细介绍了hello.o如何成为可执行的目标文件,详细介绍了hello.oELF形式和各节的意义,分析了hello的虚拟地址空间、重置进程、运行进程和动态链接过程。

(第51分)

6章 hello进程管理

6.1 进程的概念与作用

进程的概念:进程就是一个运行中的程序的实例。

进程的作用:进程向我们提供一个假象,就好像我们的程序是系统中当前运行的唯一的程序一样。我们的程序好像是独占地使用处理器和内存。处理器就好像是无间断地一条接一条地执行我们程序地指令。最后,我们程序中的代码和数据好像是系统内存中唯一的对象。

6.2 简述壳Shell-bash的作用与处理流程

在 Linux 系统中,Shell 是一个由用户运行其他程序的互动程序。(运行于用户状态的命令行解析器)

解析和执行用户命令是基本功能,重复以下处理过程。

(1) 终端程序读取用户输入键盘的命令行 。

(2) 分析命令行字符串,获取命令行参数,并构成要传递给 execve 的 argv 向量

(3) 确定第一条( 第一条、 0条) 是否内置命令行参数的 shell 命令

(4) 如果不是内部命令,请装入 fork 创建新进程/ 子进程 。

(5) 在子进程中运行 execve () 作为从第二级获取的参数 。

(6) 如果用户没有请求启动后台(命令结束 & 编号不存在) ,shell 将会wai( 或 wait… 等待任务结束后返回 。

(7) 如果用户请求后台运行(命令末端有&号) ,则返回 shell 。

6.3 Hello的fork进程创建过程

我们之前已经对fork函数有了足够的了解,过程如下:

当用户在shell中输入./hello 120L022407 孙浩 1,shell就会通过调用fork()函数创建一个新的运行的子进程,新创建的子进程几乎但不完全与父进程相同,子进程得到与父进程虚拟地址空间相同的(但是独立的)一份副本,包括代码段、数据段、共享库以及用户栈。子进程还获得与父进程打开文件描述符相同的一份副本,父进程和子进程的不同在于他们的PID不同。父进程与子进程是并发运行的独立进程,内核能够以任意方式交替执行它们的逻辑控制流的指令。

6.4 Hello的execve过程

对于execve函数:

fork 创建子进程之后,子进程调用execve 函数在当前进程的上下文中加载并运行一个新程序hello。新程序会覆盖覆盖当前进程的代码、数据、栈,但拥有和当前进程相同的PID,并继承已打开的文件描述符和信号上下文。execve函数加载并运行可执行目标文件hello,创建一组新的代码、数据、堆和栈段,设置PC 指向_start 的地址,调用main函数,并将控制传递给新程序的主函数,同时传递参数列表argv和环境变量envp。如果出现错误,如hello文件不存在,execve会返回到调用程序,否则execve调用一次从不返回。

6.5 Hello的进程执行

进程提供给程序的抽象 :
(1) 独立逻辑控制流媒体提供假象,就像我们的流程被独家使用一样
(2) 个人地址空间就像我们的程序独家使用CPU内存一样提供虚拟空间。

上下文信息:上下文就是内核重新启动一个被抢占的进程所需要的状态,包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构。

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

进程调度:当内核选择一个新的进程运行时,称内核调度了这个进程。在内核调度了一个新的进程运行后,它就抢占当前进程,并使用一种称为上下文切换的机制来将控制转移到新的进程,上下文切换包含:

1. 保存当前进程的上下文

2. 恢复某个先前被抢占的进程被保存的上下文

3. 将控制传递给这个新恢复的进程。

hello程序开始时运行在用户模式,在调用 sleep 之后转入内核模式,内核处理休眠请求,定时器开始计时,内核进行上下文切换将当前进程的控制权交给其他进程,当休眠结束时定时器发送一个中断信号,此时进入内核状态执行中断处理程序,将hello进程从等待队列中移出并重新加入到运行队列,之后hello进程就可以继续进行自己的逻辑控制流。

正在上传…重新上传取消正在上传…重新上传取消

6.6 hello的异常与信号处理

1.异常

(1). 中断:程序运行过程中随时可能有来自外部I/O设备的信号引起的中断。

(2). 陷阱:使用了sleep,exit等系统调用。当用户程序调用sleep函数时,会执行一个syscall指令,将控制转移给内核,内核将运行陷阱处理程序,解析参数,调用sleep函数,调用后返回到用户进程中引起异常的下一条指令。

(3). 程序执行时可能存在缺页故障。

2.信号处理

(1)程序正常执行

正在上传…重新上传取消正在上传…重新上传取消

(2) Ctrl-Z

在程序执行过程中,用户键入Ctrl+Z,内核会发送一个SIGTSTP信号到前台进程组中的每个进程,子进程hello被停止(挂起),成为后台挂起进程。同时父进程shell收到SIGTSTP信号后,调用信号处理程序,打印提示信息,并开始等待用户输入下一条命令。输入ps命令,可以看到hello在进程列表中。输入fg 1将后台hello程序变更到前台继续运行,hello子进程继续从被停止的位置执行,打印完10条信息后,读入用户输入的任意字符,然后进程终止。此时再次使用ps命令,发现hello已不在进程列表中。

正在上传…重新上传取消正在上传…重新上传取消

(3)Ctrl-C

用户通过键盘输入Ctrl-C会导致内核发送一个SIGINT信号到前台进程组的每个进程,默认情况是终止前台作业。按下Ctrl-C后使用ps命令,发现进程列表中没有hello,表明hello已被终止。

正在上传…重新上传取消正在上传…重新上传取消

(4)不停乱按

在程序运行中乱按不会影响程序正常运行,输入会被缓存到stdin,当程序运行getchar函数的时候,会读取一个以’\n’结尾的字符串作为输入。

正在上传…重新上传取消正在上传…重新上传取消

6.7本章小结

本章介绍了进程的概念及作用,shell的作用及其处理流程,并分析了hello的fork进程创建过程、execve过程和进程执行过程,最后根据不同情况分析了hello运行过程中的异常和信号处理。

(第61分)

7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:

是指由程序产生的和段相关的偏移地址部分,程序经过编译后出现在汇编代码中的地址。逻辑地址用来指定一个操作数或者是一条指令的地址。是由一个段标识符加上一个指定段内相对地址的偏移量,表示为 [段标识符:段内偏移量]。

线性地址:

线性地址是逻辑地址到物理地址变换之间的中间层。程序代码会产生逻辑地址,或者说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。是hello中的虚拟内存地址。

虚拟地址:

虚拟地址就是线性地址。每个进程都有独立且结构相同的虚拟地址空间,从0x400000开始。

物理地址:

CPU通过地址总线的寻址,找到真实的物理内存对应地址。 CPU对内存的访问是通过连接着CPU和北桥芯片的前端总线来完成的。在前端总线上传输的内存地址都是物理内存地址。

7.2 Intel逻辑地址到线性地址的变换-段式管理

段落标识符: 段落内由两个移动量构成的逻辑地址。据说,断识别者是16位长的断选择者。前13位是索引号。后面的3位包括显示代码寄存器、堆栈寄存器还是数据寄存器的硬件细节。

索引编号是"段落描述符( segment descriptor) "的索引。段落说明者具体说明段落。将段落说明字分组为"段落说明字表 " 。 在段落标识符前13位可从段落说明符中找到具体内容。丹妙寺,这句话很重要,说明单标识字的具体作用,丹妙寺一节由8字节组成。

Basefield是包含段落第一个字节的线性地址,即表示一段起始位置的线性地址。所有流程都是自己的,即"LDT"等局部、局部、局部、局部、局部说明者(GDT和何时、何时、何时、何时使用LDT?这出现在段落选择者的 T1 字段 。=0、GDT、=1 将内存内的地址和大小存储在CPU的gdtr控制寄存器中,而LDT则存储在ledtr寄存器中。

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

1、看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。我们就有了一个数组了。

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

3、把Base + offset,就是要转换的线性地址了。

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

线性地址,即虚拟地址,虚拟内存被分割成称为多个虚拟页(VP),类似地,物理内存被分割为物理页(PP)。系统使用页表将虚拟页映射到物理页,每一个页表条目由有效位和一个n位的地址字段组成。如果设置有效位说明该页已缓存到物理内存,否则未缓存。有效位为0且地址字段不为空时指向一个虚拟页在磁盘上的起始地址。

CPU通过MMU将虚拟地址翻译成物理地址,通过VPN找到对应的页表条目,如果已缓存则命中,否则不命中,发生缺页故障,需要操作系统内核与硬件合作处理。此时MMU会选择一个牺牲页,用将产生缺页的虚拟页替换牺牲页,并更新页表,然后重新执行地址翻译。

正在上传…重新上传取消

正在上传…重新上传取消正在上传…重新上传取消

7.4 TLB与四级页表支持下的VA到PA的变换

TLB:每次CPU产生一个虚拟地址,MMU(内存管理单元)就必须查阅一个PTE,以便将虚拟地址翻译为物理地址。为了降低不命中带来的巨大时间开销,在MMU中包括了一个关于PTE的小的缓存,称为翻译后备缓存器(TLB)。TLB是一个小的、虚拟寻址的缓存,其中每一行都保存着一个由单一PTE组成的块。TLB通常有高的相连度,从虚拟地址中的页号提取出组选择和行匹配的索引和标记字段。

四级页表:VPN被解释成从低位到高位的4段,从高地址开始,第一段VPN作为第一级页表的索引,用以确定第二级页表的基址;第二段VPN作为第二级页表的索引,用以确定第三级页表的基址;第三段VPN作为第三级页表的索引,用以确定第四级页表的基址;第四段VPN作为第四级页表的索引,若该位置的有效位为1,则该表项存储的是PPN。在上述过程中,只要有一级页表条目的有效位为0,下一级页表就不存在,对子页表的访问将产生缺页故障,需要从磁盘载入内存。

正在上传…重新上传取消正在上传…重新上传取消

7.5 三级Cache支持下的物理内存访问

物理地址的结构包括组索引CI(下7-12位) ,使用该数字进行组索引,使用组索引找到组索引后组成。假设我们的cache 使用的是 8 号块,如果匹配的显示位置 CT(前排 40 位) 是一个成功且查找的块有效位valid 显示的值为 1 , 则根据数据偏移量CO(后排 6 位)取出所需数据并返回。

如果数据未成功匹配或匹配,则标志为 1 ,则将在下一阶段缓存中查询数据 。 (L2 Cache-> L3 Cache-> 主页保存) 将搜索到的数据装入cache 。此时,我们面临着替换谁的问题。通常情况下, 当我们使用常见的简单替换方法查询获得的数据后,例如,如果我们映射的组中有大量空格,则保留到组内; 否则,组内发生有效的分组冲突时,决定应用最近最小使用( lfu) 策略,然后用牺牲块替换。

7.6 hello进程fork时的内存映射

在shell输入命令行后,内核调用fork创建子进程,为hello程序的运行创建上下文,并分配一个与父进程不同的PID。通过fork创建的子进程拥有父进程相同的区域结构、页表等的一份副本,同时子进程也可以访问任何父进程已经打开的文件。当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同,当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间。

7.7 hello进程execve时的内存映射

fork创建hello子进程后,在子进程中调用execve函数,加载并运行可执行程序hello,主要步骤如下:

1. 删除已存在的用户区域,也就是将shell与hello都有的区域结构删除。

2. 然后映射私有区域,即为新程序的代码、数据、bss和栈区域创建新的区域结构,均为私有的、写时复制的。映射共享区域,将一些动态链接库映射到hello的虚拟地址空间。

3. 设置PC,使之指向hello程序的代码入口。

经过这个内存映射的过程,在下一次调度hello进程时,就能够从hello的入口点开始执行了。

正在上传…重新上传取消正在上传…重新上传取消

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

在ppt中讲解的缺页问题:当指令引用一个虚拟地址,通过查找页表发现,该虚拟地址对应的物理地址所在的物理页不在内存中,需要从磁盘调入,即发生了缺页故障。

当发生缺页故障时,控制转移到处理程序,处理程序从磁盘加载相应的页面,然后将控制转移给引起缺页故障的指令。接着指令再次执行,相应的物理页面已被加载到内存中,页面命中。

正在上传…重新上传取消正在上传…重新上传取消

7.9动态存储分配管理

动态内存分配器维护着一个进程的虚拟内存区域,称为堆。系统之间细节不同,但是不失通用性,假设堆是一个请求二进制零的区域,它紧接在未初始化的数据区域后开始,并向上生长(向更高的地址),对于每个进程,内核维护着一个变量brk,它指向堆的顶部。

分配器将堆视为一组不同大小的块的集合来维护。每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。

1. 隐式空闲链表:空闲块是通过头部中的大小字段隐含地连接着的。分配器可以通过遍历堆中所有的块,从而间接地遍历整个空闲块的集合。

正在上传…重新上传取消正在上传…重新上传取消

2. 显式空闲链表:显式空闲链表是将空闲块组织为某种形式的显式数据结构。因为程序不需要一个空闲块的主体,所以实现这个数据结构的指针可以存放在这些空闲块的主体里面。如在每个空闲块中,都包含一个前驱与一个后继指针。

正在上传…重新上传取消正在上传…重新上传取消

3. 分离空闲链表:分配器维护一个空闲链表数组,每个空闲链表和一个大小类关联,链表是显式或隐式的。

正在上传…重新上传取消正在上传…重新上传取消

7.10本章小结

本章简述了在计算机中的虚拟内存管理,虚拟地址、物理地址、线性地址、逻辑地址的区别以及它们之间的变换模式,以及段式、页式的管理模式,在了解了内存映射的基础上重新认识了共享对象、fork和execve,同时认识了动态内存分配的方法与原理。

(第7 2分)

8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:所有IO设备都被模型化为文件,所有的输入和输出都能被当做相应文件的读和写来执行。
   设备管理:Linux内核有一个简单、低级的接口,成为Unix I/O,是的所有的输入和输出都能以一种统一且一致的方式来执行。

8.2 简述Unix IO接口及其函数

1. 打开和关闭文件

打开文件:进程是通过调用open 函数来打开一个已存在的文件或者创建一个新文件的,int open(char *filename, int flags, mode_t mode),其中open 函数将filename 转换为一个文件描述符,并且返回描述符数字。返回的描述符总是在进程中当前没有打开的最小描述符。flags 参数指明了进程打算如何访问这个文件,mode 参数指定了新文件的访问权限位。

关闭文件:进程通过调用close 函数关闭一个打开的文件。int close(int fd)。

2. 读写文件

应用程序是通过分别调用read 和write 函数来执行输入和输出的。

ssize_t read(int fd, void *buf, size_t n);

ssize_t write(int fd, const void *buf, size_t n);

3. 读取文件元数据

应用程序可以通过调用stat和fstat函数,检索到关于文件的信息(元数据)。stat函数以一个文件名作为输入,并填写一个stat数据结构中的各个成员。Fstat函数是相似的,只不过是以文件描述符而不是文件名作输入。

int stat(const char *filename, struct stat *buf);

int fstat(int fd, struct stat *buf);

8.3 printf的实现分析

printf参数中的…表示传递参数的个数不确定,arg是一个字符指针,表示…中的第一个参数,即输出的时候格式化串对应的值。

正在上传…重新上传取消正在上传…重新上传取消

vsprintf 程序按照格式fmt结合参数args生成格式化之后的字符串,并返回字串的长度。

正在上传…重新上传取消正在上传…重新上传取消

write(buf,i)函数接受buf与需要输出的参数个数,执行写操作,把buf中的i个元素的值输出。write函数中,先给寄存器传递参数,然后执行系统调用sys_call

正在上传…重新上传取消

sys_call将字符串“Hello 120L022407 孙浩”中每个字符对应的ASCII码值复制到显存中。字符显示驱动子程序根据ASCII找到字模库相应的字形,并将每一个点的RGB颜色信息写入到显示vram,然后系统显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

正在上传…重新上传取消正在上传…重新上传取消

8.4 getchar的实现分析

getchar 的源代码为:

int getchar(void)

{

static char buf[BUFSIZ];

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;

}

用户输入的字符被存放在键盘缓冲区中,当用户键入回车之后,getchar开始从stdin流中每次读入一个字符,getchar函数的返回值是用户输入的第一个字符的ASCII码,如出错返回-1,且将用户输入的字符输出到屏幕。如用户在按回车之前输入了不止一个字符,其他字符会保留在键盘缓存区中,等待后续getchar调用读取。也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完为后,才等待用户按键。

异步异常-键盘中断的处理:当用户按键时,键盘接口会收到一个该按键的键盘扫描码,同时产生一个中断请求,中断请求运行键盘中断子程序,从键盘接口取得该按键的扫描码,将按键扫描码转成ascii码,保存到系统的键盘缓冲区。

getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

8.5本章小结

本章介绍了linux的IO设备管理办法、unix IO接口及函数、printf的实现分析和getchar的实现分析。

(第81分)

结论

hello所经历的过程:

1.用C语言编写hello.c文件。

2.预处理:通过预处理器(cpp)解释预处理指令,生成hello.i文件。

3.编译:编译器(ccl)将解释完预处理指令的hello.i文件进行编译,得到汇编程序(文本)hello.s文件。

4.汇编:汇编器(as)将汇编程序hello.s中的汇编语言转换成机器语言,生成重定位信息,将这些代码和信息生成为一个可重定位目标程序(二进制)hello.o。

5.链接:由于hello程序调用了printf函数,其存在于一个名为printf.o的单独的预编译好了的目标文件中,所以链接器(ld)将hello.o和printf.o链接,处理合并为hello文件。

6.创建进程:在shell命令行输入./hello,shell解释命令并调用fork函数创建一个子进程。

7.加载程序:加载器调用execve函数,在当前进程的上下文中运行hello程序。

8.内存管理:运行hello时,内存管理单元MMU、翻译后备缓冲器TLB、多级页表机制、三级cache等计算机中的各个组成部件共同运转和配合,完成对内存地址的解析、请求、返回、访问。

9.异常处理:如果产生缺页异常,则缺页处理程序选择合适的牺牲页替换,并重新加载相应命令。

10.结束:当hello运行完毕,向父进程shell发送SIGCHLD信号,提示进程已终止,然后父进程回收hello。

感悟:

经过计算机系统这门课的学习,思考,实践,我对于计算机系统的运行机制,程序的编译、链接机制;操作系统的相关知识,Linux的相关操作与系统调用函数等。也让我知道了想要写出更高效的代码必须了解计算机底层的知识。想成为一名优秀的程序员仍需很大努力。

(结论0分,缺失 -1分,根据内容酌情加分)

附件

文件名

作用

hello.i

hello.c经cpp预处理得到的修改了的源文件

hello.s

hello.i经过cc1编译得到的汇编文件

hello.o

hello.s经过as汇编得到的可重定位目标文件

hello

hello.o经ld链接得到的可执行目标文件

hello1.elf

ELF格式下的hello

hello0.txt

hello.o的反汇编代码

(附件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.

[7]  兰德尔E.布莱恩特,大卫R.奥哈拉伦. 深入理解计算机系统. 北京:机械工业出版社,2016年11月第1版.

[8]  [转]printf 函数实现的深入剖析 - Pianistx - 博客园 (cnblogs.com).

[9]  https://www.cnblogs.com/pianist/p/3315801.html.

(参考文献0分,缺失 -1分)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值