程序人生hello

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ysQ2eXGP-1684838384290)(RackMultipart20230523-1-g7n6yh_html_a7fdd1a7de1510f1.jpg)]

计算机系统

大作业

题 目 程序人生-Hello’s P2P

专 业 人工智能领域方向(2+X模式)

学   号 2021112381

班   级 21WL026

学 生 邓家颖

指 导 教 师 吴锐

计算机科学与技术学院

2023** 年 5 月**

摘 要

Hello程序的生命周期是从一个源文件开始,也就是程序员利用编辑器创建并保存的文本文件,源程序实际是由值0和1组成的位序列,8位(bit)组成一组称为一个字节(byte),每个字节表示程序中某个文本字符。Hello world程序的生命周期包括程序创建和程序执行,程序执行又可以细分为系统运行、输出简单信息、终止程序。通过编译系统程序将程序创建的源文件转化成程序可执行的可执行目标文件。hello.c经过预处理生成一个新的源程序文件hello.i,它包含了所有的源程序文件和所有被插入的头文件。编译器将hello.i转换成汇编语言,生成一个汇编语言文件hello.s,它包含了汇编语言代码。汇编器将汇编语言转换成机器语言,生成目标文件hello.o,它包含了机器语言代码。链接器将目标文件与库函数链接,生成可执行文件hello,它包含了可执行代码和数据。

关键词: 程序;预处理;汇编;编译;链接

(摘要** 0 分,缺失 -1 分, 根据内容精彩称都酌情加分 0-1 分 **

目 录

******1 **章 概述 - 4 -

1.1 Hello简介 - 4 -

1.2 环境与工具 - 4 -

1.3 中间结果 - 5 -

1.4 本章小结 - 5 -

******2 **章 预处理 - 6 -

2.1 预处理的概念与作用 - 6 -

2.2在Ubuntu下预处理的命令 - 6 -

2.3 Hello的预处理结果解析 - 6 -

2.4 本章小结 - 6 -

******3 **章 编译 - 7 -

3.1 编译的概念与作用 - 7 -

3.2 在Ubuntu下编译的命令 - 7 -

3.3 Hello的编译结果解析 - 7 -

3.4 本章小结 - 7 -

******4 **章 汇编 - 8 -

4.1 汇编的概念与作用 - 8 -

4.2 在Ubuntu下汇编的命令 - 8 -

4.3 可重定位目标elf格式 - 8 -

4.4 Hello.o的结果解析 - 8 -

4.5 本章小结 - 8 -

******5 **章 链接 - 9 -

5.1 链接的概念与作用 - 9 -

5.2 在Ubuntu下链接的命令 - 9 -

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

5.4 hello的虚拟地址空间 - 9 -

5.5 链接的重定位过程分析 - 9 -

5.6 hello的执行流程 - 9 -

5.7 Hello的动态链接分析 - 9 -

5.8 本章小结 - 10 -

****6 hello 进程管理 **** - 11 -

6.1 进程的概念与作用 - 11 -

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

6.3 Hello的fork进程创建过程 - 11 -

6.4 Hello的execve过程 - 11 -

6.5 Hello的进程执行 - 11 -

6.6 hello的异常与信号处理 - 11 -

6.7本章小结 - 11 -

****7 hello 的存储管理 **** - 12 -

7.1 hello的存储器地址空间 - 12 -

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

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

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

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

7.6 hello进程fork时的内存映射 - 12 -

7.7 hello进程execve时的内存映射 - 12 -

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

7.9动态存储分配管理 - 12 -

7.10本章小结 - 13 -

****8 helloIO 管理 **** - 14 -

8.1 Linux的IO设备管理方法 - 14 -

8.2 简述Unix IO接口及其函数 - 14 -

8.3 printf的实现分析 - 14 -

8.4 getchar的实现分析 - 14 -

8.5本章小结 - 14 -

结论 - 15 -

附件 - 16 -

参考文献 - 17 -

第1章 概述

1.1 Hello简介

根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。

Hello程序的生命周期是从一个源文件开始,也就是程序员利用编辑器创建并保存的hello.c文件。

hello.c经过预处理生成一个新的源程序文件hello.i,它包含了所有的源程序文件和所有被插入的头文件。

编译器将hello.i转换成汇编语言,生成一个汇编语言文件hello.s,它包含了汇编语言代码。

汇编器将汇编语言转换成机器语言,生成目标文件hello.o,它包含了机器语言代码。

链接器将目标文件与库函数链接,生成可执行文件hello,它包含了可执行代码和数据。如图 1所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2nTNzQ3p-1684838384292)(RackMultipart20230523-1-g7n6yh_html_952d24febfdeff11.png)]

图 1 编译系统

shell是一个命令行解释器,它输出一个提示符,等待输入一个命令行,然后执行这个命令。如果该命令行的第一个单词不是一个内置的shell命令,那么shell就会假设这是一个可执行文件的名字,它将加载并运行这个文件。shell将加载并运行hello程序,然后等待程序终止。hello程序在屏幕上输出它的消息,然后终止。shell随后输出一个提示符,等待下一个输入的命令行。

1.2 环境与工具

硬件环境、软件环境以及开发和调试工具如下,图 2为CPUZ下的计算机硬件详细信息。

1.2.1 硬件环境

X64 CPU; 3.20GHz; 16G RAM; 512G HD Disk

1.2.2 软件环境

Windows11 64位; Vmware 17; Ubuntu 20.04 LTS 64位

1.2.3 开发工具

Visual Studio 2022 64位; CodeBlocks 64位

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LDHBJ2Rj-1684838384292)(RackMultipart20230523-1-g7n6yh_html_386cdc7cde4cbd16.png)]

图 2CPUZ下CPU的基本信息

1.3 中间结果

列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。

hello.c 源程序

hello.i 经过预处理的文件,包含了源程序文件和头文件

hello.s 编译后的汇编语言文件,包含了汇编语言代码

hello.o 汇编后的可重定位执行文件,包含了机器语言代码

hello 链接后的可执行文件,包含了可执行代码和数据

