【计算机系统大作业】hello的一生

摘 要
文章细致地描述了C语言文件hello.c如何一步步地从高级语言代码到在计算机中运行的过程,涉及到C文件的预处理、编译、汇编、链接、进程管理、存储管理以及IO管理。每一个过程都有其不可替代的作用,从C语言程序到汇编代码再到机器代码逐级翻译成计算机能看懂的语言,再由操作系统为这个程序创建进程分配空间,在执行过程中到磁盘寻址,在多级页表与cache的支持下把所需的数据加载至物理内存中,同时管理IO设备,及时响应IO设备的信号。程序运行结束后回收所有数据,释放空间。这样就完成了一个程序的运行。
关键词:预处理;编译;汇编;链接;进程管理;存储管理;IO管理;
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)

目 录

第1章 概述 - 5 -
1.1 HELLO简介 - 5 -
1.2 环境与工具 - 5 -
1.3 中间结果 - 5 -
1.4 本章小结 - 5 -
第2章 预处理 - 7 -
2.1 预处理的概念与作用 - 7 -
2.2在UBUNTU下预处理的命令 - 7 -
2.3 HELLO的预处理结果解析 - 8 -
2.4 本章小结 - 8 -
第3章 编译 - 9 -
3.1 编译的概念与作用 - 9 -
3.2 在UBUNTU下编译的命令 - 9 -
3.3 HELLO的编译结果解析 - 10 -
3.3.1数据 - 10 -
3.3.2算数操作 - 10 -
3.3.3关系操作 - 10 -
3.3.4数组操作 - 11 -
3.3.5控制转移 - 11 -
3.3.6函数操作 - 12 -
3.4 本章小结 - 13 -
第4章 汇编 - 14 -
4.1 汇编的概念与作用 - 14 -
4.2 在UBUNTU下汇编的命令 - 14 -
4.3 可重定位目标ELF格式 - 14 -
4.3.1 ELF头 - 14 -
4.3.2节头部表 - 14 -
4.3.3 .text节代码段 - 15 -
4.3.4 .rela.text节重定位节 - 15 -
4.3.5几个数据节 - 15 -
4.3.6 符号表 - 16 -
4.4 HELLO.O的结果解析 - 16 -
4.5 本章小结 - 17 -
第5章 链接 - 18 -
5.1 链接的概念与作用 - 18 -
5.2 在UBUNTU下链接的命令 - 18 -
5.3 可执行目标文件HELLO的格式 - 18 -
5.3.1ELF头 - 18 -
5.3.2节头部表 - 18 -
5.3.3.text节代码段 - 19 -
5.3.4几个数据节 - 19 -
5.3.5符号表 - 20 -
5.4 HELLO的虚拟地址空间 - 20 -
5.5 链接的重定位过程分析 - 21 -
5.6 HELLO的执行流程 - 22 -
5.7 HELLO的动态链接分析 - 22 -
5.8 本章小结 - 23 -
第6章 HELLO进程管理 - 24 -
6.1 进程的概念与作用 - 24 -
6.2 简述壳SHELL-BASH的作用与处理流程 - 24 -
6.3 HELLO的FORK进程创建过程 - 24 -
6.4 HELLO的EXECVE过程 - 24 -
6.5 HELLO的进程执行 - 24 -
6.6 HELLO的异常与信号处理 - 25 -
6.7本章小结 - 27 -
第7章 HELLO的存储管理 - 28 -
7.1 HELLO的存储器地址空间 - 28 -
7.2 INTEL逻辑地址到线性地址的变换-段式管理 - 28 -
7.3 HELLO的线性地址到物理地址的变换-页式管理 - 28 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 29 -
7.5 三级CACHE支持下的物理内存访问 - 29 -
7.6 HELLO进程FORK时的内存映射 - 29 -
7.7 HELLO进程EXECVE时的内存映射 - 29 -
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的实现分析 - 33 -
8.5本章小结 - 33 -
结论 - 33 -
附件 - 34 -
参考文献 - 35 -

第1章 概述
1.1 Hello简介
P2P过程:C语言文本文件hello.c首先经过语言预处理器展开头文件、进行宏替换、去掉注释、条件编译生成hello.i文件,然后由编译器进行编译,生成hello.s文件,里面存放着hello.c的汇编代码,然后由汇编器转化为机器代码,生成hello二进制可重定位目标文件,最后由链接器链接生成hello.o可执行文件。用户在终端输入./hello命令,shell会创建一个新的进程,通过exectve系统函数执行hello文件。
020过程:程序开始执行,首先系统分配一段虚拟内存,再将hello的代码加载进对应的物理内存中,在这之前先删除内存中原有的用户区以及其他相关数据。执行目标程序,进程结束后,回收虚拟内存空间,再删除掉相关的数据。在内存中从0又回到0。
1.2 环境与工具
软件环境:Windows 10 64位,VMware Pro,Ubuntu 18.04 64位
硬件环境:X64 CPU;1.99GHz ; 8G RAM;
1.3 中间结果
中间结果如表1-1所示:
表1-1 中间结果
hello.c 存放C语言源代码
hello.i 语言与处理器预处理后生成的文件
hello.s 存放汇编代码
hello.o 可重定位目标文件,链接用
hello 链接后的可执行文件,可以直接执行
1.txt 汇编阶段hello.o的反汇编代码
2.txt hello的反汇编代码
1.4 本章小结
概述了P2P和020的过程,展示了实验环境,列出了所有中间文件
(第1章0.5分)

第2章 预处理
2.1 预处理的概念与作用
概念:
编译预处理是C语言编译程序的一个步骤,用于解释处理C语言源程序中的各种预处理指令,如#include、#define、#ifdef、注释等
作用:

  1. 展开头文件
    形如#include<stdio.h>的指令,把头文件中的代码合并入hello.i。
  2. 宏替换
    定义宏常量、宏表达式,撤销已定义的宏等。
  3. 条件编译
    条件编译宏,如#ifdef、#if语句等,使一部分代码只在满足一定条件时编译。
  4. 去掉注释
    在.i文件中删除所有注释,添加行号信息文件名信息,便于调试。
    2.2在Ubuntu下预处理的命令
    在Ubuntu下的gcc预处理指令如图2-1所示:

图2-1 预处理指令
生成的hello.i文件如图2-2所示:

图2-2 部分hello.i文件
2.3 Hello的预处理结果解析
可以看到,预处理过后,头文件全部被合并入hello.i文件中,从短短的23行扩充到了3060行,被合并的头文件有#include <stdio.h>、#include <unistd.h>、#include <stdlib.h>。
同时所有注释也都去掉了。
2.4 本章小结
本章实现了hello.c的预处理阶段,生成了hello.i文件,直观地看到了预处理前后的变化,总结了预处理的概念和作用。
(第2章0.5分)

第3章 编译
3.1 编译的概念与作用
概念:
检查语法,将预处理后的C代码转化为更低级的汇编代码
作用:

  1. 分析词法、检查语法
    处理单词符号,检查符号串是否合法,检查语法的逻辑结构是否正确。
  2. 生成汇编语言文件
    生成hello.s,存有汇编代码。
  3. 代码优化
    运行时间更短、占用空间更少。
    3.2 在Ubuntu下编译的命令
    Ubuntu下编译命令如图3-1所示:

图3-1 编译命令
生成的hello.s文件如图3-2所示:

图3-2 部分hello.s文件
3.3 Hello的编译结果解析
3.3.1数据
常量:字符串常量保存在.rodata节中,如图3-3所示:

图3-3 字符串常量在.rodata中保存
变量(局部):
i保存在-4(%rbp)中,用movl $0, -4(%rbp)指令赋初值为0。
argc从寄存器edi读出保存在-20(%rbp)中。
argv从寄存器rsi中保存到-32(%rbp)中。
赋值:
argc,argv,i的赋值如图3-3所示:

图3-4 argc,argv,i的赋值
除此之外,还有利用lea指令进行赋值,如图3-4所示:

图3-5 用lea指令赋值
3.3.2算数操作
自加运算:for循环里每次i++,如图3-6所示:

