第一章 计算机系统漫游的大致概念总结
这一章主要是对整个计算机系统做了一个大致粗略的讲解,并对有些点做了一个以后会在后面章节讲解的介绍,闲话不多说,写上对这一章的第一次概括归纳,先把整个知识框架先粗略的搭建后,熟悉后成为自己的东西后,再回过头来对框架进行细化美化。
先由一个hello world的C语言程序作为引申,对源文件、信息、编译过程做了大致的介绍
这个才通过编译器编写的.c为后缀名的文件被称为文本文件,一般也把只有ASCII字符的称为文本文件,而其他的被称为二进制文件
位和上下文组成信息:系统中的信息都是由一串比特表示的,但是区别在于在不同的上下文中,这串比特表示的意思就不尽相同,比如可能是表示一个整数,也可能表示一个机器指令。
gcc编译器将源文件编译为目标文件,也就是可执行文件。这整个翻译过程有4个阶段,由前到后分别是由预处理器参与的预处理阶段、编译器参与的编译阶段、汇编器参与的汇编阶段以及最后链接器参与的链接阶段
预处理阶段是由预处理器cpp读取文件内容中#开头的句子指定的系统头文件,并将其插入文本中,生成另一个以.i为后缀名的C程序的过程
编译阶段是编译器ccl将这个变化的C程序编译为由汇编语言构成的以.s为后缀名的程序文件的过程
汇编阶段是指汇编器as将这些汇编语言翻译为机器语言指令,并将其打包为以.o结尾的二进制文件的过程
链接阶段是这样一个阶段,程序中有printf函数,而printf函数存在于已经预编译好的printf.o的目标文件中,通过链接器将这个目标文件合并到此文件中,然后生成最后的可执行文件,并加载到内存中执行
随后作者告诉我们了解编译器如何工作有什么益处
首先可以优化程序性能,比如当我们了解了C语言转化为机器代码的方式,就可以判断switch和if-else谁的效率高,while和for谁效率高,函数调用的开销如何等等。我们就可以了解我们程序代码是否存在问题,能否通过修改某些代码来提高性能
其次就是可以理解链接时出现的错误,一些最令人困扰的程序错误往往与链接器有关
最后就是可以避免安全漏洞,比如解决缓冲区益处问题等等,了解编译器如何工作也是我们进行安全变成的第一步
作者再通过讲述系统硬件的组成来阐述执行hello这个程序的过程
系统硬件是由总线、I/O设备、主存、处理器组成
总线:贯穿整个系统的一组电子管道,作为各个设备传递信息字节的通道;总线总是传递定长的字节块,也就是字,一般系统有着规定的固定的字长,比如4个字长的系统就是32位系统,8个字长的系统也就是64位系统。(MD,这样看来32位64位的指的是不是总线能传递多长的字节块的意思啊)
I/O设备:我们的键盘鼠标输入设备,显示器就是输入设备,磁盘就是输入输出设备;I/O设备是通过控制器或适配器与I/O总线相连。
主存:也就是我们平时所说的内存,是一种临时存储设备,处理器执行的程序以及程序所需要的数据就在主存上
处理器:CPU,解释或执行主存中的指令;核心是寄存器,也叫做程序计数器。指令集架构的简单实现
程序执行过程:键盘上敲下./hello时,shell接收到根据USB控制器传来的命令,然后执行程序,可以通过直接存储器存储将磁盘中的hello程序的数据不通过处理器直接到达内存,然后处理器执行内存中的指令,并把‘hello world\n’这个字符串的字节从主存复制到寄存器文件中,再把寄存器文件的中的字节复制给显式设备,最后显式输出到屏幕中
紧接着作者通过对高速缓存的描述来讲解存储器层次结构
高速缓存:系统花了大量的时间来把信息从一个地方挪到另一个地方,比如hello程序的机器指令数据先是存放在磁盘上,程序加载时将其从磁盘复制到主存,然后在运行程序时再将其复制到处理器,这些复制过程都是一种开销啊,减少了程序的真正工作,比如打篮球,投篮是真正的动作,效率最高的方式是球到手直接三分起投,但是不停的传球再投球就肯定不是效率最高的方式了,所以我们要减少复制这个过程,但是处理器从各存储介质上读取数据的效率是这样的:寄存器文件>>主存>>磁盘。就是在这种形式下,诞生了高速缓存存储器,存放近期可能会需要的信息,就是直接让处理器读取,而不需要前面的复制过程。其中L1高速缓存在处理器芯片上,读取数据的效率跟寄存器基本持平,L2高速缓存通过特殊的总线与CPU连接,速度虽然不及L1,但是也比主存高多了。L1和L2是由静态随机访问存储器(SRAM)的硬件技术实现的。而现在的系统已经有了L3,其读取数据是不及L2,但是依然比主存高把?
存储器层次结构:在处理器与较大较慢的存储设备之间插入一层高速缓存,已经成为了提高处理器读取数据提升效率的常见方式,也在不满意处理器在当时最快的高速缓存中读取数据的速度的念头中,一次次的插入一层高速缓存,直到现在形成了这么一个层次结构。目前是七层存储器结构(怎么有点抄袭OSI的赶脚),从上到下,存储字节容量越大,读取数据速度越慢,造价也越低,从层次名称L0到L6,分别是寄存器、L1高速缓存、L2高速缓存、L3高速缓存、主存、本地磁盘、远程存储设备(分布式文件系统或Web服务器提供的),上一层通常都作为其低一层的高速缓存,比如寄存器缓存的就是L1高速缓存的行,依次类推,而本地磁盘也就是网络磁盘或者说是远程存储设备的高速缓存。这种层次结构给我的感觉就有点像我们存取衣服的方式,比如在自己家里是有着自己所有的衣服,但是工作后回家不方便(读取数据效率低),我们在外面租房子住,就从拿出自己一部分衣服出来放在租房里,然后再把适合这个季度的衣服拿出来单独放一边,再然后从中选出自己常穿的拿出来又单独放一边。这样我们(也就是处理器)能够提高效率的换衣服(读取数据)。
处理器中的高速缓存的结构:每一核中,寄存器缓存的是L1数据高速缓存,L2统一的高速缓存分为两个部分缓存在L1中,L1数据高速缓存和L1指令高速缓存,最后每个核心瓜分了L3的高速缓存,换句话说L1和L2是每个核心都单独拥有的,而L3是多个核心大家公用的。
再接着通过讲述操作系统的两个基本功能来引申出操作系统的几个抽象概念,并对内核做了一定的阐述
操作系统:当shell加载和运行hello程序以及hello程序输出自己的消息时,shell和hello程序其实并没有直接与处理器、主存、显示屏接触,而是在操作系统的帮助下,才能有与这几位大佬接触的机会。这也就是操作系统的功能了,它一般是处于应用程序与硬件设备(处理器,主存等)之间,使其让应用程序拥有操作硬件设备的机制,还有一个基本功能就是不让硬件被失控的应用程序滥用。而操作系统通过几个抽象概念来实现这两个功能,比如文件(I/O设备的抽象)、虚拟内存(I/O设备和主存的抽象)和进程(I/O设备、主存和处理器的抽象),你要想文件不就是存储在磁盘上的,我们读写文件不相当于就是在操作磁盘吗?也不就是应用程序操作硬件设备的现象的吗。这里我们也发现了总是有中间层这种东西的存在,而有这种多层分级的概念也就是整个系统能良好且高效率的运作吧?比如就像现在的外卖机制,通过这个机制,看似用户和商家有了无地理位置限制的面对面接触,用户(应用程序)点什么,商家(硬件设备)准备什么,然后外卖系统(操作系统)分配骑手进行配送(操作设备)。而不需要用户直接找到商家,或者直接打电话像以前那种让商家店里的员工进行配送的那种形式来进行低效率的运作。而只需要通过外卖系统这个中间层,就能让用户和商家的交互有了更好的体验度。用户负责点餐,外卖系统负责向商家传达指示和配送,商家只管接收指示然后准备饭菜。只因为加了外卖系统这样一个中间层,就使得效率和体验度上升了一个指数级。这大概就是中间层的魅力吧,也可以说是精细分工,分工合作的美丽8
进程:进程就是操作系统对正在运行的程序的一种抽象,它使得造成了一种假象,让我们觉得这个程序独占处理器、内存、I/O设备,以及处理器在不停地一条接一条的在执行程序的指令,程序的代码和数据也是内存中的唯一的对象一样。而其实处理器能并发执行多个程序,也就是一条指令与另一条指令交错地执行,而多核处理器可以在同一时刻执行多个程序,但是无论是单核还是多核,一个CPU看上去都像是在并发的执行多个程序,这个就是通过不停的上下文切换来实现。而上下文就是操作系统保持跟踪进程运行所需的状态信息,信息包括PC和寄存器文件的当前值以及主存的内容。同一时刻单处理器只能运行一个进程的代码,当操作系统将控制权从当前进程交给新进程的时候,就会进行上下文切换,操作系统将保存当前进程的上下文,然后恢复新进程的上下文,接着新进程获取控制权,然后从上一次停止的地方继续运行起来。上下文切换就是操作系统内核管理的。
内核:内核是操作系统代码中常驻主存的一部分,当应用程序需要操作系统进行某些操作时,比如读写文件,然后它就执行一条特殊的系统调用命令,将控制权交给内核,内核再执行对应的操作并返回给程序。但是内核并不是一个独立的进程,它是系统管理全部进程所用代码和数据结构的集合。
线程:线程是组成一个进程的执行单元,使得一个进程能有多个控制流。它使得一个进程处理的事件能并发的处理,它运行在进程的上下文中,它能共享进程同样的代码和全局数据,也就是因为线程之间共享数据比进程要容易些,所以多线程的执行效率比多进程要高,就比如说是多进程是大家只能做自己的,想要交流很困难,而线程就是大家相互交流一起协作做事一样,那肯定就是有交流有协作的团队做事效率高啊!所以有时候要想提高程序的运行速度,可以使用多线程来实现
虚拟内存:同样是操作系统的一个抽象概念,它让每个进程觉得是自己独占了内存。每个进程看到的内存都是一致的,称为虚拟地址空间。虚拟地址空间分为五个区域,从下往上分别是程序代码和数据区(进程一开始运行就被指定了大小)、堆(运行时动态扩展和伸缩)、共享库(存放C标准库和数字库)、栈(编译器用作函数调用的,调用一个函数时扩展,函数返回时收缩)、内核虚拟内存(为内核保留的,里面的内容和内核代码定义的函数必须通过调用内核来执行)。虚拟内存的运作需要硬件与操作系统软件的精密且复杂的交互,还有处理器生成的每个地址的硬件翻译。基本思想就是进程的虚拟内存的内容存储在磁盘上,让主存作为其的高速缓存。
文件:文件就是字节序列,就这么简单,每个I/O设备都可以看成一个文件,比如磁盘、键盘、显示屏、甚至是网络,不是说linux中一切都是文件吗?就是这样的。系统中的输入输出都是通过一小组称为Unix I/O的系统函数调用读写文件来实现的。正是由于文件对I/O设备的抽象,它让应用程序提供了一个统一的视图,让我们能看待所有各式各样的I/O设备。不是有文件这种大家早已习惯的抽象,要是什么让你看到电脑直接在I/O设备上写写画画什么0101的,那多膈应人。。。
然后作者讲述了一下网络通信
系统之间的网络通信:网络也是一种I/O设备,它使得系统与系统之间能够传输数据,当系统从主存复制一串字节到网络适配器时,数据流就会通过网络到达另一台机器。
最后讲了几个会贯穿计算机系统所有方面的重要主题
Amdahl定律:对于系统某一部分的加速,其对整个系统性能的影响与这部分的重要性以及加速程度有关。假设系统目前执行时间为Told,某部分的执行时间与系统执行时间的比例为α,该部分的加速比因子是k,那么系统新的执行时间Tnew = (1-α )Told + (αTold)/k. 通过换算可以得出整个系统加速比S = Told/Tnew = 1 / ( (1-α) + α/k). 根据这个加速比,我们可以判断出一个某部分的初始耗时比例为60%(α=0.6)以及其加速比例因子为3的系统,整个系统获取的加速比才1.67,足以见得要想大大提升系统的性能,要么提高某一部分更高的加速程度,要么就多个部分一起提升速度
并发和并行:因为让处理器能够同时做更多的事情,就可以同时达到使计算机能做更多的事和使计算机运行得够快这两个需求,所以我们是必须要深入研究并发并行的。所谓并发就是指一个同时具有多个活动的系统,而并行则指使用并发来加快一个系统的运行。并行可以在计算机的多个抽象层次上运用。这里有三个值得主要的层次,线程级并发、指令级并行(处理器同时执行多条指令)、单指令多数据并行(一条指令执行多个操作)。不过感觉讲的有点笼统,没有说清楚线程并发和进程并发的优劣,我也没有分清楚线程并发是不是可以实现同时运行多个的并行状态,哦对了,前面对并行以及并发的解释我觉得有点问题的。我觉得并发就是指的是类似并行,但并不是完全是,是通过时间间隔极短的交错执行实现的。而我觉得并行就是真正意义上的同时执行多个。等吧,看第12章是如何讲解的。
抽象的重要性:对复杂的具体的抽象,使得我们不用关心具体的复杂而是只需要专注于简约的抽象。比如JAVA中对具体函数规定的API,就让程序员不需要关心函数的内部才可以使用这些代码。除了进程、虚拟内存、文件这些个抽象概念,还有对处理器硬件抽象的指令集架构以及对操作系统与硬件进行抽象的虚拟机。我感觉抽象这个东西,是让我们从浅到深的了解计算机,让我们先对复杂的实现的抽象进行了解,再对这个复杂实现进行了解,走一个循序渐进的过程。也可以让我们在不了解硬件软件复杂的交互的情况下,通过抽象的东西来理解计算机的结构和运作吧。