哈工大2024春csapp大作业——程序人生-Hello’s P2P

计算机系统

大作业

题     目  程序人生-Hello’s P2P 

专       业  网络空间安全          

学     号  2022110717            

班     级  2203901               

学       生  胡丁鹏              

指 导 教 师  史先俊                

计算机科学与技术学院

2024年5月

摘  要

本文将以hello.c程序为例,从计算机系统的角度阐述一个c代码从源程序到可执行程序的转变过程,以及其作为进程的运行过程。C语言源程序首先需要经过预处理、编译、汇编、链接等步骤,才能生成二进制可执行目标程序,而这个可执行程序在运行过程中,计算机的各个硬件,如处理器、I/O设备、主存也会与程序密切配合,这也涉及到操作系统的进程调度和管理。

关键词:预处理; 编译; 汇编; 链接; 进程管理; 存储管理; IO管理                         

目  录

第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简介

1.1.1. P2P:(1).编写c语言源代码;

          (2).对源代码进行预处理、编译、汇编、链接过程,生成二进制可执行目标文件hello;

          (3).在shell中运行可执行文件,通过fork()函数创建子进程,execve()加载并将参数传递给新进程的主函数main运行,这样hello就有了自己的进程;

1.1.2. 020:源程序被创造编写,编译生成可执行目标文件,运行时拥有了自己的进程,也在内存中存储了相关信息,进程终止之后被回收并释放,即从0到0。

1.2 环境与工具

Windows11 64位;Vmware 11以上;Ubuntu 20.04

Visual Studio 2018 64位;CodeBlocks 64位;vi/vim/gedit+gcc

1.3 中间结果

hello.i           预处理生成的文本文件

hello.s          .i文件编译后得到的汇编语言文件

hello.o          .s文件汇编后得到的可重定位目标文件

hello             .o经过链接生成的可执行目标文件

1.4 本章小结

    本章介绍了P2P020的过程,编写本论文使用的软硬件环境和开发调试工具,列出了本实验过程中得到的中间结果文件及其作用

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

预处理指预处理器(cpp)根据以字符 # 开头的命令,修改原始的 C 程序,包括宏定义# define、文件包含# include、条件编译# if def 等最后将修改后的文件以.i格式保存。

2.2在Ubuntu下预处理的命令

2.3 Hello的预处理结果解析

Hello.i文件中,首先是各种被包含的头文件信息:

然后是这些头文件中以typedef定义的各种变量:

接着是这些头文件的主题内容:

最后是main函数的内容:

2.4 本章小结

本章简要介绍了linux下预处理的相关命令,简单介绍了预处理后的生成的.i文件内容。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译是指从 .i .s 即预处理后的文件到汇编语言程序,这一过程中,编译器要检查代码的规范性、是否有语法错误等,以确定代码的实际工作。在检查无误后,编译器会把代码翻译成汇编语言。

3.2 在Ubuntu下编译的命令

3.3 Hello的编译结果解析

3.3.1. 数据存储:

字符串常量,位于只读数据段(.rodata):

-20(%rbp):通过movl %edi, -20(%rbp)存储参数argc的值:

-32(%rbp):通过movq %rsi, -32(%rbp)存储参数argv的值:

-32(%rbp):通过movl $0, -4(%rbp)初始化局部变量i:

3.3.2. 判别式:

argc!=5表示为:

i<10表示为:

3.3.2. 类型:

所操作的数据类型通过汇编指令的选择和操作数的大小来体现,比如汇编代码根据操作变量类型使用不同的mov指令:

3.4 本章小结

本章简要介绍了linux下编译的相关命令,简单介绍了生成的.s文件的内容,以及其中中各汇编指令的作用以及对应的源代码。

(第32分)

第4章 汇编

4.1 汇编的概念与作用

汇编是指汇编器(as)将hello.s翻译成机器语言指令,并把这些指令打包成一种叫做可重定位目标程序的格式,保存在.o文件中。.o是一个二进制文件,它包含的17个字节是函数main的指令编码。

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

首先使用readelf命令,将elf结果保存到elf.txt中:

4.3.1. elf头:

elf头主要包含了描述ELF文件整体结构和属性的信息,包括ELF标识、目标体系结构、节表偏移、程序头表偏移等:

4.3.2. 节头部表:

节头部表描述了ELF文件中各个节的信息,比如节的名称、类型、偏移、大小等。

4.3.3. 重定位节:

重定位节包含了在代码中使用的一些外部变量信息,在链接的时候需要根据重定位节的信息对于某些变量符号进行修改。链接的时候链接器会根据重定位节的信息对于外部变量符号决定选择何种方法计算正确的地址,例如通过偏移量等信息计算。

4.3.4. 符号表:

符号表 .symtab 是目标文件中非常重要的一部分,它列出了所有定义的符号,包括函数、变量和节。符号表在链接和调试过程中起着至关重要的作用。

4.4 Hello.o的结果解析

首先使用objdump -d hello.o >hello.asm查看hello.o的反汇编,并保存在hello.asm中。

主要不同:

(1). 反汇编文件中每条指令前面都会有一串十六进制的编码。

(2). 分支转移时,.s文件中会跳转到代码段:

而在反汇编文件中,则是直接跳转到当前过程的起始地址加上偏移量得到的直接目标代码地址:

(3). 函数调用时,.s文件中call指令后紧跟函数的名字:

而在反汇编文件中,是重定位条目所影响的地址偏移量,即call指令中地址字段的起始位置:

4.5 本章小结

本章简要介绍了linux下汇编的相关命令,介绍了生成的.o文件类型,对其可重定位目标elf格式进行了分析,并对比了反汇编得到的.asm文件和.s文件的区别。

(第41分)

第5章 链接

5.1 链接的概念与作用

链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接使得分离编译成为可能,无需将大型的应用程序组织成为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其他文件。

5.2 在Ubuntu下链接的命令

5.3 可执行目标文件hello的格式

5.3.1. elf头:

ELF 头描述文件的总体格式。它还包括程序的入口点(entry point),也就是当程序运行时要执行的第一条指令的地址。

5.3.2. 节头:

这里的节头内容更加详细。

5.3.3. 程序头:

5.3.4. 重定位节:

5.3.5.符号节:

5.4 hello的虚拟地址空间

打开edb,可以看到程序从地址0x401000开始。查看.elf 中的程序头部分。程序头表在执行的时候被使用,它告诉链接器运行时加载的内容并提供动态链接的信息,各表项功能如下:

PHDR:程序头表

INTERP:程序执行前需要调用的解释器

LOAD:保存常量数据、程序目标代码等

DYNAMIC:保存动态链接器使用信息

NOTE:保存辅助信息

GNU_STACK:异常标记

GNU_RELRO:保存重定位后只读区域的位置

5.5 链接的重定位过程分析

与可重定位目标文件hello.o的反汇编的结果相比,主要有两个地方不同,一是扩充了很多函数代码,包括程序加载后执行main前的一些准备工作,以及hello需要用到的一些库函数的定义等;二是在main中,原本在hello.o中等待重定位而暂时置0的地址操作数被设置为了虚拟地址空间中真正的地址。

5.6 hello的执行流程

可知:

0x401000 hello!_init    0x4010f0   hello!start   


0x4011d6 hello!main    0x40127c  hello!_fini

5.7 Hello的动态链接分析

当程序调用一个由共享库定义的函数时,由于编译器无法预测这时候函数的地 址是什么,因此这时,编译系统提供了延迟绑定的方法,将过程地址的绑定推迟到 第一次调用该过程时。通过 GOT 和过程链接表 PLT 的协作来解析函数的地址。在 加载时,动态链接器会重定位 GOT 中的每个条目,使它包含正确的绝对地址,而 PLT 中的每个函数负责调用不同函数。那么,通过观察 edb,便可发现 dl_init 后.got.plt 节发生的变化   

                   

执行前:

执行后:

5.8 本章小结

本章简要介绍了linux下链接的相关命令,介绍了生成的二进制可执行文件,对其可重定位目标elf格式进行了分析,并分析了其执行流程和动态连接过程。

(第51分)

第6章 hello进程管理

6.1 进程的概念与作用

