概述
计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 计算机类
学 号
班 级
学 生 * *
指 导 教 师 *刘宏伟 *
计算机科学与技术学院
2019年12月
摘 要
本篇文章以程序员最熟悉的hello.c入手,力求“浅入深处”,在Linux环境下,着重分析其从程序到进程,从编辑到回收的过程,争取串联起其背后的计算机系统的作用以及相关操作的原理
关键词:计算机系统;Linux;
(摘要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:编辑器编辑hello.c,hello.c经过cpp的预处理、ccl的编译、as的汇编、ld的链接最终成为可执行目标程序hello,在shell中键入启动命令后,shell为其fork,产生子进程
之后shell为其execve,映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入
main函数执行目标代码,CPU为运行的hello分配时间片执行逻辑控制流,调用系统级I/O,当程序运行结束后,shell父进程负责回收hello进程。
以上全部便是020的过程。
1.2 环境与工具
CPU:Intel i5-8300H
内存:8GB
Linux发行版版本:Ubuntu 18.04
虚拟机版本:VMwareWorkStation 15
开发工具:gcc as ld edb objdump readelf
1.3 中间结果
hello.i | 预处理之后文本文件 |
---|---|
hello.s | 编译之后的汇编文件 |
hello.o | 汇编之后的可重定位目标程序 |
hello | 链接之后的可执行文件 |
hello.elf | hello.o的elf格式 |
hello2.elf | hello的elf格式 |
hello.objdump | hello.o的反汇编 |
hello2.objdump | hello的反汇编 |
1.4 本章小结
本章简单概括了hello的一生,并简要提出后面将要介绍的相关环节
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
1.概念
预处理器(cpp)根据以字符#开头的命令,修改原始的C程序
2.作用
展开头文件
宏替换
去掉注释
条件编译
2.2在Ubuntu下预处理的命令
gcc -E -o hello.i hello.c
图2.1 预处理命令
2.3 Hello的预处理结果解析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gqshjgtw-1577167676176)(media/df87c8276af6aca08bb59a87184da1df.jpg)]
图2.2 hello.i中main函数的部分
整个hello.i是一个非常长的一个文件,在main函数之前是hello.c前三个头文件的依次展开(stdio.h,stdlib.h,units.h)
下面以stdio.h展开的一小部分为例子
图2.3 stdio.h的部分展开
可以看到stdio.h里面的函数都已经被展开,包括printf,sprintf等
2.4 本章小结
Hello.c本身是不能直接被运行,需要进行一系列的处理,在本章中,通过预处理器进行操作修改之后,得到hello.i文件,开启了hello.c在计算机系统漫游的第一站
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
概念:编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序
作用: 进行语法检查,生成汇编语言程序(文本)
3.2 在Ubuntu下编译的命令
gcc -s hello.i -o hello.s
图3.1 编译命令
3.3 Hello的编译结果解析
3.3.1 数据
Hello.s的数据主要有两种,整数和字符串
字符串
图3.2 hello.s中的字符串
分析
.LC0展示的是printf里面的内容 其中汉字采取了UTF编码,一个汉字占3个字节
.LC1展示的是第二个printf里面的内容
整数
图3.3 hello.s中的常值 sleepsecs
分析
sleepsecs作为全局变量存储在.data段 大小4个字节 被设定为long型
数值为2
其他还有int i 以及argc
argc 作为第一个参数传入
i 局部变量 存储在栈中
图3.4 hello.s中的变量i
通过分析代码以及汇编,i存储位置在%rbp-0x4的位置
3.3.2 赋值
程序中赋值的只有i以及sleepsecs
sleepsecs
全局变量 在3.3.1中已经详细解读 此处不再赘述
i
图3.5 hello.s中i的赋值
查看汇编用的是movl语句进行赋值
3.3.3 类型转换
涉及的类型转换只有一处,关于sleepsecs的部分
在图3.1中,可以看到原来sleepsecs是浮点型的2.5,取整为long型的,而且值为2
3.3.4 算数操作
i++
在图3.4中的第一条语句,用addl实现了i++
3.3.5关系操作
argc!=3
图3.6 argc的逻辑操作
汇编语言中用了cmpl语句进行逻辑操作
i<10
详见图3.4中cmpl语句,将i的值与9进行比较
3.3.6数组
argv[]
图3.7 hello.s中argv[]
两次(%rax)分别为argv[1]和argv[2]的地址
3.3.7 控制传递
if(argc!=3)
图3.8 argc的控制传递
直接采用je语句进行跳转
for语句
图3.9 for循环操作
整个for循环贯穿.L2,.L3,.L4
.L2进行了i的初始化
.L3进行了循环条件判断,满足条件进入.L4进行操作,实现循环控制
3.3.8 函数操作
hello.c包含以下函数
main printf exit getchar sleep
图3.10 hello.s中的函数
除了main以外 都是库函数 用call语句进行调用 调用前 如有必有要传递参数
3.4 本章小结
本章hello到了自己的第二站,变成了我们本来不怎么熟悉的汇编形式,通过对汇编语言所写的hello.s进行结构,可以知道其实我们C语言里面的信息已经被编译器翻译成相对应的语句,继而hello可以继续在计算机系统漫游
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
概念 汇编器将hello.s翻译成机器语言指令 把指令打包成可重定位目标程序
作用 将hello.s翻译成最底层指令,机器可读
4.2 在Ubuntu下汇编的命令
as hello.s -o hello.o
图4.1 汇编指令
4.3 可重定位目标elf格式
指令 readelf -a hello.o > hello.elf
图4.2 节头部表
4.3.1 节头部表
如图4.2所示 节头部表展示了各个节的相关信息
4.3.2 重定位节
图4.3 重定位节
.text节需要的重定位信息
8条重定位的信息分别对应以下函数调用
第一个字符串puts函数 exit函数 第二个字符串 printf函数 sleepsecs sleep函数
getchar函数
根据其中给定的信息 可计算出*refptr 进而进行重定位(PC相对寻址)
4.3.3 .rela.eh_frame
图4.4 .rela.eh_frame
eh_frame重定位信息
4.3.4符号表
存放程序中引用和定义的函数和全局变量的信息
4.4 Hello.o的结果解析
图4.6 反汇编结果(1)
图4.7 反汇编结果(2)(图4.6续)
分析
分支控制
在反汇编中不再有.L3这类的辅助标注,而是有了准确的地址
函数调用
有了准确地址和重定位信息,.s中callq后面是函数名
而反汇编函数调用中的callq是下一条指令指向的地址
.rodata调用
图4.8 hello.s中对全局变量的调用
在反汇编中,同样没有了.LC0,而是直接设置为0,并同时添加了重定位信息
4.5 本章小结
本章hello有一次经过as的处理,变成我们无法直接直接阅读的hello.o,通过反汇编之后,我们可以直接对其进行分析,发现经过重定位之后,hello不再是一个孤立的个体,可以与我们的库函数连接起来了,那么它很快就可以进行最终的登场亮相了
(第4章1分)
第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.1 链接命令
5.3 可执行目标文件hello的格式
指令 readelf -a hello > hello2.elf
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GqpUIUCt-1577167676188)(media/ebdfe62a93da63eaf38e167163b0bf13.jpg)]
图5.2 节头部表(1)
图5.3 节头部表(2)(图5.2续集)
section header里面列出了所有节信息
size代表大小 offset代表程序中的偏移量
address代表程序被载入到虚拟地址的起始位置
5.4 hello的虚拟地址空间
图5.4 程序头表
程序头表在执行的时候被使用,它告诉链接器运行时加载的内容并提供动态链接的信息。每一个表项提供了各段在虚拟地址空间和物理地址空间的大小、位置、标志、访问权限和对齐方面的信息
图5.5 EDB查看hello
从0x400000到0x401000,程序被载入
每个节的排列与5.2中的声明相同
5.5 链接的重定位过程分析
通过图5.2可知,hello的节数明显增多
而且在hello的反汇编中可以看到加入了动态链接共享库,也就是说
链接器的作用是
将hello.c调用的函数进行引入
图5.6 hello的反汇编(部分)
重定位分析
函数调用
因为已经引进了动态链接库,所以链接器将寻址类型是PLT32的进行解析,调用值也被修改为下一条指令与PLT中函数的相对值
.rodata调用
与hello.o的算法一致,链接器计算下一条指令与目标地址之差,并赋给call后的值
5.6 hello的执行流程
图5.7 部分流程函数
ld-2.27.so!_dl_start
ld-2.27.so!_dl_init
hello!_start
libc-2.27.so!__libc_start_main
-libc-2.27.so!__cxa_atexit
-libc-2.27.so!__libc_csu_init
hello!_init
libc-2.27.so!_setjmp
-libc-2.27.so!_sigsetjmp
–libc-2.27.so!__sigjmp_save
hello!main
hello!puts@plt
hello!exit@plt
*hello!printf@plt
*hello!sleep@plt
*hello!getchar@plt
ld-2.27.so!_dl_runtime_resolve_xsave
-ld-2.27.so!_dl_fixup
–ld-2.27.so!_dl_lookup_symbol_x
libc-2.27.so!exit
5.7 Hello的动态链接分析
动态链接的基本原理
PLT+GOT
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J0oPpeo6-1577167676191)(media/df9ad4dd7c40607c832f03316919da61.jpg)]
图5.8 确定.got.plt位置
根据elf文件的结果,.got.plt的虚拟起始地址为0x601000
用edb观察以下其实起始是什么情况
如下图所示
图5.9 .got.plt起始状态
再来看一下dl_init之后是什么状态
如下图所示
图5.10 dl_init之后.got.plt状态
根据5.10 GOT[1]是0x7f0758824170 GOT[2]是0x7f0758612680
可以用edb追踪以下这两个位置是什么
图5.11 GOT[1]的地址指向的信息
可以发现指向是重定向表
图5.12 GOT[2]的地址指向的信息
发现指向的是动态链接器的程序
在之后的函数调用时,首先跳转到PLT执行.plt中逻辑,第一次访问跳转时GOT地址为下一条指令,将函数序号压栈,然后跳转到PLT[0],在PLT[0]中将重定位表地址压栈,然后访问动态链接器,在动态链接器中使用函数序号和重定位表确定函数运行时地址,重写GOT,再将控制传递给目标函数。之后如果对同样函数调用,第一次访问跳转直接跳转到目标函数。
5.8 本章小结
本章通过各种方式分析hello的elf形式,反汇编,还有进行edb单步调试,分析了hello的虚拟空间与动态链接等问题
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
概念:一个执行中的过程实例
作用:为用户提供了以下假象:我们的程序好像是系统中当前运行的唯一程序一样,我们的程序好像是独占的使用处理器和内存,处理器好像是无间断的执行我们程序中的指令,我们程序中的代码和数据好像是系统内存中唯一的对象
6.2 简述壳Shell-bash的作用与处理流程
作用:Shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口。它接收用户输入的命令并把它送入内核去执行
处理流程:shell首先检查命令是否是内部命令,若不是再检查是否是一个应用程序(这里的应用程序可以是Linux本身的实用程序,如ls和rm,也可以是购买的商业程序,如xv,或者是自由软件,如emacs)。然后shell在搜索路径里寻找这些应用程序(搜索路径就是一个能找到可执行程序的目录列表)。如果键入的命令不是一个内部命令并且在路径里没有找到这个可执行文件,将会显示一条错误信息。如果能够成功找到命令,该内部命令或应用程序将被分解为系统调用并传给Linux内核。
6.3 Hello的fork进程创建过程
子进程获得与父进程用户级虚拟地址空间相同的(但是独立的)一份副本
包括代码和数据段,堆,共享库以及用户栈,还获得与父进程任何打开文件描述符相同的副本
6.4 Hello的execve过程
子进程调用execve函数(传入命令行参数)在当前进程的上下文中加载并运行一个新程序即hello程序,execve调用驻留在内存中的被称为启动加载器的操作系统代码来执行hello程序,加载器删除子进程现有的虚拟内存段,并创建一组新的代码、数据、堆和栈段。新的栈和堆段被初始化为零,通过将虚拟地址空间中的页映射到可执行文件的页大小的片,新的代码和数据段被初始化为可执行文件中的内容。最后加载器设置PC指向_start地址,_start最终调用hello中的main函数
6.5 Hello的进程执行
hello进程最初运行在用户模式,当调用sleep之后,进入内核模式
内核通过上下文切换,将控制权交给其他进程,定时器时间到sleep设定时间时,发送一个中断信号,内核进行中断处理,hello可以继续自己的逻辑控制流
当hello调用getchar的时候,实际落脚到执行输入流是stdin的系统调用read,hello之前运行在用户模式,在进行read调用之后陷入内核,内核中的陷阱处理程序请求来自键盘缓冲区的DMA传输,并且安排在完成从键盘缓冲区到内存的数据传输后,中断处理器。此时进入内核模式,内核执行上下文切换,切换到其他进程。当完成键盘缓冲区到内存的数据传输时,引发一个中断信号,此时内核从其他进程进行上下文切换回hello进程
6.6 hello的异常与信号处理
图6.1 hello正常结束
如图6.1所示,hello正常结束,进程被回收
图6.2 按下ctrl+c的结果
如图6.2所示,父进程收到SIGINT信号,直接结束hello并回收hello进程
图6.3 按下ctrl+z的结果
父进程收到SIGSTP的信号,hello进程被挂起但是没有回收,此时其后台作业号为1
用fg 1命令将其调入前台 将继续执行
QQ截图20191223140344
图6.4 乱按的结果
当getchar的时候读出一个’n’结尾的字串(作为一次输入),其他字串会当做shell命令行输入。
6.7本章小结
在本章中hello不再是一个程序,而是以进程的什么在计算机系统漫游,本章通过总结进程的基本概念,并结合hello进程实际情况,对进程加以分析总结,以及信号机制等分析
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:由段地址+偏移量组成,在汇编语言里面所看见的地址,都是逻辑地址
线性地址:逻辑地址经过段机制翻译成线性地址,在页式管理中作为输入
虚拟地址:与线性地址基本类似
物理地址:真实物理内存的地址
7.2 Intel逻辑地址到线性地址的变换-段式管理[1]
图7.1 段地址(段选择符)组成
图7.2 每个段的段描述符
段管理机制
如图7.2所示,整个虚拟内存被分成了若干“段”
通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符
需要关注的是base,因为会提供段的起始位置线性地址
图7.3 段式管理地址变换
翻译机制
首先,给定一个完整的逻辑地址[段选择符:段内偏移地址],
1、看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。我们就有了一个数组了。
2、拿出段选择符中前13位,可以在这个数组中,查找到对应的段描述符,这样,它了Base,即基地址就知道了。
3、把Base + offset,就是要转换的线性地址了。
7.3 Hello的线性地址到物理地址的变换-页式管理
图7.4 页表管理地址翻译
系统将虚拟内存和物理内存分割成若干页 Linux下每个页大小4kb
图7.4介绍的是最简单的使用页表的地址翻译
通过页表基址寄存器PTBR+VPN在页表中获得条目PTE,一条PTE中包含有效位、权限信息、物理页号,如果有效位是0+NULL则代表没有在虚拟内存空间中分配该内存,如果是有效位0+非NULL,则代表在虚拟内存空间中分配了但是没有被缓存到物理内存中,如果有效位是1则代表该内存已经缓存在了物理内存中,可以得到其物理页号PPN,与虚拟页偏移量共同构成物理地址PA
7.4 TLB与四级页表支持下的VA到PA的变换
图7.5 Core i7的地址翻译
如图7.5所示,翻译机制大致如下
CPU产生虚拟地址VA,VA传送给MMU,MMU使用前36位VPN作为TLBT(前32位)+TLBI(后4位)向TLB中匹配,如果命中,则得到PPN(40bit)与VPO(12bit)组合成PA(52bit)。如果TLB中没有命中,MMU向页表中查询,CR3确定第一级页表的起始地址,VPN1(9bit)确定在第一级页表中的偏移量,查询出PTE,如果在物理内存中且权限符合,确定第二级页表的起始地址,以此类推,最终在第四级页表中查询到PPN,与VPO组合成PA,并且向TLB中添加条目。
如果查询PTE的时候发现不在物理内存中,则引发缺页故障。如果发现权限不够,则引发段错误。
7.5 三级Cache支持下的物理内存访问
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fUblD069-1577167676198)(media/5697535e4c4d1cf45e8ccbe712f5aa71.png)]
图7.6 三级cache访问内存(Core i7)
以L1为例
使用CI(后六位再后六位)进行组索引,每组8路,对8路的块分别匹配CT(前40位)如果匹配成功且块的有效位为1,则命中(hit),根据数据偏移量CO(后六位)取出数据返回
没有匹配成功或者有效位为0,则不命中,继续顺序访问L2,L3,主存
替换规则
如果有空闲块可直接替换
没有空闲块则发生冲突,可以采用LRU算法进行替换
7.6 hello进程fork时的内存映射
当fork函数被shell进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID,为了给这个新进程创建虚拟内存,它创建了当前进程的mm_struct、区域结构和页表的原样副本。它将这两个进程的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。
7.7 hello进程execve时的内存映射
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dES2weii-1577167676198)(media/69abbc0b562f3b4fd212d8917f63bf61.png)]
图7.7 加载器映射用户地址空间方式
加载并运行hello需要以下几个步骤:
1.删除已存在的用户区域,删除当前进程虚拟地址的用户部分中的已存在的区域结构。
2.映射私有区域,为新程序的代码、数据、bss和栈区域创建新的区域结构,所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为hello文件中的.text和.data区,bss区域是请求二进制零的,映射到匿名文件,其大小包含在hello中,栈和堆地址也是请求二进制零的,初始长度为零。
3.映射共享区域,
hello程序与共享对象libc.so链接,libc.so是动态链接到这个程序中的,然后再映射到用户虚拟地址空间中的共享区域内。
4.设置程序计数器(PC),execve做的最后一件事情就是设置当前进程上下文的程序计数器,使之指向代码区域的入口点。
7.8 缺页故障与缺页中断处理
缺页故障
当指令引用一个虚拟地址,在MMU中查找页表时发现与该地址相对应的物理地址不在内存中
缺页中断处理
缺页处理程序是系统内核中的代码,选择一个牺牲页面,如果这个牺牲页面被修改过,那么就将它交换出去,换入新的页面并更新页表。当缺页处理程序返回时,CPU重新启动引起缺页的指令,这条指令再次发送VA到MMU,这次MMU就能正常翻译VA了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oPuBcy7M-1577167676199)(media/8c9657e523c0c1f7283f077906689c72.png)]
图7.8 Linux缺页处理
如图7.8
缺页处理结果会对应三种情况,1,2两种情况的处理如图所示,正常缺页上面已经讲到不再赘述
7.9动态存储分配管理
printf函数会调用malloc,下面简述动态内存管理的基本方法与策略:
基本方法
动态内存分配器维护着一个进程的虚拟内存区域,称为堆。分配器将堆视为一组不同大小的块的集合来维护。每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。
分配器分为两种基本风格:显式分配器、隐式分配器。
显式分配器:要求应用显式地释放任何已分配的块。
隐式分配器:要求分配器检测一个已分配块何时不再使用,那么就释放这个块,自动释放未使用的已经分配的块的过程叫做垃圾收集。
基本策略
隐式空闲链表(首次适配+边界检查合并)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U7qlEtXL-1577167676199)(media/cefb2160db762b45eb57f6aabd8b0a12.jpg)]
图7.9 使用边界标记的堆块的格式
原理 将整个内存空间变成一个大链表,不直接对空闲块链接
堆块的设置采用图7.9的办法
7.10本章小结
本章以地址翻译为线索,串联起了x86-64系统的内存管理体系,详解了一个程序的存储体系结构
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
所有的IO设备都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行,这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单低级的应用接口,称为Unix
I/O。
8.2 简述Unix IO接口及其函数
Unix I/O接口统一操作:
1.打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备,内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件,内核记录有关这个打开文件的所有信息。
2.Shell创建的每个进程都有三个打开的文件:标准输入,标准输出,标准错误。
3.改变当前的文件位置:对于每个打开的文件,内核保持着一个文件位置k,初始为0,这个文件位置是从文件开头起始的字节偏移量,应用程序能够通过执行seek,显式地将改变当前文件位置k。
4.读写文件:一个读操作就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n,给定一个大小为m字节的而文件,当k>=m时,触发EOF。类似一个写操作就是从内存中复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k。
5.关闭文件,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中去。
Unix I/O函数:
int open(char* filename,int flags,mode_t mode)
int close(fd)
ssize_t read(int fd,void *buf,size_t n)
ssize_t wirte(int fd,const void *buf,size_t n)
8.3 printf的实现分析[2]
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本体函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xE2OeKdO-1577167676199)(media/3924e9210ce15162a1a44b8905de521d.jpg)]
图8.1 vsprintf函数
vsprintf函数作用
格式化。它接受确定输出格式的格式字符串fmt。用格式字符串对个数变化的参数进行格式化,产生格式化输出
write实现:
write:
mov eax, _NR_write
mov ebx, [esp + 4]
mov ecx, [esp + 8]
int INT_VECTOR_SYS_CALL
最后int调用了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
printf总流程
从vsprintf生成显示信息,到write系统函数,到syscall.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回
8.5本章小结
本章对于系统级的输出输出有了更加全面和深刻的剖析
同时对printf和getchar实现进行分析
(第8章1分)
结论
Hello的一生
编辑:通过编辑器/IDE编辑hello.c
预处理:将hello.c调用的所有外部的库展开合并到一个hello.i文件中
编译:将hello.i编译成为汇编文件hello.s
汇编:将hello.s会变成为可重定位目标文件hello.o
链接:将hello.o与可重定位目标文件和动态链接库链接成为可执行目标程序hello
运行:在shell中输入./hello 1170300825 lidaxin
创建子进程:shell进程调用fork为其创建子进程
运行程序:shell调用execve,execve调用启动加载器,加映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入
main函数。
访问内存:MMU将程序中使用的虚拟内存地址通过页表映射成物理地址。
动态申请内存:printf会调用malloc向动态内存分配器申请堆中的内存。
I/O管理: printf,getchar会调用系统I/O
信号:如果运行途中键入ctr-c ctr-z则调用shell的信号处理函数分别停止、挂起。
结束:shell父进程回收子进程,内核删除为这个进程创建的所有数据结构。
感悟总结
现在计算机系统的建立绝对不是一个人的功劳,而是许许多多的科学家智慧的结晶,通过CSAPP的学习,让我们可以更好的从软件这个方向去了解整个计算机体系大厦。或许我们可以从纯底层硬件的角度去看计算机系统,可能就是另外一番体验,本论文更多是以软件视角来看,如果与硬件结合更紧密,可能收获会更多
(结论0分,缺失 -1分,根据内容酌情加分)
附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.i | 预处理之后文本文件 |
---|---|
hello.s | 编译之后的汇编文件 |
hello.o | 汇编之后的可重定位目标程序 |
hello | 链接之后的可执行文件 |
hello.elf | hello.o的elf格式 |
hello2.elf | hello的elf格式 |
hello.objdump | hello.o的反汇编 |
hello2.objdump | hello的反汇编 |
(附件0分,缺失 -1分)
参考文献
[1] 段管理:https://www.cnblogs.com/zengkefu/p/5452792.html
[2] printf解析:https://www.cnblogs.com/pianist/p/3315801.html
[3] CSAPP教科书
(参考文献0分,缺失 -1分)
会调用malloc向动态内存分配器申请堆中的内存。
I/O管理: printf,getchar会调用系统I/O
信号:如果运行途中键入ctr-c ctr-z则调用shell的信号处理函数分别停止、挂起。
结束:shell父进程回收子进程,内核删除为这个进程创建的所有数据结构。
感悟总结
现在计算机系统的建立绝对不是一个人的功劳,而是许许多多的科学家智慧的结晶,通过CSAPP的学习,让我们可以更好的从软件这个方向去了解整个计算机体系大厦。或许我们可以从纯底层硬件的角度去看计算机系统,可能就是另外一番体验,本论文更多是以软件视角来看,如果与硬件结合更紧密,可能收获会更多
(结论0分,缺失 -1分,根据内容酌情加分)
附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.i | 预处理之后文本文件 |
---|---|
hello.s | 编译之后的汇编文件 |
hello.o | 汇编之后的可重定位目标程序 |
hello | 链接之后的可执行文件 |
hello.elf | hello.o的elf格式 |
hello2.elf | hello的elf格式 |
hello.objdump | hello.o的反汇编 |
hello2.objdump | hello的反汇编 |
(附件0分,缺失 -1分)
参考文献
[1] 段管理:https://www.cnblogs.com/zengkefu/p/5452792.html
[2] printf解析:https://www.cnblogs.com/pianist/p/3315801.html
[3] CSAPP教科书
(参考文献0分,缺失 -1分)
最后
以上就是冷艳月饼为你收集整理的HIT 2019 Autumn CSAPP大作业第1章 概述第2章 预处理第3章 编译第4章 汇编第5章 链接第6章 hello进程管理第7章 hello的存储管理第8章 hello的IO管理结论附件参考文献附件参考文献的全部内容,希望文章能够帮你解决HIT 2019 Autumn CSAPP大作业第1章 概述第2章 预处理第3章 编译第4章 汇编第5章 链接第6章 hello进程管理第7章 hello的存储管理第8章 hello的IO管理结论附件参考文献附件参考文献所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复