计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 航天学院
学 号
班 级 2036017
学 生
指 导 教 师 史先俊
计算机科学与技术学院
2022年5月
本大作业以hello的一生,介绍了程序的P2P过程和020过程,对hello.c到hello的过程、hello的进程管理、存储管理以及UNIX I/O进行了分析和讲述,是对所学知识的考验与整理。
关键词:hello;P2P;020;
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目 录
2.2在Ubuntu下预处理的命令.......................................................................... - 5 -
3.2 在Ubuntu下编译的命令............................................................................. - 6 -
4.2 在Ubuntu下汇编的命令............................................................................. - 7 -
5.2 在Ubuntu下链接的命令............................................................................. - 8 -
5.3 可执行目标文件hello的格式.................................................................... - 8 -
6.2 简述壳Shell-bash的作用与处理流程..................................................... - 10 -
6.3 Hello的fork进程创建过程..................................................................... - 10 -
6.6 hello的异常与信号处理............................................................................ - 10 -
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 -
8.1 Linux的IO设备管理方法.......................................................................... - 13 -
8.2 简述Unix IO接口及其函数....................................................................... - 13 -
第1章 概述
1.1 Hello简介
根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
Hello的P2P过程:P2P是From Program to Program,就是将程序转变为进程的过程。源文件hello.c经过预处理(hello.i),编译(hello.s),汇编(hello.o),最后链接生成可执行文件hello.out。
Hello的020过程:020是From Zero-0 to Zero-0,shell先用fork创建一个进程,用execve加载并执行可执行文件hello,建立起虚拟内存地址空间和磁盘文件的映射,建立虚拟内存和物理内存的映射,进入main函数,CPU为程序分配时间片执行逻辑控制流,程序调用系统I/O。程序结束后,shell会回收相应的进程。
1.2 环境与工具
列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。
硬件环境:CPU: Intel(R) Core(TM) i7-10870H CPU @ 2.20GHz 2.21 GHz;
RAM: 16.0 GB (15.8 GB 可用);
软件环境:Windows 10 64位;Ubunt 20.04.4;VMWare Workstation PRO 14.0;
开发工具:VS2022;GDB;EDB;Codeblocks;
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等
hello.c | 源文件 |
hello.i | hello.c的预处理结果 |
hello.s | hello.i的编译结果 |
hello.o | hello.s 汇编的结果 |
hello | hello.o经过链接得到的可执行文件 |
hello.elf | hello的elf文件 |
hello_o | hello.o的elf文件 |
hello_objdump | hello的反汇编文件 |
hello_o_objdump | hello.o的反汇编文件 |
1.4 本章小结
第一章对hello的P2P和020过程做了介绍,并列出本次大作业的硬件环境、软件环境、开发工具,展示了各个过程产生的中间文件。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
1.预处理:是做些代码文本的替换工作。处理 # 开头的指令 , 比如拷贝 #include 包含的文件代码, #define 宏定义的替换 , 条件编译等,就是为编译做预备工作的阶段,主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。
2.作用:
(1)在编译预处理时,对程序中所有出现的宏名(#define),都用宏定义中的字符串去代换,这称为宏替换或宏展开。在调用中,要用实参去代换形参。
(2)将注释删除,只保留程序的有效部分。
(3)处理所有条件预处理指令,根据#if、#endif、#ifdef来判断执行编译的条件。
(4)遇到#include时将头文件的内容插入到该命令的位置,将头文件和当前源文件连接起来。
2.2在Ubuntu下预处理的命令
预处理指令:cpp hello.c > hello.i,如下图:
结果是产生了hello.i:
2.3 Hello的预处理结果解析
hello.i的内容如下:
图片仅展示部分内容,经过预处理,hello.c被拓展成了3000多行的hello.i,原来源文件hello.c中的内容得到保留,但大部分内容是头文件stdio.h、unistd.h、stdlib.h中的内容,如各种变量、函数、结构体的定义等等。
2.4 本章小结
本章主要是介绍了预处理的定义和功能,将.c文件变为.h文件,从hello.c到hello.h内容的变化可看出预处理的作用:宏替换、头文件展开、注释消除等功能。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译:编译器(ccl)将hello.i翻译成汇编语言的hello.s的过程,将hello.i中的代码转化为汇编指令,形成文本文件hello.s。
作用:
1.语法检查:编译器会对hello.i中的代码的词法、语法等进行检查,若存在错误则会产生错误提示信息。
2.优化:由程序代码转变成汇编语言,运行时间会更短或占用的资源会更少。
3.2 在Ubuntu下编译的命令
编译指令:gcc -S hello.i -o hello.s,如图:
结果是产生了hello.s,如图:
3.3 Hello的编译结果解析
hello.s的内容如下图,hello.s中的语言已变成了汇编语言,下面将会对汇编是如何对其中的各种数据类型和各类操作进行我自己的解释。
3.3.1 局部变量 i,argc
int i:
int argc:
以i为例,局部整型变量i储存在栈中,当有输入时会先储存在寄存器中,在使用时会放入栈中。
3.3.2 数组 cahr* argv[]
char*类型的数组存储的是首地址,由上图可知argv[]的首地址存储在%rbp-32(即-32(%rbp)),moveq将其(argv[0])传给rax。addq指令将rax+16指向了argv[2],同理,rdx存储的就是argv[2]。这种方法类似于二维数组的调用方法。
3.3.3 控制转移:if、for
(1)if:
用cmpl指令判断%rbp-20(即argc)是否等于4,若相等,跳过if语句中的内容(即26行到29行),若不等,则执行下一行(即26行,if语句中的内容)。
(2)for:
先执行movl指令,给i(%rbp-4)赋值为0,再跳转到L3去判断i是否符合循环范围,然后按循环内的语句执行,每次循环i都会加1(第51行的addl指令)。
3.3.4关系操作:!=、<
(1)!=:
用je进行判断是否相等,若相等,则跳到L2,若不相等,则按顺序执行。
(2)<:
用jle来判断i(-4(%rbp))是否等于7,即<8,若等于则跳到L4(for中的语句),若不想等,则按顺序执行。
3.3.5函数操作:printf()、exit()、sleep()、atoi()
(1)printf():
printf函数在汇编中变成了puts,直接将储存在%rip和%rdi中的字符串输出。
(2)exit():
先用movl指令将edi赋值为1,然后执行exit。
(3)sleep():
(4)atoi():
3.3.6赋值:=
直接用movl指令将i赋值为1(i=1)。
3.4 本章小结
本章中编译操作把hello.i变成了hello.s,并对汇编语言中各类数据类型和各类操作进行了详细的介绍。
(以下格式自行编排,编辑时删除)
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
汇编:将汇编语言的基本指令的文本hello.s变成机器语言的文本hello.o。
作用:将汇编语言变成机械语言。
4.2 在Ubuntu下汇编的命令
汇编指令:gcc -c hello.s -o hello.o,结果如下图:
4.3 可重定位目标elf格式
分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。
输入readelf -a hello.o > hello_o生成hello.o的elf格式,elf头的信息如下:
由elf头可知hello.o的elf格式文件的类别、数据、ABI版本等等信息。
(1)其各节的基本信息:
节头包含了各节的名称、类型、地址、偏移量等信息
(2)重定位项目分析:
重定位节包含了偏移量、信息、类型、符号值、符号名称+加数等信息。
1.偏移量:要修改的位置在.text节的偏移量;
2.类型:重定位类型;
3.符号名称:重定位符号名称;
4. R_X86_64_PC32:重定位一个使用32位PC相对地址的引用;
5. R_X86_64_PLT32:重定位一个使用32位绝对地址的引用;
4.4 Hello.o的结果解析
objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。
说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。
在终端中输入objdump -d -r hello.o指令,结果如下图:
hello.s内容如下:
由两图对比可得:hello.s与hello.o获得的反汇编代码近似相同,但存在部分区别:
(1)hello.s为十进制文本,hello.o的反汇编代码为十六进制;
(2)在hello.s中利用符号实现跳转,hello.o的反汇编代码根据地址跳转;
(3)在调用函数时,hello.s用callq+函数名,而反汇编采用callq+地址。
4.5 本章小结
本章讲述了hello.s经过汇编变成hello.o的过程,并了解了hello.o的elf格式内容和反汇编,找到了编译文件同反汇编文件的映射关系。
(第4章1分)
第5章 链接
5.1 链接的概念与作用
注意:这儿的链接是指从 hello.o 到hello生成过程。
链接:是将各种代码和数据片段收集开组告成一个单一文件的过程,这个文件可被加载(复制)到内存并执行。在这里就是将重定位文件hello.o与其他文件按照适当的格式连接起来生成可执行文件hello。
作用:可直接将重定位文件同库函数连接起来,优化程序结构。
5.2 在Ubuntu下链接的命令
使用ld的链接命令,应截图,展示汇编过程! 注意不只连接hello.o文件
使用ld的链接命令生成hello,结果如下:
使用ld命令将重定位文件hello.o同linux已有的库链接起来,形成可执行文件hello
5.3 可执行目标文件hello的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
在终端输入readelf -a hello > hello_elf生成hello.o的elf格式,elf头的信息如下:
可看出其与hello.o的elf文件基本相同。
由节头可看到hello中各节的信息。
5.4 hello的虚拟地址空间
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
edb加载hello的界面如下:
由5.3中elf的节头可知.text节开始位置是0x4010f0,从上图可看到hello相应地址的内容,再去查看hello的反汇编文件对应地址的内容:
可看到内容相同,即反汇编文件和edb反汇编相同。
5.5 链接的重定位过程分析
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
通过objdump -d -r hello.o(hello)指令,可得到如下结果:
由图可知:(1)hello.o只有.text节,而hello有其它节。
(2)在函数和具体参数上,hello.o的反汇编文件是用地址来代替函数名和数值,而hello是直接表示出来的。
(3)hello的函数、参数与虚拟内存存在一一映射,而hello.o的函数地址都是从0开始的。
5.6 hello的执行流程
使用edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。
5.7 Hello的动态链接分析
对于动态的链接库,编译器无法实时的表示出函数运行的地址,需要重新添加重定位,链接器对此采用延迟绑定的方法,用got存储函数的目标地址,got.plt中存储目标地址的代码逻辑。由hello的elf文件hello_elf可知.got和.got.plt的地址:
使用edb查看对应地址的内容:
运行后:
可知运行前后对应地址发生了变化,这就是根据延迟绑定的方法调整的结果。
5.8 本章小结
本章介绍了源文件hello.c到可执行文件hello的最后一步,着重对执行流程和动态链接进行了分析。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
1.进程:经典定义就是一个执行中程序的实例。系统的每个程序都运行在某个进程的上下文中。每次用户通过向shell输人一个可执行目标文件的名字,运行程序时,shell 就会创建一个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件。应用程序也能够创建新进程,并且在这个新进程的上下文中运行它们自己的代码或其他应用程序。
2.作用:
(1)一个独立的逻辑控制流,它0.提供一个假象,好像我们的程序独占的使用处理器。
(2)另一个私有的地址空间,它提供一个假象,好像我们的程序独占的使用内存系统。
6.2 简述壳Shell-bash的作用与处理流程
1.作用:Bash Shell是一个命令解释器,它在操作系统的最外层,负责用户程序与内核进行交互操作的一种接口,将用户输入的命令翻译给操作系统,并将处理后的结果输出至屏幕。
2.处理流程:
1)从终端读入输入的命令。
2)将输入字符串切分获得所有的参数。
3)如果是内置命令则立即执行。
4)否则调用相应的程序执行。
5)shell 应该接受键盘输入信号,并对这些信号进行相应处理。
6.3 Hello的fork进程创建过程
当运行可执行文件hello是,shell会对命令进行解析,若不是内置命令,则会通过调用fork函数常见一个子进程,该子进程和父进程是并发运行的独立进程,内核能以任意方式交替执行它们的逻辑控制流中的指令。它们拥有相同但独立的地址空间,对同一变量有自己的私有空间,互不影响。它们之间也可以共享文件。
6.4 Hello的execve过程
当调用execve函数时会在当前进程的上下文中加载并运行一个新的程序,若该程序是hello,那么加载器将会删除已有的内存,重新定义新的一组参数,例如hello里的argc、argv[]等,再将新的参数初始化为原文件中的内容,最后再将其与源程序连接上。
6.5 Hello的进程执行
结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。
当可执行文件hello开始执行时,此时运行在用户态,当hello进程调用printf函数时引发系统调用,使程序进入核心态,当调用完成后又会切回用户态。当调用sleep函数时,也会切入核心态,但进程将会进行上下文切换而不是将控制返回给进程。
6.6 hello的异常与信号处理
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
信号异常包括四种情况:中断、陷阱、故障和终止。下面是对几种输入指令的尝试。
1.正常情况:
2.不停乱按(不包括终止信号):
可看出:当程序还在执行时乱按对其并无太大影响,但在程序运行完成后将会按照乱输入的语句一条一条的判断其是否是有效指令。
(3)按Ctrl+z:
可知:当按下Ctrl+z时,程序将会挂起,但没有回收。
1.若在此时输入ps或jobs,则:
jobs 将会展示后台进程,ps在可以查看当前瞬间进程的动态。
2.若在此时输入pstree:
pstree指令会以树形结构展示进程和进程之间的关系。
3.若输入fg:
fg指令则会将后台剩下的进程调出并继续执行。
4.若输入kill:
kill指令将挂起的hello进程终止。
(4)若按下Ctrl+c,效果如下:
可看到Ctrl+c不仅将hello进程停止,并且将其回收。
6.7本章小结
本章以hello为例详细的介绍了进程、shell的定义、作用及一般工作流程,并对hello的异常和信号处理进行了探讨。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
(以下格式自行编排,编辑时删除)
结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。
1.逻辑地址:CPU所生成的地址。逻辑地址是内部和编程使用的、并不唯一。例如,你在进行C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址(偏移地址),不和绝对物理地址相干。以hello为例,反汇编文件中看到的地址就是逻辑地址。
2.线性地址:指虚拟地址到物理地址变换的中间层,是处理器可寻址的内存空间(称为线性地址空间)中的地址。程序代码会产生逻辑地址,或者说段中的偏移地址,加上相应段基址就成了一个线性地址。如果启用了分页机制,那么线性地址可以再经过变换产生物理地址。若是没有采用分页机制,那么线性地址就是物理地址。
3.虚拟地址:是CPU保护模式下的一个概念,保护模式是80286系列和之后的x86兼容CPU操作模式,在CPU引导完操作系统内核后,操作系统内核会进入一种CPU保护模式,也叫虚拟内存管理,在这之后的程序在运行时都处于虚拟内存当中,虚拟内存里的所有地址都是不直接的,所以你有时候可以看到一个虚拟地址对应不同的物理地址。
4.物理地址:加载到内存地址寄存器中的地址,内存单元的真正地址。在前端总线上传输的内存地址都是物理内存地址,编号从0开始一直到可用物理内存的最高端。这些数字被北桥(Nortbridge chip)映射到实际的内存条上。物理地址是明确的、最终用在总线上的编号,不必转换,不必分页,也没有特权级检查。
7.2 Intel逻辑地址到线性地址的变换-段式管理
以hello(用edb加载)为例:
可看到指令:call 0x7fdfdff0bdf0,可知0x7fdfdff0bdf0就是逻辑地址,必须加上DS数据段的基地址,才能构成线性地址,即0x7fdfdff0bdf0是DS数据段内的偏移。
在保护模式下,段信息没有直接存储在段寄存器中。Intel是将段描述符集中存储在GDT或LDT中,段寄存器存储的是描述符在其中的索引值。即先在段寄存器中找到对应的段描述赋在GDT或LDT中的索引值,再到GDT或LDT中查找段描述符。在linux中逻辑地址基本等于线性地址。
7.3 Hello的线性地址到物理地址的变换-页式管理
分页的基本原理是把内存划分成大小固定的若干单元,每个单元称为一页,每页包含4k字节的地址空间。这样每一页的起始地址都是4k字节对齐的。为了能转换成物理地址,我们需要给CPU提供当前任务的线性地址转物理地址的查找表,即页表。
在保护模式下,控制寄存器CR0的最高位PG位控制着分页管理机制是否生效,如果PG=1,分页机制生效,需通过页表查找才能把线性地址转换物理地址。如果PG=0,则分页机制无效,线性地址就直接做为物理地址。
以call 0x7fdfdff0bdf0为例,将0x7fdfdff0bdf0转换成二进制,再去最高10位,转换成十进制是n,CPU回去查看页表第n项,里面存放的就是页表的物理地址。
7.4 TLB与四级页表支持下的VA到PA的变换
基于四级页表,虚拟地址(VA)被划分成4个VPN和1个VPO。每个VPNi都是到第i级页表的索引,第j级页表中的每个PTE都指向第j+1级的某个页表的基址。第k级页表中的每个PTE包含某个物理页面的PPN。VPO与对应的部分组合起来就得到了物理地址(PA)。
7.5 三级Cache支持下的物理内存访问
(以下格式自行编排,编辑时删除)
在三级缓存L1、L2、L3的支持下,内存管理单元会将物理地址发送到L1,L1会查找CT(标记)、CI(组索引)、CO(偏移量),若查找到CT则找到偏移量为CO的数据,将其返回到CPU;若无,则按L2、L3的顺序依次查找。
7.6 hello进程fork时的内存映射
当fork函数被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的pid。为了给这个新进程创建虚拟内存,它创建了当前进程的mm_struct、区域结构和页表的原样副本。它将两个进程中的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有写时复制。
当fork从新进程返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间的抽象概念。
7.7 hello进程execve时的内存映射
当hello调用了execve函数:execve(“a.out”, NULL, NULL),execve会在当前进程中加载并运行包含在可执行目标文件a.out中的程序,用a.out程序有效的替代来当前程序。在加载和运行a.out前需进行:
(1)删除已存在的用户区域。
(2)映射私有区域。
(3)映射共享区域。
(4)设置程序计数器。
7.8 缺页故障与缺页中断处理
缺页指的是DRAM缓存不命中,如下图所示,CPU引用了VP3的一个字,VP3并未缓存在DRMA中。地址翻译硬件从内存中读取PTE3,从有效位推断出VP3未被缓存,触发一个缺页异常。
这时会调用内核中的缺页异常处理程序,该程序会选择一个牺牲页(图中的VP4),反映出VP4不再缓存在主存中。
接下来,内核从磁盘复制VP3到内存中的PP3,更新PTE3,此时VP3已缓存在主存中,页命中已经能正常处理。
7.9动态存储分配管理
Printf会调用malloc,请简述动态内存管理的基本方法与策略。
动态内存分配器维护着一个进程的虚拟内存区域,称为堆。系统之间的细节不同,但是不失通用性,假设堆是一个请求二进制零的区域,它紧接在未初始化的数据区域后开始,并向上生长(向更高的地址)。对于每个进程,内核维护着一个变量brk,它指向堆的顶部。
分配器将堆视为一组不同大小的块的集合来维护。每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可以用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被释放。这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。
hello中的printf函数调用了malloc函数,malloc函数会返回一个指针,指向大小至少为size字节的内存块,会将这个块内的数据对象类型对齐。每次必须从空闲块中分配空间,在申请空间时将空闲的空间碎片合并,以尽量减少浪费。
7.10本章小结
本章介绍了hello的存储及调用时的过程,并且对流程、缺页异常及处理方式都进行了简略的表述。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:文件
设备管理:unix io接口
一个Linux文件就是一个m字节的序列,所有的I/O设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行。
8.2 简述Unix IO接口及其函数
(1)Unix I/O接口:
1.打开文件:一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备,内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件,内核记录有关这个打开文件的所有信息。应用程序只需记住这个描述符。Linux shell创建的每个进程开始时都有三个打开的文件: 标准输入(描述符为0)、标准输出(描述符为1)和标准错误(描述符为2)。
2.Linux shell创建的每个进程开始时都有三个打开的文件:标准输入(描述符为0)。标准输出(描述符为1)和标准错误(描述符为2)。头文件<unistd.h>定义了常量STDIN_FILENO、STDUOT_FILENO和STDERR_FILENO,它们可用来代替显式的描述符值。
3.改变当前的文件位置。 对于每个打开的文件,内核保持着一个文件位置k,k初始为0,表示的是从文件开头起始的字节偏移量,应用程序能够通过执行seek,显式地将改变当前文件位置k。
4.读写文件。一个读操作就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n。给定一个大小为m字节的而文件,当k>=m时执行读操作会触发一个成为end-of-file(EOF)的条件,应用程序能检测到这个条件。在文件结尾处并没有明确的“EOF符号”。类似一个写操作就是从内存中复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k。
5.关闭文件。当应用完成了对文件的访问之后,它就通知内核关闭这个文件。作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。无论一个进程因为何种原因终止时,内核都会关闭所有打开的文件并释放它们的内存资源。
(2)函数:
1.open函数:进程调用open函数来打开一个已存在的文件或者创建一个新文件,open函数将文件名转换为一个文件描述符,并且返回描述符数字。返回的描述符总是在进程中当前没有打开的最小描述符。
2.ssize_t read:进程调用该函数来读文件。
3. ssize_t write:进程调用该函数来写文件。
4.close函数:进程调用该函数来关闭文件。
8.3 printf的实现分析
先从printf函数的函数体来看:
函数中主要是调用了vsprintf函数,该函数接受确定输出格式的字符串fmt,用格式字符串将个数变化的参数进行格式,也就是字符串类型。之后再调用write函数,输出长度为i的字符串buf。
write函数会调用sys_call函数,将字符串复制到显卡中,然后启动字符显示驱动子程序,从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
(以下格式自行编排,编辑时删除)
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
当程序调用 getchar 时,程序就等着用户按键。用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止(回车字符 \n 也放在缓冲区中),当用户键入回车之后,getchar() 函数才开始从输入缓冲区中每次读取一个字符,getchar 函数的返回值是用户输入的字符的 ASCII 码。
8.5本章小结
(以下格式自行编排,编辑时删除)
本章介绍了主要讲述了UNIX I/O接口及常用函数,printf函数、getchar函数的实现。让本人对进程与UNIX I/O的关系有了更深的理解
(第8章1分)
结论
用计算机系统的语言,逐条总结hello所经历的过程。
你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
源文件hello.c经过预处理形成hello.i,对hello.i进行编译形成汇编语言文件hello.s,再经过汇编形成二进制文件hello.o,最后使用ld指令链接形成可执行文件hello。
在linux下运行hello,shell为其创造子进程,调用execve为其加载、执行,最后回收子进程。
本次大作业对本人来说是一次很大的挑战,也让我对本学期所学有了更深的理解,对其整体也有了更清楚的认识,希望能在不远的期末考试中取得满意的成绩。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.c | 源文件 |
hello.i | hello.c的预处理结果 |
hello.s | hello.i的编译结果 |
hello.o | hello.s 汇编的结果 |
hello | hello.o经过链接得到的可执行文件 |
hello.elf | hello的elf文件 |
hello_o | hello.o的elf文件 |
hello_objdump | hello的反汇编文件 |
hello_o_objdump | hello.o的反汇编文件 |
(附件0分,缺失 -1分)
参考文献
为完成本次大作业你翻阅的书籍与网站等
[1] 物理地址和逻辑地址_TuxedoLinux的博客-CSDN博客_逻辑地址和物理地址
[2] 详解:物理地址,虚拟地址,内存管理,逻辑地址之间的关系_17岁boy想当攻城狮的博客-CSDN博客_物理地址和虚拟地址
[3] Linux内存管理:逻辑地址到线性地址和物理地址的转换_pi9nc的博客-CSDN博客
[4] 深入理解计算机系统
[5] CSDN
[6] printf 函数实现的深入剖析 - Pianistx - 博客园 (cnblogs.com)
(参考文献0分,缺失 -1分)