马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
盘算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 将来技术人工智能模块
学 号 2022113063
班 级 wl028
学 生 杨亦乔
指 导 教 师 郑贵滨
盘算机科学与技术学院
2024年5月
摘 要
本文深入剖析了Linux情况下C语言程序的编译、链接与实行流程,以hello.c程序为例,全面解析了代码从源文件到可实行文件的变化过程。文章起首介绍了预处理惩罚、编译、汇编等编译阶段,每个环节都对程序的布局和功能产生重要影响。随后,探讨了链接阶段,详细论述了目的文件归并、地址空间重定位等关键操纵。
在程序实行阶段,文章偏重分析了操纵系统在历程创建、调度和资源分配中的作用,特殊是用户态与核心态的切换以及系统调用的处理惩罚。同时,对操纵系统的内存管理机制举行了深入探讨,包罗内存映射、动态存储分配以及缺页故障的处理惩罚计谋。
别的,文章还探讨了Linux系统在IO设备管理方面的计谋,怎样通过Unix IO接口实现程序与外部设备的交互。通过这些分析,本文不仅加深了对C语言程序编译实行过程的明白,也对操纵系统的内存和IO设备管理有了更深入的熟悉。
关键词:盘算机系统、编译过程、C语言程序、内存管理、IO设备管理
;
目 录
第1章 概述
1.1 Hello简介
1.2 情况与工具
1.3 中心结果
1.4 本章小结
第2章 预处理惩罚
2.1 预处理惩罚的概念与作用
2.2在Ubuntu下预处理惩罚的命令
2.3 Hello的预处理惩罚结果解析
2.4 本章小结
第3章 编译
3.1 编译的概念与作用
3.2 在Ubuntu下编译的命令
3.3 Hello的编译结果解析
3.4 本章小结
第4章 汇编
4.1 汇编的概念与作用
4.2 在Ubuntu下汇编的命令
4.3 可重定位目的elf格式
4.4 Hello.o的结果解析
4.5 本章小结
第5章 链接
5.1 链接的概念与作用
5.2 在Ubuntu下链接的命令
5.3 可实行目的文件hello的格式
5.4 hello的假造地址空间
5.5 链接的重定位过程分析
5.6 hello的实行流程
5.7 Hello的动态链接分析
5.8 本章小结
第6章 HELLO历程管理
6.1 历程的概念与作用
6.2 简述壳Shell-bash的作用与处理惩罚流程
6.3 Hello的fork历程创建过程
6.4 Hello的execve过程
6.5 Hello的历程实行
6.6 hello的异常与信号处理惩罚
6.7本章小结
第7章 HELLO的存储管理
7.1 hello的存储器地址空间
7.2 Intel逻辑地址到线性地址的变更-段式管理
7.3 Hello的线性地址到物理地址的变更-页式管理
7.4 TLB与四级页表支持下的VA到PA的变更
7.5 三级Cache支持下的物理内存访问
7.6 hello历程fork时的内存映射
7.7 hello历程execve时的内存映射
7.8 缺页故障与缺页中断处理惩罚
7.9动态存储分配管理
7.10本章小结
第8章 HELLO的IO管理
8.1 Linux的IO设备管理方法
8.2 简述Unix IO接口及其函数
8.3 printf的实现分析
8.4 getchar的实现分析
8.5本章小结
结论
附件
参考文献
第1章 概述
1.1 Hello简介
在开始Hello 简介之前,我想先谈谈Hello 的重要意义:"Hello, World!"程序,虽然简单,却承载着深远的意义,它不仅是编程以致盘算机教诲的起点,让初学者通过这个经典的例子明白盘算机的根本概念,而且成为了编程语言特性展示的窗口,让程序员迅速把握新语言的根本布局。它照旧盘算机社区的一个传统,新成员通过编写这个程序来庆祝他们加入盘算机世界。别的,"Hello, World!"作为一个跨语言的桥梁,促进了不同背景程序员之间的交换,同时也是技术演示和教学中不可或缺的根本示例。它不仅是编程文化的符号,代表着开放、分享和创新,也是技术普及和教诲平权的象征,鼓励更多人到场到技术学习和创新中来。"Hello, World!"程序还激励和开导着程序员不断探索、学习和发展,见证了盘算机技术的发展,成为了全球程序员共同的语言和毗连点。
Hello程序的P2P过程:
Hello程序的生命周期(P2P过程)涉及从源代码到历程的转换,分为两个主要阶段:编译和实行。在编译阶段,源代码通过编译器(如gcc)转换成可实行文件(program)。实行阶段则由shell触发,通过调用execve函数,将程序加载到内存并创建一个历程(process)来运行。
编译过程的四个阶段:
1. 预处理惩罚阶段:预处理惩罚器(cpp)处理惩罚以#开头的预处理惩罚指令,对原始C程序举行须要的文本更换和条件编译,天生一个修改后的C程序,通常以.i为扩展名。
2. 编译阶段:编译器(如gcc中的ccl)将预处理惩罚后的C程序(hello.i)翻译成汇编语言程序(hello.s)。
3. 汇编阶段:汇编器(as)将汇编语言程序(hello.s)转换成呆板语言指令,并打包成可重定位目的文件格式(hello.o)。
4. 链接阶段:链接器(ld)归并hello.o文件中的目的代码与其他须要的代码片断,天生最终的可实行文件(hello),该文件随后可被加载到内存中并由系统实行。
实行过程的020生命周期:
当shell吸收到运行hello程序的命令时,它通过execve函数加载程序,映射到假造内存,并将程序载入对应的物理内存。程序初始化时,通过mmap系统调用来申请内存空间。随后,程序进入main函数实行其核心逻辑,CPU为该历程分配时间片,控制程序的实行流程。程序实行完毕后,由父历程shell举行资源接纳,内核随后删除程序的相关信息并释放占用的资源。
流程图简述:
流程图展示了从shell命令的输入到hello程序的实行,再到程序终止和资源接纳的完整生命周期。它清晰地描绘了程序从源代码到历程的转换,以及实行过程中的关键步骤和系统调用。
1.2 情况与工具
列出你为编写本论文,折腾Hello的整个过程中,利用的软硬件情况,以及开发与调试工具。
硬件情况:X64 CPU;3.2GHz;16G RAM
软件情况:Windows11 64位;MacOS12.4;Ubuntu 20.04.4
开发和调试工具:gcc;objdump;edb;clion;vim等
1.2.1 硬件情况
型号名称: MacBook Pro
型号标识符: Mac14,7
芯片: Apple M2
核总数: 8(4性能和4能效)
内存: 16 GB
系统固件版本: 7459.121.3
操纵系统加载程序版本: 7459.121.3
序列号(系统): HM4L652RPX
硬件UUID:93F36213-A827-5D9B-9E93-8815D6D97743
预置UDID: 00008112-000848482E31401E
激活锁状态: 已启用
设备名称 kalinka
处理惩罚器 12th Gen Intel(R) Core(TM) i5-12400F 2.50 GHz
机带 RAM 16.0 GB (15.8 GB 可用)
设备 ID 05931DD7-7FEB-4D66-83D6-5A6A0CEA31DE
产物 ID 00330-80000-00000-AA682
系统类型 64 位操纵系统, 基于 x64 的处理惩罚器
笔和触控 没有可用于此显示器的笔或触控输入
1.3 中心结果
hello.i:预处理惩罚得到的C程序
hello.s:编译得到的汇编语言程序
hello.o:汇编得到的可重定位目的程序文件
hello_fromo.s:hello.o反汇编得到的汇编语言程序
hello:链接得到的可实行目的文件
hello_fromout.s:hello反汇编得到的汇编语言程序
1.4 本章小结
本章介绍了hello的P2P、020过程,完成此大作业利用的开发情况和工具,以及由hello.c天生的中心文件,包罗hello.i,hello.s,hello.o,hello_o.s,hello和hello_out.s。
(第1章0.5分)
第2章 预处理惩罚
2.1 预处理惩罚的概念与作用
预处理惩罚是编译过程的第一阶段,它发生在实际编译之前。在C语言和其他一些编程语言中,预处理惩罚器(通常由编译器的一部分或一个独立的工具来实现)负责处理惩罚源代码文件中的预处理惩罚指令。这些指令以井号(#)开始,指示预处理惩罚器举行特定的操纵。以下是预处理惩罚阶段的一些关键功能:宏更换:预处理惩罚器可以定义宏(#define),并在源代码中更换宏名称为其对应的值或代码。比方,`#define PI 3.14159` 会将源代码中的所有 `PI` 更换为 `3.14159`;文件包罗:预处理惩罚器可以包罗其他文件的内容,通过 `#include "filename.h"` 或 `<filename.h>` 指令。这允许代码重用和模块化;条件编译:预处理惩罚器支持条件编译,允许根据不同的条件包罗或清除代码段。比方,`#ifdef`, `#ifndef`, `#endif`, `#else` 等指令可以根据宏是否定义来包罗或清除代码;除此以外还可能实行错误检查,行控制,注释处理惩罚,预定义宏等任务
预处理惩罚完成后,源代码文件会被转换成一个新的文本文件,通常以 `.i` 或 `.ii` 作为扩展名,这个文件随后会被编译器进一步编译成目的代码。预处理惩罚是编译过程中不可或缺的一步,它使得程序计划更加机动和强盛。
2.2在Ubuntu下预处理惩罚的命令
2.3 Hello的预处理惩罚结果解析
可以看出,在预处理惩罚阶段,hello.c中的头文件#include被完整的解释在main函数上方,同时代码中注释的内容被删除。
2.4 本章小结
本章扼要介绍了预处理惩罚过程并通过对hello.c举行预处理惩罚,详细而详细地介绍了预处理惩罚的概念和作用,表现了其引入头文件、并删除注释的功能。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译是将高级编程语言编写的源代码转换成盘算机硬件可以大概实行的低级呆板代码的过程;它涉及多个步骤,旨在优化代码、办理依赖关系以及天生呆板可明白的指令;编译器在编译过程中会检查源代码中的语法错误、类型错误和其他潜在问题,确保天生的代码是正确的;同时,编译器会对代码举行优化,以提高程序的实行效率,包罗消除冗余盘算、优化循环布局、改进内存访问模式等;编译器还处理惩罚代码中的依赖关系,确保所有须要的库和模块在编译时被正确链接;最终,编译的结果是天生一个可实行文件,它包罗了程序的所有呆板指令和资源,可以直接在操纵系统上运行;编译器允许开发者为不同的平台和架构编写代码,通过编译过程天生适用于特定平台的可实行文件;在编译过程中还会处理惩罚程序的内存分配请求,确保程序运行时可以大概有效地管理内存资源;编译器可以天生包罗性能分析信息的代码,资助开发者相识程序的性能瓶颈并举行优化;编译过程遵循特定的标准和规范,确保天生的程序可以大概在预期的情况中稳固运行;通过编译,可以隐蔽源代码的详细实现细节,只暴露可实行文件,从而提高程序的安全性。
简单来说编译过程中,编译器(ccl)将C语言编码的hello.i翻译成汇编的hello.s。
3.2 在Ubuntu下编译的命令
3.3 Hello的编译结果解析
完整的汇编代码如下:
.file "hello.c"
.text
.section .rodata
.align 8
.LC0:
.string "\347\224\250\346\263\225: Hello \345\255\246\345\217\267 \345\247\223\345\220\215 \346\211\213\346\234\272\345\217\267 \347\247\222\346\225\260\357\274\201"
.LC1:
.string "Hello %s %s %s\n"
.text
.globl main
.type main, @function
main:
.LFB6:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movl %edi, -20(%rbp)
movq %rsi, -32(%rbp)
cmpl $5, -20(%rbp)
je .L2
leaq .LC0(%rip), %rax
movq %rax, %rdi
call puts@PLT
movl $1, %edi
call exit@PLT
.L2:
movl $0, -4(%rbp)
jmp .L3
.L4:
movq -32(%rbp), %rax
addq $24, %rax
movq (%rax), %rcx
movq -32(%rbp), %rax
addq $16, %rax
movq (%rax), %rdx
movq -32(%rbp), %rax
addq $8, %rax
movq (%rax), %rax
movq %rax, %rsi
leaq .LC1(%rip), %rax
movq %rax, %rdi
movl $0, %eax
call printf@PLT
movq -32(%rbp), %rax
addq $32, %rax
movq (%rax), %rax
movq %rax, %rdi
call atoi@PLT
movl %eax, %edi
call sleep@PLT
addl $1, -4(%rbp)
.L3:
cmpl $9, -4(%rbp)
jle .L4
call getchar@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size main, .-main
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
依据以上的汇编代码,分别举例一下操纵:
数据类型:
1. 常量
- 代码中的`.LC0`和`.LC1`是常量字符串,它们存储在程序的只读数据段(`.rodata`)中。
- `.LC0: .string "Hello, World!"` 定义了一个字符串常量。
2. 变量。
- 局部变量:通过栈帧访问,如`movl %edi, -20(%rbp)`将函数参数移动到栈上的局部变量位置。
3. 表达式
- 表达式在编译时被睁开,如`cmpl $5, -20(%rbp)`比力局部变量与常量5。
4. 类型
- 类型信息在汇编中通常表现为操纵数的大小和对齐,如`movq`操纵64位的值。
5. 宏
- 宏在预处理惩罚阶段睁开,如`endbr64`可能是一个宏,用于防止循环睁开优化。
赋值和初始化:
1. 赋值操纵:
- `movl $0, -4(%rbp)`将立刻数0赋值给局部变量。
2. 赋初值/不赋初值:
- 局部变量通常在利用前初始化,如上例中的`movl $0, -4(%rbp)`。
类型转换:
1. 隐式类型转换:
- 在`call atoi@PLT`调用中,字符串隐式转换为整数。
算术操纵:
1. 根本算术操纵:
- `addl $1, -4(%rbp)`将局部变量的值增加1。
2. 递增/递减操纵:
- 通过增加或减少变量的值实现,如`addl $1, -4(%rbp)`。
逻辑/位操纵:没有出现
关系操纵:
- `cmpl $9, -4(%rbp)`和`jle .L4`展示了怎样利用比力操纵和条件跳转实现关系操纵。
数组/指针/布局操纵:
1. 指针操纵:
- `movq -32(%rbp), %rax`加载指向第一个参数的指针。
2. 布局操纵:
- 通过偏移量访问布局体成员,如`addq $24, %rax`和`movq (%rax), %rcx`。
控制转移:
1. if/else:
- `je .L2`实现if条件跳转。
2. for/while/do-while:
- `jmp .L3`和`jle .L4`实现循环控制。
函数操纵:
1. 参数转达:
- `movl %edi, -20(%rbp)`和`movq %rsi, -32(%rbp)`展示了怎样将函数参数转达到栈上。
2. 函数调用:
- `call printf@PLT`展示了函数调用。
3. 局部变量:
- 如前所述,局部变量通过栈帧访问。
4. 函数返回:
- `movl $0, %eax`设置返回值,`ret`指令返回。
3.4 本章小结
在本章中,我们深入探讨了编译过程的核心概念及其重要性。通过过细分析hello.c的源代码怎样被转换成hello.s汇编代码,我们揭示了C语言与底层汇编语言之间的直接接洽。我们不仅观察了函数的创建和竣事,还详细探讨了常量和变量怎样在汇编层面被存储和处理惩罚。
我们进一步分析了参数的加载机制以及函数调用的过程,这些都是程序可以大概实行更复杂操纵的根本。同时,我们对程序流程控制布局,如if条件判断和for循环,怎样在汇编层面实现举行了深入的讨论。这些控制布局对于程序的逻辑流程至关重要。
除此之外,我们还关注了返回值的设置方式,这是函数实行结果转达的关键环节。通过这些分析,我们不仅加深了对编译过程的明白,还对程序怎样从高级语言转化为呆板可实行指令有了直观的熟悉。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
汇编过程或汇编链接涉及将汇编语言编写的代码转换成盘算机可以直接实行的呆板指令;汇编器是一个程序,它读取用汇编语言编写的源文件,并将其转换成呆板码,它将每条汇编指令映射到对应的呆板指令,这个过程涉及到指令集架构(ISA)的规则,确保每条汇编指令有一个唯一的二进制表示;同时,汇编器解析代码中的符号,如标签和常量,将它们更换为内存地址或立刻数,并为代码和数据分配内存地址,确保指令和数据在内存中正确定位。
汇编过程天生的呆板码是盘算机硬件可以大概明白和实行的二进制代码,它是针对特定平台和处理惩罚器架构的,这意味着汇编代码需要为每种目的架构重新汇编;现代汇编器也可能实行一些优化,好比指令选择和调度,以提高天生代码的效率;在有外部符号引用的情况下,汇编器会确保这些符号在最终的可实行文件或库中得到正确的解析;别的,汇编器可以天生调试信息,这些信息对于后续的程序调试和性能分析至关重要;在天生最终的可实行文件或库文件时,汇编器会处理惩罚代码和数据的重定位,确保它们在内存中的地址正确;汇编过程是构建软件过程中的一个环节,通常在编译之后,链接之进步行;汇编器确保汇编代码符合目的处理惩罚器的指令集架构,从而天生兼容的呆板码;对于性能要求极高的代码段,汇编过程可以天生高度优化的呆板码,以达到最佳性能。
4.2 在Ubuntu下汇编的命令
4.3 可重定位目的elf格式
,在终端输入readelf -h hello.o得到的的ELF头如下图所示。第1-4个表示文件类型,第5个02说明为64位目的,第6个01说明为小端法,第7个01指ELF版本,第8个00说明未指定运行操纵系统,第9个00指ABI版本,余下为补零位。图中还能直观地获取一些其他信息。
节头表如下图所示。每节的名称、类型、地址、偏移等信息都包罗在图中。
利用readelf -r hello.o命令查看重定位节如下:可以看出,比方puts,printf,getchar等不在hello.c中定义但是仍然调用的函数出现,以便下一阶段链接器将hello.o与stdio标准库中的文件文件组合。
4.4 Hello.o的结果解析
利用objdump -d -r hello.o > hello_o.s得到hello.o的反汇编文件hello_fromo.s,它与hello.s相比力,代码最终完成的逻辑相同,可能有少量的细微步骤或目的的达成方式有些许不同。
但是整体上另有以下区别:
总的来说hello_o.s 是编译后、链接前的对象文件的反汇编,而 hello.s 是链接后、实行前的最终汇编代码。
当利用 `objdump` 工具对目的文件 `hello.o` 举行反汇编操纵时,得到的 `hello_fromo.s` 文件与源汇编文件 `hello.s` 在几个关键方面存在差异:
1. 立刻数的16进制表示:
`hello_fromo.s` 文件中的立刻数将以16进制情势展示,而不是10进制。16进制是汇编语言中表示数字的标准方式,因为它更简洁,且易于与二进制举行转换。比方,一个立刻数可能显示为 `0x1A3` 而不是 `419`。
2. 伪指令和声明的缺失:
`hello_fromo.s` 文件中不会包罗伪指令或额外的声明,这些通常用于为汇编器提供指令,如定义常量、分配内存空间或指定节(sections)。在源汇编文件 `hello.s` 中,程序员可能会显式地利用这些伪指令来组织代码和数据。
3. 直接地址的利用:
在 `hello_fromo.s` 中,跳转和调用指令将利用直接的内存地址,而不是节标记或函数名。这意味着,比方,一个跳转指令可能显示为 `jmp 0x123456` 而不是 `jmp .Lsome_label` 或 `jmp some_function`。这种直接地址引用在链接过程中会被解析为正确的目的地址。
4. 操纵数大小的省略:
反汇编文件 `hello_fromo.s` 中的指令可能不会明白指出操纵数占用的字节大小。在汇编语言中,操纵数的大小通常由指令的编码方式决定,而在反汇编的输出中,这个信息可能被以为是显而易见的,因此被省略。比方,一个移动指令可能简单地写作 `mov val, reg` 而不是 `movl val, reg` 或 `movq val, reg`,此中 `l` 或 `q` 分别指明了32位或64位操纵的大小。
这些差异是可以明白的,实在际上是反映了 `objdump` 工具的输出特点,它偏重于展示目的文件中的实际呆板指令,而不是提供汇编编程时可能需要的额外上下文信息。这种格式的输出更适合于分析和明白编译后的程序举动,而不是作为编写或编辑汇编代码的参考。
4.5 本章小结
本章介绍了汇编的概念、作用。通过gcc完成hello.s天生hello.o为实例,并分析了汇编天生ELF格式文件的过程,除此以外还将hello.o返回汇编成为hello_fromo.s,并比力了两者的相同与不同之处
第5章 链接
5.1 链接的概念与作用
链接是软件编译过程中的一个关键步骤,它发生在编译器天生汇编代码并被汇编器转换成呆板码之后;链接器是一个工具,用于将一个或多个编译后天生的目的文件(.o 文件)以及库文件(库中也包罗目的文件)组合起来,创建一个单一的可实行文件或库文件;链接器负责解析代码中的符号引用,将它们与定义这些符号的代码段毗连起来,这包罗变量、函数、常量等;链接器还为所有代码和数据分配最终的内存地址,确保程序中的每个部分在实行时都能正确访问;链接器还会举行重定位,调整代码和数据中的地址引用,确保它们与最终的内存布局同等;
链接器另有许多重要的作用:链接器将分散在多个目的文件中的代码和数据整合到一个可实行文件中,使得程序可以作为一个整体运行;程序通常会调用标准库或其他外部库中的函数,链接器负责将这些库中的代码整合到最终的可实行文件中;链接器办理程序中的符号引用问题,确保每个符号(变量、函数等)都能正确地与其定义绑定;链接器可以对程序的内存布局举行优化,好比通过归并相同内容的代码段或数据段来减小最终文件的大小;通过链接过程中的优化,可以提高程序的加载速率和运行效率;链接器还支持动态链接,允许程序在运行时加载和解析库代码,这有助于共享代码和减少内存占用;链接器可以天生包罗调试信息的可实行文件,这对于后续的程序调试和性能分析非常重要;链接器在天生最终可实行文件时,可以应用各种安全步伐,如地址空间布局随机化(ASLR)等;链接器确保最终天生的可实行文件与操纵系统和硬件平台兼容;链接是软件构建过程中的最终步骤之一,它确保了程序的各个构成部分可以大概协同工作,天生一个可以被操纵系统加载和实行的完整程序;通过链接,开发者可以大概将分散的代码片断和库函数组合成一个完整的应用程序,为用户提供最终的软件产物。
5.2 在Ubuntu下链接的命令
5.3 可实行目的文件hello的格式
在命令行输入:readelf hello –segmenst查看hello的段信息。可以看到段有PHDR,INTERP,LOAD,DYNAMIC,NOTE,GUN_PROPERTY,GUN_STACK,GUN_RELRO,并且在filesize、memsize等处并给出了它们的物理与假造地址、大小、偏移、节映射等信息。
hello的段信息:
5.4 hello的假造地址空间
利用edb加载hello,查看本历程的假造地址空间各段信息,利用edb加载hello查看本历程的假造地址空间各段信息如下图所示,从0x0000000000401000开始,到0x0000000000402000竣事。根据5.3中的elf表中可以查找到.init等的起始地址,仿照云云便可以在edb中找到对应。
5.5 链接的重定位过程分析
利用objdump -d -r hello > hello_fromout.s得到hello的反汇编文件hello_fromout.s,与hello.o的反汇编文件hello_o.s作对比,如下图所示:
通过对比 `hello_fromout.s` 和 `hello_fromo.s`,我们可以更清晰地明白链接过程以及链接器在此中所扮演的脚色:
1. 重定位节和符号定义:
`hello_fromout.s` 展示了链接器怎样将所有相同类型的节(sections)归并为同一的新聚合节,并为这些聚合节以及输入模块中定义的每个节和符号分配了运行时内存地址。这意味着程序中的每条指令和全局变量如今都有了一个唯一的运行时内存地址。相比之下,`hello_fromo.s` 中的地址是相对的或尚未分配,因为链接过程尚未发生。因此,`hello_fromout.s` 中的内容会更加丰富,因为它包罗了完整的程序布局和确切的内存地址信息。
2. 重定位节中的符号引用:
在 `hello_o.s` 中,代码节和数据节中的符号引用可能包罗重定位指令,如 `R_X86_64_PC32` 或 `R_X86_64_PLT32`,这些指令指示链接器在链接过程中需要对地址举行调整。在 `hello_out.s` 中,这些引用已经被链接器修改,指向了正确的运行时地址。这意味着所有的 `lea`(加载有效地址)和 `call`(调用)指令如今都包罗了详细的、可以直接跳转或调用的内存地址。
3. 库函数的整合:
`hello_fromout.s` 展示了链接器怎样处理惩罚库函数的整合。所有对库函数的调用,如 `printf` 或 `malloc`,在 `hello_fromo.s` 中可能只是作为符号引用存在,而在 `hello_out.s` 中,这些调用已经被解析为详细的库代码。`hello_fromout.s` 中的代码和数据布局代表了程序在内存中的最终形态。链接器不仅处理惩罚了重定位,还可能对代码和数据举行了优化,好比归并相同代码段或常量池,以及调整数据布局以提高内存访问效率。
4. 程序的自包罗性:
`hello_fromout.s` 中的程序是自包罗的,意味着它包罗了所有须要的代码和数据,可以独立运行。而 `hello_fromo.s` 可能依赖于其他目的文件或库文件中的代码和数据。
总的来说,通过对比 `hello_fromout.s` 和 `hello_fromo.s`,我们不仅可以看到链接器在重定位和符号解析方面的工作,还可以明白链接器怎样整合代码、数据和库函数,以及它怎样优化程序的内存布局和性能。这些步骤是将编译后的目的文件转换成一个完整、可实行程序的关键环节。
5.6 hello的实行流程
在Hello实行的过程中hello调用与跳转的各个子程序名如下表所示:
子程序名
| hello!_start
| libc-2.31.so!__libc_start_main
| hello!_init
| hello!main
| hello!printf@plt
| hello!atoi@plt
| hello!sleep@plt
| hello!getchar@plt
| hello!exit@plt
| hello!__libc_csu_init
| hello!_fini
|
5.7 Hello的动态链接分析
在elf中可以看到.got的地址
利用edb调试,dl_init(即初始化)前该区域内容如下图所示:
dl_init(即初始化)后该区域内容如下图所示:
在初始化后,.got地址处的内容发生了改变,全局偏移量表存储函数的绝对目的地址,过程链接表利用这些地址举行跳转。在加载时,动态链接器会修正全局偏移量表,将每个条目的地址变为指向实际目的函数的绝对地址,实现动态链接,这是因为动态链接器通过过程链接表和全局偏移量表协作。
5.8 本章小结
本章介绍了链接的概念和作用,以hello为例,分析了可实行文件的假造地址空间,并比力了hello_fromout.s和hello_fromo.s,由此分析了重定位的过程与动态链接的过程。
(第5章1分)
第6章 hello历程管理
6.1 历程的概念与作用
历程是盘算机中程序实行的动态实例,拥有独立的内存地址空间;它由操纵系统分配和管理,每个历程都有其独特的历程控制块(PCB),记录着历程的属性和状态;历程实现了代码的隔离实行,确保了程序间相互独立;并发性允许多个历程在宏观上同时运行,提高了资源的利用率。
历程的作用在于实现资源的公道分配和管理,每个历程独立利用CPU时间、内存和I/O设备;调度方面,操纵系统通过调度算法决定历程的运行顺序,实现多任务处理惩罚;隔离性提供了稳固性和安全性,每个历程的失败不会影响其他历程;历程间的通信机制允许数据交换和协作,支持复杂的应用程序运行。
6.2 简述壳Shell-bash的作用与处理惩罚流程
Shell是操纵系统用户界面的一部分,它提供了一个交互式的命令行情况;用户可以通过Shell实行各种系统命令和程序,Shell解释用户输入的命令并将其转换为系统调用;Shell处理惩罚流程开始于读取用户输入的命令,接着举行语法分析,确定命令的类型和参数;对于内置命令,Shell会直接实行而无需创建新历程;对于外部命令,Shell通过`fork`系统调用创建一个新的子历程,然后在子历程中利用`execve`加载并实行指定的程序;如果命令需要在后台运行,Shell会立刻返回,继承监听新的用户输入;对于前台运行的命令,Shell会通过`waitpid`或其他期待调用,挂起运行直到相关程序完成实行;一旦程序终止,Shell负责接纳其利用的资源,并继承实行用户的后续命令。)
6.3 Hello的fork历程创建过程
fork起首举行系统调用:在hello程序中,当需要创建一个新历程时,会用fork系统调用。fork调用实行后,它会复制当进步程(父历程)的地址空间,包罗代码段、数据段、堆、栈等,创建一个新的子历程。fork调用在父历程中返回新创建子历程的PID(历程ID),而在子历程中返回0。这样,父历程和子历程就可以通过返回值来区分本身的身份。子历程是一个独立的实体,拥有本身的历程ID,并且有本身的实行栈。从fork返回之后,子历程和父历程将独立地实行后续的代码。虽然子历程复制了父历程的资源,但是子历程会设置本身的一些特定属性,如更改工作目录、打开文件等,以便实行特定的任务。
6.4 Hello的execve过程
execve函数可以在当进步程的上下文中加载并运行一个程序。在运行hello时shell的子历程利用execve加载hello,并调用启动代码设置栈,将控制权转达给hello的main函数。
6.5 Hello的历程实行
1. 初始阶段:
- 用户在终端输入`./hello`命令,Shell通过`execve`系统调用加载`hello`程序到其地址空间,更换当前Shell历程的内容,准备实行`hello`程序。
2. `fork`系统调用:
- `hello`程序实行到需要创建子历程的部分,调用`fork`。操纵系统创建一个新的子历程,复制父历程的地址空间,包罗代码段、数据段、堆、栈等。
- `fork`调用在父历程中返回子历程的PID,在子历程中返回0。
3. 子历程的独立实行:
- 子历程开始独立实行,它从`fork`调用之后的代码继承运行。如果`hello`程序计划为在子历程中实行特定的任务,此时子历程将举行这些任务。
4. 历程上下文信息:
- 每个历程都有本身的历程控制块(PCB),包罗了历程状态、程序计数器、寄存器集合、调度信息等。操纵系统利用这些信息举行历程管理和调度。
5. 历程时间片和调度:
- 在多任务操纵系统中,多个历程可以并发实行。操纵系统的调度器按照时间片轮转或其他调度算法,分配CPU时间给各个历程。
- 当一个历程的时间片用完,调度器会将其置于停当状态,并选择另一个历程运行。
6. 用户态与核心态的转换:
- 在实行系统调用(如`fork`、`execve`或其他I/O操纵)时,历程从用户态切换到核心态,此时操纵系统接管CPU,实行系统调用请求的操纵。
- 系统调用完成后,操纵系统将历程从核心态切换回用户态,历程继承实行用户空间的代码。
7. 历程的期待和竣事:
- 如果`hello`程序中的子历程需要在父历程中期待其竣事,父历程可以利用`waitpid`系统调用期待子历程竣事,并获取其退出状态。
- 子历程完成任务后,会通过`exit`系统调用竣事本身,释放资源,并将退出状态报告给父历程。
8. 资源接纳和历程终止:
- 父历程通过`waitpid`接纳子历程的资源,操纵系统将子历程利用的内存、打开的文件等资源标记为可用。
- `hello`程序实行完成后,也会通过`exit`调用竣事本身,操纵系统接纳其所有资源,并将控制权返回给Shell。
6.6 hello的异常与信号处理惩罚
hello在实行过程中可能出现的异常有中断、陷阱和系统调用、故障、终止;
运行时乱按并按回车,可以看到对程序运行并无影响
在运行时按control + z可以停止程序运行
分别按ps、jobs观察运行结果如上图
输入pstree可以得到上图
最后可以利用kill 杀掉历程,终止京城,可以看到在kill前后,kill前hello存在,kill后hello 不再存在。
6.7本章小结
本章介绍了历程的概念和作用,Shell-bash的作用,以及fork历程创建过程、execve过程,形貌了历程的实行和hello的异常和信号处理惩罚。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
1. 逻辑地址(也称程序地址或相对地址):
- 逻辑地址是程序在编译时天生的地址,它们是相对于程序的起始点盘算的偏移量。
- 在实行"Hello"程序时,逻辑地址用于指令和变量的引用,比方,一个变量可能在代码中以相对于某个固定点的偏移量被引用。
2. 线性地址(也称假造地址):
- 线性地址是程序在实行时利用的地址,它们构成了一个平坦的地址空间。
- 当"Hello"程序运行时,CPU将逻辑地址转换为线性地址,这个过程称为地址转换。
- 线性地址空间由操纵系统管理,可能包罗程序代码、数据、堆、栈等多个段。
3. 假造地址:
- 假造地址是程序在运行时看到的地址,它对程序员是透明的,由操纵系统和内存管理单元(MMU)管理。
- 在现代操纵系统中,假造地址通常与线性地址是同一个概念,它们都指的是程序可以大概访问的地址空间。
4. 物理地址:
- 物理地址是实际内存中的实际位置,即RAM中的地址。
- 当CPU通过假造地址访问内存时,MMU将假造地址转换为物理地址,这一过程称为地址映射或内存寻址。
在实行"Hello"程序的过程中,当程序引用一个变量或需要实行一条指令时,CPU会利用逻辑地址。操纵系统和MMU共同工作,将这些逻辑地址转换为假造地址,然后通过页表等机制将假造地址映射到物理地址,最终实现对实际内存的访问。这种分层的地址转换机制使得程序可以大概以一种抽象和简化的方式访问内存,同时操纵系统可以有效地管理内存资源,实现内存掩护、共享和假造化等高级功能。
7.2 Intel逻辑地址到线性地址的变更-段式管理
Intel体系布局中,逻辑地址到线性地址的转换过程涉及到段式管理,这是一种内存管理技术,用于提供更大的地址空间并允许程序分段。以下是段式管理的概念和转换过程:
- 段式内存管理:它允许操纵系统将内存分割成多个段(segment),每个段可以独立地管理。段寄存器:CPU中有一些特殊的寄存器,如CS(代码段寄存器)、DS(数据段寄存器)、ES、FS、GS和SS(堆栈段寄存器),它们存储着当前段的基地址。
1. 逻辑地址的利用:
- 在编写程序时,程序员利用的是逻辑地址,也就是相对于段基址的偏移量。
2. 段寄存器的选择:
- 根据访问的数据类型(代码、数据、堆栈等),CPU会自动选择相应的段寄存器。
3. 基地址的加权:
逻辑地址中的偏移量与段寄存器中的基地址相加,得到完整的物理地址。
4. 段界限检查:
- 在地址转换过程中,操纵系统还会检查天生的地址是否超出了段的界限。如果超出界限,将引发一个段错误(segmentation fault)。
5. 实际的物理地址获取:
- 如果地址在界限内,它将被送到内存管理单元(MMU),MMU会将这个地址转换为实际的物理地址。
7.3 Hello的线性地址到物理地址的变更-页式管理
页式管理过程中,CPU起首将假造地址发送给内存管理单元MMU,由MMU逐级查找页表PTE,将其转换为物理地址。CPU再根据物理地址逐级访存,获得所需数据。
7.4 TLB与四级页表支持下的VA到PA的变更
TLB(Translation Lookaside Buffer)和四级页表机制是Intel处理惩罚器中实现假造地址(VA)到物理地址(PA)转换的关键技术。以下是对这两个概念的重新组织和介绍:
TLB(Translation Lookaside Buffer):
- TLB是一个高速缓存,用于存储最近或频繁访问的假造地址到物理地址的映射关系。
- 当处理惩罚器需要访问内存时,它起首检查TLB,看是否已经有了所需的VA到PA的映射。如果TLB掷中,即找到了映射关系,处理惩罚器就可以直接利用这个映射,明显减少访问耽误。
- 如果TLB未掷中,即没有找到映射关系,处理惩罚器将转向页表举行查找。
四级页表:
- 四级页表是Intel处理惩罚器中用于存储假造地址到物理地址映射的另一种机制,它允许处理惩罚器支持非常大的地址空间。
- 在四级页表中,假造地址被分为多个部分,每部分通过一系列索引和表项来定位物理页框号。
- 起首,假造地址的一部分用作页目录的索引,页目录中的每个条目指向一个页表。
- 接着,假造地址的另一部分用作所选页表中的索引,页表中的每个条目指向一个页。
- 依此类推,经过四次这样的索引和查找,最终得到物理页框号。
- 将物理页框号与假造地址中的最后一部分(页内偏移)结合,就得到了完整的物理地址。
TLB与四级页表的协同工作:
- 当TLB未掷中且需要从页表中解析地址时,处理惩罚器会加载须要的页表项到TLB中,为将来的访问提供快速映射。
- 这种机制确保了纵然在TLB未掷中的情况下,处理惩罚器也能通过页表查找到正确的物理地址,并将新发现的映射关系缓存到TLB中,以便后续快速访问。
- 结合TLB的快速访问和四级页表的详细映射,Intel处理惩罚器可以大概高效地处理惩罚假造地址到物理地址的转换,从而提高整体的内存访问性能。
通过这种分层的页表布局和TLB的快速缓存,Intel处理惩罚器不仅可以大概管理庞大的地址空间,还可以大概保持高效的内存访问速率,满意现代应用程序对性能的需求。
7.5 三级Cache支持下的物理内存访问
在现代盘算机体系布局中,三级Cache(L1、L2、L3)构成了一个高效的内存访问层级。L1 Cache作为最接近CPU核心的缓存,拥有最快的访问速率,当CPU请求数据时,起首在L1 Cache中举行搜索。如果L1 Cache掷中,数据几乎可以立刻被返回,避免了对主存的访问,从而明显提拔了访问效率。
当L1 Cache未掷中时,请求会转向L2 Cache。L2 Cache的容量较L1更大,尽管访问速率略慢,但仍然远快于主存。如果数据在L2 Cache中被找到,访问速率虽然略低于L1 Cache,但仍然非常迅速。
若L2 Cache也未掷中,请求最终会到达L3 Cache。L3 Cache是系统中最大的缓存,其计划目的是进一步减少对主存的访问频率。L3 Cache的掷中可以进一步提拔CPU性能,因为它的数据传输速率接近于主存。
当三级Cache均未掷中时,CPU将不得不访问物理内存,也就是主存。这一过程相对较慢,因为内存位于CPU外部,需要通过总线举行数据交换。在期待数据返回期间,系统可能会暂停其他操纵。
一旦数据从主存返回,L3 Cache会起首吸收并缓存数据,随后L2和L1 Cache也会根据需要举行更新,这样做可以减少将来对主存的访问次数,从而低落耽误,提高系统的整体性能。当数据被频繁访问时,高缓存掷中率可以带来更明显的性能提拔。
7.6 hello历程fork时的内存映射
在Linux操纵系统中,当hello历程实行fork系统调用时,会触发一系列内存映射操纵,以创建一个新的子历程。起首,内核会为子历程创建须要的数据布局,如历程控制块(PCB)和历程形貌符,并分配一个唯一的历程标识符(PID)。
接着,内核会复制父历程的内存管理布局,包罗内存形貌符(mm_struct)、区域布局(vm_area_struct)和页表。这些布局定义了历程的假造内存布局和内存映射关系。
为了实现写时复制(Copy-On-Write, COW)机制,内核将父子历程中的所有页面标记为只读,并将区域布局标记为私有的写时复制。这意味着,当任一历程实验写入这些页面时,内核将捕获写操纵,并为该历程创建一个新的物理内存页副本,同时将该页的访问权限更改为可写。
在fork操纵完成后,子历程的假造内存布局与父历程在fork调用时的布局完全相同。这表示子历程继承了父历程的所有内存映射,包罗代码、数据、堆栈以及共享库的映射。
如果父子历程中的任何一个在fork之后实验写操纵,写时复制机制将确保只有实际被修改的页面才会被复制,从而避免了不须要的内存复制,提高了内存利用效率。
7.7 hello历程execve时的内存映射
当hello历程调用execve函数以实行一个新程序时,会发生一系列内存映射的变更。起首,当进步程的实行上下文将被终止,包罗打扫栈、关闭文件形貌符等,这会释放大部分内存映射。
随后,execve会加载新的程序到内存中。内核为新程序创建一个新的、独立的内存映射,这通常包罗代码段(text)、数据段(data)、堆(heap)和栈(stack)。如果新程序需要加载共享库,内核会为每个历程创建一个私有映射,以确保线程安全并允许写时复制。
在新程序开始实行前,内核会重置历程的内存映射,确保新程序的代码、数据和动态分配的内存区域与新加载的程序文件相对应。这可能涉及到加载新的页表和重新初始化区域布局。
由于execve更换了整个历程的实行情况,之进步程的内存映射通常会被打扫,以避免安全和资源问题。最后,内核会根据新程序的入口点初始化新历程的状态,包罗设置堆栈指针、程序计数器等。
7.8 缺页故障与缺页中断处理惩罚
缺页故障是操纵系统中的一种常见异常,当程序访问的内存页不在物理内存(RAM)中,而是在磁盘上的交换区或文件系统中时,就会发生。处理惩罚缺页故障的流程如下:
中断相应:CPU检测到缺页故障时,会中断当前实行流程,并跳转到缺页中断处理惩罚程序。
查找页表:处理惩罚程序检查页表,确认发生故障的页是否确实不在物理内存中。
加载页面:确认缺页后,操纵系统从磁盘读取相应页面到物理内存,这是最耗时的步骤。
更新页表:页面加载后,操纵系统更新页表,标记该页为有效,并记录物理地址。
重新实行指令:更新页表后,重新实行导致缺页故障的指令。
内存管理:如果物理内存已满,操纵系统可能需要实行页面置换算法,如LRU或FIFO,以腾出空间。
优化内存利用:在整个过程中,操纵系统会积极优化内存利用,确保系统性能。
7.9动态存储分配管理
动态存储分配是程序运行时请求内存的一种方式。以下是动态内存管理的根本方法与计谋:
内存分配:利用malloc等函数从堆中分配内存,返回指向分配内存的指针。若分配失败,返回NULL。
内存释放:利用free函数释放malloc分配的内存,使内存块可被重新分配。
内存管理:操纵系统或运行时库负责管理堆内存,跟踪内存分配与空闲状态。
内存分配计谋包罗:
初次顺应(First-fit):找到第一个足够大的空闲块。
最佳顺应(Best-fit):找到最小的足够大的空闲块。
最坏顺应(Worst-fit):分配最大的空闲块。
伙伴系统(Buddy System):分割内存为对称块,通过归并减少碎片。
内存接纳计谋包罗:
立刻接纳:释放内存时立刻归并空闲块。
耽误接纳:保存释放的内存块,直到需要时归并。
内存碎片管理涉及:
内部碎片:分配的内存块内部未利用空间。
外部碎片:空闲内存被分割成小块,难以满意大内存请求。
内存分配器优化技术包罗:
池分配(Pool Allocation):预先分配固定大小内存块,快速分配接纳。
垃圾接纳(Garbage Collection):自动跟踪不再利用的内存,无需程序员显式调用free。
良好的动态内存管理计谋对提高程序性能和稳固性至关重要,需要仔细思量内存分配和释放的时机,避免内存泄漏和悬挂指针等问题。
7.10本章小结
本章以笔墨性叙述介绍为主,主要介绍了hello的储存管理技术,包罗地址空间的转换、存储器数据的访问,程序加载时的内存映射,缺页处理惩罚与动态内存分配等相关内容。(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
在Linux系统中,I/O设备管理采用了一种同一的文件抽象模型。这意味着,无论是网络接口、磁盘驱动器照旧终端设备,它们都被视作文件对象。通过这种方式,所有的输入输出操纵都被简化为对文件的读写操纵。Linux内核通过Unix I/O接口,为应用程序提供了一个简洁而底层的访问接口,使得对各种设备的访问都遵循同一的模式。
8.2 简述Unix IO接口及其函数
Unix I/O接口的核心在于提供了一组标准的系统调用,使得应用程序可以大概以同一的方式实行输入输出操纵。以下是Unix I/O接口的几个关键功能:
打开文件:应用程序通过调用内核来打开一个文件,从而获得对I/O设备的访问权限。每个Linux历程在启动时都会打开三个标准文件:标准输入(文件形貌符0)、标准输出(文件形貌符1)和标准错误(文件形貌符2)。
改变文件位置:内核为每个打开的文件维护一个当前文件位置指针,初始值为0,应用程序可以修改这个位置指针来读取或写入文件的不同部分。
读写文件:读取操纵将文件中的字符复制到内存中,而写入操纵则相反。当到达文件末尾时,读取操纵会触发EOF(文件竣事)条件。
关闭文件:完成文件访问后,应用程序通知内核关闭文件,释放相关资源。
Unix I/O接口的关键函数包罗:
open:打开一个文件或创建一个新文件,并返回一个文件形貌符。
close:关闭一个已打开的文件形貌符。
read:从指定的文件形貌符读取数据到内存中。
write:将数据从内存写入到指定的文件形貌符。
8.3 printf的实现分析
vsprintf函数的详细实现如下图所示,基于下图我们对实在现举行详细的分析。。
`printf`函数的实现涉及从格式化字符串天生显示信息,到调用底层系统函数write,再到操纵系统内核的陷阱处理惩罚机制。以下是详细过程:
1. 格式化字符串处理惩罚:`printf`起首调用`vsprintf`函数,该函数解析格式化字符串`fmt`,并结合可变参数`args`天生最终的格式化输出字符串。`vsprintf`内部利用一个循环来处理惩罚格式化指令,比方`%x`用于输出十六进制数,`%s`用于输出字符串等。
2. 系统调用write:天生的字符串存储在缓冲区`buf`中,`printf`随后调用系统调用`write`来输出这个缓冲区的内容。`write`函数的原型为:
```c
ssize_t write(int fd, const void *buf, size_t n);
```
此中`fd`是文件形貌符,`buf`是要写入的数据缓冲区,`n`是缓冲区的大小。
3. 陷阱和系统调用处理惩罚:在Linux系统中,`write`函数通过一个陷阱(比方`int 0x80`或`syscall`指令)触发系统调用。这个指令导致控制权转移到操纵系统内核,内核中的系统调用表会根据`eax`寄存器的值(在x86架构中,`eax`通常用于转达系统调用号)找到对应的系统调用函数。
4. 字符显示驱动子程序:内核中的write系统调用处理惩罚函数将数据写入到指定的文件形貌符,如果是标准输出(文件形貌符1),内核会调用字符显示驱动子程序。该子程序负责将ASCII码转换为字模库中的字模数据。
5. 显示VRAM更新:字模数据随后被映射到显示VRAM(Video RAM),VRAM存储了每个像素点的RGB颜色信息。显示芯片按照设定的刷新频率逐行读取VRAM中的数据。
6. 信号线传输:显示芯片通过信号线将RGB分量传输给液晶显示器,从而在屏幕上渲染出最终的字符显示。
整个过程从用户空间的`printf`调用开始,通过系统调用接口进入内核,最终由硬件驱动程序将数据呈如今用户的屏幕上,展示了从软件到硬件的完整路径。har的实现分析
8.4 getc
(### 8.4 getchar的实现分析
`getchar`函数的实现与异步异常处理惩罚精密相关,特殊是键盘中断的处理惩罚。以下是详细的实现过程:
1. 键盘中断处理惩罚子程序:当用户按下键盘上的一个键时,键盘硬件天生一个扫描码,这个扫描码随后触发一个中断请求。操纵系统中的键盘中断处理惩罚子程序被调用,负责处理惩罚这个中断。
2. 扫描码转换:键盘中断处理惩罚子程序吸收到扫描码后,将其转换成对应的ASCII码。这一转换依赖于键盘布局和操纵系统的键盘驱动程序。
3. 键盘缓冲区:转换得到的ASCII码随后被保存到系统的键盘缓冲区中。键盘缓冲区是一个暂时存储区域,用于暂存用户输入的字符,直到它们被读取
4. 返回读取的字符:`read`函数从键盘缓冲区中读取一个字符,并将其返回给`getchar`。如果缓冲区中没有字符,`read`会期待,直到有字符可用。当用户按下回车键时,表示输入竣事,`read`会返回一个特殊的EOF(文件竣事)标记。
5. `getchar`返回:`getchar`函数最终返回`read`读取的字符。如果读取成功,它返回读取的ASCII码;如果遇到EOF,它返回一个特定的值(通常是-1),表示没有更多的输入。
整个过程展示了从用户按键到`getchar`函数返回字符的完整流程,涵盖了硬件中断、操纵系统中断处理惩罚、系统调用以及用户空间的I/O操纵。
8.5本章小结
本章深入探讨了Linux系统中的I/O设备管理方法,详细解释了Unix I/O接口及其提供的系统调用函数。通太过析printf和getchar函数的实现,我们可以看到标准I/O库是怎样与底层系统调用交互,以提供格式化输出和字符输入功能的。别的,还讨论了异步异常处理惩罚,特殊是键盘中断的处理惩罚机制,以及它怎样影响字符输入。这些内容为明白Linux系统中的I/O操纵提供了坚实的根本。
(第8章1分)
结论
预处理惩罚(Preprocessing)是路程的起点,`hello.c`中的源代码被送入预处理惩罚器的怀抱。预处理惩罚器是个细心的工匠,它移除注释,扩展宏定义,包罗头文件,为后续的编译打下坚实的根本。;接下来,编译器这位大师登场。它将人类可读的C语言代码转化为呆板能明白的汇编语言。这个过程就像是将诗歌翻译成一种只有呆板能懂的机密语言;随后,汇编器这位翻译家将汇编语言进一步转化为呆板码。这是将抽象的概念详细化为盘算机硬件能直接实行的指令的过程;最后,链接器这位智者将编译天生的目的文件与其他库文件结合起来,形成一个完整的可实行文件。它就像是将散落的珍珠串成一串美丽的项链。
当`hello`程序被实行时,操纵系统这位巨大的指挥家将可实行文件加载到内存中。它就像是将脚本搬上舞台,准备开始一场出色的演出;CPU这位主角开始按照脚本(指令)行动。它控制着程序的每一个步骤,从打印"Hello, World!"到期待用户的回车,每一个动作都精确无误。;在实行过程中,如果遇到信号这样的不测情况,操纵系统会举行异常处理惩罚,就像是在演出中应对突发状况,确保一切可以大概顺利举行。;当`hello`程序完成它的任务,输出了那句经典的问候之后,它优雅地退出舞台。操纵系统这位指挥家再次出现,将历程的资源接纳,内存释放,确保系统的整洁有序。
在整个过程中,`hello.c`不仅仅是代码,它是一段有情感、有生命力的路程。从预处理惩罚到链接,我们看到了一个想法怎样被赋予形态;从加载到终止,我们见证了一个历程的完整生命周期。这不仅是技术的展示,更是盘算机科学邪术的表现,每一次运行都是对这段奇妙路程的一次新的探索和体验。(结论0分,缺失 -1分,根据内容酌情加分)
附件
hello.i:预处理惩罚得到的C程序
hello.s:编译得到的汇编语言程序
hello.o:汇编得到的可重定位目的程序文件
hello_fromo.s:hello.o反汇编得到的汇编语言程序
hello:链接得到的可实行目的文件
hello_fromout.s:hello反汇编得到的汇编语言程序
(附件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分)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |