《深入理解计算机系统》读书笔记(ch1)

本科时尝试读过此书,未竟。如今工作碰到诸多琐碎的问题,深感对程序与系统在各阶段交互的深入理解之重要-很多耗费时间的问题仅仅是因为知识的不足。重读此书自然是希望尽读此书,增进内功。每日坚持记录一点,计划每周读完一个章节,12周学习完第一遍。

信息表示

计算机中文件的表示可分为两种:第一种是只由ASCII构成的 文本文件; 其他的称作 二进制文件 。编译分为四个阶段:预处理,编译,汇编,链接。预处理和编译都生成的是文本文件(.i 被修改的源程序 和 .s 汇编程序)。汇编阶段生成可重定位的目标程序(.o 二进制文件);链接阶段生成可执行的目标程序(二进制文件)。不同高级语言编译阶段生成的汇编程序的构成都是一样的,汇编器将汇编语言翻译成机器语言。本书中用到两种机器语言:IA32 和 x86-64。

总线

贯穿系统的电子管道在各个部件间传递信息,称作总线。通常总线被设计为传送定长的字节块,也就是(word)。字长是字中的字节数,是一个重要的系统参数。大多数系统的字长是4个字节(32位)或8个字节(64位)。

CPU

CPU的核心是一个字长的寄存器,成为程序计数器(PC)。任何时刻PC都指向内存中的某条机器语言指令,CPU就不断执行PC指向的指令 并 更新PC使其指向下一条指令。CPU是按照一个简单的模型来操作的,这个模型由指令集结构决定。这个模型中,指令顺序执行,而每条指令表示着一系列的步骤。指令集中的指令不多,主要是围绕着内存,寄存器文件和ALU进行。现代CPU中为加速程序执行做了大量的优化,因此需要区分指令的效果和达到效果的实现,前者称为指令集结构(ch3),后者称为微体系结构(ch4)

运行hello程序

在键入 ./hello 命令后,shell将字符通过 I/O总线 和 系统总线 逐一读入寄存器,然后通过 系统总线 和 内存总线 放到内存。敲下回车键时,shell知道输入结束,开始加载可执行的hello文件,其中包括 代码数据(例如 :“hello world\n” 字符串)。加载过程如果使用DMA技术(ch6),可以直接从磁盘加载到内存而无需经过寄存器。目标文件加载到内存后,CPU开始执行main函数的机器语言指令。这些指令将 “hello world\n” 从主存复制到寄存器,然后从寄存器复制到显示设备,最终显示在屏幕上。

高速缓存至关重要

对程序员而言,上述的诸多复制操作就是开销。对于系统设计者而言,主要目标就是使这些复制操作更快完成。一般而言,存贮器越大越便宜也越慢。利用L1,L2,L3的SRAM高速缓存,以及程序执行的局部性原理,大部分存贮器操作都能在高速缓存中完成。第六章将讲述如何利用这些高速缓存。

操作系统管理硬件

操作系统的两个基本功能:1)防止硬件被应用程序滥用 2)向应用提供简单一致的机制来控制硬件设备。这两个功能的实现仰赖于几个基本的抽象概念:进程,虚拟存储器,文件。文件是对I/O设备的抽象表示,虚拟存储器是对主存和磁盘I/O的抽象表示,进程则是对处理器,主存和I/O设备的抽象表示。

Posix标准

Unix系统标准化规范,涵盖了C语言接口,shell和相关工具,线程和网络编程等。因为这些规范,Unix版本间的差异逐渐消失。

进程

进程作为对处理器,主存和I/O设备的抽象表示,向应用程序提供了一种假象,即系统上好像只有此程序在运行。 

并发

CPU看上去像在并发的执行多个进程,这是通过处理器的进程间切换来实现的。操作系统会跟踪进程运行所需的所有状态信息,也就是上下文。向下文信息包括例如PC,寄存器文件的当前值以及主存的内容。进程间切换是通过上下文切换来实现的,也就是保存当前进程的上下文,恢复新进程的上下文,然后把控制权交给新进程。

以hello程序说明上下文切换过程:

  1. shell通过系统调用执行hello程序
  2. 系统调用将控制权转给操作系统
  3. 操作系统保存shell上下文信息
  4. 创建一个新的hello进程及上下文
  5. 将控制权传递给hello进程
  6. hello进程终止后操作系统恢复shell的上下文
  7. 将控制权传回给shell

进程的实现需要操作系统和低级硬件间紧密合作,具体原理将在ch8中解释。

