计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业
学 号
班 级
学 生
指 导 教 师
计算机科学与技术学院
2025年5月
本文深入探索了“hello”程序背后复杂而精妙的计算机系统运作机制。从程序员借助文本编辑器完成源代码编写,到预处理器解析宏定义、处理文件包含等指令生成预处理后的文件;从编译器将预处理文件转化为汇编代码,再到汇编器将其进一步转换为机器语言的目标文件,每个步骤都环环相扣。链接器在其中扮演着关键角色,它将目标文件与库文件进行链接,生成最终可执行文件。
当我们在shell中运行该可执行文件时,操作系统通过fork函数创建新进程,并借助execve函数加载并执行程序。在此过程中,虚拟地址空间被巧妙的映射,程序得以在内存中顺利运行,期间涉及的函数调用与指令执行皆与系统紧密协作。运行时,程序依据调度算法分配 CPU 时间片,访问内存,处理 I/O 操作,甚至应对用户通过键盘输入的各类信号,如Ctrl+C、Ctrl+Z等。直至程序执行完毕,子进程终止,父进程负责回收资源,内核清理相关数据结构,宣告“hello”程序生命的终结。
这一完整的生命历程,让我们清晰见证了计算机系统从软件到硬件各层面如何协同工作,充分展现了编译器、操作系统和硬件架构的巧妙设计,也为我们深入理解计算机系统的设计与实现提供了极具价值的实践案例。
关键词:预处理、编译、汇编、链接、内存管理、信号处理
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目 录
2.2在Ubuntu下预处理的命令............................................................................. - 5 -
5.3 可执行目标文件hello的格式........................................................................ - 8 -
6.2 简述壳Shell-bash的作用与处理流程........................................................ - 10 -
6.3 Hello的fork进程创建过程......................................................................... - 10 -
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.2 简述Unix IO接口及其函数.......................................................................... - 13 -
第1章 概述
1.1 Hello简介
(1)P2P简介
源程序是hello.c;预处理:编译器的预处理器以#开头的预处理指令如#include、#define等对hello.c进行预处理操作得到修改后的源程序hello.i;编译:在编译阶段编译器将预处理后的源代转换为汇编语言代码hello.s;汇编:汇编器将汇编指令翻译成二进制形式的机器指令,并生成目标文件hello.o,包含可重定位的机器代码和符号表信息;链接:链接器将目标文件与所需的库文件(如标准C库)进行链接,生成可执行文件hello,可以直接在操作系统上运行;运行:在Shell中运行可执行文件hello,操作系统通过fork函数调用创建一个新的进程,为Hello程序分配独立的进程空间。
(2)O2O简介
在Shell环境下,使用相应指令来调用fork函数,从而生成一个新的子进程。随后借助execve函数实现程序的加载与运行,将其映射到对应的虚拟内存区域,分配所需的物理内存,进而运行hello程序。等hello程序执行完成之后,父进程会回收hello子进程,内核则清除与之相关的数据信息。
1.2 环境与工具
处理器:13th Gen Intel(R) Core(TM) i9-13900HX 2.20 GHz
机带RAM:16.0 GB
系统类型:64 位操作系统, 基于 x64 的处理器
软件环境:Windows 11 家庭中文版,VMware® Workstation 16 Pro,Ubuntu 20.04.4 LTS
开发与调试:vim、gdb、objump、gcc、readelf等
1.3 中间结果
附件1:hello.c
作用:源程序
附件2:hello.i
作用:预处理后生成的.i文件
附件3:hello.s
作用:经过编译器编译生成的汇编语言文件
附件4:hello.o
作用:汇编生成的可重定位的目标文件
附件5:hello.elf
作用:hello.o的elf格式,用于展示可重定位的elf文件格式
附件6:hello.asm
作用:hello.o的反汇编格式,用汇编语言的格式来观察目标文件
附件7:hello
作用:链接后生成的可执行的目标文件
附件8:hello_exe.elf
作用:hello的elf格式,用于展示可执行的elf文件格式
附件9:hello_exe.asm
作用:hello的反汇编格式,用汇编语言的格式来观察可执行的目标文件
1.4 本章小结
本章第一部分对"hello"程序进行了相对全面的概述,阐释了P2P和O2O这两个概念的意义及运作过程。第二部分详细描述了完成作业的过程中所使用的硬件环境、软件环境以及开发工具。第三部分简单概括了从.c源文件到生成可执行文件所经历的主要步骤和流程。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
预处理是指在C语言程序正式编译之前,由预处理器对源代码进行处理的过程。
预处理的作用:
- 宏定义的处理:宏定义是通过#define指令实现的,它允许用户定义一个标识符,并将其与一段代码或值关联起来。预处理器会将源代码中所有出现的宏名替换为对应的宏体。宏的本质是替换。
- 头文件包含:使用#include等指令可以将指定的头文件内容插入到当前源文件中。头文件通常包含函数声明、宏定义、数据类型定义等内容,这些内容在编译时需要被包含到源文件中,以便编译器能够正确地识别和处理。
- 条件编译:根据预定义的宏或条件表达式的值来决定是否编译某段代码。
2.2在Ubuntu下预处理的命令
gcc -m64 -no-pie -fno-PIC -E hello.c -o hello.i

图 1 在Ubuntu下预处理的命令
2.3 Hello的预处理结果解析
发现经过处理之后的文件变成了3061行,3075行之前均为头文件。main函数中的代码没有被改变,而开头的头文件#include <stdio.h>#include <unistd.h> #include <stdlib.h>被扩展了很多行,除此之外原本所有的注释全部都被删除掉了

图 2 Hello的预处理结果1

图 3 Hello的预处理结果2
2.4 本章小结
本章介绍了预处理的概念和作用、Ubuntu下的预处理指令,此外还包括了hello.c文件的预处理结果hello.i的文本文件解析,详细描述了预处理的内涵。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译是指编译器将预处理后的源代码转换为汇编语言代码hello.s。编译过程可以分为多个阶段,包括词法分析、语法分析、语义分析等。
编译的作用:
- 检查语法和语义错误:如果源代码中存在语法错误或语义错误等,编译器会报错并指出错误的位置和原因。
- 代码优化:优化的目的是提高程序的运行效率和减小程序的体积。
- 生成汇编代码
3.2 在Ubuntu下编译的命令
gcc -S hello.i -o hello.s -fno-PIC -no-pie -m64

图 4 在Ubuntu下编译的命令
3.3 Hello的编译结果解析
3.3.1字符串常量

图 5 源代码中的字符串常量
储存在只读字符串中,通过标签.LC0和.LC1引用。

图 6 .LC0等引用图示
此外可以发现使用字符串常量时都是直接使用标号:

图 7 直接使用标号图示
这样做便于在后续计算地址后换为对应地址。
3.3.2局部变量
![]()
图 8 源代码中的局部变量
循环变量的i,存储在栈中,偏移量为-4(%rbp)

图 9 局部变量图示
3.3.3赋值
![]()
图 10 源代码中的赋值行为
在循环开始时对局部变量i赋值为0作为计数器。

图 11 赋值图示
3.3.4关系操作

图 12 源代码中的关系操作
本段代码共有两个关系操作,分别为判断argc是否等于5以及局部变量i是否小于10,其在汇编语言中如图:

图 13 汇编语言中的关系操作图示
即关系操作在汇编语言中均由cmp和条件跳转实现。
3.3.5类型转换
通过调用atoi函数将argv[4]的字符串转换为整数

图 14 类型转换图示
3.3.6算术操作
算术操作中的add在汇编语言中使用add进行实现,减法指令使用sub进行实现。

图 15 算术操作图示
3.3.7数组和指针操作

图 16 源代码中的数组和指针操作
在源代码中使用了argv这个数组的变量值。

图 17 汇编代码的数组和指针操作图示
可以观察到汇编代码中使用首地址加偏移量访问数组,同时指针的解引用通过movq (%rax),rax实现。
3.3.8控制转移
(1)分支结构

图 18 源代码中的分支结构
源代码中存在这样一个分支结构,其在汇编语言中的表现如下:

图 19 分支结构图示
执行流程:如果-20(%rbp)存储的argc等于5就跳过中间的语句,否则就顺序执行到exit函数结束。
(2)循环结构

图 20 源代码中的循环结构
源代码中存在这样一个循环结构,其在汇编语言中的表现如下:

图 21 循环结构图示
L4是每次循环会执行的操作。先初始化i为1,跳转到L3来进行判断,若小于等于就跳转进L4,再在L4里面执行i++的操作。顺序执行到L3位置再次进行判断是否跳转,直到i>9不再跳转,顺序执行退出循环。
3.3.9函数操作
(1)参数传递
printf 中的参数通过寄存器来进行传递:

图 22 参数传递图示
(2)函数调用
puts、exit、sleep的函数调用如图:

图 23 函数调用图示
printf、atoi函数调用先前已经提过,在此不再赘述
(3)函数返回

图 24 函数返回图示
恢复应由被调用者保存的寄存器的值,恢复旧的栈顶指针%rbp,跳转回原控制流地址。
3.4 本章小结
本章着重介绍了编译器将hello.i文件转换为hello.s文件的过程,首先解释了编译的含义及其作用,然后展示了本次实验中编译所用的指令,最后分析了生成的hello.s文件中如何进行赋值、关系操作、类型转换等操作。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
汇编是程序开发过程中的一个重要步骤,位于编译和链接之间。汇编的主要任务是将汇编语言源代码(hello.s)转换为目标文件(hello.o)。目标文件是二进制格式的中间文件,包含了机器语言指令和符号信息,但尚未完全准备好直接运行。
汇编的作用:
- 将汇编语言转换为机器语言并生成.o文件
- 进行符号解析、生成重定位信息便于链接器解析
4.2 在Ubuntu下汇编的命令
gcc -m64 -no-pie -fno-PIC -c hello.s -o hello.o

图 25 在Ubuntu下汇编的命令
4.3 可重定位目标elf格式
在shell中输入readelf -a hello.o > hello.elf 指令获得 hello.o 文件的 ELF 格式。
-
-
- elf头
-
- 标识字段:此字段长度为 16 字节,用于确定文件类型及结构,涵盖了文件的魔数、类别、字节顺序以及 ELF 版本等相关信息。
- 文件类型:明确 ELF 文件的种类,像可执行文件、共享对象文件、目标文件等均属于此类别。
- 机器类型:确定 ELF 文件所适配的目标机器架构,常见的有 x86、ARM、PowerPC 等架构类型。
- 版本:给出 ELF 文件的版本编号。
- 入口地址:在可执行文件中,标明程序开始执行的入口地址。
- 程序头起点:指出程序头表在文件中的偏移位置,程序头表内包含对 ELF 文件中各段进行描述的信息。
- 节头表偏移量:确定节头表在文件中的偏移位置,节头表包含对 ELF 文件中各节进行描述的信息。
- 标志:囊括了一些用于阐述文件特性的标志位。
- ELF头的大小:明确 ELF 文件头所占的大小。
- 程序头表中每个条目的大小:确定程序头表中每个条目所占的大小。
- 程序头表中条目的数量:给出程序头表中包含的条目总数。
- 节头表中每个条目的大小:明确节头表中每个条目所占的大小。
- 节头表中条目的数量:给出节头表中包含的条目总数。
- 字符串表的节头索引:确定节头表中字符串表节对应的索引,字符串表主要用于存储节名以及程序头表中的段名。

图 26 elf头
4.3.2节头表
记录各节名称、类型、地址、偏移量、大小、全体大小、旗标、链接、信息、对齐。

图 27 节头表
4.3.3重定位节

图 28 重定位节
.rel.text节是一个.text节中位置的列表。当链接器把这个目标文件和其他文件组合时,需要修改这些位置。一般而言,任何调用外部函数或者引用全局变量的指令都需要修改,而调用本地函数的指令不需修改。
4.3.4符号表

图 29 符号表
.symtab节中包含ELF符号表。这张符号表包含一个条目的数组,存放一个程序定义和引用的全局变量和函数的信息。
4.4 Hello.o的结果解析
4.4.1增加了指令机器码

图 30 增加指令机器码图示
每一条指令前面都有了该指令的机器语言对应的16进制表示。
4.4.2标号被替换为具体地址

图 31 标号被替换为具体地址图示
可以观察到标号被替换为具体地址,即跳转的位置被表示为与类似主函数+段内偏移量的确定的地址,包括跳转、常量和函数调用。
4.4.3有重定位条目