图3-6 自加运算i++的汇编代码
3.3.3关系操作
argc与4的比较:如果不等就执行if中的代码,相等就跳过,汇编后变成如果相等就跳到.L2继续执行,如图3-7所示:

图3-7 argc与4比较的汇编代码
i与8的比较:如果小于就继续循环,否则结束循环,汇编后变成i与7的比较,如果小于等于就循环否则退出,如图3-8所示:

图3-8 i与8比较的汇编代码
3.3.4数组操作
对数组argv[]的访问:指针**argv存放在-32(%rbp)中,用argv+8,argv+16分别表示argv[1]和argv[2],如图3-9所示:

图3-9 对数组argv[]的访问
3.3.5控制转移
if语句:如果argc和4不相等就执行if中的代码,相等就跳过,汇编后变成如果相等就跳到.L2继续执行,如图3-10所示:

图3-10 if语句的汇编代码
for循环语句:在.L2中i初始化为0,然后跳转到.L3判断i与8的大小,如果小于就继续循环,否则结束循环,汇编后变成i与7的比较,如果小于等于就跳转到.L4中循环否则退出,每次循环自加1,,如图3-11所示:

图3-11 for循环的汇编代码
3.3.6函数操作
main函数:从终端传递参数argc,argv[],argc从寄存器edi读出保存在-20(%rbp)中,argv从寄存器rsi中保存到-32(%rbp)中,如图3-4所示,函数结束后通过ret指令返回,如图3-12所示。

图3-12 main函数的返回
printf函数:
1) 使用call puts函数输出字符串,参数为rdi,如图3-13所示:

图3-14 call puts的汇编代码
2) 使用call printf指令,第一个参数rdi,第二个参数rsi,第三个参数rdx,如图3-14所示:

图3-14 call printf的汇编代码
atoi函数:使用call atoi指令,rdi为参数,如图3-15所示:

图3-14 call atoi的汇编代码
sleep函数:使用call sleep指令,rdi为参数,如图3-16所示:

图3-14 call sleep的汇编代码
exit函数:参数传递,将1放在%edi中作为参数传递;函数调用,使用call指令进行调用。
3.4 本章小结
本章完成了编译步骤,查看了hello.i编译成汇编语言之后的代码,深度详细解析了各个数据、操作的汇编实现,分析了编译器对数据的存储与处理方式,体现出了编译器的基本逻辑。
(第3章2分)

第4章 汇编
4.1 汇编的概念与作用
概念:汇编器把汇编代码转换机器码,生成可重定位目标文件hello.o
作用:把汇编代码转化为机器代码、合并节与符号表,生成可重定位hello.o文件
4.2 在Ubuntu下汇编的命令
Ubuntu下汇编命令如图4-1所示:

图4-1 汇编指令
4.3 可重定位目标elf格式
4.3.1 ELF头
ELF头以一个16字节序列开始,描述生成该文件的系统的字的大小和字节顺序,然后是ELF头大小,目标文件类型,机器类型,节头部表的偏移以及节头部表中条目的大小和数量等信息,帮助链接器进行链接成可执行文件。
ELF头如图4-2所示:

图4-2 ELF头
4.3.2节头部表
节头部表中展示了节名称、类型、地址、偏移量、大小、对齐等信息。
节头部表如图4-3所示:

图4-3 节头部表
4.3.3 .text节代码段
.text节中保存了机器代码

图4-4 .text节
4.3.4 .rela.text节重定位节
包含.text节中需要进行重定位的信息,链接器根据重定位节中的信息对可重定位的目标文件进行链接得到可执行文件,如图4-5所示。

图4-5 .rela.text节
4.3.5几个数据节
.data节存放了有初值的全局变量和静态变量,.bss存放未初始化的全局变量和静态变量,.rodata节中存放只读数据,如图4-6所示:

图4-6 几个数据节
4.3.6 符号表
符号表.symtab中存有函数和全局变量的信息,如图4-7所示。

图4-7 符号表
4.4 Hello.o的结果解析
objdump -d -r hello.o > 1.txt 分析hello.o的反汇编,得到1.txt文件,部分内容如图4-8所示。

