文章目录
1hello.c
#include <stdio.h>
int main()
{
printf("hello, world")
return 0;
}
1.1信息就是位加上下文
-
信息在计算机系统中的表示方式是通过比特(bit)来实现的。比特是表示信息的最小单位,可以是0或1。八个比特组成一个字节(byte),这是计算机处理数据的基本单位。
-
程序的生命周期从源程序(源文件)开始的,源程序(如hello.c)是程序员通过编辑器创建并保存的文本文件。这些文件本质上是由一串比特(0和1)组成的。根据文件内容的不同,可以将文件分为两类:
-
文本文件:只包含ASCII字符的文件。例如,其他都是二进制文件。源代码文件hello.c就是文本文件。
-
信息的本质是一串比特,而区分不同数据对象的唯一方法在于读取这些比特时的上下文。
1.2程序被其他程序翻译成不同格式
- 源程序到目标程序的步骤:预处理,编译,汇编,链
-
预处理:
预处理器根据 # 开头的命令修改原始的 c 程序。比如根据 #include<stdio.h> 命令把头文件 stdio.h 的内容直接插入到程序文件中,是对文本文件的修改,得到hello.i。 -
编译阶段:
将hello.c翻译成汇编语言的文本文件。得到hello.s。 -
汇编阶段
汇编器(as)将hello.s翻译成机器语言指令打包到可重定位目标程序,得到hello.o(二进制文件)。 -
链接阶段
hello程序中,printf 函数是一个标准 C 库函数,存于 printf.o 中,这是一个单独的预编译好了的目标文件。链接器将printf.o与汇编得到的hello.o(二进制文件)合并就得到可执行目标文件。
注:
- 四个步骤对应的后缀:ciso
- 四个步骤对应的gcc选项:ESco(后面选项包括了前面的步骤,例如-o直接生成可执行文件,./hello即可运行)
- 前两个步骤对应的文件类型是文本类型
- 汇编语言为不同高级语言和编译器提供了通用的输出语言
- 只有机器指令可以被计算机直接运行
1.3 了解编译系统如何工作是大有用处的
- 优化程序性能
- 理解链接时出现的错误
- 避免安全漏洞
1.4 处理器读并解释储存在内存中的指令
- shell 是一个命令行解释器,它输出一个提示符(>>),等待输入一个命令行,然后执行命令。如果输入的第一个单词是可执行文件的名字,就运行该文件。
注:
- 输入第一个单词后面的是相应的选项(char* argv[]),
例如
gcc -o hello hello.c
其他命令也是可执行文件,被配置在相应系统的环境变量文件中)
1.4.1 系统的硬件组成
- 由总线、I/O 设备、处理器、主存储器四个部分组成
-
总线:一次可以传输一个定长的字节块,称为字。64位系统即总线一次可以传输 64 位比特(8字节),64位总线的一个字是 8 字节。32位总线的一个字是 4字节。
-
I/O (输入/输出)设备
每个 I/O 设备通过一个控制器或适配器与 I/O 总线相连。
控制器是 I/O 设备本身或主板上的芯片组,适配器则是一块插在主板上的卡。 -
主存
主存是由一组动态随机存取存储器(DRAM)组成的。
从逻辑上看,存储器是一个线性的字节数组,每个字节都有唯一的地址(就像数组的下标)。 -
处理器(CPU)
处理器是解释存储在主存中指令的引擎。
处理器的核心是一个程序计数器(PC)程序计数器大小为一个字,存储CPU即将执行的下一条指令的地址。(类似指针)
处理器不断执行程序计数器指向的指令。每执行一条,程序计数器更新一次,指向下一条指令。(不一定是下一条) -
处理器会按照指令执行模型(指令集架构)解释指令中的位并执行相应操作。操作是围绕主存、寄存器文件、算数/逻辑单元(ALU)进行的。
寄存器文件:单个字长,有唯一的名字。
ALU(算术/逻辑单元):计算新的数据和地址值。 -
指令集架构:每条机器指令的效果
微体系架构:处理器实际上是如何实现的
磁盘属于i/o设备
1.4.2 运行 hello 程序
- 执行目标文件时,shell 程序将磁盘目标文件中的字符逐个读入寄存器(流),然后放到主存中。之后处理器就开始执行目标文件的机器语言指令。
(利用直接存储器存取(DMA)技术可以不通过寄存器,直接将数据从磁盘到达内存。)
以输出打印 hello world 为例,处理器将 hello world 的字节复制到寄存器文件,再复制到显示设备,最终显示在屏幕上。
1.5 高速缓存至关重要
系统设计者需要使这些复制操作尽可能快的完成。
较大的存储设备比较小的存储设备运行的慢。
高速设备的价格远高于低速设备。
设计者使用高速缓存(cache)用来解决处理器与主存间存取速度的差异。让高速缓存里存放可能经常访问的数据,大部分的内存操作都在高速缓存中快速完成。
1.6 存储设备形成层次结构
套娃高速缓存的思想:上一层的存储器作为低一层的高速缓存。
从上到下,容量更大,运行更慢,每字节价格更便宜。
1.7操作系统管理硬件
所有应用对硬件的操作都必须通过操作系统
操作系统的两个基本功能:
- 防止硬件被失控的应用程序滥用
- 向应用程序提供简单一致的机制来控制复杂的低级硬件设备
操作系统通过三个基本的抽象概念实现这两个功能:
- 文件:对I/O 设备的抽象表示
- 虚拟内存:对主存和磁盘的抽象表示
- 进程:对处理器、主存和 I/O 设备的抽象表示
1.7.1 进程
- 进程:操作系统正在运行的一个程序实体的一种抽象。看起来好像只有这一个程序在运行。
- 并发运行:一个进程的指令和另一个进程的指令是交错执行的。一个系统可以同时运行多个进程,实际上这些进程是并发运行的。
- 操作系统通过上下文切换来实现并发运行。上下文是跟踪进程运行所需的所有状态信息,可能存在于PC、寄存器文件、主存等地方。
任何时刻,单处理器只能执行一个进程的代码。
操作系统内核是操作系统代码常驻主存的部分,从一个进程到另一个进程的转换是由内核管理的。
内核不是一个独立的进程,是系统管理全部进程所用代码和数据结构的集合。
1.7.2 线程
- 一个进程可以由多个线程组成,每个线程都运行在进程的上下文,共享同样的代码和全局数据。
多线程之间比多进程之间更容易共享数据,且线程一般来说比进程更高效。 - 并发:x核n*x线程处理器,将不同的线程分配到多个处理器中,使程序运行更快。
1.7.3 虚拟内存
虚拟内存使每个进程都以为自己独占了主存。每个进程看到的内存都是一致的,即虚拟地址空间。
在linux中,每个进程看到的虚拟地址空间由以下几个部分组成:
地址:… ffff ffff(最大地址)
地址:0000 0000 …(最低地址)
地址从低到高:
程序代码和数据
对所有进程来说,代码都是从同一个固定地址开始,紧接着是与全局变量对应的数据区。代码和数据区都是按照可执行文件的内容初始化的。代码和数据区在进程开始运行时就被指定了大小。
-
堆(向上增长)
而运行时堆是根据 malloc 和 free 函数的调用在运行时动态地扩展和收缩的。 -
共享库
地址空间的中间部分,用来存放像C 标准库、数学库等都属于共享库的代码和数据。内容是映射进来而非这个进程独有。 -
栈
编译器用它来实现函数调用(push/pop)。当调用函数时,栈增长,从函数返回时,栈收缩。栈空间并不算大。 -
内核虚拟内存
应用程序不可读写此区域,但可以申请系统调用,运行内核代码定义的函数,保证计算机安全。
1.7.4 文件
文件就是字节序列,仅此而已。
每个 I/O 设备,包括磁盘、键盘、显示器、网络,都可以看成是文件。
将它们抽象为文件,提供统一的接口。
linux 的“一切皆文件”思想。
1.8 系统之间利用网络通信
现代系统通常通过网络和其他系统连接到一起。
从一个单独的系统来看,网络可以视为一个 I/O 设备。
通过socket文件复制信息到另外一台主机。
1.9 重要主题
系统是硬件和系统软件互相交织的组合体。
1.9.1 Amdahl 定律
Amdahl 定律的主要观点:要优化整个系统,必须提升全系统中最重要的部分和可加速程度最大的部分。
1.9.2 并发和并行
两个需求:1.计算机做的更多。2.运行的更快
并发:一个通用的概念,指一个同时具有多个活动的系统。(比如分屏敲代码)
并行:用并发来使系统运行得更快。
1.9.2.1.线程级并行
传统意义上的并发执行是通过单处理器在进程间快速切换模拟出来的。
超线程又称同时多线程,它允许一个 CPU 执行多个控制流。 CPU 有的硬件有多个备份,比如程序计数器和寄存器文件,而其他硬件只有一份,比如浮点算术运算单元。(多头蛇:脑子上的器官有多个但是身子只有一个)
常规 CPU 需要约 20000 个时钟周期来切换线程,超线程 CPU 可以在单个周期的基础上切换线程,比如一个线程在等待数据装在到高速缓存,CPU 就可以去执行另一个线程。
4 核 8 线程处理器可以让8 个线程都并行执行。
多处理器从两方面提高性能:
- 减少了执行多个任务时,频繁切换,模拟并发的开销。
- 如果程序以多线程方式书写,可以提高运行速度。
1.9.2.2. 指令级并行
-
同时执行多条指令的属性称为指令级并行。
每条指令从开始到结束一般需要 20 个或更多的时钟周期,通过指令级并行,可以实现每个周期 2~4 条指令的执行速率。 -
流水线:将执行一条指令的活动分为不同的步骤,将硬件组织的工作组成一系列的阶段,每个阶段执行一个步骤,而这些阶段可以并行操作。例如分成3个步骤。
1 2 3 1 2 3
1 2 3
如果比一个周期一条指令更快,就称为超标量处理器,现在一般都是超标量。
1.9.2.3. 单指令、多数据并行
在最低层次上,现代处理器允许一条指令产生多个可以并行执行的操作,称为单指令、多数据并行,即 SIMD 并行。这种并行处理方式在处理大量相同类型的数据时非常有效,例如图像处理、信号处理和科学计算。
1.9.3 计算机系统中抽象的重要性
文件是对I/O设备的抽象
虚拟内存是对程序存储器的抽象
进程是对一个正在运行的程序的抽象
虚拟机是对整个计算机的抽象