图 32 重定位条目图示
如图所示,反汇编代码中添加了重定位条目。
本章介绍汇编的概念、作用及常用命令。深入剖析了.o文件在ELF格式下各部分的内容与格式,同时将其反汇编后的代码与.s文件进行对比,通过这样的比较可以更清晰地了解汇编语言与机器语言的差异,从而更好地掌握汇编过程的作用。
(第4章1分)
第5章 链接
5.1 链接的概念与作用
链接位于汇编之后和程序最终生成可执行文件之前。链接的主要任务是将目标文件(hello.o)以及所需的库文件组合在一起,解析符号引用,生成最终的可执行文件(hello)。
链接的作用:
- 符号解析与地址分配:hello.o中定义的符号会被记录在符号表中,而对其他目标文件中符号的引用则会被记录为外部引用。链接器的作用是解析这些外部引用,将它们与目标文件或库文件中定义的符号进行匹配,并为每个符号分配最终的内存地址。
- 重定位:链接器需要将代码和数据的相对地址转换为绝对地址,以便程序在内存中正确运行。
- 生成可执行文件:链接器将多个目标文件和库文件组合在一起,生成最终的可执行文件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

图 33 在Ubuntu下链接的命令
5.3 可执行目标文件hello的格式
5.3.1 elf头
观察可以发现其elf头与可重定位文件的elf头类似,但类型变为了EXEC类型,即可执行的。

图 34 elf头
5.3.2节头表

图 35 节头表
每一节的大小偏移量和其他属性如图所示
5.3.3程序头

图 36 程序头
程序头是一个结构数组,描述了系统准备程序执行所需的段等信息。
5.3.4 Dynamic section
Dynamic section主要用于支持动态链接和共享库的使用。

图 37 Dynamic section
5.3.5重定位节
重定位节主要作用是存储重定位信息,以便在链接过程中正确地调整和转换程序中的地址和符号引用,确保程序在内存中正确运行。

图 38 重定位节
5.3.6 符号表
符号表用于存储程序中各种符号的信息,这些符号可以是函数、全局变量、静态变量等,在程序的编译、链接和调试过程中起着关键作用。
图 39 符号表
5.4 hello的虚拟地址空间
使用gdb/edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
使用gdb加载hello。
可以在hello.elf的程序头表中进行一一对应。程序头表在程序执行时使用,它能够告诉链接器运行时需要加载的内容,并提供动态链接信息。

图 40 info files查看

图 41 info proc mappings查看
5.5 链接的重定位过程分析

图 42 重定位过程分析
对比之后可以发现,hello和hello.o反汇编生成的代码部分完全相同,二者的区别在于:
- 因为动态连接器将共享库hello.c用到的函数加入到了elf中,所以链接后的反汇编文件hello1.asm中,多了.plt等函数的代码。
- 调用指令的call函数在动态链接后,可以直接找到puts@plt表进行跳转,因为在链接过程中,链接器解析了重定位条目,并计算相对距离,修改了对应位置的字节代码为PLT 中相应函数与下条指令的相对地址。
5.6 hello的执行流程
图 43 使用的函数
从开始加载到完成执行,这些过程构成了程序的完整历程:动态链接器首先在_start中设置运行环境并调用__libc_csu_init函数,再跳转进入main函数;在main函数内部通过各自的PLT条目调用相关的标准库函数;最后依次执行__libc_csu_fini函数、_fini函数并通过exit返回操作系统。
5.7 Hello的动态链接分析
5.7.1 ELF .dynamic段关键表项

图 44 ELF .dynamic段关键表项
5.7.2未解析前程序入口处GOT条目

图 45 未解析前程序入口处GOT条目
5.7.3第一次调用printf后GOT条目(已解析)

