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

article/2025/6/30 6:46:51

计算机系统

大作业

题     目  程序人生-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分)


http://www.hkcw.cn/article/MFNcNOjcoY.shtml

相关文章

程序人生的学习密码:终身学习促进职业生涯飞跃

程序人生的学习密码:终身学习促进职业生涯飞跃 关键词:终身学习、程序员成长、知识体系构建、学习方法论、技术迭代、职业发展、认知升级 摘要:在技术快速迭代的IT领域,程序员的职业生涯能否实现持续飞跃,核心在于是否构建了高效的终身学习体系。本文从认知科学和职业发展…

软件测试找工作|20道银行项目高频面试题

小编给大家上面试干货啦&#xff01;把前两天整理的银行项目面试题系列汇总给你们复习吼&#xff01; 先来看下面试题的目录叭...... 1、介绍一下贷款的项目&#xff1f; 贷款项目是银行业务中的重要组成部分&#xff0c;它是指银行向客户提供资金&#xff0c;让客户在约定的期…

【老张的程序人生】我命由我不由天:我的计算机教师中级岗之旅

在计算机行业的洪流中&#xff0c;作为一名20年计算机专业毕业的博主&#xff0c;我深知这几年就业的坎坷与辉煌。今天&#xff0c;我想与大家分享我的故事&#xff0c;一段关于梦想、挑战与坚持的计算机教师中级岗之旅。希望我的经历能为大家提供一个发展方向&#xff0c;在计…

程序人生-Hello’s P2P(2025)

计算机系统 大作业 题 目 程序人生-Hello’s P2P 专 业 计算机与电子通信 学   号 2023111735 班 级 23L0509 学 生 杨祥锐 指 导 教 师 史先俊 …

程序人生-Hello‘s P2P

计算机系统 大作业 题 目 程序人生-Hello’s P2P 专 业 生命科学和医学学院 学   号 2023113108 班   级 2352001 学 生 杜若林 指 导 教 师 刘松波 计算机科学与技术…

2025哈工大计统PA-P2P程序人生

摘 要 作此论文的目的是为了了解程序从输入终端到在终端中显示运行的一系列过程。本文详细分析了计算机在生成hello可执行文件的预处理、编译、汇编、链接、进程管理等整个生命周期&#xff0c;解析了hello程序从初始状态输入到结束执行被回收的全部过程&#xff0c;查看并注…

程序人生Hello’s P2P CSAPP大作业

计算机系统 大作业 题 目 程序人生-Hello’s P2P 专 业 计算学部 学   号 2023112327 班   级 23L0509 学 生 朱永久      指 导 教 师 …

数据库系统概论(十五)详细讲解数据库视图

数据库系统概论&#xff08;十五&#xff09;数据库视图 前言一、什么是视图&#xff1f;二、视图的作用1. 保护数据安全2. 屏蔽表结构变化3. 简化复杂查询4. 多角度展示数据 三、如何创建视图&#xff1f;语法格式&#xff1a;5种常见视图类型&#xff1a; 四、更新视图的限制…

面向对象进阶 | 深入探究 Java 静态成员与继承体系

个人主页 文章专栏 文章目录 个人主页文章专栏 一、static&#xff08;静态&#xff09;1.static 静态变量代码展示内存图 2.static 静态方法工具类&#xff1a;练习&#xff1a; 3.static注意事项4.重新认识main方法 二、继承1.继承概述2.继承的特点3.子类到底能继承父类中的…

Python趣学篇:用Pygame打造绚烂流星雨动画

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 专栏介绍&#xff1a;《Python星球日记》 目录 一、项目简介与效果展示二、技术栈与核…

【大模型学习】项目练习:视频文本生成器

&#x1f680;实现视频脚本生成器 视频文本生成器 &#x1f4da;目录 一、游戏设计思路二、完整代码解析三、扩展方向建议四、想说的话 一、⛳设计思路 本视频脚本生成器采用模块化设计&#xff0c;主要包含三大核心模块&#xff1a; 显示模块&#xff1a;处理用户输入和…