图4-8 1.txt文件中的反汇编代码
经过与hello.s文件的对比,不难发现,在hello.s中,跳转地址以.L1这样的符号表示,但是在1.txt中,跳转地址表示成函数首地址+段内偏移量的形式。1.txt中显示了每一条指令的地址偏移量,而且显示了机器码,hello.s中都是没有的,1.txt中还显示了重定位信息。由于1.txt中只对.text节进行了反汇编,文件中没有其他节的信息。
机器语言由操作码和操作数组成,与汇编语言是一一对应的,操作码就是代表要进行何种操作,操作数就是该操作的对象。机器语言的跳转、函数调用是使用与当前位置(%rip)的偏移量表示目标地址的,而在汇编代码中使用的是函数首地址+偏移量的形式来表示的。反汇编代码还会为跳转语句都加上目标地址方便阅读。
4.5 本章小结
本章完成了汇编步骤,查看了ELF文件中的信息以及各节中的信息,对比了hello.o反汇编的代码与hello.s的区别,同时也研究了机器码与汇编代码的对应关系以及跳转不同表示方法。
(第4章1分)

第5章 链接
5.1 链接的概念与作用
概念:
将源文件中用到的库函数与汇编生成的目标文件.o合并生成可执行文件
作用:
合并符号表,进行符号解析、完成地址的重定位、生成可执行文件
5.2 在Ubuntu下链接的命令
链接的命令如图5-1所示。

图5-1 Ubuntu下的链接命令
5.3 可执行目标文件hello的格式
5.3.1ELF头
hello的ELF头如图5-2所示。

图5-2 hello的ELF头
5.3.2节头部表
hello的节头部表如图5-3所示。

图5-3 hello的节头部表
5.3.3.text节代码段
hello的代码段如图5-4所示,存有机器码。

图5-4 hello的代码段
5.3.4几个数据节
hello的只读数据节如图5-5所示。

图5-5 hello的只读数据节

hello的.data节和.bss节都为空。
5.3.5符号表
hello的符号表如图5-7所示。

图5-7 hello的符号表
5.4 hello的虚拟地址空间
使用edb加载hello,5.3中的各节内容在虚拟地址空间区域都可以看到,比如.text节从0x4010f0开始到0x401234,如图5-8所示。
再比如.rodata节从0x402000开始到0x40203a结束,如图5-9所示。
用户栈中也有内容,如图5-10所示。

图5-8 edb中显示的.text节的虚拟空间

图5-9 edb中显示的.rodata节的虚拟空间

图5-10 edb中显示的用户栈
5.5 链接的重定位过程分析
objdump -d -r hello 反汇编hello,如图5-11所示。

图5-11 部分hello的汇编代码
与hello.o的反汇编代码进行对比,发现hello的反汇编代码中,程序不从0开始,而是一个虚拟地址,所有的代码和数据都有了自己的具体的虚拟地址而不是以偏移的形式存在了。hello.o中跳转以及函数调用的地址在hello中也都被更换成了虚拟内存地址。又增加了一些节,如.init,.plt等,调用的外部函数、库函数都在反汇编代码中有所体现,像puts,printf等已被链接,引用时直接寻址引用而不再间接引用。这说明,链接时,外部的库中的函数直接被链接在hello文件中,使用时只在当前程序的内存区进行访问执行就可以了。
链接过程:链接器为可重定位文件分配虚拟空间地址,根据重定位节里的信息进行重定位,然后根据符号表,把每个符号的内存位置联系起来,然后修改对这个符号的引用,生成可执行程序。
重定位:链接器会将相同类型的节合并成为一个节,给代码和符号定义新的虚拟地址,然后链接器根据hello.o中的可重定位节,修改代码节和数据节中相应位置的内容完成重定位。
5.6 hello的执行流程
使用edb执行hello,从加载hello到_start,到call main,以及程序终止的过程中调用与跳转的各个子程序名或程序地址如下。
_dl_start_user
_dl_init
_start
_libc_start_main
_cxa_atexit
_libc_csu_init
_init
_setjmp
sigsetjmp
_sigjmp_save

