CSAPP大作业

摘 要
摘要是论文内容的高度概括,应具有独立性和自含性,即不阅读论文的全文,就能获得必要的信息。摘要应包括本论文的目的、主要内容、方法、成果及其理论与实际意义。摘要中不宜使用公式、结构式、图表和非公知公用的符号与术语,不标注引用文献编号,同时避免将摘要写成目录式的内容介绍。

关键词:预处理;编译;汇编;链接;进程管理;储存管理;IO管理;虚拟机;EDB的使用;反汇编代码的查看:elf文件的分析

本论文的主要目的,是了解分析hello从编写代码开始到被回收结束的过程。主要包括hello的介绍、预处理、编译、汇编、链接、进程管理、储存管理、IO管理这八个章节,每个章节中穿插了EDB、反汇编、elf文件等等内容。主要采用EDB、objdump等方法来生成所需要的文件并进行分析。本论文详细描述本文的目的,并且最终理顺了hello从头到尾的整个过程,研究内容对理解底层程序与操作系统的实现原理具有一定指导作用,对初学计算机系统的人具有一定的帮助性。(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目 录
第1章 概述 - 4 -
1.1 Hello简介 - 4 -
1.2 环境与工具 - 4 -
1.3 中间结果 - 5 -
1.4 本章小结 - 5 -
第2章 预处理 - 5 -
2.1 预处理的概念与作用 - 6 -
2.2在Ubuntu下预处理的命令 - 6 -
2.3 Hello的预处理结果解析 - 6 -
2.4 本章小结 - 7 -
第3章 编译 -8 -
3.1 编译的概念与作用 - 8 -
3.2 在Ubuntu下编译的命令 - 8 -
3.3 Hello的编译结果解析 - 9 -
3.4 本章小结 - 18 -
第4章 汇编 - 19 -
4.1 汇编的概念与作用 - 19 -
4.2 在Ubuntu下汇编的命令 - 19 -
4.3 可重定位目标elf格式 - 19 -
4.4 Hello.o的结果解析 - 22 -
4.5 本章小结 - 23 -
第5章 链接 - 24 -
5.1 链接的概念与作用 - 24 -
5.2 在Ubuntu下链接的命令 - 24 -
5.3 可执行目标文件hello的格式 - 24 -
5.4 hello的虚拟地址空间 - 30 -
5.5 链接的重定位过程分析 - 31 -
5.6 hello的执行流程 - 34 -
5.7 Hello的动态链接分析 - 35 -
5.8 本章小结 - 35 -
第6章 hello进程管理 - 36 -
6.1 进程的概念与作用 - 36 -
6.2 简述壳Shell-bash的作用与处理流程 - 36 -
6.3 Hello的fork进程创建过程 - 36 -
6.4 Hello的execve过程 - 37 -
6.5 Hello的进程执行 - 37 -
6.6 hello的异常与信号处理 - 38 -
6.7本章小结 - 42 -
第7章 hello的存储管理 - 43 -
7.1 hello的存储器地址空间 - 43 -
7.2 Intel逻辑地址到线性地址的变换-段式管理 - 43 -
7.3 Hello的线性地址到物理地址的变换-页式管理 - 44 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 45 -
7.5 三级Cache支持下的物理内存访问 - 46 -
7.6 hello进程fork时的内存映射 - 47 -
7.7 hello进程execve时的内存映射 - 47 -
7.8 缺页故障与缺页中断处理 - 48 -
7.9动态存储分配管理 - 49 -
7.10本章小结 - 51 -
第8章 hello的IO管理 - 52 -
8.1 Linux的IO设备管理方法 - 52 -
8.2 简述Unix IO接口及其函数 - 52 -
8.3 printf的实现分析 - 53 -
8.4 getchar的实现分析 - 56 -
8.5本章小结 - 56 -
结论 - 57 -
附件 - 58 -
参考文献 - 59 -
第1章 概述
1.1 Hello简介
1.11Hello的P2P过程
代码编写者编写代码保存至hello.c(Program),GCC编译器驱动程序读取源程序文件hello.c,并通过预处理、编译、汇编、链接(如图1所示)把它翻译为可执行目标文件hello,然后在壳(Bash)里,OS(进程管理)使用fork创建一个新进程(Process),使用mmap将其映射到相应的虚拟内存,再利用TLB、4级页表、3级Cache,Pagefile等将虚拟地址转换为物理地址,随后使用execve加载可执行文件hello,分时操作系统分配给hello微观上的一段CPU时间(时间片),接着取指译码执行hello中的每一条指令,hello作为一个进程以流水线多进程的方式在计算机中运行,这就是Hello的From Program to Process过程。 CSAPP大作业 1180301002
图1 hello.c翻译为hello的过程(书本P3)
1.12Hello的020过程
hello.c是由代码编写者编写的,一开始没有,这是0。由1.11的描述可知,hello.c翻译为hello在操作系统中以进程的形式运行着,运行结束以后,终止的进程将会被回收,同时内核会从系统中删除掉它的所有痕迹,这是0。从开始0,经过Editor、Cpp、Compiler、AS、LD、OS、CPU/RAM/IO,再到结束0,这就是Hello的一生,From Zero-0 to Zero-0过程。
1.2 环境与工具
1.21软件环境Windows10 64位;Vmware 15;Ubuntu 16.04 LTS 64位
1.2.2硬件环境X64 CPU;2GHz;2G RAM;256GHD Disk
1.2.3开发与调试工具GDB, GCC, CodeBlocks 16.01,Dev-C++ 5.111.3 中间结果①hello.i:hello.c的预处理后的结果②hello.s:hello.i的编译后的结果③hello.o:hello.s的汇编后的结果④hello.elf:hello.o的elf格式⑤hello:hello.o的链接后的结果⑥ldhello.elf:hello的elf格式⑦objdumphello:hello.o的反汇编结果⑧objdumpldhello:hello的反汇编结果
1.4 本章小结
本章介绍了Hello的P2P过程、020过程,详细介绍了完成本论文的所用的软件环境、硬件环境、开发与调试工具,罗列出了完成本论文的中间结果(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
2.1.1预处理的概念预处理器(cpp)将源程序文件hello.c翻译为文本文件hello.i
2.1.2预处理的作用预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。比如hello.c的第六行第七行第八行#include <stdio.h>、#include <unistd.h> 、#include <stdlib.h>命令分别告诉预处理器读取系统头文件stdio.h、unistd.h、stdlib.h的内容,并把它直接插入程序文本中。同时忽略程序的注释。结果就得到了另一个C程序,通常以.i作为文件的扩展名
2.2在Ubuntu下预处理的命令在hello.c的目录下,右键打开终端,输入gcc -E hello.c -o hello.i,如图2所示
CSAPP大作业 1180301002
图2 在Ubuntu下预处理的命令
在hello.c的目录下,出现了hello.i,如图3所示
CSAPP大作业 1180301002
图3 生成的hello.i文件
2.3 Hello的预处理结果解析以文本形式打开2.2所示的hello.i文件如下(截取部分):
CSAPP大作业 1180301002
图4 hello.i中部分字段可以看到:系统头文件stdio.h、unistd.h、stdlib.h的内容已经插入到hello.i中
2.4 本章小结
本章解释了预处理的概念与作用,给出了Ubuntu下预处理的命令,解析了hello的预处理结果(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
3.1.1编译的概念
编译器(ccl)将文本文件hello.i翻译为文本文件hello.s
3.1.2编译的作用
为不同高级语言的不同编译器提供了通用的输出语言。一如C编译器和Fortran编译器产生的输出文件用的都是一样的汇编语言
3.2 在Ubuntu下编译的命令
在hello.i的目录下,右键打开终端,输入gcc -S hello.i -o hello.s,如图5所示
CSAPP大作业 1180301002
图5 在Ubuntu下编译的命令
在hello.i的目录下,出现了hello.s,如图6所示
CSAPP大作业 1180301002
图6 生成的hello.s文件
3.3 Hello的编译结果解析
3.3.1 hello.s的内容以文本形式打开2.2所示的hello.s文件如下:
CSAPP大作业 1180301002
图7 hello.s的内容
3.3.2 hello.c的内容用Dev-C++ 5.11打开hello.c文件如下:
CSAPP大作业 1180301002
图8 hello.c的内容
3.3.3数据
3.3.3.1常量
从3.3.2hello.c的内容可以看出常量是(如图9中黑框所示)CSAPP大作业 1180301002
图9 常量在hello.c中的位置
在3.3.1hello.s的内容中常量对应的是(如图10)
CSAPP大作业 1180301002
图10 字符串常量在hello.s中的位置(字符串常量存放在.rodata中,中文已被转码处理)
3.3.3.2变量
从3.3.2hello.c的内容可以看出变量是(如图11中黑框所示)
CSAPP大作业 1180301002
图11 变量在hello.c中的位置在3.3.1hello.s的内容中变量分别对应的是
CSAPP大作业 1180301002
图12 sleepsecs在hello.s中的位置(为全局变量,位于.data段,4字节对齐,大小为4字节)
CSAPP大作业 1180301002
图13 argc在hello.s中的位置(argc作为main的第一个参数,保存在%edi中)
CSAPP大作业 1180301002
图14 i在hello.s中的位置(为局部变量,i的保存在-4(%rbp)中)
3.3.4赋值
从3.3.2hello.c的内容可以看出赋值是(如图15中黑框所示)
CSAPP大作业 1180301002
图15 赋值在hello.c中的位置
在3.3.1hello.s的内容中赋值对应的是
CSAPP大作业 1180301002
图16 赋值sleepsecs=2.5在hello.s中的位置(由于sleepsecs为整型,2.5会变为2)
CSAPP大作业 1180301002
图17 赋值i=0在hello.s中的位置(结合3.3.3.2可知i的保存在-4(%rbp)中)
3.3.5 类型转换
从3.3.2hello.c的内容可以看出隐式类型转换是(如图18中黑框所示)
CSAPP大作业 1180301002
图18 类型转换在hello.c中的位置
在3.3.1hello.s的内容中隐式类型转换对应的是
CSAPP大作业 1180301002
图19 隐式类型转换在hello.s中的位置(由于sleepsecs为整型,2.5会变为2) 3.3.6 算术操作
从3.3.2hello.c的内容可以看出算术操作是(如图20中黑框所示)
CSAPP大作业 1180301002
图20 算术操作在hello.c中的位置
在3.3.1hello.s的内容中算术操作对应的是
CSAPP大作业 1180301002
图21 算术操作在hello.s中的位置(结合3.3.3.2可知i的保存在-4(%rbp)中)
3.3.7 关系操作
从3.3.2hello.c的内容可以看出关系操作是(如图22中黑框所示)
CSAPP大作业 1180301002
图22 关系操作在hello.c中的位置
在3.3.1hello.s的内容中关系操作对应的是
CSAPP大作业 1180301002
图23 关系操作argc!=3在hello.s中的位置(结合3.3.3.2可知argc的保存在-20(%rbp)中)
CSAPP大作业 1180301002
图24 关系操作i<10在hello.s中的位置(结合3.3.3.2可知i的保存在-4(%rbp)中)
3.3.8数组
从3.3.2hello.c的内容可以看出数组是(如图25中黑框所示)
CSAPP大作业 1180301002
图25 数组在hello.c中的位置在3.3.1hello.s的内容中数组对应的是
CSAPP大作业 1180301002
图26 数组在hello.s中的位置(argc作为main的第二个参数,保存在%rsi中)
3.3.9控制转移
从3.3.2hello.c的内容可以看出控制转移是(如图27中黑框所示)
CSAPP大作业 1180301002
图27 控制转移在hello.c中的位置
在3.3.1hello.s的内容中控制转移对应的是
CSAPP大作业 1180301002
图28 控制转移对应if(argc!=3)在hello.s中的位置(比较立即数3与-20(%rbp),如果相等就跳至.L2)
CSAPP大作业 1180301002
图29 控制转移对应for(i=0;i<10;i++)在hello.s中的位置(比较立即数9与-20(%rbp),如果小于等于就跳至.L4)
3.3.10函数操作
从3.3.2hello.c的内容可以看出函数操作是(如图30中黑框所示)
CSAPP大作业 1180301002
图30 函数操作在hello.c中的位置在3.3.1hello.s的内容中控制转移对应的是
CSAPP大作业 1180301002
图31 函数调用main()在hello.s中的位置(参数传递:将%edi设置为argc,将%rsi设置为argv;函数返回:将%eax设置为0)
CSAPP大作业 1180301002
图32 函数调用exit()在hello.s中的位置(参数传递:将%edi设置为1)
CSAPP大作业 1180301002
图33 第一次函数调用printf()在hello.s中的位置(第一次调用printf()直接输出字符串,用的是puts;参数传递:将%edi设置为字符串的首地址)
CSAPP大作业 1180301002
图34 第二次函数调用printf()在hello.s中的位置(第二次调用printf()输出字符串和参数;参数传递:将%edi设置为字符串的首地址,%rsi设置为argv[1],%rdx设置为argv[2])
CSAPP大作业 1180301002
图35 函数操作sleep()在hello.s中的位置(参数传递:将%edi设置为sleepsecs)
CSAPP大作业 1180301002
图36 函数操作getchar()在hello.s中的位置
3.4 本章小结
本章解释了编译的概念与作用,给出了Ubuntu下编译的命令,从hello.s的内容、hello.c的内容、数据(常量、变量(全局/局部))、赋值、类型转换(隐式)、算术操作、关系操作、数组、控制转移、函数操作这10个方面解析了hello的编译结果(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
4.1.1汇编的概念
汇编器(as)将hello.s翻译为机器语言指令
4.1.2汇编的作用
汇编器(as)把机器语言指令打包成为一种叫做可重定位目标程序的格式,并将结果保存在hello.o中
4.2 在Ubuntu下汇编的命令
在hello.s的目录,右键打开终端,输入gcc -c hello.s -o hello.o,如图37所示
CSAPP大作业 1180301002
图37 在Ubuntu下汇编的命令
在hello.s的目录下,出现了hello.o,如图38所示
CSAPP大作业 1180301002
图38 生成的hello.o文件
4.3 可重定位目标elf格式
在hello.o的目录,右键打开终端,输入readelf -a hello.o > hello.elf,如图39所示
CSAPP大作业 1180301002
图39 在Ubuntu下生成ELF格式的命令
在hello.o的目录下,出现了hello.elf,如图40所示
CSAPP大作业 1180301002
图40 生成的hello.elf文件
以文本形式打开hello.elf,发现:ELF 头、节头、程序头、重定位节和Symbol table
4.3.1 ELF 头
CSAPP大作业 1180301002
图41 ELF头
ELF头包含了目标文件的类型(可重定位文件)、系统架构(x86-64)、ELF头的大小(64字节)、节头大小(64字节)、节头数量(13)等等信息
4.3.2 节头
CSAPP大作业 1180301002
图42 节头
在节头中,描述了不同节的名称、大小、类型、全体大小、地址、旗标、链接、信息、偏移量和对齐。最后还有Key to Flags对旗标做出解释。
4.3.3程序头
CSAPP大作业 1180301002
图43 程序头
4.3.4 重定位节
CSAPP大作业 1180301002
图44 重定位节
第一部分重定位节是.rela.text的重定位节,包含了偏移量、信息、类型、符号值、符号名称、加数第二部分重定位节是.rela.eh_frame的重定位节,也包含了偏移量、信息、类型、符号值、符号名称、加数接下来以puts为例,解释重定位的过程:①找到符号名称为puts的行,得到基本信息,偏移量为00000000001d,类型为R_X86_64_PLT32,加数为-4②则引用地址为:代码段地址-4-(最终地址+0x00000000001d)
4.3.5 Symbol table(符号表)
CSAPP大作业 1180301002
图45 Symbol table(符号表)
Symbol table(符号表)包括了Num、Value、Size、Type、Bind、Vis、Ndx和Name.需要与节头配合使用,才能得到Ndx的对应。比如sleepsecsd,是位于.text(Ndx=1)中偏移量为0(value)处的4字节(size)的变量
4.4 Hello.o的结果解析
在hello.o的目录,右键打开终端,输入objdump -d -r hello.o >objdumphello
CSAPP大作业 1180301002
图46 在Ubuntu下反汇编的命令
在hello.o的目录下,出现了objdumphello,如图47所示
CSAPP大作业 1180301002
图47 生成的objdumphello文件
以文本形式打开objdumphello文件如下:
CSAPP大作业 1180301002
图48 objdumphello文件的内容
4.4.1操作数
两者的操作数显示形式不同:
CSAPP大作业 1180301002
图49 hello.s中立即数16以$16(十进制的形式)显示
CSAPP大作业 1180301002
图50 objdumphello中立即数16以$0x10(以十六进制的形式)显示
4.4.2分支转移
两者的分支转移方式不同:
CSAPP大作业 1180301002
图51 hello.s中利用助记符(.L3)进行跳转
CSAPP大作业 1180301002
图52 objdumphello中利用目的地址(6f)进行跳转
4.4.3函数调用
两者的函数调用方式不同:
CSAPP大作业 1180301002
图53 hello.s中利用函数名(sleep)进行调用
CSAPP大作业 1180301002
图54 objdumphello中利用目的地址(6b)进行调用
4.5 本章小结
本章解释了汇编的概念与作用,给出了Ubuntu下汇编的命令,从ELF 头、节头、程序头、重定位节和Symbol table分析了可重定位目标elf格式,从操作数、分支转移、函数调用三个方面分析了objdumphello与hello.s的差异,解析了hello的汇编结果(第4章1分)
第5章 链接
5.1 链接的概念与作用
5.1.1链接的概念
链接器(ld)将可重定位目标程序hello.o链接为可执行目标程序hello
5.1.2链接的作用
链接器(ld)负责处理文件的合并。结果的到的hello文件,是一个可执行目标文件,可以被加载到内存中,由系统执行
5.2 在Ubuntu下链接的命令
在hello.o的目录,右键打开终端,输入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如图55所示
CSAPP大作业 1180301002
图55 在Ubuntu下链接的命令
在hello.o的目录下,出现了hello,如图56所示
CSAPP大作业 1180301002
图56 生成的hello文件
5.3 可执行目标文件hello的格式
在hello的目录,右键打开终端,输入readelf -a hello > ldhello.elf,如图57所示
CSAPP大作业 1180301002
图57 在Ubuntu下生成ELF格式的命令
在hello的目录下,出现了ldhello.elf,如图58所示
CSAPP大作业 1180301002
图58 生成的ldhello.elf文件
以文本形式打开ldhello.elf,发现:ELF头、节头、程序头、段节、Dynamic section(动态节)、重定位节、Symbol table(符号表)、Histogram for bucket list length(桶列表长度直方图)、Version symbols section(版本符号部分)、Version needs section(版本需求部分)
5.3.1 ELF 头CSAPP大作业 1180301002
图59 ELF头
ELF头包含了目标文件的类型(可执行文件)、系统架构(x86-64)、ELF头的大小(64字节)、程序头大小(56字节)、节头大小(64字节)、节头数量(25)等等信息
5.3.2节头
CSAPP大作业 1180301002
CSAPP大作业 1180301002
图60节头
5.3.3 程序头
CSAPP大作业 1180301002
图61程序头
5.3.4段节
CSAPP大作业 1180301002
图62段节
5.3.5 Dynamic section(动态节)
CSAPP大作业 1180301002
图63 Dynamic section(动态节)
5.3.6 重定位节
CSAPP大作业 1180301002
图64 重定位节
5.3.7 Symbol table(符号表)
CSAPP大作业 1180301002
CSAPP大作业 1180301002
图65 Symbol table(符号表)
5.3.8 Histogram for bucket list length(桶列表长度直方图)
CSAPP大作业 1180301002
图66 Histogram for bucket list length(桶列表长度直方图)
5.3.9 Version symbols section(版本符号部分)
CSAPP大作业 1180301002
图67 Version symbols section(版本符号部分)
5.3.10 Version needs section(版本需求部分)
CSAPP大作业 1180301002
图68 Version needs section(版本需求部分)
5.4 hello的虚拟地址空间
使用edb加载hello,查看本进程的虚拟地址空间各段信息,如下图69
CSAPP大作业 1180301002
图69 本进程的虚拟地址空间各段信息
从可执行文件中加载的信息从0x400000处开始存放,其中每个部分(Type、PHDR、INTERP、LOAD、DYNAMIC、NOTE GUN_STACK、GNU_RELRO)所占的地址区间与5.33程序头的一致(如下图70)
CSAPP大作业 1180301002
图70 程序头
5.5 链接的重定位过程分析
在hello的目录,右键打开终端,输入objdump -d -r hello > objdumpldhello,如图71所示
CSAPP大作业 1180301002
图71 在Ubuntu下反汇编的命令
在hello的目录下,出现了objdumpldhello,如图72所示
CSAPP大作业 1180301002
图72 生成的objdumpldhello文件以文本形式打开objdumpldhello文件如下:
CSAPP大作业 1180301002
CSAPP大作业 1180301002
CSAPP大作业 1180301002
图73 objdumpldhello文件的内容
与hello.o的反汇编结果(objdumphello)相比,hello的反汇编结果(objdumpldhello)
多了:Disassembly of section .init
Disassembly of section .plt
Disassembly of section .text部分中多了0000000000400500 <_start>0000000000400530 <_dl_relocate_static_pie>00000000004005c0 <__libc_csu_init>0000000000400630 <__libc_csu_fini>
Disassembly of section .fini
5.5.1 分支转移
两者的分支转移方式不同
CSAPP大作业 1180301002
图74 objdumpldhello利用直接地址(400566)进行跳转
CSAPP大作业 1180301002
图75 objdumphello利用地址偏移量(34)进行跳转
5.5.2函数调用
两者的函数调用方式不同
CSAPP大作业 1180301002
图76objdumpldhello的利用直接地址(4004d0)和函数名(getchar)进行函数调用
CSAPP大作业 1180301002
图77 objdumphello的利用地址偏移量(7a)进行函数调用5.6 hello的执行流程使用edb执行hello,记录下call后面的内容如图78:
CSAPP大作业 1180301002
图78 call后面的内容(以_dl_start为例)
载入:_dl_start_dl_init
开始执行:_stat_cax_atexit_new_exitfn_libc_start_main_libc_csu_init
运行:_main_printf_exit_sleep_getchar_dl_runtime_resolve_xsave_dl_fixup_dl_lookup_symbol_x退出:_exit
5.7 Hello的动态链接
分析通过edb调试,找到dl_init的位置,并在前后设置断点,如图79所示
CSAPP大作业 1180301002
图79在 dl_init的前后位置设置断点在hello的反汇编代码里面找到_GLOBAL_OFFSET_TABLE(全局偏移表)的地址,为如图80
CSAPP大作业 1180301002
图80 _GLOBAL_OFFSET_TABLE(全局偏移表)的地址
CSAPP大作业 1180301002
图81在执行_dl_init之前,_GLOBAL_OFFSET_TABLE的值
CSAPP大作业 1180301002
图82在执行_dl_init之后,_GLOBAL_OFFSET_TABLE的值
通过前后对比:在执行_dl_init之前,_GLOBAL_OFFSET_TABLE(图81黑框所示)是全0的;在执行_dl_init之后,_GLOBAL_OFFSET_TABLE(图82黑框所示)变成了相应的值。_dl_init作用是:初始化程序,给其赋上调用的函数的地址,使这些被调用的函数链接到了动态库
5.8 本章小结
本章解释了链接的概念与作用,给出了Ubuntu下链接的命令,从ELF头、节头、程序头、段节、Dynamic section(动态节)、重定位节、Symbol table(符号表)、Histogram for bucket list length(桶列表长度直方图)、Version symbols section(版本符号部分)、Version needs section(版本需求部分)十个方面分析了可执行目标文件hello的格式,从分支转移、函数调用两个方面分析了objdumpldhello与objdumphello的差异,理顺了hello的执行流程,分析了hello的动态链接(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
6.1.1进程的概念
进程的经典定义就是一个执行中程序的实例(书本P508)
6.1.2进程的作用
在现代系统上运行一个程序时,我们会得到一个假象,就好像我们的程序是系统中当前运行的唯一的程序一样。我们的程序好像是独占地使用处理器和内存。处理器好像是无间断地一条接一条地执行我们程序中的指令。最后,我们程序中的代码和数据好像是系统内存中唯一的对象。这些假象都是通过进程的概念提供给我们的(书本P508)
6.2 简述壳Shell-bash的作用与处理流程
6.2.1Shell-bash的作用
输出一个提示符,等待输入一个命令行,然后执行这个命令(P5)
6.2.2Shell-bash的处理流程
①如果能够成功找到命令,那么该命令将被分解为系统调用传给Linux内核。②如果该命令行的第一个单词不是一个内置的shell命令,shell就会假设这是一个可执行文件的名字,他将加载并运行这个文件。③如果命令既不是一个内置的shell命令也没有找到该可执行文件,则会提示错误。(P5)
6.3 Hello的fork进程创建过程
父进程通过调用fork函数创建一个新的运行的子进程,新创建的子进程几乎但不完全与父进程相同。子进程得到与父进程用户级虚拟地址空间相同的(但是独立的)一份副本,这就意味着当父进程调用fork时,子进程可以读写父进程中打开的任何文件。父进程和新创建的子进程之间最大的区别在于它们有不同的PID。fork函数调用一次,返回两次,一次是在父进程中,返回子进程的PID,一次是在子进程中,返回0。因为子进程的PID总非0,返回值就提供一个明确的方法来分辨程序是在父进程还是在子进程中执行。父进程和子进程是并发运行的,也就是说在不同的机器上,运行的先后顺序不一样。(P513~P515)
6.4 Hello的execve过程
execve函数在当前进程的上下文中加载并运行一个新程序,函数结构如下intexecve(const char *filename, const char *argv[],const char *envp[]);execve函数加载并运行可执行目标文件filename,且带参数列表argv和环境变量列表envp。只有当出现错误时,例如找不到filename,execve才会返回到调用程序。所以,与fork一次调用返回两次不同,execve调用一次从不返回(P521)加载成功后,当加载器运行时候,它创建类似于图83的内存映像。在程序头部表的引导下,加载器将可执行文件的片(chunk)复制到代码段和数据段。接下来,加载器跳转到程序的入口点,也就是_start函数的地址。这个函数是在系统目标文件ctrl.o中定义的,对所有的C程序都是一样的。_start函数调用系统启动函数__libc_start_main,该函数定义在libc.so中。它初始化执行环境,调用用户层的main函数,处理main函数的返回值,并且在需要的时候把控制返回给内核。(P484)
CSAPP大作业 1180301002
图83 Linux x86-64运行时的内存映像
6.5 Hello的进程执行
内核为每一个进程维持一个上下文。上下文就是内核重新启动一个被抢占的进程所需要的状态。它由一些对象的值组成,这些对象包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构,比如描述地址空间的页表,包含有关当前进程信息的进程表,以及包含进程已打开文件的信息的文件表。在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程,这种决策就叫做调度,是由内核中称为调度器的代码处理的。当内核选择一个新的进程运行时,我们说内核调度了这个进程。在内核调度了一个新的进程运行后,它就抢占当前进程,并使用一种称为上下文切换的机制来将控制转移到新的进程
主要分为以下三步:①保存当前进程的上下文②恢复之前某个被抢占的进程的被保存的上下文③将控制传递给这个新恢复的进程例如sleep系统调用时,它显式地请求让调用进程休眠。此外所有系统都有产生周期性定时器中断的机制(通常为每1毫秒或每10毫秒)。每次发生定时器中断时,内核就能判定当前进程已经运行了足够长的时间,并切换到另一个新进程。当发生上下文切换时,处理器将模式从用户模式转换到内核模式,切换完毕后,会从内核模式转换回用户模式,如下图84所示,调用sleep函数时会发生的上下文切换以及用户模式和内核模式的转换(P511)
CSAPP大作业 1180301002
图84 调用sleep函数时会发生的上下文切换以及用户模式和内核模式的转换
6.6 hello的异常与信号处理
6.6.1 hello执行过程中发生的异常
CSAPP大作业 1180301002图85 hello执行过程中发生的异常
6.6.2hello执行过程中产生的信号
CSAPP大作业 1180301002
图86 hello执行过程中产生的信号
6.6.3hello执行过程中产生的信号的处理方式 hello执行过程中,如果发生图86所示的信号,每种信号对应一个事件,每个事件则会产生对应的处理方式。信号提供了一种机制,就是通知用户进程发生了某些异常。比如,如果一个进程试图除以0,那么内核就发送给他一个SIGFPE信号。如果一个进程执行了一条非法指令,那么内核就发送给他一个SIGILL信号。如果进程进行了非法内存的引用,内核就发送给它一个SIGSEGV信号。如果输入ctrl+C,那么内核就会发送一个SIGINT信号给前台进程组里面的每一个信号。一个进程可以通过向另一组进程发送一个SIGKILL信号来强制终止它。当一个子进程终止或停止时,内核会发送一个SIGCHILD信号给它的父进程。(P526~P527)
6.6.4hello执行过程中不停乱按、回车如图87所示:如果不停乱按、回车,对hello程序的执行没有影响,待其运行结束后,乱按的字符会留在缓冲区,每按一次回车,就会作为接下来的命令行的输入一次
图87hello执行过程中不停乱按、回车的结果
6.6.5hello执行过程中按Ctrl-C如图88所示:如果按Ctrl-C,会立刻终止前台作业(进程组的每个进程),然后等待用户的下一次命令行输入
图88hello执行过程中按Ctrl-C的结果
6.6.6hello执行过程中按Ctrl-z以及后续命令ps jobs pstree fg kill
如图89所示:如果按Ctrl-C,会立刻停止前台作业(进程组的每个进程),然后使用ps显示进程可以看到hello,然后jobs查看当前暂停的进程可以看到hello,利用kill杀死hello进程后,ps显示进程看不到hello,fg也不能恢复hello进程
图89 hello执行过程中按Ctrl-z以及后续命令的结果
如图90所示:如果按Ctrl-C,会立刻停止前台作业(进程组的每个进程),然后使用jobs查看当前暂停的进程可以看到hello,fg可以重新恢复hello进程
图90 hello执行过程中按Ctrl-z以及后续命令的结果
如图91所示:如果按Ctrl-C,会立刻停止前台作业(进程组的每个进程),然后使用pstree查看当前进程的进程树
图91 hello执行过程中按Ctrl-z以及后续命令的结果
6.7本章小结
本章解释了进程的概念与作用,简述了壳Shell-bash的作用与处理流程,详细叙述了Hello的fork进程创建过程、execve过程,以及Hello的进程执行,从hello执行过程中发生的异常、产生的信号以及信号的处理方式、不停乱按、回车、按Ctrl-C、按Ctrl-z以及后续命令ps jobs pstree fg kill分析了hello的异常与信号处理(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:在有地址变换功能的计算机中,访内指令给出的地址 (操作数) 叫逻辑地址,也叫相对地址。要经过寻址方式的计算或变换才得到内存储器中的实际有效地址(物理地址)。是hello.o中的相对偏移地址。(百度百科)
线性地址:线性地址是逻辑地址到物理地址变换之间的中间层。在分段部件中逻辑地址是段中的偏移地址,然后加上基地址就是线性地址。(百度百科)
虚拟地址:程序访问存储器所使用的逻辑地址称为虚拟地址。是hello中的虚拟内存地址。(P560)
物理地址:计算机系统的主存被组织成一个由M个连续的字节大小的单元组成的数组,每一个字节单元给以一个唯一的存储器地址,称为物理地址。是hello中的虚拟内存地址对应的物理地址。(P560)
7.2 Intel逻辑地址到线性地址的变换-段式管理
7.2.1 Intel的段式管理
段式内存管理方式就是直接将逻辑地址转换成线性地址,其基本组成方式是段号+段内偏移地址。首先介绍逻辑空间。逻辑空间分为若干个段,其中每一个段都定义了一组具有完整意义的信息,逻辑地址对应于逻辑空间。段是对程序逻辑意义上的一种划分,一组完整逻辑意义的程序被划分成一段,所以段的长度是不确定的。可以将段表抽象成一个大的数组集合,数组中的元素就是“段描述符”——用于描述一个段的详细信息的结构。段描述符一般是由8个字节(64位)组成。将逻辑地址转换成线性地址需要使用段表,而获得段表中一个特定的段描述符需要使用段选择符——用于找到对应的段描述符的。段选择符是一个由16位长的字段组成的,其中前13位是一个索引号,后面三位包含一些硬件细节。Intel设计的本意是:一些全局的段描述符,就放在"全局段描述符(GDT)“中,一些局部的段描述符,就放在所谓的"局部段描述符表(LDT)中”。当段选择描述符中的T1字段=0,则表示使用GDT,其内存中的大小和地址存放在CPU的gdtr寄存器中;当段选择描述符中的T1字段=1则表示使用LDT,其内存中的大小和地址存放在ldtr寄存器中。(博客)
7.2.2 Hello的逻辑地址到线性地址的变换
①给定一个完整的逻辑地址[段选择符:段内偏移地址]②查看段选择描述符中的T1字段是0还是1,可知当前要转换的是GDT中的段,还是LDT中的段③根据指定的相应的寄存器,得到其地址和大小,得到一个数组。④用段选择符中的前13位在这个数组中查找到对应的段描述符,得到基地址 ⑤把基地址+段内偏移地址,就是要转换的线性地址(博客)
7.3 Hello的线性地址到物理地址的变换-页式管理
7.3.1 Intel的页式管理页式
内存管理单元负责把一个线性地址转换为物理地址。从管理和效率的角度出发,线性地址被划分成固定长度单位的数组,称为页(page)。例如,一个32位的机器,线性地址可以达到4G,每页4KB,这样,整个线性地址就被划分为2^20页,称之为页表,该页表中每一项存储的都是物理页的基地址为了能够尽可能的节约内存,CPU在页式内存管理方式中引入了两级的页表结构,这种页式管理方式中,第一级的页表称之为“页目录”,用于存放页表的基地址;第二级才是真正的“页表”用于存放物理内存的基地址 (博客)
7.3.2 Hello的线性地址到物理地址的变换
①从CR3中取出进程的页目录的地址(操作系统在负责进程的调度的时候,将这个地址装入对应的CR3地址寄存器),取出其前20位,得到页目录的基地址; ②根据上述基地址以及线性地址的前10位,进行组合得到线性地址的前十位的索引对应的项在页目录中地址,根据该地址可以取到该地址上的值,该值就是二级页表项的基地址③根据②取到的页表项的基址,取其前20位,将线性地址的10-19位左移2位,按照与②相同的方式进行组合就可以得到线性地址对应的物理页框在内存中的地址在二级页表中的地址的起始地址,根据该地址向后读四个字节就得到了线性地址对应的物理页框在内存中的地址在二级页表中的地址,然后取该地址上的值就得到线性地址对应的物理页框在内存中的基地址④根据③取到的基地址,取其前20位得到物理页框在内存中的基址,再根据线性地址最后的12位的偏移量得到具体的物理地址,取该地址上的值就是最后要得到值(博客)
7.4 TLB与四级页表支持下的VA到PA的变换
7.4.1 TLB支持下的VA到PA的变换
①CPU产生一个虚拟地址②和③MMU从TLB中取出相应的PTE④MMU将这个虚拟地址翻译成一个物理地址,并且将它发送到高速缓存/主存⑤高速缓存/主存将所请求得数据字返回给CPU(P571)如图92所示
CSAPP大作业 1180301002
图92TLB命中和不命中的操作图
7.4.2 四级页表支持下的VA到PA的变换虚拟地址被划分为4个v*n和1个VPO。每个v*ni都是一个到第i级页表的索引(1≤i≤4)。第j级页表中的每个PTE(1≤j≤3)
都指向j+1级的某个页表的基址。第4级页表中的每一个PTE包含某个物理页面的PPN,或者一个磁盘块的地址,PPO和VPO是相同的。
CSAPP大作业 1180301002
图93使用k级页表的地址翻译(本次k=4)
7.5 三级Cache支持下的物理内存访问
①先在第一级缓存中寻找要找的数据,利用TLBI找到TLB中对应的组,再比较TLBT,若相同且有效位为1,则要找的数据就是该组内的数据②否则,在第二级缓存寻找,找到后需要缓存在第一级,若有空闲块则放置在空闲块中,否则根据替换策略选择牺牲块③否则,在第三级缓存寻找,找到后需要缓存在第一、二级,若有空闲块则放置在空闲块中,否则根据替换策略选择牺牲块④否则,在主存寻找,找到后需要缓存在第一、二、三级,若有空闲块则放置在空闲块中,否则根据替换策略选择牺牲块如图94所示
CSAPP大作业 1180301002
图94 三级Cache支持下的物理内存访问情况
7.6 hello进程fork时的内存映射
当fork函数被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID。为了给这个新进程创建虚拟内存,它创建了当前进程的mm_struct、区域结构和页表的原样副本。它将两个进程中的每个页面都标记位只读,并将两个进程中的每个区域结构都标记为私有的写时复制。当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面。因此,也就为每个进程保持了私有地址空间的抽象概念(P584)
7.7 hello进程execve时的内存映射
运行了execve(“hello”,NULL,NULL)后,加载并运行hello需要以下几个步骤:①删除已存在的用户区域。删除当前进程虚拟地址的用户部分中的已存在的区域结构。②映射私有区域。为新程序的代码、数据、bss和栈区域创建新的区域结构。所有这些新的区域都是私有的、写时复制的。代码和数据区被映射为hello文件中的.text和.data区。bss区域是请求二进制零的,映射到匿名文件,其大小包含在hello中。栈和堆区域也是请求二进制零的,初始长度为零。如图95概括了私有区域的不同映射。③映射共享区域。如果hello程序与共享对象(或目标)链接,那么这些对象都是动态链接到这个程序的,然后再映射到用户虚拟地址空间中的共享区域内。④设置程序计数器(PC)。execve做的最后一件事情就是设置当前进程上下文中的程序计数器,使之指向代码区域的入口点。下一次调度这个进程时,它将从这个入口点开始执行。Linux将根据需要换入代码和数据页面(P585)
CSAPP大作业 1180301002
图95 加载器映射用户地址空间的区域的示意图
7.8 缺页故障与缺页中断处理
7.8.1 缺页故障
在虚拟内存的习惯说法中,DRAM缓存不命中称为缺页。例如:CPU引用了VP3中的一个字,VP3并未缓存在DRAM中。地址翻译硬件从内存中读取PTE3,从有效位推断出VP3未被缓存,并且触发一个缺页异常。缺页异常调用内核中的缺页异常处理程序,该程序会选择一个牺牲页,在此例中就是存放在PP3中的VP4。如果VP4已经被修改了,那么内核就会将它复制回磁盘。无论哪种情况,内核都会修改VP4的页表条目,反映出VP4不再缓存在主存中这一事实(P564)如图96所示
CSAPP大作业 1180301002
图96 缺页之前(对VP3中的字的引用会不命中,从而触发了缺页)
7.8.2缺页中断处理接下来,内核从磁盘复制VP3到内存中的PP3,更新PTE3,随后返回。当异常处理程序返回时,它会重新启动导致缺页的指令,该指令会把导致缺页的虚拟地址重发送到地址翻译硬件。但是现在VP3已经缓存在主存中了,那么也命中也能由地址翻译硬件正常处理了(P564)如图97所示
CSAPP大作业 1180301002
图97 缺页之后(对VP3中的字的引用命中,不会触发缺页)
7.9动态存储分配管理
7.9.1动态内存分配器的基本原理
动态内存分配器维护着一个进程的虚拟内存区域,称为堆(heap)。系统之间细节不同,但不失通用性,假设堆是一个请求二进制零的区域,它紧接在未初始化的数据区域后开始,并向上(向更高的地址)。对于每一个进程,内核维护者一个变量brk(读作“break”),它指向堆的顶部。分配器将堆视为一组不同大小的块(block)的集合来维护,每个块就是一个连续的虚拟内存片(chunk),要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。分配器有两种基本风格:显式分配器和隐式分配器。两种风格都要求应用显式地分配块。它们的不同之处在于由哪个实体来负责释放已分配的块。
1.显式分配器:要求应用显式地释放任何已分配的块。例如:C标准库提供一种叫做malloc程序包的显式分配器。C程序通过调用malloc函数来分配一个块,通过调用free函数来释放一个块。C++中的new和delete操作符与C中的malloc和free相当
2.隐式分配器:也叫做垃圾收集器。要求分配器检测一个已分配块何时不再被程序所使用,那么就释放这个块。自动释放已分配块的过程叫做垃圾搜集。诸如Lisp、ML以及Java之类的高级语言就依赖垃圾收集来释放已分配的块。(书本P587)
7.9.2带边界标签的隐式空闲链表分配器原理
对于带边界标签的隐式空闲链表分配器,一个块是由一个字的头部、有效载荷、可能的一些额外的填充,以及在块的结尾处的一个字的脚部组成的。头部编码了这个块的大小(包括头部和所有的填充),以及这个块是已分配的还是空闲的。如果我们强加一个双字的对齐约束条件,那么块大小就总是8的倍数,且块大小的最低3位总是0。因此,我们只需要内存大小的29个高位,释放剩余的3位来编码其他信息。在这种情况中,我们用其中的最低位(已分配位)来指明这个块是已分配的还是空闲的。如图98所示:
CSAPP大作业 1180301002
图98 一个简单的堆块的格式
头部后面就是应用调用malloc时请求的有效载荷。有效载荷后面是一片不使用的填充块,其大小可以是任意的。需要填充有很多原因。比如,填充可能是分配器策略的一部分,用来对付外部碎片。或者也需要用它来满足对齐要求。将堆组织为一个连续的已分配块和空闲块的序列,这种结构称为隐式空闲链表,是因为空闲块是通过头部中的大小字段隐含地连接着的。分配器可以通过遍历堆中所有的块,从而间接地遍历整个空闲块的集合。注意:此时我们需要某种特殊标记的结束块,可以是一个设置了已分配位而大小为零的终止头部。Knuth提出了一种边界标记技术,允许在常数时间内进行对前面块的合并。这种思想是在每个块的结尾处添加一个脚部,其中脚部就是头部的一个副本。如果每个块包括这样一个脚部,那么分配器就可以通过检查它的脚部,判断前面一个块的起始位置和状态,这个脚部总是在距当前块开始位置一个字的距离。(书本P592~P595)如图99所示:
CSAPP大作业 1180301002
图99 使用边界标记的堆块的格式
7.9.3显式空间链表的基本原理
根据定义,程序不需要一个空闲块的主体,所以实现空闲链表数据结构的指针可以存放在这些空闲块的主体里面。例如,堆可以组织成一个双向空闲链表,在每个空闲块中,都包含一个pred(前驱)和succ(后继)指针。如图100所示:
CSAPP大作业 1180301002
图100 使用双向空闲链表的堆块的格式
使用双向链表而不是隐式空闲链表,使首次适配的分配时间从块总数的线性时间减少到了空闲块数量的线性时间。不过,释放一个块的时间可以是线性的,也可能是个常数,这取决于空闲链表中块的排序策略。维护链表有两种方法,一种方法是用后进先出(LIFO)的顺序,将新释放的块放置在链表的开始处。另一种方法是按照地址顺序,其中链表中每个块的地址都小于它后继的地址。(P603)
7.9.4放置与合并策略
①放置策略首次适配:从头开始搜索空闲链表,选择第一个合适的空闲块下一次适配:从上一次查询结束的地方开始最佳适配:检查每个空闲块,选择适合所需请求大小的最小空闲块。(P594)②合并策略立即合并:在每次一个块被释放时,就合并所有的相邻块推迟合并:等到某个稍晚的时候再合并空闲块。(P595)
7.10本章小结
本章详细描述了hello的存储器地址空间、Intel逻辑地址到线性地址的变换-段式管理、Hello的线性地址到物理地址的变换-页式管理、TLB与四级页表支持下的VA到PA的变换、三级Cache支持下的物理内存访问、hello进程fork时的内存映射、 hello进程execve时的内存映射、缺页故障与缺页中断处理以及动态存储分配管理(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
所有的I/O设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行。这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,称为Unix I/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行。(P622)
8.2 简述Unix IO接口及其函数
8.2.1 I/O接口操作
①打开文件:一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备。内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件。内核记录有关这个打开文件的所有信息。应用程序只需记住这个描述符。②Linux shell创建的每个进程开始时都有三个打开的文件:标准输入、标准输出和标准错误。③改变当前的文件位置:对于每个打开的文件,内核保持着一个文件位置k,初始为0.这个文件位置是从文件开头起始的字节偏移量。应用程序能够通过执行seek操作,显式地设置文件的当前位置为k。④读写文件:一个读操作就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n。给定一个大小为m字节的文件,当k>=m时执行读操作会触发一个称为end-of-file(EOF)的条件,应用程序能检测到这个条件。在文件结尾处并没有明确的“EOF符号”。类似地,写操作就是从内存复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k。⑤关闭文件:当应用完成了对文件的访问之后,它就通知内核关闭这个文件。作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。无论一个进程因为何种原因终止时,内核都会关闭所有打开的文件并释放它们的内存资源。(P622~P623)
8.2.2 I/O函数
①int open(char *filename, int flags, mode_t mode);进程通过调用open函数来打开一个已存在的文件或者创建一个新文件。open函数将filename转换为一个文件描述符,而且返回描述符数字。flags参数指明了进程打算如何访问这个文件。mode参数指定了新文件的访问权限位。②int close(int fd);进程通过调用close函数关闭一个打开的文件。③ssize_t read(int fd, void *buf, size_t n);应用程序通过调用read函数来执行输入。read函数从描述符为fd的当前文件位置复制最多n个字节到内存位置buf。返回值-1表示一个错误,返回值0表示EOF。否则返回值表示的是实际传送的字节数量。④ssize_t write(int fd, const void *buf, size_t n);应用程序通过调用write函数来执行输出。write函数从内存位置buf复制至多n个字节到描述符fd的当前文件位置。(P624~P626)
8.3 printf的实现分析
根据已给的博客园网站:https://www.cnblogs.com/pianist/p/3315801.html得到图101、图102、图103、图104所示代码
CSAPP大作业 1180301002
图101 printf函数的实现
CSAPP大作业 1180301002
图102 vsprintf函数的实现
CSAPP大作业 1180301002
图103 write函数的实现
CSAPP大作业 1180301002
图104 sys_call的实现
vsprintf函数接受确定输出格式的格式字符串fmt,产生格式化输出,并返回要打印的字符串的长度。write系统函数先给寄存器传参数,然后调用sys_call,sys_call将字符串中的字节从寄存器中通过总线复制到显卡的显存中,显存中存储的是字符的ASCII码。然后字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)(博客园) 8.4 getchar的实现分析
从百度百科得知图105的代码
CSAPP大作业 1180301002
图105 getchar函数的代码实现
getchar函数调用了read函数,将缓冲区的所有数据都读到buf里,并将缓冲区的长度赋值给n。若n≥1,返回buf的第一个元素否则返回EOF异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章详细描述了Linux的I/O设备管理机制,Unix I/O接口及函数,分析了printf函数和getchar函数的实现。(第8章1分)
结论
用计算机系统的语言,逐条总结hello所经历的过程。你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
①代码编写者编写代码保存至hello.c,GCC编译器驱动程序读取源程序文件hello.c②预处理器(cpp)根据以字符#开始的命令修改hello.c得到另一个C程序hello.i。③编译器(ccl)将hello.i翻译成文本文件hello.s,它包含一个汇编语言程序。④汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中。⑤链接器(ld)负责合并,结果就得到了可执行目标文件hello。(P3)⑥用户输入命令./hello… …⑦fork一个新进程并调用execve加载hello。⑧运行hello⑨hello运行结束后被回收。
开始的第一节课,老师就问我这门课学的是什么,我支支吾吾答不上来,说是学计算机系统。通过9月到12月,这4个月的学习,从昨天到今天,历经14小时做大作业,从标号①到⑨,从页码标号P3到P626,从图1到图105,hello从0到0,而我的知识却从无到有。本次大作业,包括了hello的介绍、预处理、编译、汇编、链接、进程管理、储存管理、IO管理这八个章节,每个章节中穿插了EDB、反汇编、elf文件等等内容,相当于复习了这学期学的知识,通过对代码、文件的分析,我对这八个章节的理解更进一步。虚拟机的使用贯穿整个过程,让我学会了如何使用虚拟机。课程结束了,hello也被回收了,它挥一挥手不带走一片云彩,只为在我心里留下这五色的云彩(结论0分,缺失 -1分,根据内容酌情加分)
附件
列出所有的中间产物的文件名,并予以说明起作用。
①hello.i:hello.c的预处理后的结果②hello.s:hello.i的编译后的结果③hello.o:hello.s的汇编后的结果④hello.elf:hello.o的elf格式⑤hello:hello.o的链接后的结果⑥ldhello.elf:hello的elf格式⑦objdumphello:hello.o的反汇编结果⑧objdumpldhello:hello的反汇编结果(附件0分,缺失 -1分)
参考文献
为完成本次大作业你翻阅的书籍与网站等
[1] Randal E. Bryant & David R. O’Hallaron. 深入理解计算机系统[M]. 北京:机械工业出版社,2019:1-613.
[2] gcc编译的四个阶段:预处理,编译,汇编,链接,2015 [2015-12-25 ]. https://www.shuzhiduo.com/A/qVdeYA38dP/.
[3] 百度翻译.2019[2019-12-23],https://fanyi.baidu.com/?aldtype=16047#auto/z
[4] 百度百科.2019[2019-12-23]https://baike.baidu.com/item/逻辑地址
[5] 百度百科.2019[2019-12-23]https://baike.baidu.com/item/线性地址
[6] 博客.2018[2018-04-28]https://blog.csdn.net/gdj0001/article/details/80135196
[7] printf 函数实现的深入剖析https://www.cnblogs.com/pianist/p/3315801.html
[8] 百度百科https://baike.baidu.com/item/getchar/919709
(参考文献0分,缺失 -1分)

相关文章:

  • 2021-10-09
  • 2022-01-10
  • 2021-08-09
  • 2022-02-06
  • 2022-12-23
  • 2021-07-27
  • 2021-09-04
  • 2021-08-20
猜你喜欢
  • 2021-11-05
  • 2022-01-16
  • 2021-11-28
  • 2021-04-24
  • 2022-01-24
  • 2022-01-30
  • 2022-01-15
相关资源
相似解决方案