前言:
2020年毕业到今2024年,恰好进入行业第四个年头,随着入行越久,对技术本身越发觉得空虚,空是肚子里没有真墨水,虚是虚度一天有一天,一年又一年的时间。4年时间,我把自己从研发干到版本经理,再干到项目负责人,现在干到售后的岗位。干的活确实也不算少,但是自身的知识体系是一点都没建立起来。本人从事GPU行业,做的既有上层应用适配,又有底层驱动逻辑。尤其是做售后以来,干的越久,越觉得应该把自身的知识体系建立起来,公司是内网办公,一些工作的经验记录没办法记到优快云笔记上。所以优快云主要是用做知识体系的构建笔记,想到要学什么就做个记录学什么吧!
至于为什么要学习计算机系统,是因为在工作中发现计算机系统愈发重要,同时我也恰巧有这本书。既然想做出改变,就从这本经典的《深入理解计算机系统开始》吧!
我对自己在本书的学习要求是,计算机系统知道个大概,扩展的知识能够实操验证,看一遍是不可能读明白的,看完之后能知道个大概就达到本书学习的目的了。
1.1 信息就是位+上下文
基本概念:
- 源程序:由.c程序编译而成的bit 序列,8bit为一组(字节),每个字节表示某个文本字符
- ASCII: 一个文本索引表,类似密码本的翻译手册
- 二进制文件:所有其他文件称为
- 文本文件:由ASCII码组成的文件
数据都是一串位来表示的,区分不同数据对象就是联系上下文
Tips:C语言的起源:C语言是用于Unix操作系统而设计的。
1.2 程序被其他程序翻译成不同的格式
gcc -o hello hello.c
hello.c -> cpp预处理器 -> hello.i(预处理) -> 编译器 -> hello.s -> 汇编器(as)-> hello.o -> 链接器 -> hello (可执行目标文件)
- 预处理阶段:.h 文件复制粘贴
- 编译阶段:编译器(ccl)将源文件翻译成汇编语言
- 汇编阶段:汇编器(as)翻译成机器语言,即二进制文件
- 链接阶段:链接标准库,例如hello调用的printf, printf 在已经预编译好的目标文件中,这个文件需要合并到目标程序中,这样就能得到完整的可执行目标文件。
tips:GCC 来自于最早的开源项目GNU(GNU IS NOT UNIX),GNU项目还有我们熟悉GDB 调试器。
1.3 了解编译器系统如何工作
后面讲。
1.4 处理器读并解释存储在存储器中的指令
./hello
运行hello程序。
1.4.1 系统硬件的组成
要了解hello程序运行时发生了什么?
就要了解一些硬件的基本组成。
- 总线:贯穿整个系统的电子管道,可以传递字节信息在各个部件间。设定为定长字节块(word)。有(4 个字节)32位的也有(8个字节)64位的,这里是不是常说的32位和64位操作系统(这个跟cpu的GPRS,处理器一次处理的数据有关)的区别。但是意思差不多一致,就是越大的跑道,将更加满足硬件传输的需求。
- I/O设备:每个I/O设备都通过一个控制器或适配器于I/O总线相连。
- 主存:临时存储设备 DRAM(动态存储器)
- 处理器:中央处理单元(CPU ),是执行存储在主存的引擎。处理器的核心是一个字长的存储设备(寄存器),称为PC(程序计数器),在任何时刻PC 都指向主存中的某条机器语言指令。看起来PC就像一个接单员,一直从主存中取工单(指向主存中的某条机器指令),将工单里面的计算内容放在寄存器文件中 (寄存器文件就是一个小的存储设备),然后ALU(算术逻辑单元–真正的打工王者)将寄存器文件的内容复制到输入,操作运算再到输出,存到寄存器里,最后寄存器将目标结果传到主存指定位置。大概就是这么个流程,但是处理器这么牛的东西,肯定不会这么简单。
1.4.2 运行hello程序
当我们执行./hello的时候,机器是怎么运转的呢?
从上图可以看到,从键盘开始键入./hello 并按回车开始,CPU会将hello程序的机器数据从磁盘复制到主存。利用DMA技术,可以从磁盘直接到主存(后面讲)。只要hellp中的代码和数据加载到主存,处理器就可以开始执行程序。PC通过主存到寄存器文件,在通过ALU计算结果后将目标寄存器文件靠回到显示设备上。
1.5 高速缓存
从上述的例子可以看到,要执行最简单的程序,也要经过大量的时间将数据从一个地方加载到另一个地方(磁盘->主存->寄存器文件->主存->显示),在整个程序的耗时占了大部分。拷贝耗时是整个程序运行的开销的大部分。
那么缓存越快,机器运行的效率就越高,设备性能越好!
但是对高速缓存我总结:越近越快,越快越贵 —除了空间能换时间,金钱也可以换时间。
所以为了节省成本,设计者设计高速缓存的存储器,作为暂时的集结区域,只存当前处理器需要处理的信息。
1.6 存储设备形成分级
存储结构层次分级:
1.7 操作系统管理硬件
硬件是通过操作系统来控制的
操作系统两个基本功能概括:
- 防止硬件被失控的应用程序滥用
- 想应用程序提供简单一致的机制来控制复杂繁多的低级硬件设备
抽象概念:
- 进程:对处理器、主存、I/O设备的抽象表示
- 虚拟存储器:对主存和I/O设备的抽象表示
- I/O设备 :文件是对I/O设备的抽象表示
1.7.1 进程
进程是操作系统对一个正在运行的程序的一种表示
- 一个系统可以运行多个进程
- 并发:只是看上去执行多个进程,其实都是轮流来
- 进程间的切换,交错执行命令的机制,称为上下文切换
- 哪个进程在CPU上来执行时操作系统决定的。
1.7.2 线程
一个进程可以有多个线程的执行单元组成,多线程的方案比多进程更高效。
1.7.3 虚拟存储器
虚拟存储器:是一个抽象概念,他为了让每个进程都认为自己独占了主存而设计。
虚拟化空间:每个进程看到一致的存储器,称为虚拟地址空间。
ps:上图中的地址是从下往上增的。
程序代码和数据:对于所有进程来说,代码是从同一固定的地址开始,紧接着是和C全局变量相对应的数据位置。
堆:代码和数据区随后运行的就是堆。代码和数据区是在进程运行一开始就被规定的(执行文件hello代码和数据大小固定),与此不同,随着代码中malloc和free,堆可以动态的扩展和收缩。后面详细讲
共享库:后面讲
栈:位于用户虚拟地址空间顶部的是用户栈,编译器用他实现函数调用。和堆一样,用户栈在执行程序期间动态扩展和收缩。
内核虚拟存储器: 内核总是驻留在内存中,是操作系统的一部分,地址空间的顶部是为内核保留的。
1.7.4 文件
文件 就是字节序列。每个I/O设备,包括磁盘、键盘、鼠标、显示器,网络,都视为文件,系统所有的输入输出都是通过使用系统函数调用读写文件来实现的。
Linux 的核心就是文件思想。
可以了解一下Linux之父:linux Torvalds
1.8 系统之间的网络通信
本地客户端键入命令,远端服务器运行程序,将运行的结果远程发送回客户端,客户端再显示到显示器上。
1.9 几个重要的概念
1.9.1 并发和并行
计算机发展的动力来源于需求:
1.想要计算机做的更多。
2.想要计算机运行的更快。
并发:指同时具有多个活动的系统
并行:用并发让一个系统运行的更快。
1. 线程级并发
构建出进程这个抽象,就能设计出同时执行多个程序的系统,这就是并发的抽象概念。使用线程 ,甚至可以在一个进程中执行多个控制流。
前文里面说过:并发执行只是模拟出来的概念,一台单处理器的计算机系统只能在一个时间处理一个任务,而执行的过程中可以快速切换进程,这样就是先了并发。这样的处理器系统叫做单处理器系统。
当构建一个单操作系统内核控制多处理器,就得到了一个多处理器系统。目前常见的处理器都是多核多线程的(超线程)。
顾名思义,多核,就是将多个CPU集成到一个芯片上。每个核都配备有独立的L1和L2高速缓存。
超线程:同时多线程,是一项允许CPU执行多个控制流的技术,这个就涉及到硬件设计,简单来说就是,CPU计算过程中需要将数据装载到高速缓存,而这个转载的过程又不需要计算,所以这个时间CPU可以切到另一个线程,处理另一个已经完成数据装载的任务。
多处理器芯片的出现,就可以完成上述两个需求。如果需求够大,可以叠100个核上去都行。
2.指令级并行
在更底层的层次里,现代处理器可以同时执行多条指令的属性,叫做指令级并行。原来不能指令级并行的处理器,完成一个指令通常需要2~10个时钟周期。而当处理支持指令级并行后,可以同时处理100条命令,换算下来甚至不需要一个时钟周期就能完成一个命令,这样被称为超标量superscalar。
3.单指令、多数据并行
在底层设计上,许多处理器拥有特殊的硬件,允许一条指令产生多个可以并发执行的操作,称为单指令、多数据。SIMD并行。
1.9.2 计算机系统中抽象的重要性
抽象的使用是计算机科学的重要概念之一。
例如C语言的函数原型。
抽象有不同层次的抽象,抽象的概念,就是统一编程标准的过程,保证各种操作系统都能在一套完整的硬件平台上运行。