线程

现代系统中,一个进程可以由多个成为线程的执行单元组成,每个线程都运行在进程的上下文中,共享同样的代码和全局变量。多线程程序比多进程程序有诸多优势,例如:更方便共享数据,更高效(尤其在有多处理器的情况下)。ch12将讨论并发的概念以及如何写线程化的程序。

虚拟存储器

虚拟存储器为进程提供这样一个假象:每个进程都在独占的使用主存。每个进程看到的是一致的存贮器,称为虚拟地址空间。在Linux 中,地址空间的最上方为操作系统中的代码和数据保留(对用户进程不可见),地址空间底部存放用户进程定义的代码和数据。

从低地址向上:

  1. 程序代码和数据:对于所有进程,代码从同一虚拟地址开始,紧接着C全局变量对应的数据位置。代码和数据去的内容是直接按照可执行目标文件的内容初始化的。ch7中将介绍链接和加载,以及更多地址空间有关的内容。
  2. 堆。代码和数据区是开始运行时就规定了大小的,而调用malloc和free等函数时,堆在运行时可以动态的扩展和收缩。ch9中将介绍管理虚拟存储器以及堆的更多知识。
  3. 共享库。地址空间的中间部分用来存放向C标准库和数学库等共享库的代码和数据。ch7介绍动态链接时会详细研究共享库是如何工作的。
  4. 栈。用户虚拟地址空间的顶部时用户栈。编译器用它来实现函数调用。每次我们调用一个函数时,栈就会增长;从一个函数返回时,栈就会收缩。ch3中将学习如何使用栈。
  5. 内核虚拟存储器。内核常驻在内存中,是操作系统的一部分。地址空间顶部为内核保留,不允许应用程序读写这个区域的内容或是直接调用内核中定义的代码。

虚拟存储器运作需要硬件和操作系统软件间的精密交互,包括对处理器生成的每个地址的硬件翻译。虚拟存储器的基本思想是把一个进程虚拟存储器的内容存储在磁盘上,然后用主存作为磁盘的高速缓存。

文件

文件就是字节序列,每个I/O设备,包括磁盘、键盘、显示器,甚至网络都可以被视作文件。系统中所有输入输出都是通过UNIX I/O系统函数调用读写文件来实现的。文件为应用程序提供了一个统一的视角来看待I/O设备。

重要主题

并发和并行

并发是一个通用的概念,指的是一个同时具有多个活动的系统。并行指的是用并发使一个系统运行得更快。并行的方式可以分为以下几种

  1. 线程级并行
  2. 指令级并行
  3. 单指令多数据并行SIMD

多核处理器和超线程

多核处理器将多个CPU集成到一个芯片上,每个CPU核都有自己的L1和L2缓存,但是多个核共享更高层次的缓存以及到主存的接口。多处理器的好处:

  1. 减少了CPU模拟并发的需要
  2. 使程序运行更快-这也要求程序以多线程方式书写。ch12将更深入的探讨并发。

超线程,或者称为同时多线程(simultaneous multi-threading),它允许一个CPU执行多个控制流。超线程涉及到CPU某些硬件有多个备份,例如PC和寄存器文件,而其他硬件,例如ALU只有一份。常规CPU的线程间转换需要约20000个时钟周期,而超线程处理器可以在一个时钟周期中决定使用哪一个线程。这让CPU能更高效的利用自己的处理资源,如果某个线程必须等到数据到cache,CPU可以执行另一个线程。

指令级并行

每条指令其实从开始到结束要约20个时钟周期。现代处理器利用很多聪明技巧来同时执行多条指令。

pipeline和超标量

ch4中将研究pipeline的使用,pipeline将一条指令的执行划分为不同步骤,并将硬件组织成一系列阶段。这些阶段可以并行的操作,从而达到大概一个时钟周期一条指令。

超标量(superscalar)是指处理器达到比一个时钟周期一条指令更快的执行速率。ch5中将讲述超标量处理器的高级模型。

单指令多数据并行

许多现代处理器具有特殊的硬件,来允许一条指令产生多个可以并行执行的操作,即单指令多数据(SIMD)。例如,较新的Intel和AMD能并行的对四对单精度浮点数做加法。SIMD是为了加快处理多媒体数据的,为了使用这种特性需要借助编译器支持的特殊向量(虽然有些编译器会尝试从C程序中自动抽取SIMD并行)。

  

猜你喜欢

转载自www.cnblogs.com/42-Curry/p/9757930.html