计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 软件工程专业
学 号 1183710222
班 级 1837102
学 生 戚鸿圣
指 导 教 师 史先俊
计算机科学与技术学院
2019年12月
摘 要
关键词:本文描述了一个高级语言程序从编写到执行的全过程,使用objdump查看机器代码的反汇编内容并进行详细分析从外部存储设备,对其经过I/O桥,进入到主存,多级cache,最后在I/O中输出,最后被回收的过程进行描述,详细地介绍了高级语言程序如何通过预处理、编译、汇编和链接生成完全链接的可执行文件,描述了程序的复杂生命周期,从而了解到由高层次到底层运行机制的大致过程。编写该论文,分析了 hello程序P2P的整个过程,此次大作业结合CSAPP课本相关章节,分析hello过程出现的各种现象,理解计算机的各种行为,既可用于复习,也有利于他人阅读并学习
关键词:预处理;汇编;编译器;连接器;进程管理;内存分配;信号;虚拟地址;系统级I/O;
(摘要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简介
P2P: From Program to Process 。在linux环境下,hello.c经过预处理器cpp、编译器ccl、汇编器as、链接器ld采用虚拟地址装载入内存,最终成为可执行目标程序hello,进入shell。在shell中键入启动命令后,shell为其fork产生子进程的过程,分配对应的资源包括cpu使用权限等,然后使用execve函数加载进程。
020: shell通过execve加载并执行hello,映射虚拟内存,进入程序入口后程序开始载入物理内存,然后执行第一条指令,CPU为运行的hello分配时间片执行逻辑控制流。当程序运行结束后,shell父进程负责回收hello进程,内核删除相关数据结构。
1.2 环境与工具
软件环境:VMware 15.5pro、ubuntu-16.04、Windows10
开发与调试工具:gcc、vim、edb、hexedit、IDA、readelf、objdump
1.3 中间结果
文件名称 文件作用
hello.c 提供源程序
hello.i 预处理器cpp 预处理之后的文件
hello.s 汇编器cc1的文本文件
hello.o as 之后的可重定位目标文件
hello ld 之后的可执行目标文件
helloftx.elf hello.o 的 ELF 格式文件
hello.objdump Objdump 得到的 hello 的反汇编代码
1.4 本章小结
本章介绍了 hello 程序的 P2P,020 的整个过程。
给出了完成本次实验的软硬件开发环境和所使用的开发调试工具。
高度概括了研究过程中的中间结果,以及其用途和对研究过程产生的作用。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
概念:C 语言的预处理主要有三个方面的内容: 1.宏定义; 2.文件包含; 3.条件编译。 预处理命令以符号“#”开头。预处理器 cpp 根据以字符#开头的命令(宏定义、条件编译),修改原始的 C程序,将引用的所有库展开合并成为一个完整的文本文件。比如hello.c中第6行的#include<stdio.h>命令高速预处理器读取系统头文件stdio.h的内容,并插入程序文本中。结果就得到了另一个C程序,通常是以.i作为文件拓展名。
作用:经过预编译处理后,得到的是预处理文件(如:hello.i) ,它还是一个可读的文本文件,但不包含任何宏定义。
1.宏定义的替换
预处理的过程中,将标识符用字符串替代。
#define 标识符 字符串:将宏名替换为字符串。
#define 宏名(参数表) 字符串:除了一般的字符串替换,还要做参数代换。
2.文件包含
程序中的头文件一般以#include<库名.h>的形式给出,在预处理过程中会将这些封装好的代码直接插入程序中,在以后的编译中直接作为源文件编译。
3.条件编译
包括双分支条件编译形如
#if 条件表达式
…
#else
…
#endif
和多分支条件编译
如
#ifdef A
…
#elif defined B
...
#endif
或者
#define B 1
#ifdef A
...
#elif B
...
#endif
2.2在Ubuntu下预处理的命令
2.3 Hello的预处理结果解析
调用的头文件
Main函数在hello.i的最后面表示已经预处理完毕
PS这里使用的是第一次老师上传的错误的hello,后面使用正确的hello函数,但是内容几乎无影响,只是sleep(argv【3】)变为sleep(atoi(argv【3】))
这是我第三回上当了,每次群里上传的我都第一时间下,然后又不用那个包,建议以后直接发一个,统一口径。
带有extern的函数,这些外部函数便是预处理过程中对#include这样的头文件的展开的插入。
2.4 本章小结
本章介绍了hello.c的预处理阶段,经过预编译处理后,得到的是一个完整的预处理文件(如:hello.i) ,它还是一个可读的文本文件,但不包含任何宏定义。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
概念:
编译过程就是将预处理后得到的预处理文件(如 hello.i)进行词法分析、语法分析、语义分析和中间代码生成、优化后,生成汇编代码文件。编译器中按一定的规则将代码翻译成机器码,汇编语言相比高级语言更加贴近于机器,这种汇编语言的集合称为指令集。
作用:
将高级语言翻译成汇编语言,不同的高级语言经过编译器后的得到的汇编语言可以是相同的。在本例中其将文本文件 hello.i 翻译成文本文件 hello.s,它包含一个汇编语言程序。
注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序
3.2 在Ubuntu下编译的命令
这个用的还是先前错误的hello,所以报waring,从3.3开始使用了正确的代码。
3.3 Hello的编译结果解析
3.3.1 汇编代码分析
可以看到.s文件远没有.i文件的行数那么多。
注意到main之前的部分以.开始,这表示其并不属于函数内容而是汇编器的标记内容
如.file 表示指定的文件名为“hello.c”
.section 。rodata 是只读代码段
。align 表示为8字节对齐
。string 表示"Hello %s %s\n" 代表字符串类型
。text 表示代码段
。globl main表示main是一个全局符号
。type main @function 表示main为函数
3.3.2 数据分析
Lc0中的。String内容为c程序中的输出语句的内容的utf-8
Lc1中的。Srting内容对应printf(“hello %s %s\n”,argv[1],argv[2])
Int i这里的i是一个局变作为循环变量来进行计数,编译器将局部变量存放在栈和寄存器中,hello.s程序中i存放在-4(%rbp),占据4字节,对应于汇编语句movl $0, -4(%rbp)
Int argc 指的是main函数参数的传入个数,在这里用寄存器传递。通过edi传递并放在-20(%rbp)处,用来判断是否有足够的参数。
立即数 在代码中以$开头,表示本身就是立即数的数或者被宏替换的数。
字符串数组 argv 数组作为一个 char* 类型的数组,char* 的大小是 8 个字节,所以argv[1]加 0x8,而 argv[2]加 0x10。对应于L4的开头7条语句
3.3.3 类型转换
这里使用了库函数atoi,作用是将字符串表示的数转化为实际的浮点数(double),这里出现了一次字符和浮点数的显式转换。属于显式转换。
转换之后的值作为sleep函数的参数,由 sleep函数的定义可知参数是无符号整型,进行了一次隐式转换,将浮点double数转换为unsigned int
头文件为#include <unistd.h>
函数名unsigned int sleep (unsigned int seconds) 单位为秒
c语言中的类型转换存在优先级,一般情况下类型的级别从高到低依次是
long double
double、float
unsigned long
long long
unsigned long
long
unsigned int
int
在赋值表达式语句中,计算的最终结果会被转换成赋值变量的类型。
3.3.5 算术分析
图片来自Computer Systems- A Programmer's Perspective, 3rd ed. - Randal E. Bryant, David R. O'Hallaron (Pearson, 2015, 9780134092669)
在hello.s中的算术命令有二
加法 如
addq $8, %rax 表示对参数计数器加1时指针位置+8
减法 如
subq $32, %rsp 为main开辟栈帧,将栈顶指针减去0x32
3.3.6 关系操作
关系操作由cmp和test指令完成,条件码根据这两种指令设置。
本例中涉及的语句主要有判断输入参数是否为4,和for(i=0;i<8;i++)循环判断
分别对应以下
3.3.7 控制转移
关系操作修改条件码,控制操作根据条件码进行跳转。
前一个跳转L2判断,如果参数数量不与立即数4不相等,设置条件码zf,je根据zf位进行跳转或继续执行。
后一个跳转L4判断,如果判断计数器是否为7,如果不为7则循环,循环中又会将计数器加一,计数器从0开始,即为8次循环
3.3.8 函数操作
函数调用的基本过程,首先将%rbp 通过 push 指令存储在栈中,并将%rsp 栈指针赋值给%rbp。通过 sub 指令减小栈指针地址从而为局部变量分配空间。然后将%edi 和%rsi 寄存器中的值存储在栈中。具体函数分析如下:
Main函数本身的参数调用,其中%edi代表argc,%rsi代表argv[]
Printf和puts函数调用 第一次printf实际上只有一个字符串参数,故编译器优化使用puts
第二次为printf
Exit(1),状态1退出
Sleep函数,这里sleep函数在之前的类型转换中已经有说。
Getchar函数调用,%eax置零,call getchar
此部分是重点,说明编译器是怎么处理C语言的各个数据类型以及各类操作的。应分3.3.1~ 3.3.x等按照类型和操作进行分析,只要hello.s中出现的属于大作业PPT中P4给出的参考C数据与操作,都应解析。
3.4 本章小结
经过编译后,得到的汇编代码文件(如 hello.s)还是可读的文本文件,但是CPU 无法理解和执行它。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
概念:
汇编指令是机器指令的符号表示,它们都是机器级指令,所构成的程序称为机器级代码。
作用:
汇编器as用来将汇编语言源程序转换为机器指令序列,把这些指令打包成可重定位目标程序的格式,输出为.o 目标文件。
注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。
4.2 在Ubuntu下汇编的命令
4.3 可重定位目标elf格式
首先有必要看一下elf文件的格式
使用readelf -h hello.o 查看文件头信息,根据文件头信息可以知道文件为可重定向文件,有13个节,入口为0x0,补码,小端排序等等信息
使用readelf -S hello.o 查看文件节头表,这个表中包含了这个重定位文件中的各个节所对应名称大小,偏移,对齐,地址等等信息。
使用readelf -s hello.o 查看符号表,得到不同符号的标码,值,空间大小,全局性与否,和ndx整数索引。其中1为.text节,3为.data节以此类推
其中ndx处abs意为绝对的,不调整到任何区段地址的偏移
使用readelf -r hello.o可以查看elf文件的重定位信息。
首先我们可以看到,这个表中有8个重定位信息,分别是.L0,puts,exit,.L1,printf
Atoi ,sleep ,getchar的重定位声明
通过这个重定位表,我们可以得到不同代码的符号类型32,或pc32,分别如下
而重定位条目格式如下
重定位算法教材中也给出了
4.4 Hello.o的结果解析
受限于我的虚拟机只有800x600分辨率,我只能把文件放到win10下展示。
4.4.1 机器语言和汇编语言的映射
从总的流程看,hello.s与hello.o流程相同,hello.o的反汇编文件中每行十六进制数表示一个指令,一一对应。机器语言由二进制数构成,分为操作数和操作码
4.4.2 机器语言和汇编语言不一致
对于一套机器指令,有的序列代表寄存器的编号,将操作码与代表寄存器的序列结合后生成了新的二进制序列。此时出现不一致。
4.4.3 函数调用
Hello.s反汇编文件中调用函数为call +地址,而hello.s中则为函数名。=
4.4.4 分支转移
Hello.s文件中使用标志进行分支转移,如jmp,jl指令,而在反汇编中则使用了相对寻址的方式进行分支控制
说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。
4.5 本章小结
hello.s通过汇编生成hello.o文件,文件类型从文本文件转换成二进制文件。hello.o使用objdump进行反汇编后可以得到机器指令和汇编指令的一一对应。得到hello.o后,便可进行链接ld操作生成可执行文件。
(第4章1分)
第5章 链接
5.1 链接的概念与作用
(以下格式自行编排,编辑时删除)
概念
链接是将关联的所有可重定位的目标文件结合到一起,形成一个具有统一地址空间的可执行目标文件的过程。它可以执行与编译时,加载时和运行时。
作用:
模块化编写程序,整体链接,增强工程的分治能力和代码的可利用率。
提高了空间利用率,源程序文件中无需包含共享库的所有代码,直接调用即可。可执行文件运行时的内存中只需要包含所调用的函数的代码。
注意:这儿的链接是指从 hello.o 到hello生成过程。
5.2 在Ubuntu下链接的命令
使用ld的链接命令,应截图,展示汇编过程! 注意不只连接hello.o文件
5.3 可执行目标文件hello的格式
Readelf -a hello
首先有必要看一下可执行目标文件的elf格式。
同上关于可重定向目标文件的分析,可以看到,这个可执行目标文件明显变得更大了
他有25个节头
有48个入口
有9个符号,其中新出现的weak和local分别表示弱符号和局部变量。
5.4 hello的虚拟地址空间
Section.interp的虚拟地址在高亮处0x400520
Section.Interp的虚拟地址可以看到在光标高亮处0x4001c8
第一个有读/执行权限,开始于内存0x400000,总大小0x790字节,其中包括elf头,程序头部表和.init,.text,.rodata节。
其二有读写权限,开始于内存0x600790,总大小0x1ec字节。
可执行目标文件各节的基本信息都描述了各节在具体虚拟地址空间的相对位置关系。每一个表项提供了各段在虚拟地址空间和物理地址空间的大小、位置、标志、访问权限和对齐方面的信息。
Readeelf -d -r hello
5.5 链接的重定位过程分析
两者的差别主要在
1链接时新增函数
2增加新的节 如init节
3 函数调用方式 hello中没有hello.o的重定位条目,并且跳转地址和函数调用地址都变成了虚拟内存地址。对于hello.o的反汇编代码,函数只有在链接之后才确定执行地址,这使得在rela.text节中加入了重定位条目,这也是重定位目标文件和可执行目标文件的主要区别所在。
5.6 hello的执行流程
流程如下
_init<0x0000000000400468>
puts@plt<0x0000000000400490>
puts@plt<0x00000000004004a0>
printf@plt<0x00000000004004b0>
_libc_start_main@plt<0x00000000004004c0>
getchar@plt<0x00000000004004d0>
atoi@plt<0x00000000004004e0>
exit@plt<0x0000000004004f0>
sleep@plt<0x0000000000400500>
_start<0x0000000000400520>
Main<0x000000000040054a>
__libc_csu_init<0x00000000004005e0>
__libc_csu_fini<0x0000000000400650>
_fini<0x0000000000400654>
5.7 Hello的动态链接分析
(以下格式自行编排,编辑时删除)
分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。
5.8 本章小结
链接可以在编译时由静态编译器完成,也可以在加载时和运行时由动态链接器完成。
链接器文件为目标的二进制文件,包括三种不同格式,可重定位的,可执行的,共享的。
练习了从ld链接器将hello.o的链接命令,到elf文件的查看,分析可执行文件的相关信息,hello的重定位过程,执行流程等等。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
(以下格式自行编排,编辑时删除)
概念
进程是一个运行中的程序的实例。进程为程序的运行创建上下文,即为程序运行搭建环境。
作用
进程创造了一种假象,即程序独占地使用处理器和内存,并且不间断地执行其指令。
6.2 简述壳Shell-bash的作用与处理流程
作用:系统的用户界面。提供用户与内核交互的接口,代表用户运行其他程序。
处理流程:
1)从终端读入输入的命令。
2)将输入字符串切分获得参数
3)判断是否为内置指令,如果是内置命令则立即执行
4)如果不是那么就调用相应的程序为其分配子进程并运行
5)shell接受键盘输入信号,并处理信号
6.3 Hello的fork进程创建过程
Fork函数创建一个新的运行子进程,也就是hello程序,hello进程几乎但不全与shell相同,hello进程得到与shell用户级虚拟地址空间相同但独立的副本,包括代码数据段,堆栈共享库等等。这就意味着它拥有可以任意读写shell的权限。Shell和hello的最大不同在于pid不同。
6.4 Hello的execve过程
(以下格式自行编排,编辑时删除)
创建进程后,在子进程中判断pid,判断处于子进程中就会execve函数在当前进程的上下文中加载并运行新程序。
函数如图所示
execve调用一次并从不返回。
6.5 Hello的进程执行
(以下格式自行编排,编辑时删除)
结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。
上下文切换如图
进程的上下文切换由如下组成
1 决定是否进行切换,在操作系统的判断下又适当的时机进行切换
2 保存进程上下文,当进程切换时先前执行的进程的上下文会被保留
3 进程调度算法,选择一处于就绪状态的进程
4 恢复或装填所选进程的上下文,将cpu控制权交还给所选进程。
时间片
一个进程执行它的控制流的一部分的每个时间段叫做时间片。
用户程序调用系统 API 函数称为系统调用;发生系统调用时会暂停用户程序,转而执行内核代码,访问内核空间,这称为内核模式。
用户空间保存的是应用程序的代码和数据,其他程序一般无法访问。当执行应用程序自己的代码时,称为用户模式。
在处理操作系统陷阱时,
1,内核根据系统调用号查找系统调用入口表,找到相应的内核子程序的地址。
2,内核确定该系统调用所要求的参数个数。
3,拷贝用户地址空间参数
4,保存当前上下文,执行系统调用代码。
核心态:当 CPU 正在运行内核代码时(内核代码是共享的)。
用户态:当 CPU 正在运行用户代码时。
用户模式:不可以访问内核空间(>=0x80000000)
内核模式:可以访问任何有效虚拟地址,包括内核空间。一个线程可以访问其他任何线程地址空间。
对于本例
一个主要的上下文切换为sleep的调用,shell初始运行在用户模式,在hello进程调用sleep时进入内核,内核处理休眠请求并挂起当前进程,开始计时。内核将上下文切换转移控制权到其他进程。当定时器到时间时发送中断信号,此时内核进行中断处理,hello进程从等待队列中切出。
另外一个上下文切换为getchar处,其内容与sleep基本相同,在此不表。
6.6 hello的异常与信号处理
正常运行,循环八次,每次输出输入的字符串,之后等待命令行输入回车结束程序
中途输入crtl-z 内核向前台发送sigstp信号,前台被挂起,知道通知他继续的进程到来才从暂停处继续运行
中途输入crtl-c 内核向前台发送sigint信号,前台终止,内核再向父进程发送sigchld信号,通知父进程回收子进程。
中途乱按只会将乱按的字符串输出,程序正常执行。
Ps打印当前进程状态
打印进程树(部分)
Jobs 打印进程状态信息
Fg 1 继续执行被停止的进程
输入kill,kill会根据信号的值和要发送的进程的pid,杀死进程,之后fg 1使其继续,会得到任务已经终止的信息。
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
6.7本章小结
Shell中执行是通过fork函数及execve创建了新的进程并执行程序,进程拥有着与父进程相同却又有独立环境,与其他系统并发执行,拥有各自的时间片,在内核调度下有序进行程序执行。
异常分为中断,陷阱,故障,终止四类。对他们的处理是通过不同的信号进行反馈,程序能够对不同的信号调用信号处理子程序。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
(以下格式自行编排,编辑时删除)
逻辑地址 概念: :
逻辑地址是指由程序产生的与段相关的偏移地址部分。
对应于 hello.o 里面的相对偏移地址。
线性地址:
逻辑地址经过段机制后转化为线性地址,即综合标识符和偏移量得到的地址。
虚拟地址:
现代操作系统提供了一种内存管理的抽像,即虚拟内存。进程使用虚拟内存中的地址,即虚拟地址,虚拟地址经过MMU地址翻译可以得到物理地址。与线性地址相同。hello.s 中使用的就是虚拟空间的虚拟地址。
物理地址:
真实的内存对应的地址。内存中,第一个字节为0,第二个字节为1…以此类推。CPU访问内存就是通过物理地址寻址的方式。
20 位物理地址计算方法:物理地址=段地址*16d+偏移地址。是指出目前 CPU 外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。如果启用了分页机制,那么线性地址会使用页目录和页表中的项变换成物理地址。如果没有启用分页机制,那么线性地址就直接成为物理地址了。
7.2 Intel逻辑地址到线性地址的变换-段式管理
(以下格式自行编排,编辑时删除)
8086共设计了20位宽的地址总线,物理地址=段地址*16d+偏移地址。即逻辑地址。将内存分为不同的段,每个段有段寄存器对应,段寄存器有一个栈、一个代码、两个数据寄存器。
7.3 Hello的线性地址到物理地址的变换-页式管理
系统将虚拟页作为进行数据传输的单元。Linux下每个虚拟页大小为4KB。物理内存也被分割为物理页,内存管理单元负责进行地址翻译,MMU使用页表plt将虚拟页到物理页的映射。页表内容为最终物理页的物理内存起始地址
首先MMU进行虚拟地址的解析,将高位虚拟页号(VPN)部分解析分成组索引和标记两部分,之后从首地址存放在页表基址寄存器(PTBR)的页表中寻找对应项。得到物理页号作为物理地址的标记部分,然后将虚拟地址低位与之结合,生成新的物理地址。
7.4 TLB与四级页表支持下的VA到PA的变换
页表一般很大,这使得处理器在引入MMU时取指,访存要访问两次内存。首先通过查询页表得到物理地址,然后访问该物理地址读取指令,数据,为了减少MMU导致cpu性能下降,引入TLB,起到类似cache的作用。
Tlb的项分为标识和数据,标识存放一部分虚拟地址。而数据部分存放物理页号等内容。虚拟地址与TLB中项的映射有三,全相联,直接映射,组相联。
7.5 三级Cache支持下的物理内存访问
首先cpu发出虚拟地址,在tlb中寻找,如果命中,那么pte发送给一级cache,否则就先在页表更新pte,如果有空闲块则将目标块放置到空闲块中,否则将缓存中的某个块驱逐,将目标块放到被驱逐块的原位置,然后再找地址。
7.6 hello进程fork时的内存映射
在shell中输入命令行后,内核调用fork创建子进程, Fork的子进程拥有与父进程相同的区域结构与页表等副本,同时可以对父进程的任何已打开文件进行访问
7.7 hello进程execve时的内存映射
execve调用启动加载器代码,在当前进程的上下文中加载并运行可执行目标文件hello。首先删除已存在的用户区域,创建新的区域结构,然后映射私有区域,即将栈、堆、.bss请映射到匿名文件,.data和.text节由hello提供。设置PC,指向代码区域入口映射共享区域,对hello进行动态链接,将链接对象的.data、.text节映射到hello的虚拟地址空间。最后设置PC指向hello的入口。
7.8 缺页故障与缺页中断处理
缺页时内核调用缺页处理程序,处理程序会先判断地址是否合法,如果不合法触发段错误,然后检查是否有权限,如果没有触发保护异常,程序终止。两次检查都成功后内核选择牺牲页替换,更新页表,然后将控制转移给hello,再次触发缺页故障
7.9动态存储分配管理
进程在运行时,动态存储器分配器维护一个进程的虚拟存储器区域,称为堆。堆主要具有填充字、预言块、负载块、结尾块四个结构部分。每个负载块只有分配或空闲两种形式。显式分配器要求程序显式地释放已分配块,隐式分配器则使用“垃圾收集”的策略隐式地回收不再使用的已分配块。
堆中的块组织形式有:隐式空闲链表,显示空闲链表,分离空闲链表
Printf 会调用 malloc :printf 会引用全局变量 stdout,malloc。
7.10本章小结
磁盘上有一块连续的区域作为虚拟内存,虚拟内存作为磁盘到主存的cache。
虚拟内存采用虚拟地址寻址的方式。虚拟地址会翻译得到物理地址。若未找到就触发缺页中断,经过处理程序后,再次执行时即可命中。
堆采用动态存储空间管理的策略,申请目标大小的块,并在使用结束后释放它。释放时需要对相邻的空块进行合并,保证内存块的连续性。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:
Linux中所有的IO设备都被抽象成文件,输入和输出抽象为对文件的写和读操作。设备模型展示了总线和它们所控制设备的实际链接。
设备管理:是通过unix io接口实现的
8.2 简述Unix IO接口及其函数
linux 提供如下 IO 接口:
read 和 write -- 最简单的读写函数
readn 和 writen -- 原子性读写操作
recvfrom 和 sendto -- 增加了目标地址和地址结构长度的参数
recv 和 send -- 允许从进程到内核传递标志
readv 和 writev -- 允许指定往其中输入数据或从其中输出数据的缓冲区
recvmsg 和 sendmsg -- 结合了其他 IO 函数的所有特性,并具备接受和发送辅助数据的能力
Unix I/O 接口提供了以下函数供应用程序调用:
打开文件:int open(char *filename, int flags, mode_t mode);
返回值是一个文件描述符 path顾名思义就是文件名 oflage文件是打开方式 第三个形参应用于创建文件时使用
关闭文件:int close(int fd);
返回值 成功返回0 失败—1 关闭文件描述符
读文件:ssize_t read(int fd, void *buf, size_t n);
返回值是文件读取字节数 在好几种情况下会出现返回值不等于文件读取字节数 也就是第三个参数nbytes的情况 第二个形参buf读取到buf的内存 文件偏移量(current file offset)受改变
写文件:ssize_t write(int fd, const void *buf, size_t n);
返回值是文件写入字节数 fd是文件描述符 将buf内容写入nbytes个字节到文件 但这里需要注意默认情况是需要在系统队列中等待写入(打开方式不同也会不同)
8.3 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;
}
https://www.cnblogs.com/pianist/p/3315801.html
vsprintf的作用就是格式化输出
接受确定输出格式的格式字符串fmt,用格式字符串对个数变化的参数进行格式化,并产生格式化的输出。
Write系统函数
write:
mov eax, _NR_write
mov ebx, [esp + 4]
mov ecx, [esp + 8]
int INT_VECTOR_SYS_CALL
wirte其中一个参数为1
1表示tty对应的一个文件句柄
1表示向当前显示器里写入数据
Int INT_VECTOR_SYS_CALL 表示通过系统使用sys——call函数。
sys_call:
call save
push dword [p_proc_ready]
sti
push ecx
push ebx
call [sys_call_table + eax * 4]
add esp, 4 * 3
mov [esi + EAXREG - P_STACKBASE], eax
cli
ret
call save是为了保存中断前进程的状态。syscall将输入的参数(ACSII码)从寄存器传送到显卡的显存中。
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
#include "sys/syscall.h"
#include <stdio.h>
int getchar(void)
{
char c;
return (read(0,&c,1)==1)?(unsigned char)c:EOF
//EOF定义在stdio.h文件中
}
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。用户按键时键盘接口得到一个按键所对应的扫描码,同时产生一个中断请求。中断请求抢占当前进程并运行键盘中断子程序,之后将按键扫描码转为ascii码保存到键盘缓冲区。
getchar等调用read系统函数,通过系统调用read读取按键ascii码,直到接受到回车键才返回。Getchar进行封装,使函数功能变成读取字符串第一个字符后返回。
8.5本章小结
输入输出i/o是主存和外部设备如终端,网络之间拷贝数据的过程,输入操作是i/o设备拷贝到主存,输出与之相反
Unix中所有的i/o设别被模型化为文件。
(第8章1分)
结论
本次大作业的流程如下
用户编写hello.c的源代码并保存。
对hello.c进行预处理操作,根据以“#”开头的命令,修改原始的C程序,得到hello.i。
对hello.i进行编译操作,将高级语言转化成汇编语言,得到hello.s。
对hello.s进行汇编操作,将汇编语言转化成机器语言,此时的hello.s被打包成可重定位目标程序,保存在hello.o中。
将hello.o与库进行链接操作,符号解析和重定向后,链接完成得到可执行目标文件hello。
在shell中输入命令 ./hello 1183710222 戚鸿圣 2,shell对命令进行解析,并通过fork创建新进程。随后调用execve将hello程序加载到当前的上下文中,将PC设置为hello的第一条指令,开始执行hello。
CPU为hello进程分配时间片,在时间片中hello进程仿佛独占地使用CUP和内存。当被调度到内核程序并返回到hello进程时,hello进程可以从终止的位置继续执行。
执行过程中,MMU将虚拟地址翻译成物理地址,并通过页表映射到内存,以访问内存。
prinft在被调用时会通过malloc申请额外的堆空间。
执行结束后(可以是执行到程序末尾,也可以是键入ctrl-C信号),hello进程向父进程发送SIGINT信号,表明hello进程已经终止,此时父进程将子进程回收,内存空间得到释放。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
hello.c源代码
hello.c预处理得到的hello.i
hello.i编译后生成的hello.s
hello.s汇编后生成的hello.o
hello.o与动态库链接得到的hello可执行文件
helloobj.s使用objdump查看hello得到的结果
(附件0分,缺失 -1分)
参考文献
为完成本次大作业你翻阅的书籍与网站等
[1]Computer Systems- A Programmer's Perspective, 3rd ed. - Randal E. Bryant, David R. O'Hallaron
[2] 林来兴. 空间控制技术[M]. 北京:中国宇航出版社,1992:25-42.
[3] 辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.
[4] 赵耀东. 新时代的工业工程师[M/OL]. 台北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).
[5] 谌颖. 空间交会控制理论与方法研究[D]. 哈尔滨:哈尔滨工业大学,1992:8-13.
[6] KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.
[7] 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分)