main

puts@plt
exit@plt
_dl_runtime_resolve_xsave
_dl_fixup
_dl_lookup_symbol_x
exit
5.7 Hello的动态链接分析
在dl_init前,重定位条目如图5-12所示。

图5-12 重定位项目
在dl_init后,这些条目拥有了自己的具体的虚拟地址,在虚拟内存空间中占有了一席之地,比如,.rodata节中的字符串的虚拟空间如图5-9所示。调用的外部库中的函数如puts,exit,printf等如图5-13所示。

图5-13 库函数重定位后的虚拟空间地址
5.8 本章小结
本章完成了链接步骤,分析了hello的ELF文件中各节的信息以及与hello.o的区别,观察了链接过程中重定位所产生的虚拟地址空间,研究了重定位过程,分析了动态链接的过程。使用静态链接,每次更新代码都需要重新编译链接,但是可以有效防止其他人的恶意攻击,动态链接节省了很多内存空间,而且更新代码无需重新链接,便于模块化设计。
(第5章1分)

第6章 hello进程管理
6.1 进程的概念与作用
概念:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
作用:进程使得程序好像独占了当前的CPU和内存,处理器似乎全心全意地为该程序服务。通过进程,计算机操作系统能够更好地管理不同程序的运行,方便进程间上下文的切换,计算机操作系统的运行更加高效。
6.2 简述壳Shell-bash的作用与处理流程
作用:为用户提供了一个对系统内核进行访问的窗口,用户使用shell就可以访问操作系统内核了。
处理流程:
1) 按行读入终端的命令行
2) 以特殊字符如空格分割命令行
3) 检查第一个单词,判断是否是内核内置指令,如果是就执行,如果不是就创建进程并运行指令
4) 实时响应IO设备的信号
6.3 Hello的fork进程创建过程
shell执行终端输入的命令./hello,然后内核调用fork函数创建新的子进程,得到父进程的一份拷贝,虚拟地址空间相同但是互相独立。
6.4 Hello的execve过程
子进程创建好之后,会调用execve函数,将原来的程序全部覆盖掉,释放掉原来的程序的空间,再把hello程序加载到内存空间中,启动代码设置栈,传递参数argv[]和环境变量envp给main函数,控制权在main函数手中。
6.5 Hello的进程执行
上下文信息:执行进程所需要的信息,以便进行进程切换
进程时间片:CPU为进程分配时间片,每个进程有属于自己的时间片,在时间片内运行,时间片用完就转移控制权进行上下文切换执行其他进程去了。
进程调度及用户态与核心态转换:执行hello程序时,进程处在用户态,如果运行中发现main函数的参数不是3,就执行exit函数强制结束进程,控制从hello的进程转移到内核,hello进程就被回收了。如果输入的参数是3个,就继续执行程序,循环执行printf和sleep函数,执行sleep函数时进程会暂时进入休眠状态,控制会转交给内核,内核保存上下文信息,保存所有数据如寄存器、用户栈、条件码等,删除hello进程,然后转而加载执行其他进程,在休眠结束后,sleep函数就发送一个信号通知内核,执行中断信号处理程序,控制权回到内核手中,结束当前进程并保存上下文信息,同时根据hello进程的上下文信息,恢复hello进程,再把控制传递给hello进程,继续执行hello程序。
6.6 hello的异常与信号处理
异常有四种:中断、陷阱、故障、终止。
中断:异步,IO设备的输入会使内核执行中断处理程序处理外部中断信号。中断处理程序是硬件中断的异常处理程序。
陷阱:执行到某一条指令后跳转执行相应程序,结束后继续执行下一条指令。
故障:出现错误情况,有可能被修正。如果成功,就返回到当前指令继续执行,否则终止程序。
终止:硬件发生错误,程序会被强制终止,不可逆。
信号:进程信号通过kill函数发送给进程。
信号:
SIGINT:Ctrl-C产生这个信号,中断当前进程。
SIGSTOP:Ctrl-Z产生这个信号,挂起进程。
SIGCONT:fg命令会向进程发送该信号,切换到该进程继续执行(如果停止了)。
SIGTRAP:sleep函数会触发这个信号,调用陷阱异常处理程序,并跟踪陷阱。
SIGCHLD:exit函数会发送这个信号,子进程结束,通知父进程。
随便乱按如图6-1所示。