1.4 本章小结

本章简单介绍了Hello的P2P,020的过程,列举了实验的硬件环境、软件环境以及开发和调试工具,并展示了过程中的文件。

(第** 1 0.5 **分)

第2章 预处理

2.1 预处理的概念与作用

2.1.1预处理概念

预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。预处理器是编译器的一部分,它读取以字符#开头的命令,读取系统头文件的内容,并将它们直接插入到程序文本中。这个过程生成一个新的源程序文件,它包含了所有的源程序文件和所有被插入的头文件。

2.1.2预处理作用

将源代码中的预处理指令(以#开头)和宏定义(以#define开头)进行处理,生成新的代码。例如,比如hello.c中第1行的#include <stdio.h>命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入程序文本中。结果就得到了另一个C程序,通常是以.i作为文件扩展名。

2.2在Ubuntu下预处理的命令

gcc -E hello.c -o hello.i

预处理过程如图 3所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oVrenq1R-1684838384293)(RackMultipart20230523-1-g7n6yh_html_568514ae1a2c8d7e.png)]

图 3 预处理过程

2.3 Hello的预处理结果解析

经过预处理之后,hello.c转化为hello.i文件,打开该文件,如图 4所示,文件的内容显著增加,但仍为可以阅读的C语言程序文本文件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lkOWpX0z-1684838384294)(RackMultipart20230523-1-g7n6yh_html_f1f112ed3e937f06.png)]

图 4hello.i文件

对比源程序发现,预处理指令被扩展,但源程序并没有变化。进一步发现预处理指令对源程序的宏进行了宏展开,stdio.h、unist.h、stdlib.h头文件内容被包含。原始的代码在最后,如图 5

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8d6zSr5A-1684838384294)(RackMultipart20230523-1-g7n6yh_html_7a64f7ba5c1b6cae.png)]

图 5hello.i文件

2.4 本章小结

本章介绍了预处理的相关概念及其所进行的一些处理,介绍了预处理的概念和作用,展示了预处理的命令和生成的文件并进行了简单的分析。得到hello.i文件是对源程序的补充和替换,是在其基础上进行修改的源程序的结论。

(第** 2 0.5 **分)

第3章 编译

3.1 编译的概念与作用

3.1.1编译概念

编译器(eel)将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序。编译器是将高级语言(如C、C++等)源代码转换成汇编语言的程序。编译器将源代码转换成汇编语言,生成一个汇编语言文件,它包含了汇编语言代码。

3.1.2编译作用

编译的作用是将源代码翻译成机器语言,以便计算机能够理解和执行。

3.2 在Ubuntu下编译的命令

gcc -S hello.i -o hello.s

编译过程如图 6所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G0qBYsOB-1684838384295)(RackMultipart20230523-1-g7n6yh_html_9a70d3b131fc86e.png)]

图 6 编译过程

3.3 Hello的编译结果解析

生成的汇编代码如图 7所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O60Sqhz1-1684838384295)(RackMultipart20230523-1-g7n6yh_html_d71a7042ca8e67db.png)]

图 7 汇编代码

3.3.1汇编初始部分

如图 8所示,在main函数前的内容展示了节名称。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rnqJTljM-1684838384296)(RackMultipart20230523-1-g7n6yh_html_ce12b78942731155.png)]

图 8 汇编初始部分

.file 声明源文件为"hello.c"

.text 表示代码节

.section.rodata 表示只读数据段

.align 声明对指令或数据的存放地址进行对其的方式,8字节对齐

.string 声明一个字符串

.globl 声明全局变量

.type 声明一个符号的类型(函数类型或数据类型)

3.3.2数据部分

(1)常量:作为立即数直接出现在汇编代码中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rm5rq9aZ-1684838384296)(RackMultipart20230523-1-g7n6yh_html_e616df5ac8eff35d.png)]

该语句对应hello.c中的第13行,是对常量4和argc的比较。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pUVGNQPp-1684838384297)(RackMultipart20230523-1-g7n6yh_html_2525ed0aadada263.png)]

该语句对应hello.c中的第17行,是对常量8和变量i的比较。

(2)局部变量:该程序只含有局部变量int i

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sjr3SCDf-1684838384297)(RackMultipart20230523-1-g7n6yh_html_d53272a3cf16e41c.png)]

变量i的初始化对应上图,编译器将i储存在栈中。

(3)main函数参数int argc ,char *argv[]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yx858cSB-1684838384298)(RackMultipart20230523-1-g7n6yh_html_4b11528bac0f8f92.png)]

main函数参数存储如图,int argc存储在-20(%rbp),后者存储在-32(%rbp)。

(4)字符串

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-21kSflYl-1684838384298)(RackMultipart20230523-1-g7n6yh_html_4b11528bac0f8f92.png)]

这两个字符串存储在只读数据段中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0hxmHDy5-1684838384299)(RackMultipart20230523-1-g7n6yh_html_9e06392959c240a.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZMtxQzNw-1684838384299)(RackMultipart20230523-1-g7n6yh_html_fd3000ddd73bcbf2.png)]

这两个字符串作为printf的参数。

3.3.3全局函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-miNhtLWo-1684838384300)(RackMultipart20230523-1-g7n6yh_html_4b164e47b8c4c931.png)]

hello.c中只声明了一个全局函数int main(int argc,char *argv[]),可通过汇编代码得知,如图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y2YjOETQ-1684838384301)(RackMultipart20230523-1-g7n6yh_html_b051a0cc4b94a9ac.png)]

3.3.4赋值操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hdh75cD2-1684838384301)(RackMultipart20230523-1-g7n6yh_html_c4b985c4513a7cfa.png)]

hello.c中的赋值操作仅有i=0;在汇编代码中由mov指令实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y5uAlMf5-1684838384302)(RackMultipart20230523-1-g7n6yh_html_f442bc33bf8c92ee.png)]

3.3.5算术操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bt34zRnk-1684838384302)(RackMultipart20230523-1-g7n6yh_html_c4b985c4513a7cfa.png)]

