写在最前面的话:
在大一下学期的时候其实就已经萌生了读完csapp并做完里面所有lab的想法,但当时痴迷于acm所以最后以失败告终。
现在大二上的我对自己的目标更加清晰,希望这次自己可以坚持下去
1. 信息就是位+上下文
拿一个最简单的hello程序举例
# include<stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
hello 程序是从一个编译器创建并保存的文本文件(源文件)hello.c开始的。
源文件实际上就是一个由0和1组成的位序列。8个位为1组称为字节,每个字节表示一个文本字符,现代计算机用一个整数值来表示每个字符也就是ASCII码
像hello.c这样只由ASCII码字符构成的文件称为文本文件,所有其他文件为二进制文件
上下文 : the circumstances that form the setting for an event, statement, or idea, and in terms of which it can be fully understood and assessed.
大概意思就是操作系统保持跟踪进程运行所需的所有状态信息。这种状态就是上下文
2. 程序被其他程序翻译成不同的格式
为了在系统上运行hello.c程序,每条C语句都被其他程序转化为一系列低级机器语言指令,这些指令按照可执行目标程序的格式打好包,并以二进制磁盘文件的形式存放起来。
hello.c(源程序/文本)\(\rightarrow\)预处理器(预处理)\(\rightarrow\)hello.i(修改了的源程序)\(\rightarrow\)编译器(编译)\(\rightarrow\)hello.s(汇编程序/文本)\(\rightarrow\)汇编器(汇编)\(\rightarrow\)hello.o(可重定位目标程序/二进制)\(\rightarrow\)链接器(链接)\(\rightarrow\)hello(可执行目标程序/二进制)
- 预处理阶段。预处理器(cpp)将所有宏展开插入到程序文本中,本质上还是一个文本。得到hello.i
- 编译阶段。编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,包含一个汇编语言程序
- 汇编阶段。汇编器(as)将hello.s翻译成机器语言指令,将这些指令打包成可重定位目标程序hello.o
- 链接阶段。像hello程序调用的printf函数存在于一个名为printf.o的单独的预编译好的目标文件。链接阶段就是将这些文件合并到hello.o中
注 : 最后的hello程序存放在磁盘上
3.shell
如果我们想在Unix系统上运行可执行文件hello,我们将它的文件名输入到称为shell的应用程序中:
linux> ./hello
hello,world
linux>
shell其实就是一个命令行解释器
输出一个提示符:
linux>
然后等待输入一个命令行,执行这个命令。执行完后输出一个提示符等待下一个输入的命令行
4. 运行hello 程序
当我们在键盘上输入字符串
./hello
- shell程序将字符逐一读入寄存器(cpu内部),再把它存放到内存(cpu外部)中
- 在键盘敲回车键后,shell知道我们已经结束命令的输入,shell执行一系列指令将hello目标文件中的代码和和数据从磁盘复制到主存(cpu外部)。数据包括最终会被输出的字符串"hello,world\n"
- 当hello中的代码和数据中的代码和数据被加载到主存,处理器开始执行hello程序的main程序中的机器语言指令。这些指令将"hello world\n"字符串中的字节从主存(cpu外部)复制到寄存器(cpu内部)文件,再从寄存器文件复制(经总线)到显示设备显示到屏幕上
5.高速缓存至关重要
在刚才运行程序的过程里我多次强调了cpu内部和cpu外部。
系统花费大量时间把信息从一个地方挪到另一个地方,比如hello程序的机器指令最初存放在磁盘上,运行程序时,指令从磁盘到主存到处理器,这些都是开销,减慢了程序"真正"工作
那么我们可以采用更小更快的存储设备(高速缓存存储器/cache)存放处理器近期可能会需要的消息,cache位于cpu内部。
接下来会有一个很兴奋的消息告诉大家:
意识到cache存在的应用程序员能够利用cache将程序的性能提高一个数量级
至于该怎么提高之后会深入讨论
6. 存储设备形成层次结构
直接贴张图在这里
正如可以运用不同的高速缓存来提高程序性能一样,程序员同样可以利用对整个存储器层次结构的理解来提高程序性能。
7. 操作系统管理硬件
当shell加载和运行hello程序以及hello程序输出自己消息时,shell和hello程序都没有直接访问键盘、显示器、磁盘或者主存。它们依靠操作系统(应用程序和硬件之间插入的一层软件)提供的服务
操作系统两个基本功能 :
(1)防止硬件被失控的应用程序滥用
(2)向应用程序提供简单一致的机制来控制复杂而又通常大不相同的低级硬件设备
操作系统通过几个抽象概念实现这两个功能
- 进程:进程是对一个正在运行的程序的一种抽象。一个系统上可以运行多个进程,每个进程都好像在独占地使用硬件。单处理器系统只能执行一个进程的代码。当操作系统决定把控制权从当前进程转移到另一进程就会进行上下文切换
-
线程:在现代系统中,一个进程由多个称为线程的执行单元构成,每个线程都运行在进程的上下文中,共享同样代码和全局数据
-
虚拟内存:虚拟内存是一个抽象概念。每个进程看到的内存都是一致的,称为虚拟地址空间
-
文件:linux系统的哲学思想是一切皆为文件。每个文件就是字节序列,每个I/O设备都可以看成是文件。系统中的所有输入输出都是通过使用一小组称为Unix I/O的系统调用读写文件来实现的
8. 系统之间的利用网络通信
从一个单独的系统来看,网络可视为一个I/O设备。当系统从主存复制一串字节到网络适配器时,数据流经过网络到达另一台机器。
回到hello示例,在远端运行hello程序如下图所示
9. 并发和并行
并发: 指一个同时具有多个活动的系统
并行: 指用并发来使一个系统运行的更快
- 线程级并发
使用线程,我们能够在一个进程中执行多个控制流。当构建一个由单操作系统内核控制的多处理器组成的系统时,我们就得到了一个多处理器系统。使得cpu更好地利用它的处理资源
- 指令级并行
现代处理器可以同时执行多条指令的属性称为指令级并行。在流水线中,将执行一条指令所需要的活动划分为不同的步骤,将处理器的硬件组织成一系列阶段。每个阶段执行一个步骤,这些步骤可以并行操作,用来处理不同指令的不同部分
- 单指令、多数据并行
现代处理器拥有特殊的硬件,允许一条指令产生多个可以并行执行的操作,这种方式称为单指令,多数据,即SIMD并行。
10. 小结
- 计算机内部的信息被表示为一组组的位,它们依据上下文有不同的解释方式。程序被其他程序翻译成不同的形式,开始是ASCII文本,然后被编译器和链接器翻译成二进制可执行文件
- 处理器读取并解释存放在主存里的二进制指令。因为计算机花费大量时间在内存、I/O设备和CPU寄存器之间复制数据,所以系统中的存储器被划分层次结构。
- 操作系统内核是应用程序和硬件之间的媒介。它提供三个抽象:
(1)文件是对I/O设备的抽象
(2)虚拟内存是对主存和磁盘的抽象
(3)进程是处理器、主存和I/O设备的抽象 - 最后网络提供了计算机系统之间通信的手段,从特殊系统的角度看,网络就是一种I/O设备