图6-1 进程运行中随便乱按
运行ps,jobs,pstree,fg,kill 等命令如图6-2、图6-3、图6-4所示。

图6-2 运行ps,jobs结果图

图6-3 运行pstree部分结果图

图6-4 运行fg,kill结果图
6.7本章小结
本章完成了hello的进程管理,描述了shell的工作原理和作用,分析了操作系统内核为hello创建进程、执行程序、处理外部异常、信号的过程,深入研究了hello程序执行过程中的用户态与内核态切换、控制权转移。
(第6章1分)

第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:机器语言中程序生成的和段相关的偏移地址,包含一个标识符和一个地址偏移量,作为程序的中间地址供CPU执行。hello.o反汇编代码文件中显示的各项地址就是逻辑地址。
线性地址:是由逻辑地址转换为物理地址的中间结果。包括逻辑地址和对应段的基址。
虚拟地址:为了便于操作而虚拟出来的一种地址,与虚拟页一一对应,对应磁盘空间的页,程序运行时将磁盘中对应页的内容加载到物理地址,虚拟地址又对应着物理地址。
物理地址:真实存在的地址,表示CPU外部地址总线上的寻址物理内存的地址,用于内存级芯片的单元寻址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
获得一个逻辑地址后,首先获取前16位段标识符,检查T1字节是否为1,再从gdtr或ldtr中得到GDT和LDT的地址和大小,然后得到段标识符的前13位,加上GDT或LDT的地址得到逻辑地对应段的段描述符的地址,然后根据段描述符的地址取得段描述符,并获得Bass信息,得到段的基地址,将基地址加上段内偏移量得到线性地址
7.3 Hello的线性地址到物理地址的变换-页式管理
首先把线性地址的最高10位(22至31位)作为页目录表的索引,对应表项所包含的页码指定页表。
然后,再把线性地址的中间10位(12至21位)作为所指定的页目录表中的页表项的索引,对应表项所包含的页码指定物理地址空间中的一页。
最后,把所指定的物理页的页码作为高20位,把线性地址的低12位作为32位物理地址的低12位。
最近使用的线性—物理地址转换函数存储在处理器内部的页转换高速缓存中。在访问存储器页表之前总是先查阅高速缓存,仅当必须的转换不在高速缓存中时,才访问存储器中的两级页表。
页目录表项中的存在位P表明对应页表是否有效。如果P=1,表明对应页表有效,可利用它进行地址转换;如果P=0,表明对应页表无效。如果试图通过无效的页表进行线性地址到物理地址的转换,那么将引起页故障。因此,页目录表项中的属性位P使得操作系统只需给覆盖实际使用的线性地址范围的页表分配物理页。页故障处理程序再申请物理页,从磁盘上把对应的页表读入,并把对应页目录表项中的P位置1
7.4 TLB与四级页表支持下的VA到PA的变换
TLB是MMU中对应PTE的缓存,组织形式为组相联。PTE虚拟地址被划分为:a1位、a2位、a3位、a4位。MMU先是在TLB中找到PTE,把虚拟地址的后p+t-1位值当作组号,找到TLB组,把VPN剩下的位作为标记,与该组中的标记进行对比,如果pte的标记相同且有效位为1,就获得pte中的ppn,否则就把vpn传给cache和内存。一级页表的基地址保存在寄存器中,把a1位值作为索引,得到对应页表中的值,作为二级页表的基址,再把a2位值作为索引,找到三级页表的基址,直到获得四级页表的pte值,得到vpn所对应的ppn的值,再由ppn+vpo就可以得到va对应的pa。
7.5 三级Cache支持下的物理内存访问
首先访问一级cache,如果不命中就再访问二级cache,如果还是不命中就继续访问三级cache,如果还是不命中就访问主存,如果主存缺页就访问硬盘。如果找到就返回结果。
访问每一级cache时先找组索引,与标志位进行对比,如果标记为有效的话就去下一级cache中取出内容,然后返回。
7.6 hello进程fork时的内存映射
内核为每个进程都创建独立的虚拟地址空间。当前进程调用fork函数时,内核给新进程一个PID并加载数据。新进程的虚拟内存和调用fork时存在的虚拟空间内存相同。每个进程中的每个区域结构都被标记为写时复制,如果其中一个进程要更改其私有部分的内容,就会触发故障处理程序,将要写的页复制一份,然后进行更改。对于共享部分,不同进程的不同虚拟页会映射到同一个内存物理页。
7.7 hello进程execve时的内存映射
1)删除已存在的用户区域。删除虚拟地址用户部分中已存在的其他进程的各种区域结构。
2)映射私有区域。创建新的区域结构,存放代码、数据、bss和栈。标记为写时复制。
3)映射共享区域。把hello动态连接的共享对象映射到虚拟地址的用户空间中的共享区域内。
4)设置程序计数器。设置当前进程上下文中的程序计数器,指向代码区域的入口。
7.8 缺页故障与缺页中断处理
访问的虚拟页不在内存中时,会引发缺页故障,此时cpu的中的当前虚拟地址所在的虚拟页的PTE有效位为0。
缺页中断处理就是在遇到缺页故障时,把内存中的一个页替换为新的页,并更新页表。如果所选择的页有所修改,就要先把原来的页写入磁盘再替换。
7.9动态存储分配管理
动态内存分配器维护着一个进程的虚拟内存区域,称为堆。在分配器中,堆以一系列不同大小的存储空间碎片的形式被管理,看做一个个内存块。每个块有两种状态,已分配和空闲,是一个连续的虚拟内存片。已分配的块供进程使用,未分配的空闲块可用来新分配给某些数据结构。已分配的块使用之后被显式或隐式地释放,成为空闲块以供下一次分配。
分配器有两种基本风格:显式分配器和隐式分配器。分配块时都是显式的。它们的不同之处在于如何释放已分配的块。
1)显式分配器:显式地释放已分配的块。例如,C程序提供一种叫做malloc程序包的显式分配器。C程序通过调用malloc函数来分配一个块,并通过调用free函数来释放一个块。C++的new和delete操作符与C中的malloc和free相当。malloc,可以通过使用mmap和munmap函数,显式地分配和释放堆内存,或者使用sbrk函数。sbrk函数通过将内核的brk指针增加incr来扩展和收缩堆。
2)隐式分配器:分配器自动维护堆,检测一个已分配块何时不再被程序所使用,去释放这个块。隐式分配器也叫做垃圾收集器,而自动释放未使用的已分配的块的过程叫做垃圾收集。例如,例如Lisp、ML以及Java之类的高级语言就依赖垃圾收集来释放已分配的块。
两种组织空闲空间链表的方式:隐式空间链表和显示空间链表。
1)隐式空间链表:隐式空闲链表分配器,一个块是由一个字的头部、有效载荷块的结尾处的一个字的尾部及其他内容组成的。头部标记了这个块的大小以及状态(已分配还是空闲)用最低位(已分配位)来指明这个块是已分配的还是空闲的。头部后面是动态分配的有效载荷。有效载荷后面是一片不使用的填充块,其大小可以是任意的。填充可能是分配器策略的一部分,用来对付外部碎片。或者也需要用它来满足对齐要求。堆被组织为一个连续的已分配块和空闲块的序列,隐式链表将所有块(无论是已分配的还是空闲的)串起来,整个链表中每一块的状态(分配或者空闲)以及链接信息隐含在每一块的头部,在分配时需要依次遍历每一个块,去寻找合适的空闲块。
2)显示空间链表:将空闲块串联起来形成一个双向链表,在每个空闲块的主体中,都包含一个pred和succ指针。使用双向链表,使首次适配的分配时间从块总数的线性时间减少到了空闲块数量的线性时间。释放一个块的时间可以是线性的,也可能是个常数,这取决于空闲链表中块的排序策略。有两种排序方法,一种方法是用后进先出的顺序维护链表,将新释放的空闲块插入在链表的开始处。另一种方法是按地址大小顺序排列,把新释放的空闲块按照地址大小顺序插入链表。
分配策略:
首次适配:每次都从堆的开始寻找空闲块,直到找到符合要求的空闲块。
下次适配:每次都从上次已分配的地方开始寻找空闲块,直到找到符合要求的空闲块。
最佳适配:遍历所有空闲块,直到找到最合适的空闲块。
如果找不到合适的块,就使用extend函数,把堆扩大。
7.10本章小结
本章探究了hello的存储管理体系,包括存储器地址、从逻辑地址到物理地址的变换过程、段式管理和页式管理方法,研究了多级页表以及多级cache在寻址加载内存方面的具体作用和工作方式,还探究了进程的处理,fork和execve的内存映射处理过程以及缺页时的处理。
(第7章 2分)

第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:文件
IO设备被模型化为文件,所有的输入和输出操作都被视为读和写操作,引出一个应用接口,Unix I/O。
文件的类型包括:普通文件(包含任意数据的文件)、目录(包含一组将文件名映射到文件的链接)、套接字(与另一个进程进行跨网络通信)、命名通道、符号链接、字符和块设备
设备管理:unix io接口
8.2 简述Unix IO接口及其函数
1)打开文件:某个应用给内核发送打开文件的请求来请求访问设备,内核随后返回一个描述符用来标识这个文件,系统内核中记录着这个文件所有的信息。如果文件不存在就创建一个新的文件。
接口函数:int open(char* filename,int flags,mode_t mode);
2)读写文件:
读操作就是把文件中若干字节的内容复制到内存。接口函数:ssize_t read(int fd,void *buf,size_t n);
写操作就是把内存中若干字节的内容复制到文件。接口函数:ssize_t wirte(int fd,const void *buf,size_t n);
超出文件大小时就会返回EOF。
3)关闭文件:应用访问文件结束后就通知内核关闭文件,内核清空访问文件所需要的数据结构,回收文件描述符。接口函数:int close(fd);
4)改变文件位置:接口函数为lseek()函数
8.3 printf的实现分析
vsprintf(buf, fmt, arg)接受确定输出格式的格式字符串fmt,用格式字符串对个数变化的参数进行格式化,产生格式化输出。它最终会返回一个长度,即要打印出来的字符串的长度。vsprintf生成显示信息,到write系统函数,陷阱系统调用int 0x80或syscall。
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
从键盘读入字符时,运行键盘中断处理程序。getchar等函数调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。然后从stdio流中每次读入一个字符,其他字符暂存在键盘输入缓冲区中返回字符的ASCII码。
8.5本章小结
本章探究了Linux下的IO管理策略以及Linux系统下各种文件操作、控制台操作的原理和方法。
(第8章1分)
结论
预处理:hello.c到hello.i,展开头文件、宏替换、条件编译、条件编译、去注释
编译:hello.i到hello.s,将高级C语言转换为汇编语言
汇编:hello.s到hello.o,将汇编语言转换为机器码
链接:hello.o到hello,链入库、重定位,生成可执行文件
fork:创建进程,分配虚拟空间
execve:加载进程数据至物理内存,运行程序
实时响应I/O信号
进程结束,由父进程杀死僵死子进程,内存空间被释放
计算机系统是一个庞大的系统,为了实现一个很简单的过程也需要牵扯到非常多的部件,这些部件密切配合,共同完成任务。如果想要写出高效的程序必须仔细考虑各部件间的工作关系,考虑非常多的细节。
(结论0分,缺失 -1分,根据内容酌情加分)

附件
hello.c,存放C语言源代码
hello.i,语言与处理器预处理后生成的文件
hello.s,存放汇编代码
hello.o,可重定位目标文件,链接用
hello,链接后的可执行文件,可以直接执行
1.txt,汇编阶段hello.o的反汇编代码
2.txt,hello的反汇编代码
(附件0分,缺失 -1分)

参考文献
为完成本次大作业你翻阅的书籍与网站等
[1]深入理解计算机系统
[2]百度百科

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值