hello.c中的算术操作为for循环每次循环结束后i++,该操作在汇编代码中实现如下。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sOalOqZr-1684838384303)(RackMultipart20230523-1-g7n6yh_html_7ffa0ab7a7eeefcc.png)]

3.3.6关系操作

hello.c中存在两个关系操作,分别为:

(1)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-imIzWCOq-1684838384304)(RackMultipart20230523-1-g7n6yh_html_9a01d830ba367601.png)]

汇编代码为下图,该指令比较argc和立即数4,根据结果设置条件码,并根据条件码判断是否跳转。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0sfVr35p-1684838384305)(RackMultipart20230523-1-g7n6yh_html_5b0ec1b14546c11a.png)]

(2)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CqazpxLj-1684838384305)(RackMultipart20230523-1-g7n6yh_html_4eacffc3264c4d77.png)]

汇编代码更直观的体现为i<=7,是for循环的条件,作用同上。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MdJMesPD-1684838384306)(RackMultipart20230523-1-g7n6yh_html_944d4b6ff5764c0f.png)]

3.3.7控制转移指令

设置过条件码后,通过条件码来进行控制转移,本程序中存在两个控制转移。

(1)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ib16WPFU-1684838384306)(RackMultipart20230523-1-g7n6yh_html_9a01d830ba367601.png)]

判断argc是否为4,如果不为4,则执行if语句,否则执行其他语句,在汇编代码表现为如果条件码为1,则跳转到.L2,否则执行cmpl指令后的指令。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RaHCqbRZ-1684838384307)(RackMultipart20230523-1-g7n6yh_html_5b0ec1b14546c11a.png)]

(2)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6EmQjBhM-1684838384307)(RackMultipart20230523-1-g7n6yh_html_4eacffc3264c4d77.png)]

在for循环每次结束判断一次i<8,翻译为汇编语言后,通过条件码判断每次循环是否跳转到.L4。而在for循环初始要对i设置为0,如下图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uk1gGBLJ-1684838384308)(RackMultipart20230523-1-g7n6yh_html_21176978ec24856f.png)]

3.3.8函数操作

(1)main函数

参数传递:该函数的参数为int argc,char *argv[],具体参数传递地址和值都阐述过。

函数调用:通过使用call内部指令调用语句进行函数调用,并且将要调用的数地址数据写入栈中,然后自动跳转到这个调用函数内部。main函数里调用了printf、exit、sleep函数

局部变量:使用了局部变量i用于循环。具体局部变量的地址和值都阐述过。

(2)printf函数

参数传递:printf函数调用参数argv[1],argv[2]。

函数调用:该函数调用了两次:第1次将寄存器%rdi设置为待传递字符串"用法: Hello 学号 姓名 秒数!\n"的起始地址;第二次将其设置为" Hello %s %s\n"的起始地址。使用寄存器rsi完成对argv[l]的传递,用rdx完成对argv[2]的传递。

(3)exit函数

参数传递与函数调用:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOvb5cTV-1684838384309)(RackMultipart20230523-1-g7n6yh_html_e0653249f54b16bf.png)]

(4)atoi、sleep函数

参数传递与函数调用:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aoefr5n7-1684838384310)(RackMultipart20230523-1-g7n6yh_html_7f03f841885aad45.png)]

atoi函数将参数arg[3]放入寄存器%rdi中用作参数传递,使用call指令调用。

然后,将转换完成的秒数从%eax传递到%edi中,edi存放sleep的参数,再使用call调用。

(5)getchar函数

无参数传递,直接使用call调用。

3.3.9类型转换

atoi函数将字符串转换为sleep函数所需要的整型参数。

3.4 本章小结

本章主要讲述了编译阶段中编译器将hello.i文件翻译为hello.s文件的过程,简单概括了编辑的概念和作用,展示了编译的命令,并对产生的hello.s文件中的汇编代码进行了相应的各种操作分析。通过理解了这些编译器编译的机制,我们可以理解源程序和汇编中分别如何实现这些功能。

(第** 3 2 **分)

第4章 汇编

4.1 汇编的概念与作用

4.1.1汇编概念

汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序(relocatable object program)的格式,并将结果保存在目标文件hello.o中。

4.1.2汇编作用

将汇编语言转换为机器能识别的机器语言,使文件被连接后能被机器识别和执行。

4.2 在Ubuntu下汇编的命令

gcc -c hello.s -o hello.o

汇编过程如所图 9示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z5yPMpq4-1684838384311)(RackMultipart20230523-1-g7n6yh_html_6afa0c9a919f248.png)]

图 9 汇编过程

4.3 可重定位目标elf格式

分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。

(1)ELF Header

命令:readelf -h hello.o

ELF以一个16字节的序列开始,这个序列描述了生成该文牛的系统的字的大小和字节顺序。ELF头剩下的部分包含了帮助链接器语法分析和解释日标文件的信息,其中包括ELF头的大小、目标文件的类型(如可重定位、可执行或者共享的)、机器类型(如x86-64)、节头部表的文件偏移,以及节头部表中条目的大小和数量。不同节的位置和大小是有节头部表描述的,其中目标文件中每个节都有一个固定大小的条目)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KMcS5A2q-1684838384311)(RackMultipart20230523-1-g7n6yh_html_df47f7e48db14ed2.png)]

图 10ELF Header

(2)节头部表Section Headers

命令:readelf -S hello.o

节头部表,包含了文件中出现的各个节的语义,包括节的类型、位置和大小等信息。由于是可重定位目标文件,所以每个节都从0开始,用于重定位。在文件头中得到节头表的信息,然后再使用节头表中的字节偏移信息得到各节在文件中的起始位置,以及各节所占空间的大小,同时可以观察到,代码是可执行的,但是不能写;数据段和只读数据段都不可执行,而且只读数据段也不可写。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jsjxntYa-1684838384312)(RackMultipart20230523-1-g7n6yh_html_6adbf8ec1e479477.png)]

图 11 节头部表Section Headers

(3)符号表.symtab

命令:readelf -s hello.o