进程就是一个执行中程序的实例。进程提供给应用程序的关键抽象:一个独立的逻辑控制流;一个私有的地址空间。通过逻辑控制流和私有地址空间的抽象,进程提供给用户一种假象:就好像我们的程序是系统中当前运行的唯一的程序一样。我们的程序好像是独占地使用处理器和内存。处理器就好像是无间断地一条接着一条地执行我们程序中的指令。最后,我们程序中的代码和数据好像是系统内存中唯一的对象。

6.2 简述壳Shell-bash的作用与处理流程

Shell是一个命令行解释器,它输出一个提示符,等待输入一个命令行,然后执行这个命令。如果该命令行的第一个单词不是一个内置的Shell命令,那么Shell就会假设这是一个可执行文件的名字,它将加载并运行这个文件(shell 就会创建一个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件)。在运行Hello时,Shell将加载并运行Hello程序,然后等待程序终止。程序终止后,Shell随后输出一个提示符,等待下一个输入的命令行。

6.3 Hello的fork进程创建过程

在shell接受到./hello这个命令行后,它会对其进行解析,发现是加载并运行一个可执行文件的命令,于是它会先创建一个对应./hello的作业,再用fork()创建一个子进程,这个子进程与父进程几乎与父进程完全相同,它们有着相同的代码段、数据段、堆、共享库以及栈段,但它们的pid与fork的返回值是不同的,因此可以进行区分。然后,父进程(即shell主进程)会将新创建的子进程用setpgid()放在一个新的进程组中,这样这个进程组就对应./hello这个作业,shell可以通过向进程组中的所有进程发信号的方式管理作业。

6.4 Hello的execve过程

execve 函数在当前进程的上下文中加载并运行一个新程序

#include <unistd.h>

int execve(const char *filename, const char *argv[], const char *envp[]);

// 如果成功,则不返回,如果错误,则返回 -1。

execve 函数加载并运行可执行目标文件 filename,且带参数列表 argv 和环境变量列表 envp。只有当出现错误时,例如找不到 filename,execve 才会返回到调用程序。所以,与 fork—次调用返回两次不同,execve 调用一次并从不返回。

6.5 Hello的进程执行

进程正常运行是依赖于其上下文的,上下文是由程序正确运行的状态组成的,这些状态包括存放在内存里的程序的代码、数据、栈、寄存器、所占用的资源等,总之,在程序正常运行的时候,这些上下文状态绝不能被异常破坏。

然而,进程是需要不断进行切换的。当前运行在CPU的进程每隔一段时间就需要切换至其它进程。假设hello进程现在正在运行,突然发生了由主板上的时钟芯片引发的时钟中断(属于异步异常),然后处理器会从用户态立刻转入到内核态(拥有最高的管理特权级),控制流转入操作系统内核程序,内核会将hello进程目前的上下文暂时保存起来,然后通过进程调度程序找到要切换的进程B,加载B的被保存的上下文,将控制流交给进程B,处理器重新转入到用户态。

并且,操作系统会给每个进程分配时间片,它决定了当前进程能够执行它的控制流的连续一段时间。

在hello程序被执行的时候,初始时正常运行,然后hello调用sleep函数,这时sleep通过syscall引发异常(陷阱),转入内核态,内核保存hello的上下文,然后将hello进程置于休眠态,切换到其它进程。等到休眠时间到了的时候,此时时钟中断使得控制流从其它进程跳到内核,内核发现hello进程的休眠时间到了,就把hello解除休眠状态。之后在应当进行进程切换的时候,恢复hello的上下文,控制流转入hello进程,处理器切换到用户态。

6.6 hello的异常与信号处理

主要有以下几类异常:

(1). 乱按:不断按下包括回车的乱输入,发现无影响:

(2). Ctrl-C:进程终止并被回收:

(3). Ctrl-Z: 进程暂停,输入ps,会显示所有的进程及其状态

输入jobs, 显示暂停的进程:

输入pstree, 显示进程树,显示所有进程的情况:

输入fg,使第一个后台作业变成前台作业,这会让hello变为前台执行:

输入kill -9 23020,杀死hello进程:

6.7本章小结

本章简要介绍了进程的概念和作用,fork()execve()函数的使用等,展示了hello进程的创建,执行,终止以及各个命令,如psjobs等的作用。

(第61分)

第7章 hello的存储管理

7.1 hello的存储器地址空间

7.1.1. 逻辑地址:包含在机器语言指令中用来指定一个操作数或一条指令的地址。它促使程序员把程序分成若干段。每一个逻辑地址都由一个段(segment)和偏移量(offset)组成,偏移量指明了从段开始的地方到实际地址之间的距离。对应于hello.o中的相对偏移地址。

7.1.2. 线性地址:逻辑地址到物理地址变换之间的中间层。程序代码会产生逻辑地址,或说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。如果启用了分页机制,那么线性地址能再经变换以产生一个物理地址。若没有启用分页机制,那么线性地址直接就是物理地址。

7.1.3. 虚拟地址:程序运行中程序中使用的逻辑地址,程序通常运行在虚拟地址空间中。由于是段式存储模式,所以虚拟地址是二维的,用段基址和段内位移表示。

7.1.4. 物理地址:线性地址经过页式变换得到的实际内存地址,定位实际要访问的内存单元。这个操作由内存管理单元MMU执行。计算机系统的贮存被组织称一个有M个连续的字节大小的单员组成的数组。每个字节都有一个唯一的物理地址。

7.2 Intel逻辑地址到线性地址的变换-段式管理

对于一个以“段:偏移地址”形式给出的逻辑地址,CPU将会通过其中的16位段选择子定位到GDT/LDT中的段描述符,通过这个段描述符得到段的基址,与段内偏移地址相加得到的64位整数就是线性地址。这就是CPU的段式管理机制,其中,段的划分,也就是GDT和LDT都是由操作系统内核控制的。

7.3 Hello的线性地址到物理地址的变换-页式管理

虚拟地址空间会被分为若干页,即分页机制。CPU对于一个线性地址会取它的高若干位,通过它们去存储在内存中的页表里查询对应的页表条目,得到这个线性地址对应的物理页起始地址,然后与线性地址的低位(页中的偏移)相加就是物理地址。

7.4 TLB与四级页表支持下的VA到PA的变换

在现代操作系统中,地址转换过程中除了页表机制外,还使用了转换后备缓冲(Translation Lookaside Buffer, TLB)来加速虚拟地址(VA)到物理地址(PA)的转换。下面我们详细说明TLB和四级页表机制下虚拟地址到物理地址的转换过程。

7.4.1 TLB

TLB是一种高速缓存,用于存储最近使用的虚拟地址到物理地址的映射。通过TLB,可以避免每次地址转换都进行多级页表查找,从而加速地址转换过程。

7.4.2 四级页表机制

四级页表机制将虚拟地址转换为物理地址时,通过四级页表结构进行映射。每一级页表大小为512项,每项指向下一级页表或物理页。

7.4.3 TLB和四级页表结合的地址转换过程

(1)从虚拟地址(VA)提取各级索引和页内偏移

假设虚拟地址为 VA,PML4 索引位于高9位,PDPT 索引位于下一个9位,PD 索引位于再下一个9位,PT 索引为最后一个9位,页内偏移& 0xFFF为低12位。

(2)CPU首先在TLB中查找虚拟地址的映射。如果命中(TLB hit),则直接使用缓存的物理地址。如果未命中(TLB miss),则需要进行页表查找。

(3)四级页表查找(在TLB miss的情况下):

使用PML4索引在PML4表中查找,找到对应的PDPT表地址。使用PDPT索引在PDPT表中查找,找到对应的PD表地址。使用PD索引在PD表中查找,找到对应的PT表地址。使用PT索引在PT表中查找,找到物理页框地址。

(4)计算物理地址:

物理地址 = 物理页框地址 + 页内偏移

7.5 三级Cache支持下的物理内存访问

物理地址由块偏移(CO)、组索引(CI)、标记(CT)三部分组成,用于进行面向Cache的组选择和行匹配。首先在一级Cache下找,若发生不命中,则到下一级缓存即二级Cache下找,若不命中则到三级Cache下访问。

7.6 hello进程fork时的内存映射