BFS每日刷题

目录 P1332 血色先锋队 173. 矩阵距离 P1162 填涂颜色 P1506 拯救oibh总部 P2895 [USACO08FEB] Meteor Shower S P3395 路障 P1332 血色先锋队 #include <iostream> #include <cstring> #include <queue> using namespace std; int dis[600][600]; in…

中国城市规模指数(1992-2023)

1816 中国城市规模指数(1992-2023) 数据简介 中国城市规模指数&#xff0c;参考丁从明等&#xff08;2020&#xff09;的做法&#xff0c;通过中国夜间灯光数据&#xff0c;提取其中各城市夜间灯光值大于等于22的区域取其平均值&#xff0c;再取其自然对数即为城市规模指数数…

基于贝叶斯优化神经网络的光伏功率预测综述

基于贝叶斯优化神经网络的光伏功率预测综述 一、贝叶斯优化的基本原理与核心组件 贝叶斯优化&#xff08;Bayesian Optimization, BO&#xff09;是一种基于概率模型的全局优化方法&#xff0c;特别适用于高成本评估的黑盒函数优化问题。其核心由代理模型和采集函数构成&…

【Zephyr 系列 4】串口通信进阶:打造自己的 AT 命令框架

&#x1f9e0;关键词&#xff1a;Zephyr、UART、串口通信、AT命令、Shell、RTOS &#x1f4cc;适合人群&#xff1a;希望开发设备控制协议、调试接口、CLI 命令的嵌入式开发者 &#x1f3af; 本篇目标 使用 Zephyr 提供的 UART API 与 Shell 模块 实现一套可扩展的 ATCMD 风格…

Docker 镜像原理

目录 操作系统基础 Union FS(联合文件系统) 再看 Docker 镜像是什么 镜像实现原理 docker 镜像加载原理 docker 是操作系统层的虚拟化&#xff0c;所以 docker 镜像的本质是在模拟操作系统。我们先看下操作系统是什么。 操作系统基础 操作系统由&#xff1a;进程调度子系统、…

仓颉语言---Socket编程

一、什么是Socket编程&#xff1f; 1.定义 Socket&#xff08;套接字&#xff09;可以被理解为网络上两个进程之间通信的端点。它是网络通信的抽象表示&#xff0c;封装了底层网络协议的复杂性&#xff0c;为应用程序提供了一个简单统一的接口。 Socket 编程是一种网络编程范式…

格密码-LWE问题

格密码是一种备受关注的 PQC 算法&#xff0c;其安全性基于最坏情况下格问题的困难性&#xff0c;它是来自于 Regev 密码系统和 Lyubashevsky-Peikert-Regev 密码系统的思想。 2022 年&#xff0c;NIST 完成了 PQC 第三轮标准化过程&#xff0c;共有四种候选算法被选中进行标准…

力扣刷题Day 67:N 皇后(51)

1.题目描述 2.思路 方法1&#xff08;自己写的传统型回溯&#xff09;&#xff1a;将第一行的皇后依次放置在0 ~ n - 1上&#xff0c;可以发现0 ~ (⌊n / 2⌋ - 1)上的放置方案与⌈n / 2⌉ ~ (n - 1)上的放置方案是对称的&#xff0c;此外&#xff0c;如果n是奇数的话还需额外…

超级 AI 助手进阶攻略:Reflection 模式,开启 Agent 智能飞跃之旅

反思之言&#xff1a; 你有没有注意到&#xff0c;同样是AI&#xff0c;有些能帮你写代码、做决策&#xff0c;甚至聊人生&#xff1b;而有些却连基本的问题都答不对&#xff1f;这背后其实有一个关键差异&#xff1a;它会不会“反思”自己。 所谓Reflection&#xff08;反思…