.symtab节中包含ELF符号表,这张符号表包含一个条目的数组,存放一个程序定义和引用的全局变量和函数的信息。该符号表不包含局部变量的信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LY5Gx5A2-1684838384312)(RackMultipart20230523-1-g7n6yh_html_114cd724875621d6.png)]

图 12 符号表.symbol

(4)重定位节.rela.text

命令:readelf -r hello.o

重定位节:一个.text节中位置的列表,包含.text节中需要进行重定位的信息,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。一般而言,任佝调用外部函数或者引用全局变员的指令都需要修改,而调用本地函数的指令不需修改,可执行目标文件中不包含重定位信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eRQTu99q-1684838384313)(RackMultipart20230523-1-g7n6yh_html_829fc5efddfc5915.png)]

图 13 重定位节.rela.text

4.4 Hello.o的结果解析

(以下格式自行编排,编辑时删除)

使用objdump -d -r hello.o,得到如图 14所示hello.o的反汇编,并与第3章的hello.s进行对照分析。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DuxkqP22-1684838384313)(RackMultipart20230523-1-g7n6yh_html_315d3200c86941f7.png)]

图 14hello.o的反汇编

(1)增加机器语言

每一条指令增加了十六进制的表示,即该指令的机器语言。

(2)操作数进制

反汇编文件中的所有操作数都改为十六进制。

(3)分支转移

反汇编的跳转指令中,所有跳转的位置被表示为主函数十段内偏移量这样确定的地址,而不再是段名称。

(4)函数调用

反汇编文件中对函数的调用与重定位条目相对应。观察下面两个call指令调用函数,在hello.s中为

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UpYpIOvS-1684838384314)(RackMultipart20230523-1-g7n6yh_html_11d76385aa7916b1.png)]

而在反汇编中调用函数为

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Wi4sj1K-1684838384314)(RackMultipart20230523-1-g7n6yh_html_6a5ba25f3821f2f0.png)]

在可重定位文件中call后面不再是函数名称,而是一条重定位条目指引的信息。

4.5 本章小结

本章简单概括了汇编的概念和作用,对hello.s进行了汇编,生成了hello.o可重定位目标文件,并且分析了可重定位文件的ELF头、节头部表、符号表和可重定位节。利用objdump进行反汇编,分析hello.s和hello.o反汇编代码的不同之处。

(第** 4 1 **分)

第5章 链接

5.1 链接的概念与作用

5.1.1链接概念

链接是将各种代码和数据片段收集并组合为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行与编译时,就是在源代码被翻译为机器代码时;也可以执行与加载时,也就是程序被加载器加载到内存并执行时;甚至执行于运行时,也就是由应用程序来执行。

5.1.2链接作用

链接是由叫做链接器的程序执行的。hello程序调用了printf函数,它是每个C编译器都提供的标准C库中的一个函数。printf函数存在于一个名为printf.o的单独的预编译好了的目标文件中,而这个文件必须以某种方式合并到我们的hello.o 程序中。链接器(Id)就负责处理这种合并。结果就得到hello文件,它是一个可执行目标文件(或者简称为可执行文件),可以被加载到内存中,由系统执行。

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

链接过程如图 15所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hDIPzDmN-1684838384315)(RackMultipart20230523-1-g7n6yh_html_d1a5b18d32beece3.png)]

图 15 链接过程

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

(1)ELF头

指令:readelf -h hello

与hello.o的ELF头比较,不同之处在于文件类型和节的大小

hello:类型为EXEC(可执行文件),节数目27

hello.o:类型为REL(可重定位文件),节数目14

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8WJ3sfAC-1684838384315)(RackMultipart20230523-1-g7n6yh_html_723a44cfca7b5.png)]

图 16ELF头

(2)节头部表Section Headers

指令:readelf -S hello

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l6RzVIG2-1684838384316)(RackMultipart20230523-1-g7n6yh_html_d07794b466753d2d.png)]

图 17 节头部表Section Headers

(3)符号表.symbol

指令:readelf -s hello

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hsV1D6XN-1684838384317)(RackMultipart20230523-1-g7n6yh_html_2a153318e551960d.png)]

图 18 符号表.symbol

(4)重定位节.rela.txt

指令:readelf -r hello

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0ZIeLfC4-1684838384317)(RackMultipart20230523-1-g7n6yh_html_9dddcfecd0ab9572.png)]

图 19 重定位节.rela.txt

5.4 hello的虚拟地址空间

通过edb观察到hello的虚拟地址空间开始于0x400000,结束于0x401ff0

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sqe7Wu9G-1684838384317)(RackMultipart20230523-1-g7n6yh_html_4170ae1f39cb833f.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o09dTccN-1684838384318)(RackMultipart20230523-1-g7n6yh_html_4a41cafed9c08837.png)]

根据5.3中的节头部表,可以找到各个节的信息。比如.txt节,虚拟地址开始于0x4010f0大小为0x145。

5.5 链接的重定位过程分析

命令:objdump -d -r hello

与hello.o的反汇编文件对比发现,hello的反汇编中多了许多节。前者中只有一个.text节,而且只有一个main函数,函数地址也是默认的0x000000。后者中有.init,.plt,.text三个节,而且每个节中有很多函数。库函数的代码都已经链接到了程序中,程序各个节变的更加完整,跳转的地址也具有参考性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hd9LDXEL-1684838384318)(RackMultipart20230523-1-g7n6yh_html_2e3068ae4f013c59.png)]

图 20hello的反汇编

(1)重定位节和符号定义链接器将所有类型相同的节合并在一起后,这个节就作为可执行目标文件的节。然后链接器把运行时的内存地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号,当这一步完成时,程序中每条指令和全局变量都有唯一运行时的地址。

(2)重定位节中的符号引用这一步中,连接器修改代码节和数据节中对每个符号的引用,使他们指向正确的运行时地址。

(3)重定位条目当编译器遇到对最终位置未知的目标引用时,它会生成一个重定位条目。代码的重定位条目放在.rel.txt中。

5.6 hello的执行流程