当 fork 函数被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的 PID。为了给这个新进程创建虚拟内存,它创建了当前进程的 mm_struct、区域结构和页表的原样副本。它将两个进程中的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。

7.7 hello进程execve时的内存映射

以execve函数加载并运行 a.out 需要以下几个步骤:

删除已存在的用户区域。删除当前进程虚拟地址的用户部分中的已存在的区域结构。

映射私有区域。为新程序的代码、数据、bss 和栈区域创建新的区域结构。所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为 a.out 文件中的. text 和. data 区。bss 区域是请求二进制零的,映射到匿名文件,其大小包含在 a.out 中。栈和堆区域也是请求二进制零的,初始长度为零。图 9-31 概括了私有区域的不同映射。

映射共享区域。如果 a.out 程序与共享对象(或目标)链接,比如标准 C 库 libc.so,那么这些对象都是动态链接到这个程序的,然后再映射到用户虚拟地址空间中的共享区域内。

设置程序计数器(PC)。execve 做的最后一件事情就是设置当前进程上下文中的程序计数器,使之指向代码区域的入口点。

7.8 缺页故障与缺页中断处理

当出现缺页故障时,即DRAM缓存不命中,此时调用缺页处理程序,内存会确定一个牺牲页,若页面被修改,则换出到磁盘,再将新的目标页替换牺牲页写入,缺页处理程序页面调入新的页面,并更新内存中的 PTE。缺页处理程序返回到原来的进程,重启导致缺页的指令。

7.9动态存储分配管理

7.9.1 动态内存管理的基本方法

虽然可以使用低级的mmap和munmap函数来创建和删除虚拟内存区域,但是C程序员还是会觉得当运行时需要额外虚拟内存时,用动态内存分配器更方便,也有更好的可移植性。

(1)显式分配器

要求应用显式地释放任何已分配的块。例如,c标准库提供一种叫做malloc程序包的显式分配器。c程序通过调用malloc函数来分配一个块,并通过调用free函数来释放一个块。c++中的new和delete操作符与c中的malloc和free相当。

(2)隐式分配器

要求分配器检测一个已分配块何时不再被程序所使用,那么就释放这个块。隐式分配器也叫做垃圾收集器,而自动释放未使用的已分配的块的过程叫做垃圾收集,例如Lisp、ML以及Java之类的高级语言就依赖垃圾收集来释放已分配的块。

7.9.2 动态内存管理的策略

(1)带边界标签的隐式空闲链表

带边界标签的隐式空闲链表使用边界标签(boundary tags)来管理内存块,内存块之间没有显式的指针链接。每个内存块包含头部和尾部的边界标签,这些标签存储块的大小和状态(分配或空闲)。

(2)显示空间链表

显式空闲链表使用链表来维护所有空闲块,链表中的每个节点都包含指向下一个和上一个空闲块的指针。这种方式提供了更高效的空闲块管理。

7.10本章小结

本章简述了计算机中的各类地址及他们之间的相互转换,并探究了计算机的虚拟内存系统内部的工作原理,从内存角度重新审视了前面的若干知识,如fork、execve函数,最后解释了动态内存管理的基本方法与策略。

(第7 2分)

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:所有的 I/O 设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被当作对相应文件的读和写来执行

设备管理:Linux内核有一个简单、低级的接口,称为Unix I/O,使得所有的输入和输出都能以一种统一且一致的方式来执行。

8.2 简述Unix IO接口及其函数

8.2.1 Unix I/O接口

Unix I/O接口,使得所有的输入和输出都能以一种统一且一致的方式来执行:

(1)打开文件:一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备。内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件;Linux shell创建的每个进程开始时都有三个打开的文件,标准输入(描述符为0)、标准输出(描述符为1)和标准错误(描述符为2);

(2)改变当前的文件位置:对于每个打开的文件,内核保持着一个文件位置k,初始为0;

(3)读写文件:一个读操作就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n;类似地,写操作就是从内存复制n>0个字节到文件,从当前文件位置k开始,然后更新k;

(4)关闭文件:当应用完成了对文件的访问之后,它就通知内核关闭这个文件。

8.2.2 Unix I/O函数

(1)进程通过调用open函数来打开一个已存在的文件或者创建一个新文件的:int open(char *filename, int flags, mode_t mode)

open函数将filename转换为一个文件描述符,并且返回描述符数字;flags参数也可以是一个或者更多位掩饰的或,为写提供给一些额外的指示;mode参数指定了新文件的访问权限位。

(2)close函数

进程通过调用close函数关闭一个打开的文件。

(3)read函数

应用程序是通过分别调用read来执行输入,ssize_t read(int fd, void *buf, size_t n);

read函数从描述符为fd的当前文件位置复制最多n个字节到内存位置buf。返回值-1表示一个错误,而返回值0比怕是EOF。否则返回值表示的是实际传送的字节数量。

(4)write函数

应用程序是通过调用write函数来执行输出,ssize_t write(int fd, const void *buf, size_t n);

write函数从内存位置buf复制至多n个字节到描述符fd的当前文件位置。

(5)lseek函数

通过调用lseek函数,应用程序能都显示地修改当前文件的位置。

8.3 printf的实现分析

首先,printf开辟一块输出缓冲区,然后用vsprintf在输出缓冲区中生成要输出的字符串。之后通过write将这个字符串输出到屏幕上。而write会通过syscall陷阱跳到内核,内核的显示驱动程序会通过这些字符串及其字体生成要显示的像素数据,将它们传到屏幕上对应区域的显示vram中。显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

8.4 getchar的实现分析

首先,getchar会开辟一块静态的输入缓冲区,若输入缓冲区为空,则调用read向输入缓冲区中读入一行字符串。而read会通过syscall陷阱跳到内核,内核会使得调用方不断等待。当按下键盘后,键盘中断处理程序执行,向输入缓冲区中放入由键盘端口读入的扫描码转换成的字符,直到按下回车后调用方不再等待。那么getchar所做的事情其实就是不断地从输入缓冲区中取下一个字符,如果没有则等待输入。

8.5本章小结

本章介绍了系统级IO、Unix/IO的基本知识,I/O 是系统操作不可或缺的一部分,学习系统的IO有助于理解其他的系统概念。最后分析了printf、getchar两个标准化的IO函数。

(第81分)

结论

(1)hello.c在预处理之后,将头文件的内容插入到程序文本中,得到hello.i;

(2)编译器对hello.i进行编译,从而得到汇编文件hello.s;

(3)经过汇编器汇编,得到与汇编语言一一对应的机器语言指令,在汇编之后,得到了可重定位目标文件hello.o,其是一个二进制文件;

(4)链接器对hello.o中调用函数的指令进行重定位,得到可执行目标文件hello;

(5)在计算机运行hello,OS就fork()为hello创建一个子进程,hello就在这个进程当中运行;

(6)运行时,首先,在hello中的地址为虚拟地址,要经历虚拟地址映射为线性地址,线性地址映射到物理地址,才能对该地址进行操作;

(7)hello程序正常运行,输出结果,过程中通过文件管理I/O设备;

(8)最后由shell父进程回收终止的hello进程。

你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。

学习了hello的完整生命流程,我了解到了现代计算机的精妙的设计思想,了解到计算机底层还有这么复杂的原理,加深了我继续探索,更深入的了解计算机,学习计算机的兴趣。

(结论0分,缺失 -1分,根据内容酌情加分)

附件

hello.i        预处理生成的文本文件

hello.s        .i文件编译后得到的汇编语言文件

hello.o              .s文件汇编后得到的可重定位目标文件

hello          .o经过链接生成的可执行目标文件

elf3.txt            hello.out的elf文件

hello.asm   hello.o的反汇编代码文件

elf.txt         hello.o的elf文件

(附件0分,缺失 -1分)

参考文献

为完成本次大作业你翻阅的书籍与网站等

[1]  《深入理解计算机系统(原书第三版)》 Randal E.Bryant David R.O’Hallaron 机械工业出版社

[2]   [[转]printf 函数实现的深入剖析 - Pianistx - 博客园](https://www.cnblogs.com/pianist/p/3315801.html)

(参考文献0分,缺失 -1分)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值