图 46 第一次调用printf后GOT条目(已解析)
5.8 本章小结
在本章,首先阐述了链接的概念和作用,展示了如何使用命令链接并生成hello可执行文件,同时观察了hello.ELF文件,利用gdb了解了虚拟地址空间的使用,最后通过gdb阐述了重定位和动态链接的过程。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
进程是程序的一次动态执行实例,是操作系统进行资源分配和调度的基本单位。
进程的作用:
- 资源隔离:每个进程拥有独立的虚拟地址空间,避免相互干扰
- 并发执行的基础:在多任务操作系统中,多个进程可以并发运行,从而提高系统的资源利用率和运行效率。
- 资源管理:操作系统通过进程为单位分配CPU时间、内存等资源。
6.2 简述壳Shell-bash的作用与处理流程
作用:作为用户与内核的中介来接收用户输入的命令,解析并调用系统功能或应用程序。
处理流程:
- 读取从键盘输入的命令
- 判断命令是否正确,且将命令行的参数改造为系统调用execve() 内部处理所要求的形式
- 终端进程调用fork() 来创建子进程,自身则用系统调用wait() 来等待子进程完成
- 当子进程运行时,它调用execve() 根据命令的名字指定的文件到目录中查找可行性文件,调入内存并执行这个命令
- 如果用户没要求后台运行(命令末尾没有&号),则shell使用waitpid(或wait)等待作业终止后返回
- 如果用户要求后台运行(命令末尾有&号),则shell返回
6.3 Hello的fork进程创建过程
当输入命令后,Shell会调用fork()函数创建一个子进程。子进程会获得与父进程相同的用户级虚拟空间副本,包括数据段、代码段、共享库等,这些副本在逻辑上完全独立。此外,子进程可以访问与读写父进程打开的文件。fork()函数调用一次但返回两次:在父进程中返回子进程的PID,而在子进程中返回0。
6.4 Hello的execve过程
操作系统首先解析可执行文件的格式,将代码段和数据段加载到子进程的内存空间中,替换掉部分从父进程继承的内容。随后,它初始化程序的运行环境,设置程序的入口点,并将命令行参数和环境变量传递给程序。最后,操作系统将控制权交给程序的入口点,程序从入口函数开始执行,最终调用main()函数,从而启动hello程序的运行。
6.5 Hello的进程执行
当hello程序被加载到内存并准备执行时,操作系统会将其加入进程调度队列。根据调度算法,hello进程被分配到CPU时间片后,操作系统会保存当前运行进程的上下文信息,并切换到hello进程的上下文,恢复其寄存器状态,开始执行程序代码。
在执行过程中,hello进程可能在用户态运行用户代码,也可能因系统调用切换到核心态请求操作系统服务。
如果时间片用完或因阻塞条件暂停,操作系统会将hello进程的状态设置为就绪或阻塞,并切换到其他进程运行。
hello程序执行完成,操作系统会清理其占用的资源,父进程通过wait()获取子进程的退出状态后,hello进程的生命周期结束。
6.6 hello的异常与信号处理
- 正常执行

图 47 正常执行
- 乱按、回车

图 48 乱按、回车
屏幕上会显示按下的内容,但不影响程序的输出和执行。在执行后按下的内容会被当成指令执行。
- Ctrl-C

图 49 Ctrl-C
Ctrl-C发送SIGINT信号,当父进程收到信号时结束前台进程hello并回收。
- Ctrl-Z

图 50 Ctrl-Z
按下Ctrl+Z, shell父进程收到SIGSTP信号,信号处理函数的逻辑是打印屏幕回显、将hello进程挂起,通过ps命令我们可以看出hello进程没有被回收。此时可以通过fg把它挂回前台,可以看到hello进程又正常运行。
- KILL

图 51 KILL
使用kill之后再输入fg发现继续执行 kill杀死了进程。
6.7本章小结
本章介绍了进程的定义与作用和shell的一般处理流程,较详细的分析了hello执行过程中的进程管理,以及调用fork创建新进程、调用execve执行hello、进程执行,异常与信号处理的过程。
(第6章2分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
(1)逻辑地址:在Hello程序中,逻辑地址是程序中的指令和数据在编译后的地址表示,通常与程序中的变量、函数等相对位置相关。例如,在hello.c中定义的变量char *argv,其在程序中的逻辑地址可能是相对于程序起始地址的一个偏移量。
(2)线性地址:对于Hello程序来说,线性地址是经过段式管理后得到的地址,它是一个在段内的偏移量,并且该段已通过段描述符定义了基地址等信息。例如,当Hello程序的代码段被加载到内存中的某个位置后,线性地址就是从该段的基地址开始计算的偏移地址。
(3)虚拟地址:在Hello程序运行时,虚拟地址是程序所使用的地址空间中的地址,它由线性地址经过页式管理后映射到物理地址。例如,当Hello程序访问一个变量时,所使用的虚拟地址是程序中该变量对应的地址,而操作系统会负责将其映射到实际的物理地址。
(4)物理地址:物理地址是Hello程序实际在内存中的存储位置。当操作系统将Hello程序的虚拟地址映射到物理地址后,Hello程序的数据和指令就会被存储在物理内存的相应位置。
7.2 Intel逻辑地址到线性地址的变换-段式管理
在Intel的段式管理中,逻辑地址由段选择符和段内偏移量组成。段选择符用于选择相应的段描述符,该描述符包含段的基地址等信息。当Hello程序的指令通过逻辑地址访问时,CPU会根据段选择符找到段描述符,将段内偏移量加上基地址,从而得到线性地址。转换时,线性地址=段基址+偏移量。
7.3 Hello的线性地址到物理地址的变换-页式管理
在页式管理中,线性地址会被划分成页目录项、页表项和页内偏移量等部分。以x86架构为例,线性地址通常分为页目录索引、页表索引和页内偏移量。操作系统会维护页目录和页表,页目录项指向页表的物理地址,页表项指向物理页框的物理地址。当Hello程序的线性地址需要转换为物理地址时,CPU首先根据页目录索引找到对应的页目录项,从中获取页表的物理地址;然后根据页表索引找到页表项,得到物理页框的地址;最后将物理页框地址与页内偏移量组合,得到物理地址。
7.4 TLB与四级页表支持下的VA到PA的变换
在Hello程序运行过程中,TLB用于缓存虚拟地址到物理地址的映射关系。当CPU需要将虚拟地址转换为物理地址时,首先会在TLB中查找对应的映射条目。如果命中(即找到对应的映射关系),则直接使用TLB中的物理地址进行内存访问,加快了地址转换的速度。四级页表可以更高效地管理虚拟地址到物理地址的映射。以Hello程序为例,当VA需要转换为PA时,CPU会依次访问四级页表中的各个表项。从根页表开始,根据虚拟地址的不同部分逐步定位到叶页表项,最终得到物理地址。这个过程涉及到多次内存访问,而TLB的存在就是为了减少这种访问次数,提高地址转换效率。
7.5 三级Cache支持下的物理内存访问
现代处理器通常配备了L1、L2和L3三级Cache。在Hello程序运行时,处理器首先会尝试从L1 Cache中获取所需的数据或指令。如果L1 Cache未命中,则依次访问L2和L3 Cache,直至找到所需数据或从物理内存中读取。当Hello程序的物理地址确定后,处理器会根据该地址在物理内存中查找相应的数据或指令。为了加快访问速度,操作系统和硬件会将频繁访问的数据或指令存储在Cache中。例如,当Hello程序中的一个循环频繁访问某个变量时,该变量所在的物理内存块可能会被加载到L1 Cache中,以便下次访问时可以快速获取。
7.6 hello进程fork时的内存映射
父进程执行fork函数生成hello子进程时,内核先给新进程安排独立PID,同时复制父进程的虚拟内存布局。共享的物理页面先设为只读模式,区域属性定义为私有且开启写时复制功能。此时,父子进程的虚拟地址空间完全一样,共享所有物理页面。后续只要有一个进程想改写内存,比如改全局变量,CPU就会因只读页面被写而触发异常。内核接管后,会开辟新物理页面,把原页面内容拷贝过去,更新当前进程页表指向新页面,并取消只读限制,允许写入操作。
7.7 hello进程execve时的内存映射
当执行execve()系统调用时,Hello进程会用新的程序(如另一个可执行文件)替换当前进程的地址空间。此时,操作系统会根据新程序的可执行文件重新映射内存。操作系统会将新程序的代码段、数据段等加载到Hello进程的虚拟地址空间中。同时,操作系统会设置相应的页表等结构,以便将虚拟地址转换为物理地址。新的物理内存页面会被分配给这些段,以便程序能够正常运行。
7.8 缺页故障与缺页中断处理
在Hello程序运行过程中,当访问的虚拟地址对应的物理页面不在内存中时,就会发生缺页故障。发生缺页故障时,CPU会暂停当前指令的执行,并触发缺页中断。操作系统会处理这个中断,首先确定是否是一个有效的页面引用。如果是有效的,则操作系统会从磁盘或其他存储设备中将相应的页面调入物理内存。如果内存已满,可能需要使用页面置换算法(如LRU)来选择一个页面进行替换。处理完成后,CPU会恢复被中断的指令,继续执行Hello程序。
7.9动态存储分配管理
Printf会调用malloc,请简述动态内存管理的基本方法与策略。(此节课堂没有讲授,选做,不算分)
7.10本章小结
本章详细介绍了计算机系统的存储管理机制。首先阐述了逻辑地址、线性地址、虚拟地址和物理地址的概念与相互转换过程。接着讲解了Intel段式管理和页式管理下地址变换的原理,以及TLB和四级页表在提高地址转换效率中的作用。然后介绍了三级Cache如何加速物理内存访问。在进程管理方面,解释了fork和execve系统调用的内存映射机制,以及写时复制策略的高效性。最后探讨了缺页故障处理过程。整体上,本章全面展示了Hello程序从编译到运行的存储管理全过程,揭示了计算机系统存储机制的高效与复杂。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
(以下格式自行编排,编辑时删除)
设备的模型化:文件
设备管理:unix io接口
8.2 简述Unix IO接口及其函数
(以下格式自行编排,编辑时删除)
8.3 printf的实现分析
(以下格式自行编排,编辑时删除)
[转]printf 函数实现的深入剖析 - Pianistx - 博客园
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
(以下格式自行编排,编辑时删除)
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
(第8章 选做 0分)
结论
hello程序的一生回顾:
- 源代码编写:使用文本编辑器编写 C 源代码文件 hello.c。
- 预处理:编译器的预处理器解析 hello.c 文件中的宏定义、文件包含指令(如 #include)和条件编译指令,生成预处理后的文件 hello.i。
- 编译:编译器将 hello.i 文件编译成汇编语言文件 hello.s。在此阶段,编译器进行词法、语法和语义分析,并生成中间代码。
- 汇编:汇编器将 hello.s 文件转换成机器语言的目标文件 hello.o。该文件包含程序的二进制表示,但尚未链接。
- 链接:链接器将 hello.o 文件与系统库和其他目标文件链接,生成可执行文件 hello。链接器解决符号引用,进行重定位和动态解析。
- 运行准备:用户在 shell 中输入命令 ./hello 来运行程序。shell 检查命令是否为内置指令,若不是,则调用 fork 函数创建新进程。
- 进程创建与程序加载:新创建的子进程调用 execve 函数加载并执行 hello 程序。execve 替换当前进程的内存映像,加载程序到内存并准备执行。
- 程序执行:操作系统调度器分配 CPU 时间片给进程。程序在 CPU 上执行,访问内存,并进行 I/O 操作。
- 信号处理:程序运行期间,用户可通过键盘输入发送信号,操作系统会调用相应的信号处理函数来暂停、停止或挂起进程。
- 程序终止:程序执行完毕后,子进程终止。父进程通过 wait 系统调用回收子进程的资源,内核清理为该进程分配的所有数据结构。
感想:
hello 程序的一生从源代码的编写开始,经历预处理、编译、汇编、链接等阶段生成可执行文件,随后在操作系统中被加载、执行、调度,直至最终终止和资源回收。这一过程展示了计算机系统从软件到硬件各层面的协同工作,体现了编译器、操作系统和硬件架构的设计精妙。
计算机系统庞大而复杂,即使是一个看似简单的hello程序,当我们在IDE中运行,计算机后台也是发生了很多未曾想象想象的过程。通过学习这门课,我初步了解了计算机系统,但是仍然需要在以后的学习中加深对其的理解和感悟。
(结论0分,缺失-1分)
附件
附件1:hello.c — C语言源程序
附件2:hello.i — 预处理输出(扩展宏、展开头文件后生成的.i文件)
附件3:hello.s — 编译器生成的汇编代码
附件4:hello.o — 汇编后生成的可重定位目标文件
附件5:hello.elf — 展示可重定位的elf文件格式
附件6:hello.asm — 用汇编语言的格式来观察可重定位的目标文件
附件7:hello — 最终链接生成的可执行文件
附件8:hello1.elf — 可执行文件的ELF格式展示
(附件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分)
转载自CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/2301_79709152/article/details/147892927