(1)开始执行:_start、_libc_start_main

(2)执行main:_main、_printf、_exit、_sleep、_getchar

(3)退出:_exit

程序名程序地址
_start0x4010f0
_libc_star_main0x2f12271d
main0x401125
_printf0x4010a0
_sleep0x4010c0
_getchar0x4010b0
_exit0x4010d0

5.7 Hello的动态链接分析

动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。虽然动态链接把链接过程推迟到了程序运行时,但是在形成可执行文件时(注意形成可执行文件和执行程序是两个概念),还是需要用到动态链接库。比如我们在形成可执行程序时,发现引用了一个外部的函数,此时会检查动态链接库,发现这个函数名是一个动态链接符号,此时可执行程序就不对这个符号进行重定位,而把这个过程留到装载时再进行。

PLT:PLT是一个数组,其中每个条目是16字节代码。PLT[0]是一个特殊条目,它跳转到动态链接器中。每个被可执行程序调用的库函数都有它自己的PLT条目。每个条目都负责调用一个具体的函数。

GOT:GOT是一个数组,其中每个条目是8字节地址。和PLT联合使用时,GOT[O]和GOT[1]包含动态链接器在解析函数地址时会使用的信息。GOT[2]是动态链接器在1d-linux.so模块中的入口点。其余的每个条目对应于一个被调用的函数,其地址需要在运行时被解析。每个条目都有一个相匹配的PLT条目。

通过查看hello的ELF文件如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uaJcJbzC-1684838384319)(RackMultipart20230523-1-g7n6yh_html_e878a633b9b61dd9.png)]

图 21hello的ELF文件

GOT表在调用dl_init之前的内容如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QgtSKuBJ-1684838384319)(RackMultipart20230523-1-g7n6yh_html_20dcb07a7db774e7.png)]

在dl_init调用后内容如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YV53rtHY-1684838384320)(RackMultipart20230523-1-g7n6yh_html_77e29f0c864dcaca.png)]

5.8 本章小结

本章主要介绍了链接的概念与作用,链接可分为符号定义和重定位,了解了可执行文件的ELF格式,分析了hello的虚拟地址空间,重定位过程,执行过程,动态连接过程,对链接有了更深的理解。

(第** 5 1 **分)

第6章 hello进程管理

6.1 进程的概念与作用

6.1.1进程概念

进程是一个执行中的程序的实例,每一个进程都有它自己的地址空间,一般情 况下,包括文本区域、数据区域、和堆栈。文本区域存储处理器执行的代码;数 据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储区着活动 过程调用的指令和本地变量。

6.1.2进程作用

进程为用户提供了以下假象:

1)我们的程序好像是系统中当前运行的唯一程序一样,我们的程序好像是独占的使用处理器和内存。

2)处理器好像是无间断的执行我们程序中的指令,我们程序中的代码和数据好像是系统内存中唯一的对象。

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

Linux系统中,Shell是一个交互型应用级程序,代表用户运行其他程序(是命令行解释器,以用户态方式运行的终端进程)。

其基本功能是解释并运行用户的指令,重复如下处理过程:

(1)终端进程读取用户由键盘输入的命令行。

(2)分析命令行字符串,获取命令行参数,并构造传递给execve的argv向量

(3)检查第一个(首个、第0个)命令行参数是否是一个内置的shell命令

(4)如果不是内部命令,调用fork( )创建新进程/子进程

(5)在子进程中,用步骤2获取的参数,调用execve( )执行指定程序。

(6)如果用户没要求后台运行(命令末尾没有&号)否则shell使用waitpid(或wait…等待作业终止后返回。

(7)如果用户要求后台运行(如果命令末尾有&号),则shell返回;

6.3 Hello的fork进程创建过程

在终端中输入./hello 120L021411 张洪博 1命令后,shell会处理该命令,判断出不是内置命令,则会调用fork函数创建一个新的子进程,子进程几乎但不完全与父进程相同。通过fork函数,子进程得到与父进程用户级虚拟地址空间相同的但是虚拟地址独立、PID也不相同的一份副本。

6.4 Hello的execve过程

在终端中输入./hello 2021112381 邓家颖 1命令后,shell会处理该命令,判断出不是内置命令,则会调用fork函数创建一个新的子进程,子进程几乎但不完全与父进程相同。通过fork函数,子进程得到与父进程用户级虚拟地址空间相同的但是虚拟地址独立、PID也不相同的一份副本。

6.5 Hello的进程执行

6.5.1逻辑控制流和时间片

进程的运行本质上是CPU不断从程序计数器 PC 指示的地址处取出指令并执行,值的序列叫做逻辑控制流。操作系统会对进程的运行进行调度,执行进程A->上下文切换->执行进程B->上下文切换->执行进程A->… 如此循环往复。 在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程,这种决策就叫做调度,是由内核中称为调度器的代码处理的。当内核选择一个新的进程运行,我们说内核调度了这个进程。在内核调度了一个新的进程运行了之后,它就抢占了当前进程,并使用上下文切换机制来将控制转移到新的进程。在一个程序被调运行开始到被另一个进程打断,中间的时间就是运行的时间片。

6.5.2用户模式和内核模式

Shell使得用户可以有机会修改内核,所以需要设置一些防护措施来保护内核,如限制指令的类型和可以作用的范围。

6.5.3上下文切换

如果系统调用因为等待某个事件发生而阻塞,那么内核可以让当前进程休眠,切换到另一个进程,上下文就是内核重新启动一个被抢占的进程所需要的状态,是一种比较高层次的异常控制流。

6.5.4调度

在对进程进行调度的过程,操作系统主要做了两件事:加载保存的寄存器,切换虚拟地址空间。

6.5.5用户态与核心态转换

为了能让处理器安全运行,需要限制应用程序可执行指令所能访问的地址范围。因此划分了用户态与核心态。核心态可以说是拥有最高的访问权限,处理器以一个寄存器当做模式位来描述当前进程的特权。进程只有故障、中断或陷入系统调用时才会得到内核访问权限,其他情况下始终处于用户权限之中,保证了系统的安全性。

6.6 hello的异常与信号处理

(1)正常运行hello

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oBmHIvEJ-1684838384321)(RackMultipart20230523-1-g7n6yh_html_ff6206345dca0e21.png)]

