2025-CSAPP
第1章 系统漫游
源程序
- hello程序的生命周期是从一个源程序(或者说源文件)开始的,即程序员通过编辑器创建并保存的文本文件,文件名是hello.c。
- 源程序:由值0和1组成的位(又称为比特)序列。
- 字节:8个位被组织成一组。
- 每个字节表示程序中的某些文本字符。
编译系统的阶段和用处
编译系统的阶段
预处理阶段:预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。
- 比如hello.c中第1行的#include<stdio.h>命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入程序文本中。结果就得到了另一个C程序,通常是以.i作为文件扩展名。
编译阶段:编译器(cc1)将文本文件 he11o.i翻译成文本文件 hello.s,它包含一个汇编语言程序。
汇编阶段:接下来,汇编器(as)将he11o.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序(relocatableobject program)的格式,并将结果保存在目标文件 hello.o中。
链接阶段:printf函数存在于一个名为printf.o的单独的预编译好了的目标文件中,而这个文件必须以某种方式合并到我们的he1lo.o程序中。链接器(ld)就负责处理这种合并。结果就得到hello文件,它是一个可执行目标文件(或者简称为可执行文件),可以被加载到内存中,由系统执行。
编译系统的用处
优化程序性能
- 现代编译器通常生成很好的代码。
- 需要理解机器代码和编译器将不同的C语句转换成机器代码的方式。
理解链接时出现的错误 - 一些令人困惑的编程错误都与链接器操作有关,特别是构建大型软件。
避免安全漏洞 - 缓冲区溢出漏洞是造成大多数网络和互联网服务器上安全漏洞的主要原因。
处理器硬件组成与运行过程
处理器的硬件组成
系统硬件组成图:硬件组成图 提取码7568
总线
- 贯穿整个系统的是一组电子管道。
- 携带信息字节并负责在各个部件间传递。
- 通常总线被设计成传送定长的字节块,也就是字(word)。
I/0设备
- I/O(输入/输出)设备是系统与外部世界的联系通道。
- 四个I/O设备:
- 作为用户输人的键盘和鼠标
- 作为用户输出的显示器
- 用于长期存储数据和程序的磁盘驱动器(简单地说就是磁盘).
- 每个I/O设备都通过一个控制器或适配器与I/O总线相连。控制器和适配器之间的区别主要在于它们的封装方式。
- 控制器是I/O设备本身或者系统的主印制电路板(通常称作主板)上的芯片组。
- 适配器则是一块插在主板插槽上的卡。
主存
- 临时存储设备,在处理器执行程序时,用来存放程序和程序处理的数据。
- 从物理上来说,主存是由一组动态随机存取存储器(DRAM)芯片组成的。
- 从逻辑上来说,存储器是一个线性的字节数组,每个字节都有其唯一的地址(数组索引),这些地址是从零开始的。
处理器
- 解释(或执行)存储在主存中指令的引擎。处理器的核心是一个大小为一个字的存储设备(或寄存器),称为程序计数器(PC)。
- PC指向主存中的某条机器语言指令(即含有该条指令的地址)。
- 这类操作围绕着主存、寄存器文件(register file)和算术/逻辑单元(ALU)进行。
- 一些简单操作的例子CPU在指令的要求下可能会执行这些操作。
- 加载:从主存复制一个字节或者一个字到寄存器,以覆盖寄存器原来的内容
- 存储:从寄存器复制一个字节或者一个字到主存的某个位置,以覆盖这个位置上原来的内容。
- 操作:把两个寄存器的内容复制到ALU,ALU对这两个字做算术运算,并将结果存放到一个寄存器中,以覆盖该寄存器中原来的内容。
- 跳转:从指令本身中抽取一个字,并将这个字复制到程序计数器(PC)中,以覆盖PC中原来的值。
运行过程
- 初始时,shell程序执行它的指令,等待我们输入一个命令。当我们在键盘上输人字符串/hello”后,shell程序将字符逐一读入寄存器,再把它存放到内存中。
- 键盘上敲回车键,shell程序就知道我们已经结束了命令的输人。然后执行一系列指令来加载可执行的 he1lo文件,这些指令将he11o目标文件中的代码和数据从磁盘复制到主存。
- 之后,处理器就开始执行 hello程序的 main 程序中的机器语言指令。这些指令将“hello,world\n”字符串中的字节从主存复制到寄存器文件,再从寄存器文件中复制到显示设备,最终显示在屏幕上。
高速缓存
高速缓存存储器(cache memory,简称为 cache或高速缓存),作为暂时的集结区域,存放处理器近期可能会需要的信息。
- 利用了高速缓存的局部性原理,即程序具有访问局部区域里的数据和代码的趋势。
- 通过让高速缓存里存放可能经常访问的数据,大部分的内存操作都能在快速的高速缓存中完成。
操作系统管理视图
计算机系统分层视图:计算机系统分层视图 提取码7568
进程
进程是操作系统对一个正在运行的程序的一种抽象。
- 在一个系统上可以同时运行多个进程,而每个进程都好像在独占地使用硬件。
- 并发运行,则是说一个进程的指令和另一个进程的指令是交错执行的。在大多数系统中,需要运行的进程数是多于可以运行它们的CPU个数的。
线程
- 一个进程实际上可以由多个称为线程的执行单元组成。
- 每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。
虚拟内存
- 虚拟内存为每个进程提供了一个假象,即每个进程都在独占地使用主存。
- 虚拟地址空间:每个进程看到的内存都是一致的。
进程的虚拟地址空间:进程的虚拟地址空间 提取码:7568- 程序代码和数据:对所有的进程来说,代码是从同一固定地址开始,紧接着的是和C全局变量相对应的数据位置。代码和数据区是直接按照可执行目标文件的内容初始化的,在示例中就是可执行文件hello。
- 堆:代码和数据区后紧随着的是运行时堆。代码和数据区在进程一开始运行时就被指定了大小,与此不同,当调用像mal1oc和free这样的C标准库函数时,堆可以在运行时动态地扩展和收缩。
- 共享库:大约在地址空间的中间部分是一块用来存放像C标准库和数学库这样的共享库的代码和数据的区域。
- 栈:位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数调用。
- 内核虚拟内存:地址空间顶部的区域是为内核保留的。
文件
- 文件就是字节序列,仅此而已。
- 每个I/0设备,包括磁盘、键盘、显示器,甚至网络,都可以看成是文件。
- 系统中的所有输入输出都是通过使用一小组称为UnixI/O的系统函数调用读写文件来实现的。
并发与系统层次抽象表示
并发与并行
- 并发:同时具有多个活动的系统。
- 并行:用并发使系统运行更快。
- 并行可以在计算机系统的多个抽象层次上运用。
- 三个层次,在系统层次结构中从最高到最低级。
- 线程级并发:
- 在进程抽象基础上,多个程序同时执行,导致并发;
- 使用线程可以在单一进程中有多个控制流。
系统层次抽象表示
计算机系统抽象分层图:计算机系统抽象分层图 提取码7568
- 文件是对I/0设备的抽象,虚拟内存是对程序存储器的抽象,而进程是对一个正在运行的程序的抽象。
- 虚拟机提供对整个计算机的抽象,包括操作系统、处理器和程序。
第2章 信息的表示和处理
信息位级表示与存储
- 十六进制表示法
- 从0到9,以及A到F。
- C语言,以0x或0X开头的数字常量被认为是十六进制的值。
- 参见课本练习2.1-2.3。
- 字数据大小
- 字长:指针数据的标称大小。
- 寻址和字节顺序
- 小端法:最低有效字节在最前面的方式;大端法:最高有效字节在最前面的方式。
- 参加课本练习2.5-2.6。
- 布尔代数
- 与、或、非、异或
- 参见课本练习2.8。
位级运算(C语言)
- 掩码运算
- 举例:掩码0xFF(最低的8位为1)表示一个字的低位字节。位级运算x&0xFF生成一个由x的最低有效字节组成的值,而其他的字节就被置为0。
- 参见课本练习2.12-2.13,第74页;答案参见第135-136页。
- 逻辑运算
- 逻辑或、与、非。
- 与位级运算的区别:
- 按位运算只有在特殊情况下,也就是参数被限制为0或者1时,才和与其对应的逻辑运算有相同的行为。
- 如果对第一个参数求值就能确定表达式的结果,那么逻辑运算符就不会对第二个参数求值。
- 举例:表达式 a&&5/a将不会造成被零除,而表达式 p&&*p++也不会导致间接引用空指针。
- 参见课本练习2.14-2.15,第76页;答案参见第136页。
- 移位运算
- 左移: x << y
- 位向量x左移y位:丢弃左边多余比特。
- 右边填0。
- 右移:x >> y
- 位向量x右移y位:丢弃右边多余的比特。
- 逻辑移位:左边填0。
- 算术移位:左边复制最高有效位。
- 未定义行为:移位量小于零或大于等于字长。
- 参见课本练习2.16,第77页;答案参见第136页
- 左移: x << y
无符号整数编码与运算
▪ 编码表示、扩展和截断
▪ 加法、乘法、除以2的幂
▪ 补码整数编码与运算
▪ 编码表示、扩展和截断
▪ 加法、乘法、除以2的幂
▪ 无符号与补码的转换
▪ 浮点数表示与运算
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 SEER's Study!