计算机科学与技术学院
2018年12月
摘要是论文内容的高度概括,应具有独立性和自含性,即不阅读论文的全文,就能获得必要的信息。摘要应包括本论文的目的、主要内容、方法、成果及其理论与实际意义。摘要中不宜使用公式、结构式、图表和非公知公用的符号与术语,不标注引用文献编号,同时避免将摘要写成目录式的内容介绍。
关键词:关键词1;关键词2;……;
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
本文一步步分析程序在计算机中是如何创建成文件,如何从文本文件得到汇编语言文件,计算机如何链接不同的库重定位到程序中,程序如何运行在操作系统进程中以及会遇到哪些情况,再到进程的数据是如何定位,如何存储,如何一步步且快速得到我们需要的数据。计算机的输入输出是如何实现的。阅读本文,你就能得到答案。
目 录
6.2 简述壳Shell-bash的作用与处理流程 - 10 -
7.2 Intel逻辑地址到线性地址的变换-段式管理 - 11 -
7.3 Hello的线性地址到物理地址的变换-页式管理 - 11 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 11 -
7.7 hello进程execve时的内存映射 - 11 -
第1章 概述
1.1 Hello简介
根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
用户利用C语言编程输入文本生成Hello.c->计算机对宏进行预处理生成Hello.i ->编译器编译为汇编语言,生成Hello.s -> 通过编译器汇编生成二进制文件Hello.o ->编译器链接函数库生成Hello可执行文件。
运行可执行文件,操作系统中的shell内核会利用fork创建子进程,对子进程分配相应虚拟内存,在子进程中execve加载可执行文件,Hello作为一个子进程在计算机多进程并行,CPU给Hello分配时间片,按照取指译码执行步骤逐条运行指令,在计算机以流水线多进程的方式运行每个进程。此为Hello在计算机中完成P2P(From Program to Process)的整个过程。
Hello是用户利用鼠标和键盘输入而得,Hello在计算机中是以二进制存储的,这Hello是从无到有、从0到1的过程。然后通过上述处理Hello在操作系统中以子进程运行着,操作系统已经给Hello相应的子进程分配了内存,在Hello子进程运行完,进程正常终止,操作系统回收Hello子进程,删除子进程分配的所有资源,于是Hello就从计算机中消失了,是从有到无的过程。此为Hello在计算机中完成020(From Zero-0 to Zero-0)的整个过程。
1.2 环境与工具
硬件环境
X64 CPU;2GHz;8G RAM;256GHD disk
软件环境
Windows10 64位;Vmware 11;Ubuntu 18.04;LTS 64位
开发工具
Visual Studio 2017;CodeBlocks 64位;vi/vim/gedit+gcc
Readelf;gdb/edb;objdump
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
Hello.i 计算机预处理之后的文件
Hello.s 计算机对Hello.i编译成汇编语言形式
Hello.o 汇编生成的可重定位二进制文件
Hello 编译器链接后生成的可执行文件
1.4 本章小结
简述了Hello是如何从文本文件到可执行文件,明白了进程是如何一步步生成的;
理解了计算机各部分是如何协调有序运行的。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
(以下格式自行编排,编辑时删除)
2.1.1预处理的概念:
程序设计领域中,预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。典型地,由预处理器对程序源代码文本进行处理,得到的结果再由编译器核心进一步编译。预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。比如hello.c中第一行#include<stdio.h>命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入程序文本中。结果得到了另一个C程序,通常以.i作为文件扩展名。
2.1.2预处理的作用:
预处理指令一般被用来使源代码在不同的执行环境中被方便的修改或者编译。
2.2在Ubuntu下预处理的命令
应截图,展示预处理过程!
预处理命令
gcc -no-pie -fno-PIC -E hello.c -o hello.i
图2.2-1
2.3 Hello的预处理结果解析
图2.3-1
图2.3-2
可以看到预处理后的.i文件将包含的库的全部宏加进文本中,即预处理操作
2.4 本章小结
体会了预处理在计算机中的作用,明白了预处理操作是如何执行的。
计算机通过预处理得到hello.i文件
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
(以下格式自行编排,编辑时删除)
注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序
3.1.1编译的概念
编译是指编译器做词法分析、语法分析、语义分析等,在检查无错误后后,把代码翻译成汇编语言的过程。编译器将文本文件hello.i 翻译成文本文件hello.s, 它包含一个汇编语言程序,即一条低级机器语言指令。
3.1.2 编译的作用
把文本文件转为汇编语言的形式,然后才可以执行后续的汇编操作。
汇编语言是非常有用的,它为不同的高级语言的不同编译器提供了通用的输出 语言。
3.2 在Ubuntu下编译的命令
(以下格式自行编排,编辑时删除)
应截图,展示编译过程!
编译命令
gcc -no-pie -fno-PIC -E hello.c -o hello.i
图3.2-1
编译后文本
图3.2-2
3.3 Hello的编译结果解析
(以下格式自行编排,编辑时删除)
此部分是重点,说明编译器是怎么处理C语言的各个数据类型以及各类操作的。应分3.3.1~ 3.3.x等按照类型和操作进行分析,只要hello.s中出现的属于大作业PPT中P4给出的参考C数据与操作,都应解析。
3.3.1 全局变量
对于sleepsecs有:
图3-3-1
①Sleepsecs为int类型全局变量,存储在.data节中,大小为4个字节,存储的值为2。
②因为2.5是浮点数而sleepsecs是整型,所以这里利用隐式类型转换,删去了小数部分。
③调用时
采用在对下一条指令的地址(%rip)进行相对偏移寻址得到全局变量的值
3.3.2常量:
(1)普通的数字常量,如exit(1)的1,这个是以立即数操作的;
(2) 中的字符串常量;
中的字符串常量;
对于printf中的字符串常量,如下图
①存储在.rodata节,是只读数据
②调用时
采用直接寻址方式,直接把地址值给寄存器
3.3.3局部变量
i
①局部变量是直接开在堆栈区里的
②调用时利用%rbp基址寄存器相对偏移得到
3.3.4参数
argc,argv
①参数同样是存储在对堆栈里
②调用时利用%rbp基址寄存器相对偏移得到
3.3.5表达式
(1) 赋值操作
利用数据传送指令mov实现,如
和movl中的l表示传送单字,即四个字节
(2) 算数操作
中的i++
利用加法器add指令实现
(3)关系比较指令
中的!=
中的<
是利用条件转移指令(jle,jre等)实现,如
3.3.6函数传参和函数调用和函数返回
(1)函数传参
函数传参是在函数调用前利用数据传送指令,把参数放到rdi,rsi寄存器中,如:
(2)函数调用
函数调用是利用call指令 转到相应函数指令地址实现
(3) 函数返回
返回值给eax寄存器,返回地址存放在堆栈中,如
3.4 本章小结
(以下格式自行编排,编辑时删除)
理解了汇编语言各指令含义,学会编译器是如何对程序每一步转为汇编语言的,学会了程序具体数据和操作是如何存储和实现的
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
(以下格式自行编排,编辑时删除)
注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。
4.1.1汇编的概念
把汇编语言翻译成机器语言的过程称为汇编。在机器语言中,用操作码和地址码代替相应汇编语言的指令,这样得到机器语言的二进制码,就把汇编语言变成了机器语言。
4.1.2汇编的作用
把汇编语言转换成机器语言,把.s汇编文本文件变成可重定向二进制文件
(.o文件)
4.2 在Ubuntu下汇编的命令
(以下格式自行编排,编辑时删除)
应截图,展示汇编过程!
编译命令
gcc -no-pie -fno-PIC -E hello.c -o hello.i
图4-2-1
编译后得到相应的.o文件
图4.2-2
4.3 可重定位目标elf格式
分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。
ELF头
图4.3-1
节头表:
图4.3-2
符号表
图4.3-3
4.4 Hello.o的结果解析
(以下格式自行编排,编辑时删除)
objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。
说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。
4.4.1处理步骤和结果
处理指令
图4.4-1
反汇编得到的.s文件
图4.4-2
图4.4-3
4.4.2反汇编与最开始汇编细节的对比
与hello.s相对比:
①函数调用由字符改成了对应的相对地址值
--》
②全局变量相对偏移的量由sleepsecs改成了地址0x0
--》
③字符串常量的地址由对应的节的字符改成了地址值0x0
--》
4.5 本章小结
(以下格式自行编排,编辑时删除)
体会了汇编步骤实现了对不同数据类型的重定位,明白了汇编步骤是如何把文本文件变为二进制文件的,明白了可执行可链接格式文件不同节的作用
(第4章1分)
第5章 链接
5.1 链接的概念与作用
(以下格式自行编排,编辑时删除)
注意:这儿的链接是指从 hello.o 到hello生成过程。
5.1.1链接的概念
链接是将各种代码和数据片段收集并组合成一个单一文件的过程。这个文件被加载(复制)到内存并执行。链接可以执行与编译时,也是在源代码被翻译成机器代码时,也可以执行与加载时,也就是在程序里被加载器加载到内存并执行时,甚至执行与运行时,也就是应用程序来执行,
5.1.2链接的作用
链接使得分离编译成为可能,我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把他们分解为更小的、更好管理的模块,可以独立的修改和编译这些模块。
5.2 在Ubuntu下链接的命令
(以下格式自行编排,编辑时删除)
使用ld的链接命令,应截图,展示汇编过程! 注意不只连接hello.o文件
链接命令
图5.2-1
链接结果
图5.2-2
5.3 可执行目标文件hello的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
(1)ELF头
图5.3-1
(2)节头表
图5.3-2
图5.3-3
(3)符号表
图5.3-4
图5.3-5
5.4 hello的虚拟地址空间
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
由节头表知004004e0-004006c4为.text节
图5.4-1
图5.4-2
由节头表知004006d0-00400700为.rodata节
图5.4-3
由节头表知00601040-00601054为.data节
图5.4-4
由节头表知00601054后为.bss节,为空
5.5 链接的重定位过程分析
(以下格式自行编排,编辑时删除)
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
(1) 处理命令和结果
图5.5-1
图5.5-1
(2) 重定位分析
hello将hello.o中所有可重定位地址由相对地址变成了程序运行时实际的地址位置
①函数调用由相对地址值改成了实际地址值
--》
②全局变量相对偏移的量由地址0x0改成了实际偏移地址
--》
③字符串常量的地址由地址值0x0改成了实际的地址
--》
5.6 hello的执行流程
(以下格式自行编排,编辑时删除)
使用edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。
在main函数之前执行的程序有:
_start
__libc_start_main@plt
_libc_csu_init
_init
frame_dummy
register_tm_clones
main函数之后执行的程序有:
Exit
cxa_thread_atexit_impl
fini
5.7 Hello的动态链接分析
(以下格式自行编排,编辑时删除)
分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。
动态库采用延迟绑定,在init后才会出现真正的地址
5.8 本章小结
理解了Hello程序是如何链接的,体会了链接时重定位执行了哪些操作,明白了链接是如何连接不同的库并且运行起来的
(以下格式自行编排,编辑时删除)
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
(以下格式自行编排,编辑时删除)
6.1.1进程的概念
进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
6.1.1进程的作用
进程是程序的抽象表达,有利于清晰地刻画动态系统的内在规律,有效管理和调度进入计算机系统主存储器运行的程序
6.2 简述壳Shell-bash的作用与处理流程
(以下格式自行编排,编辑时删除)
Shell是壳,也可以称为命令解释器,它接受用户命令,对命令进行解释,调用linux内核执行,shell提供了与用户与操作系统的通讯方式(鼠标和键盘),确保了内核的安全性。
Bash读取一条命令,按照记号分离,检测第一个记号是否为关键字,查询命令为函数还是内置命令,还是可执行文件,对其他记号参数扩展,然后运行命令。
6.3 Hello的fork进程创建过程
(以下格式自行编排,编辑时删除)
Shell在运行一个可执行文件时会调用fork函数创建一个子进程,子进程得到与父进程虚拟地址空间相同的一个副本,包括0代码和数据段、堆、共享库和用户栈。当父进程fork时子进程可以读写父进程打开的任何文件。Fork函数调用一次返回两次。在父进程中返回的pid为子进程pid,在子进程中返回的是父进程的pid。Hello,在shell判断为可执行文件时,就会为它fork一个进程,并创造相应虚拟地址空间用来执行Hello。
6.4 Hello的execve过程
(以下格式自行编排,编辑时删除)
execve函数加载并运行可执行目标文件hello,与form不同,execve调用一次且从不返回。在execve加载了hello后,它会调用启动代码设置栈,并将控制传递给新程序的主函数,然后开始执行main。
6.5 Hello的进程执行
(以下格式自行编排,编辑时删除)
结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。
处理器通常是用某个控制寄存器中的一个模式位来提供这种功能的,该寄存器描述了进程当前享有的特权。没有设置模式为时进程运行在用户模式中。
Shell在给hello创建子进程并且execve执行后,转为用户模式运行hello,同时操作系统会为进程创建虚拟地址空间,此时间内运行hello文件,在收到信号或者时间片过去后,操作系统保留hello的上下文,开始执行下一个时间片的任务。
一个进程和其他进程轮流运行的概念成为多任务,多任务也称为时间分片。
6.6 hello的异常与信号处理
(以下格式自行编排,编辑时删除)
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
异常的类别
类别 | 原因 | 异步/同步 | 返回行为 |
中断 | 来自i/o设备的信号 | 异步 | 总是返回到下一条指令 |
陷阱 | 有意的异常 | 同步 | 总是返回到下一条指令 |
故障 | 潜在客恢复的错误 | 同步 | 可能返回到当前指令 |
终止 | 不可恢复的错误 | 同步 | 不会返回 |
信号的类别
图6.6-1
正常运行时
图6.6-2
按ctrl-c
图6.6-3
按ctrl-z
图6.6-4
执行jobs命令
图6.6-5
执行ps命令
图6.6-6
执行fg命令
图6.6-7
发送信号
图6.6-8
6.7本章小结
(以下格式自行编排,编辑时删除)
理解了异常的意义,理解了shell是怎么创建进程执行程序的,明白了信号的机制
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
(以下格式自行编排,编辑时删除)
结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。
逻辑地址即相对地址:即用户程序中使用的地址,一个逻辑地址由两部份组成,段标识符和段内偏移量。如hello中的局部变量i就是在堆段中,i相对于堆基址偏移量就是i 的逻辑地址。
线性地址:一些全局的段描述符,就放在“全局段描述符表中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述符表”中,而实际的变量的段偏移量加上基址偏移量就是线性地址。
虚拟地址:计算机使用基于分页机制的虚拟内存。每个进程有4GB的虚拟地址空间。基于分页机制,这4GB地址空间的一些部分被映射了物理内存,一些部分映射硬盘上的交换文件,一些部分什么也没有映射。程序中使用的都是4GB地址空间中的虚拟地址。如节头表中各部分的地址就是虚拟地址,每个进程的虚拟地址都是从0x40000000开始的。
物理地址:放在寻址总线上的地址。放在寻址总线上,如果是读,电路根据这个地址每位的值就将相应地址的物理内存中的数据放到数据总线中传输。如果是写,电路根据这个地址每位的值就在相应地址的物理内存中放入数据总线上的内容。物理内存是以字节(8位)为单位编址的。
7.2 Intel逻辑地址到线性地址的变换-段式管理
(以下格式自行编排,编辑时删除)
Intel采用段页式存储管理(MMU实现)
段式管理: 逻辑地址->线性地址==虚拟地址
段寄存器用于存放段选择符,通过段选择符可以得到对应段的首地址。段选择符分为三个部分,分别是索引、TI(决定使用全局描述符表还是局部描述符表)和RPL(CPU的当前特权级)。
段寄存器结构:
图7.2-1
被选中的段描述符先被送至描述符cache,每次从描述符cache中取32位段基址,与32位段内偏移量(有效地址)相加得到线性地址
图7.2-2
7.3 Hello的线性地址到物理地址的变换-页式管理
(以下格式自行编排,编辑时删除)
页式管理: 虚拟地址->物理地址
页表 是一个页表条目的数组,将虚拟页地址映射到物理页地址。CPU的页式内存管理单元,把一个线性地址,最终翻译为一个物理地址。
图7.3-1
一个虚拟内存地址通过在页表中查找相应的虚拟内存是否分配,若未分配返回错误,若分配未缓存则返回缺页异常,否则则把虚拟内存对应的物理内存地址发给CPU,完成页式管理。
7.4 TLB与四级页表支持下的VA到PA的变换
(以下格式自行编排,编辑时删除)
基本参数
N = 2n :虚拟地址空间中的地址数量
M = 2m : 物理地址空间中的地址数量
P = 2p : 页的大小 (bytes)
虚拟地址组成部分
TLBI: TLB索引
TLBT: TLB 标记
VPO: 虚拟页面偏移量(字节)
VPN: 虚拟页号
物理地址组成部分
PPO:物理页面偏移量 (same as VPO)
PPN:物理页号
CO: 缓冲块内的字节偏移量
CI: Cache 索引
CT: Cache 标记
图7.4-1
图7.4-2
每个虚拟地址都可分为VPN和VPO,VPN对应相应的页表项,VPO对应最后的物理偏移。对每一个VPN首先去TLB中找到对应组、行、块如果找到了且行有效位为1为命中,直接由TLB得到PPN,若不命中则从四级页表里面找,VPN可分为4个9位,每一个9位对应一级页表,直至找到最后的PPN,最后使PPN与PPO联合得到物理地址。至此,完成虚拟地址和物理地址的转换。
7.5 三级Cache支持下的物理内存访问
(以下格式自行编排,编辑时删除)
每一级Cache都分为S=2^s个的组索引,每组都有E行,每一行有B=2^b个字节的块,以及一位有效位,64位的tag标记
对地址值而言分为组索引,tag标记,以及块索引
首先对地址值的组索引找到相对应的缓存中的对应组,
然后根据tag标记找到符合tag标记的那一行,如果有效值为1,则命中,在这一行中读取相对应块索引的数据
否则,为不命中,再从下一级cache执行类似操作,再找不到则再到下一级去找,如果三级cache都找不到则在内存中找到相对应的这一行,根据地址值对应的组放入缓存中,如果有没有使用的行即有效位为0的行先放入,否则会把使用最久远的那一个缓存行换去,存入现在的行,如果找到同样由上述原则进行替换。
图7.5-1
图7.5-2
7.6 hello进程fork时的内存映射
(以下格式自行编排,编辑时删除)
子进程创建时会得到与父进程虚拟地址空间相同(但是独立的)一份副本,包括代码和数据段、堆、共享库以及用户栈。子进程内存映射如下;
虚拟内存和内存映射解释了fork函数如何为每个新进程提供私有的虚拟地址空间。Fork函数为新进程创建虚拟内存。创建当前进程的的mm_struct, vm_area_struct和页表的原样副本,两个进程中的每个页面都标记为只读,两个进程中的每个区域结构(vm_area_struct)都标记为私有的写时复制(COW)。在新进程中返回时,新进程拥有与调用fork进程相同的虚拟内存,随后的写操作通过写时复制机制创建新页面。
7.7 hello进程execve时的内存映射
(以下格式自行编排,编辑时删除)
execve函数在当前进程中加载并运行新程序hello.out的步骤:删除已存在的用户区域,创建新的区域结构,代码和初始化数据映射到.text和.data区(目标文件提供),.bss和栈映射到匿名文件,设置PC,指向代码区域的入口点。Linux根据需要换入代码和数据页面。
7.8 缺页故障与缺页中断处理
(以下格式自行编排,编辑时删除)
DRAM 缓存不命中称为缺页,即虚拟内存中的页表为缓存在物理内存中。缺页导致无法得到数据,产生缺页异常。缺页异常处理程序选择一个牺牲页,然后将目标页加载到物理内存中。最后让导致缺页的指令重新启动,页面命中。
7.9动态存储分配管理
(以下格式自行编排,编辑时删除)
Printf会调用malloc,请简述动态内存管理的基本方法与策略。
虽然可以用malloc和munmap函数创建和删除虚拟内存的区域,但是利用动态内存分配器更方便也有更好的可移植性。分配器将块视为一组大小不同的块的集合来维护,每个块是一个连续的虚拟内存片,要么是分配的,要么是空闲的。已分配的块显式的供应用程序使用,空闲块用来分配,空闲块保持空闲直到被应用程序所分配,一个已分配块保持已分配状态,直到被释放。动态内存管理的策略包括首次适配、下一次适配和最佳适配。首次适配会从头开始搜索空闲链表,选择第一个合适的空闲块。搜索时间与总块数(包括已分配和空闲块)成线性关系。会在靠近链表起始处留下小空闲块的“碎片”。下一次适配和首次适配相似,只是从链表中上一次查询结束的地方开始。比首次适应更快,避免重复扫描那些无用块。最佳适配会查询链表,选择一个最好的空闲块,满足适配,且剩余最少空闲空间。它可以保证碎片最小,提高内存利用率。
7.10本章小结
(以下格式自行编排,编辑时删除)
学会了不同地址的含义,以及如何得到数据的地址,以及如何用地址来获取数据,学会了动态内存分配器的原理。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
(以下格式自行编排,编辑时删除)
设备的模型化:文件
设备管理:unix io接口
一个Linux文件就是一个m个字节的序列所有的I/O设备都被模型化为文件,而所有的输入输出都被当作相应文件读和写来进行,这种将设备优雅的映射为文件的方式,允许Linux内核引出一个简单的应用接口成为Unix I/O,这使得所有的输入输出都能以一种且一致的方式来执行。
8.2 简述Unix IO接口及其函数
(以下格式自行编排,编辑时删除)
接口就是连接CPU与外设之间的部件,它完成CPU与外界的信息传送。还包括辅助CPU工作的外围电路,如中断控制器、DMA控制器、定时器、高速CACHE。
函数:
打开文件:int open(char *filename, int flags, mode_t mode);
关闭文件:int close(int fd);
读文件:ssize_t read(int fd, void *buf, size_t n);
写文件:ssize_t write(int fd, const void *buf, size_t n);
8.3 printf的实现分析
(以下格式自行编排,编辑时删除)
https://www.cnblogs.com/pianist/p/3315801.html
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
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;
}
打印操作最终封装给用户的形式是printf()函数,它的定义在文件printf.c中。查看printf()的定义,函数中调用了putc()函数来进行输出,继续跟踪putc()函数的定义,我们发现write函数被调用了,在这里继续跟踪write函数,会发现它的声明在user.h中: int write(int,void*,int),但是并不能找到这个声明所对应的C代码形式的具体实现,这就是一个系统调用。追踪下write:
write:
mov eax, _NR_write
mov ebx, [esp + 4]
mov ecx, [esp + 8]
int INT_VECTOR_SYS_CALL
8.4 getchar的实现分析
(以下格式自行编排,编辑时删除)
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
(以下格式自行编排,编辑时删除)
本章大致了解了设备I/O的基本原理,明白了一些输入输出函数的底层实现
(第8章1分)
结论
用计算机系统的语言,逐条总结hello所经历的过程。
你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
(结论0分,缺失 -1分,根据内容酌情加分)
Hello经历的过程
(1).c文件 --> .i文件 --> .s文件 --> .o文件 --> Hello可执行文件 -->进程中运行 --> 子进程终止 --> 回收子进程
(2) 存储方式从硬盘中的二进制文本文件 --> 内存中 -->三级缓存中 -->虚拟内存 -->进程拥有区域存储Hello运行程序时花费的内存 -->进程回收 -->彻底消失
学习这门课我深深的体会到计算机系统的本质概念,学会了如何实实在在的提高程序的性能,如何避免难以察觉的错误,如何理解控制系统在计算机中的控制方法,理解不同的计算机部件发挥的作用,理解计算机系统一些很底层的实现等等等等。
我最受益良多的应该是对程序优化的理解,未曾想过矩阵转置改变行和列性能有翻天覆地的变化,运用局部变量和不运用性能也差别巨大,以后会在日常编程中仔细考虑对代码的操作。
附件
列出所有的中间产物的文件名,并予以说明起作用。
(附件0分,缺失 -1分)
列
Hello.i:编译预处理之后得到文件,用于生成.s文件
Hello.s:Hello.i编译之后的文件,用于研究程序汇编语言的实现,和生成.o文件
Hello.o: 可重定向二进制文件,还未重定向链接的文件,可用于查看计算机是如何对文件进行重定向处理的
Hello: 可执行文件,Hello.o链接后得到的文件,用于运行研究在bash中进程创建和执行文件的过程
asm1.s:hello.o反汇编得到的文件,用于研究汇编之后对文件重定向的处理
asm2.s:Hello反汇编得到的文件,用于研究链接对文件进行的重定向是如何处理的
为完成本次大作业你翻阅的书籍与网站等
[1] 百度百科--逻辑地址、线性地址
https://baike.baidu.com/item/%E9%80%BB%E8%BE%91%E5%9C%B0%E5%9D%80/3283849?fr=aladdin
[2] 优快云 关于虚拟地址和物理地址
https://blog.youkuaiyun.com/tjh_i_can/article/details/80117005
[3] 优快云 bash处理一条命令的步骤
https://blog.youkuaiyun.com/astrotycoon/article/details/78299024
[4] 大卫、兰德尔著 CSAPP《深入理解计算机系统》 机械工业出版社
(参考文献0分,缺失 -1分)