(2)Ctrl+Z

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oy1nKfm7-1684838384321)(RackMultipart20230523-1-g7n6yh_html_69f8624496f644bc.png)]

输入ctrl-z默认结果是挂起前台的作业,hello进程并没有回收,而是运行在后台下,用ps命令可以看到,hello进程并没有被回收。此时他的后台job号是1,调用fg 1将其调到前台,此时shell程序首先打印hello的命令行命令,hello继续运行打印剩下的8条info,之后输入字串,程序结束,同时进程被回收,如下图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vldq4D4H-1684838384322)(RackMultipart20230523-1-g7n6yh_html_a03eff21849f42c5.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qESRjGS4-1684838384322)(RackMultipart20230523-1-g7n6yh_html_dbd371773f9eb1f9.png)]

(3)Ctrl+C

在键盘上输入Ctrl+c会导致内核发送一个SIGINT信号到前台进程组的每个进程,默认情况是终止前台作业,用ps查看前台进程组发现没有hello进程,如图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XNEtT8aq-1684838384323)(RackMultipart20230523-1-g7n6yh_html_b44bcf0de8cb30f9.png)]

(4)乱按

无关输入被缓存到stdin,并随着printf指令被输出到结果。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yz66Y36r-1684838384323)(RackMultipart20230523-1-g7n6yh_html_73b7447d1083f15b.png)]

6.7本章小结

本章了解了hello进程的执行过程。在hello运行过程中,内核对其调度,异常处理程序为其将处理各种异常。每种信号都有不同的处理机制,对不同的shell命令,hello也有不同的响应结果。

(第** 6 1 **分)

第7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址

程序经过编译后出现在汇编代码中的地址。逻辑地址用来指定一个操作数或者是一条指令的地址。是由一个段标识符加上一个指定段内相对地址的偏移量,表示为段标识符:段内偏移量。

线性地址

逻辑地址向物理地址转化过程中的一步,逻辑地址经过段机制后转化为线性地址,为描述符:偏移量的组合形式,分页机制中线性地址作为输入。

虚拟地址

就是线性地址。

物理地址

CPU通过地址总线的寻址,找到真实的物理内存对应地址。CPU对内存的访问是通过连接着CPU和北桥芯片的前端总线来完成的。在前端总线上传输的内存地址都是物理内存地址。

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

在Intel平台下,逻辑地址(logical address)是selector:offset这种形式,selector是CS寄存器的值,offset是EIP寄存器的值。如果用selector去GDT(全局描述符表)里拿到segment base address(段基址)然后加上offset(段内偏移),这就得到了 linear address。我们把这个过程称作段式内存管理。

一个逻辑地址由段标识符和段内偏移量组成。段标识符是一个16位长的字段(段选择符)。可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段。

全局的段描述符,放在"全局段描述符表(GDT)"中,一些局部的段描述符,放在"局部段描述符表(LDT)"中。

给定一个完整的逻辑地址段选择符+段内偏移地址,看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。拿出段选择符中前13位,可以在这个数组中,查找到对应的段描述符,就得到了其基地址。Base+offset=线性地址。

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

线性地址即虚拟地址(VA)到物理地址(PA)之间的转换通过分页机制完成,而分页机制是对虚拟地址内存空间进行分页。

系统将虚拟页作为进行数据传输的单元。Linux下每个虚拟页大小为4KB。物理内存也被分割为物理页,MMU(内存管理单元)负责地址翻译,MMU使用页表将虚拟页到物理页的映射,即虚拟地址到物理地址的映射。

n位的虚拟地址包含两个部分:一个p位的虚拟页面偏移(VPO),一个n-p位的虚拟页号(VPN),MMU利用VPN选择适当的PTE,根据PTE,我们知道虚拟页的信息,如果虚拟页是已缓存的,那直接将页表条目的物理页号和虚拟地址的VPO串联起来就得到一个相应的物理地址。VPO和PPO是相同的。如果虚拟页是未缓存的,会触发一个缺页故障。调用一个缺页处理子程序将磁盘的虚拟页重新加载到内存中,然后再执行这个导致缺页的指令。

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

7.4.1 翻译后备缓冲器

每次CPU产生一个虚拟地址,MMU(内存管理单元)就必须查阅一个PTE(页表条目),以便将虚拟地址翻译为物理地址。在最糟糕的情况下,这会从内存多取一次数据,代价是几十到几百个周期。如果PTE碰巧缓存在L1中,那么开销就会下降1或2个周期。然而,许多系统都试图消除即使是这样的开销,它们在MMU中包括了一个关于PTE的小的缓存,称为翻译后备缓存器(TLB)。

7.4.2 多级页表:

将虚拟地址的VPN划分为相等大小的不同的部分,每个部分用于寻找由上一级确定的页表基址对应的页表条目

7.4.3 VA到PA的变换

处理器生成一个虚拟地址,并将其传送给MMU。MMU用VPN向TLB请求对应的PTE,如果命中,则跳过之后的几步。MMU生成PTE地址(PTEA),并从高速缓存/主存请求得到PTE。如果请求不成功,MMU向主存请求PTE,高速缓存/主存向MMU返回PTE。PTE的有效位为零,因此MMU触发缺页异常,缺页处理程序确定物理内存中的牺牲页(若页面被修改,则换出到磁盘——写回策略)。缺页处理程序调入新的页面,并更新内存中的PTE。缺页处理程序返回到原来进程,再次执行导致缺页的指令。

在多级页表的情况下,无非就是不断通过索引–地址–索引–地址重复四次进行寻找。

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

获得物理地址之后,先取出组索引对应位,在L1中寻找对应组。如果存在,则比较标志位,相等后检查有效位是否为1.如果都满足则命中取出值传给CPU,否则按顺序对L2cache、L3cache、内存进行相同操作,直到出现命中。然后再一级一级向上传,如果有空闲块则将目标块放置到空闲块中,否则将缓存中的某个块驱逐,将目标块放到被驱逐块的位置。

7.6 hello进程fork时的内存映射

在shell输入命令行后,内核调用fork创建子进程,为hello程序的运行创建上下文,并分配一个与父进程不同的PID。同时为这个新进程创建虚拟内存,创建当前进程的mm_struct、区域结构和页表的原样副本。它将两个进程中的每个页面都标记位只读,并将两个进程中的每个区域结构都标记为私有的写时复制。

当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面。

7.7 hello进程execve时的内存映射

execve函数调用驻留在内核区域的启动加载器代码,在当前进程中加载并运行包含在可执行目标文件hello中的程序,用hello程序有效地替代了当前程序。它可能会自动覆盖当前进程中的所有虚拟地址和空间,删除当前进程虚拟地址的所有用户虚拟和部分空间中的已存在的代码共享区域和结构,但它不会自动创建一个新的代码共享进程。

加载并运行hello需要以下几个步骤:

(1)删除当前进程虚拟地址中已存在的用户区域

(2)映射私有区域,为新程序的代码、数据、bss和栈创建新的区域结构。

(3)映射共享区域,将hello与libc.so动态链接,然后再映射到虚拟地址空间中的共享区域。

(4)设置当前进程上下文程序计数器(PC),使之指向代码区域的入口点。

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

页面命中完全是由硬件完成的,而处理缺页是由硬件和操作系统内核协作完成的,在指令请求一个虚拟地址时,MMU中查找页表,如果这时对应得物理地址没有存在主存的内部,我们必须要从磁盘中读出数据。在虚拟内存的习惯说法中,DRAM缓存不命中成为缺页。在发生缺页后系统会调用内核中的一个缺页处理程序,选择一个页面作为牺牲页面。

具体流程如下:

(1)处理器生成一个虚拟地址,并将它传送给MMU

(2)MMU生成PTE地址,并从高速缓存/主存请求得到它

(3)高速缓存/主存向MMU返回PTE

(4)PTE中的有效位是0,所以MMU出发了一次异常,传递CPU中的控制到操作系统内核中的缺页异常处理程序。

(5)缺页处理程序确认出物理内存中的牺牲页,如果这个页已经被修改了,则把它换到磁盘。

(6)缺页处理程序页面调入新的页面,并更新内存中的PTE。

(7)缺页处理程序返回到原来的进程,再次执行导致缺页的命令。CPU将引起缺页的虚拟地址重新发送给MMU。因为虚拟页面已经换存在物理内存中,所以就会命中。

7.9动态存储分配管理

动态内存管理的基本方法与策略:

动态内存分配器维护着一个进程的虚拟内存区域,称为堆,系统之间细节不同,但是不失通用性,假设堆是一个请求二进制零的区域,它紧接在未初始化的数据区域后开始,并向上生长(向更高地址)。对于每个进程,内核维护着一个变量brk,它指向对的顶部。

分配器将堆视为一组不同大小的块的集合来维护。每个块就是一个个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显示地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显示地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显示执行的。要么是内存分配器自身隐式执行的。分配器有两种基本风格。两种风格都要求应用显示地分配块。他们的不同之处在于由哪个实体来负责释放已分配的块。

显示分配器:要求应用显示的释放任何已分配的块。例如C标准库提供一个叫做malloc程序包的显示分配器。

隐式分配器:要求分配器检测一个已分配块何时不再被程序使用,那么就释放这个块。隐式分配器也叫垃圾收集器。

使用边界标记的堆块的格式其中头部和脚部分别存放了当前内存块的大小与是否已分配的信息。通过这种结构,隐式动态内存分配器会对堆进行扫描,通过头部和脚部的结构实现查找。

使用双向链表而不是隐式空闲链表,使首次适配的分配时间从块总数的线性时间减少到了空闲块数量的线性时间。维护链表的顺序有:后进先出(LIFO),将新释放的块放置在链表的开始处,使用LIFO的顺序和首次适配的放置策略,分配器会最先检查最近使用过的块,在这种情况下,释放一个块可以在线性的时间内完成,如果使用了边界标记,那么合并也可以在常数时间内完成。按照地址顺序来维护链表,其中链表中的每个块的地址都小于它的后继的地址,在这种情况下,释放一个块需要线性时间的搜索来定位合适的前驱。平衡点在于,按照地址排序首次适配比LIFO排序的首次适配有着更高的内存利用率,接近最佳适配的利用率。

7.10本章小结

本章介绍了存储器地址空间、段式管理、页式管理,VA到PA的变换、物理内存访问,hello进程fork时和execve时的内存映射、缺页故障与缺页中断处理、包括隐式空闲链表和显式空闲链表的动态存储分配管理。

(第** 7 2 分)**

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

Linux将文件所有的I/O设备都模型化为文件,甚至内核也被映射为文件。这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,称为Unix I/O。Linux就是基于Unix I/O实现对设备的管理。

设备的模型化:文件

设备管理:unix io接口

8.2 简述Unix IO接口及其函数

Unix IO接口:

1)打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备,内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件,内核记录有关这个打开文件的所有信息。应用程序只需记住这个描述符。

2)Linux Shell创建的每个进程都有三个打开的文件:标准输入(描述符为0),标准输出(描述符为1),标准错误(描述符为2)。

3)改变当前的文件位置。对于每个打开的文件,内核保持着一个文件位置k,初始为0,这个文件位置是从文件开头起始的字节偏移量,应用程序能够通过执行seek,显式地将改变当前文件位置k。

4)读写文件。一个读操作就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n,给定一个大小为m字节的而文件,当k>=m时,触发EOF。类似一个写操作就是从内存中复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k。

5)关闭文件。当应用完成了对文件的访问之后,它就通知内核关闭这个文件,作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。无论一个进程因为何种原因终止时,内核都会关闭所有打开的文件并释放他们的内存资源。

Unix I/O函数:

1)int open(char* filename,int flags,mode_t mode)

进程通过调用open函数来打开一个存在的文件或是创建一个新文件的。open函数将filename转换为一个文件描述符,并且返回描述符数字,返回的描述符总是在进程中当前没有打开的最小描述符,flags参数指明了进程打算如何访问这个文件,mode参数指定了新文件的访问权限位。

2)int close(fd)

进程通过调用close函数关闭一个打开的文件,fd是需要关闭的文件的描述符。

3)ssize_t read(int fd,void *buf,size_t n)

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

4)ssize_t wirte(int fd,const void *buf,size_t n)

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

8.3 printf的实现分析

printf(const char *fmt,···)

{

int i;

va_list arg = (va_list)((char *)($fmt) + 4);

i = vsprintf(buf, fmt, arg);

write(buf, i);

return i;

}

引用的vsprintf函数

int vsprintf(char *buf,const char *fmt,va_list args)

{

char *p;

char tmp[256];

va_listp_next_arg = args;

for (p = buf; *fmt; fmt++)

{

if(*fmt != ‘%’)

{

*p++ = *fmt;

continue;

}

fmt++;

switch(*fmt)

{

case ‘x’:

itoa(tmp, *((int *)p_next_arg));

strcpy(p, tmp);

p_next_arg += 4;

p += strlen(tmp);

break;

case ‘s’:

break;

default:

break;

}

return (p - buf);

}

}

vsprintf函数将所有的参数内容格式化之后存入buf,返回格式化数组的长度。write函数将buf中的i个元素写到终端。从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

8.4 getchar的实现分析

getchar的函数体如下:

int getchar(void)

{

static char buf[BUFSIZ];//缓冲区

static char* bb=buf;//指向缓冲区的第一个位置的指针

static int n=0;//静态变量记录个数

if(n==0)

{

n=read(0,buf,BUFSIZ);

bb=buf;//并且指向它

}

return(–n>=0)?(unsigned char)*bb++:EOF;

}

异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。

getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

8.5本章小结

本章主要介绍了Linux的IO设备管理方法、Unix IO接口及其函数,分析了printf函数和getchar函数的实现。

(第** 8 1 **分)

结论

用计算机系统的语言,逐条总结hello所经历的过程。

首先编写hello.c的源程序,从此hello.c诞生。此时hello.c仍是一个文本文件,还没有变成二进制文件。

对hello.c进行预处理(gcc -E),hello.c变成了hello.i。

对hello.i进行编译处理(gcc -S),hello.i变成了hello.s。

对hello.s进行汇编处理(gcc -c),hello.s变成了hello.o。此时hello变成了二进制文件。

对hello.o进行链接处理,将其与其它可重定位目标文件以及动态链接库进行链接生成可执行目标文件hello。此时hello程序就可以在计算机上执行。

在shell命令行上输入./hello 120L021411 zhb 1来运行hello程序。

shell首先判断输入命令是否为内置命令。经过检查后发现其不是内置命令,则shell将其当作程序执行。

shell调用fork函数创建一个子进程。

shell调用execve函数,execve函数会将新创建的子进程的区域结构删除,然后将其映射到hello程序的虚拟内存,然后设置当前进程上下文中的程序计数器,使其指向hello程序的入口点。

运行hello时,内存管理单元MMU、翻译后备缓冲器TLB、多级页表机制、三级cache协同工作,完成对地址的翻译和请求。

execve函数并未将hello程序实际加载到内存中。当CPU开始执行hello程序对其进行取指时,发现其对应的页面不在内存中。此时会出现缺页故障这一异常,操作系统通过异常表对缺页处理程序进行间接调用,缺页处理程序实现虚拟内存和物理内存的映射,将缺失的页面加载到内存中。此时CPU重新执行引起故障的指令,指令可以正常执行,程序从而继续向下执行。

当hello程序调用sleep函数后进程休眠进入停止状态。而CPU不会等待hello程序休眠结束,而是通过内核进行上下文切换将当前进程的控制权转移到其它进程。当sleep函数调用完成后,内核再次进行上下文切换重新执行hello进程。

当hello程序执行printf函数时,会调用malloc函数从堆中申请内存。

在hello进程执行时,当在命令行中输入Ctrl-C时,shell会向前台作业发送SIGINT信号,该信号会终止前台作业,即hello程序终止执行。当输入Ctrl-Z时,shell会向前台作业发送SIGTSTP信号,该信号会挂起当前进程,即hello程序停止执行,之后再向其发送SIGCONT信号时,hello程序会继续执行。

当hello进程执行完成后,父进程会对子进程进行回收。内核删除为这个进程创建的所有数据结构。

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

计算机系统的设计与实现是一项非常复杂的任务,需要多个层次的软件和硬件协同工作。从高级语言到机器语言,从操作系统到硬件,每个层次都有其独特的功能和特点。计算机系统的设计与实现需要深入理解计算机体系结构、操作系统、编译器、汇编器、链接器等多个方面的知识。只有深入理解这些知识,才能够设计出高效、可靠、安全的计算机系统。

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

附件

列出所有的中间产物的文件名,并予以说明起作用。

hello.c 源程序

hello.i 经过预处理的文件,包含了源程序文件和头文件

hello.s 编译后的汇编语言文件,包含了汇编语言代码

hello.o 汇编后的可重定位执行文件,包含了机器语言代码

hello 链接后的可执行文件,包含了可执行代码和数据ICS大作业论文ICS大作业论文

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

参考文献

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

[1] 林来兴. 空间控制技术[M]. 北京:中国宇航出版社,1992:25-42.

[2] 辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.

[3] 赵耀东. 新时代的工业工程师[M/OL]. 台北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).

[4] 谌颖. 空间交会控制理论与方法研究[D]. 哈尔滨:哈尔滨工业大学,1992:8-13.

[5] KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.

[6] CHRISTINE M. Plant Physiology: Plant Biology in the Genome Era[J/OL]. Science,1998,281:331-332[1998-09-23]. http://www.sciencemag.org/cgi/ collection/anatmorp.

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值