《深入理解计算机系统》—计算机系统漫游

1.1 信息就是位 + 上下文

比如一个简单的“hello world”程序的生命周期就是从一个源程序(源文件)开始的。

源程序:由0和1组成的比特位序列。

注:8个比特位形成1个字节,每个字节表示程序中的某些文本字符。

目前,大部分计算机系统都是使用ASCII码来表示文本字符的。比如一个简单的“hello world”程序如下图。

因此可以得出:hello world.c程序就是以字节序列的形式存储的。一个字节对应一个整数值,一个整数值又对应一个文本字符。

文本文件:由文本字符构成(宏观表现就是代码)。除了文本文件的都是二进制文件

由上述所讨论的内容,可以得到一个思想:系统中所有的信息——包括磁盘文件、内存中的程序、内存中存放的用户数据以及网络上传送的数据,都是由一串比特位表示的。可以通过观察上下文以及这个数据发挥的作用来解读这串比特位。

1.2 程序被其他程序翻译成不同的格式

编译器驱动程序将C语句——>低级机器语言指令——>可执行目标程序(目标文件)(二进制磁盘文件的形式存放起来)

编译系统:预处理器—>编译器—>汇编器—>链接器

预处理:预处理器(cpp)根据以字符 # 开头的命令,修改原始的C程序。比如:hello.c中第1行的 #include <stdio.h> 命令告诉预处理器读取系统头文件 stdio.h 的内容,并把它直接插入程序文本中。结果就得到了另一个C程序,通常是以 .i 作为文件扩展名。

编译:编译器(ccl)将文本文件 hello.i 翻译成文本文件 hello.s ,它包含一个汇编语言程序。该程序包含函数main的定义。

汇编:汇编器(as)将 hello.s 翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序 (relocatable object program)的格式,并将结果保存在目标文件 hello.o 中。hello.o文件是一个二进制文件,它包含的17个字节是函数main的指令编码。如果我们在文本编辑器中打开 hello.o文件,将看到一堆乱码。

链接:hello 程序调用了 printf 函数,它是每个C编译器都提供的标准C库中的一个函数。printf 函数存在于一个名为 printf .o 的单独的预编译好了的目标文件中,而这个文件必须以某种方式合并到我们的 hello.o 程序中。链接器(ld)就负责处理这种合并。结果就得到 hello 文件,它是一个可执行目标文件(可执行文件),可以被加载到内存中,由系统执行。

1.3 了解编译系统如何工作是大有益处的

优化程序性能、理解链接时出现的错误、避免安全漏洞。

1.4 处理器读并解释存储在内存中的指令

(未知)

1.4.1 系统的硬件组成

CPU:中央处理单元

ALU:算术/逻辑单元

PC:程序计数器

USB:通用串行主线

寄存器文件:一个小的存储设备,由一些单个字长的寄存器组成,每个寄存器都有唯一的名字

1. 总线:贯穿整个系统的一组电子管道。它携带信息字节并负责在各个部件间传递。通常总线被设计成传送定长的字节块,也就是字(word)。字中的字节数(字长)是一个基本的系统参数,各个系统不尽相同。

注:现在大多数的机器字长是4个字节(32位),8个字节(64位)。

2. I/O 设备:I/O(输入/输出)设备是系统与外部世界的联系通道。

上图中包含4个 I/O 设备:作为用户输入的键盘和鼠标。作为用户输出的显示器。用于长期存储数据和程序的磁盘驱动器(磁盘)。最开始,可执行程序 hello 就存放在磁盘上。

每个 I/O 设备都通过一个控制器或适配器与 I/O 总线相连。控制器和适配器之间的区别主要在于它们的封装方式控制器 I/O 设备本身或者系统的主印制电路板(主板)上的芯片组。适配器则是一块插在主板插槽上的卡。无论如何,它们的功能都是在 I/O 总线和 I/O 设备之间传递信息。

3. 主存:一个临时存储设备,在处理器执行程序时,用来存放程序和程序处理的数据。从物理上来说,主存是由一组动态随机存取存储器(DRAM)芯片组成的。从逻辑上来说,存储器是一个线性字节数组,每个字节都有其唯一的地址(数组索引),这些地址是从0开始的。一般来说,组成程序的每条机器指令都由不同的数量字节构成。与C程序变量相对应的数据项的大小是根据类型变化的。比如,在运行 Linux 的 X86 - 64 机器上,short 类型的数据需要2个字节,int 和 float 需要4个字节,而 long 和 double 需要8个字节。

4.处理器:中央处理单元(CPU),是解释(或执行)存储在主存中指令的引擎。处理器的核心是一个大小为一个字的存储设备(寄存器),即为程序计数器(PC)。在任何时刻,PC都指向主存中的某条机器语言指令(即含有该条指令的地址)。

处理器使用指令集架构按照严格的顺序执行一系列操作。操作:处理器从PC中读取指令,进行解释操作,更新PC指向,而这个新指向不一定和原指向相邻。

这些简单操作主要围绕:PC、寄存器文件、ALU进行。ALU会计算新的数据和地址值。

举例:

加载:从主存复制一个字节或一个字到寄存器中,以覆盖寄存器原来的内容。

存储:从寄存器复制一个字节或一个字到主存的某个位置,以覆盖这个位置上原来的内容。

操作:把两个寄存器上的内容复制到ALU,ALU对这两个字做算术运算,

跳转:从指令本身中抽取一个字,并将这个字复制到PC中,以覆盖PC中原有的值。

注:

指令集架构:每条机器代码指令的效果。

微体系结构:处理器实际上是如何实现的。

 1.4.2 运行 hello 程序

首先 shell 执行它的指令,然后我们输入一个命令。当输入“./hello”后,shell 程序将字符逐一读入到寄存器,再把它放到内存中。

当敲击回车时,shell 程序就明白了我们结束了命令的输入。然后shell执行一系列指令来加载可执行 hello 文件,这些指令将 hello 目标文件中的代码和数据从磁盘复制到主存。

hello 程序中的代码和数据被加载到主存,处理器就开始执行 hello 程序中的 main 程序的机器语言指令。这些指令将字符串“hello world\n”中的字节从主存复制到寄存器文件,再复制到显示设备,最后呈现在屏幕上。(具体过程如上图)

1.5 高速缓存至关重要

处理器与主存之间各有优势,处理器处理的速度快但是容量小,主存的容量大单是处理速度慢。针对这种差异,设计了高速缓存存储器(cache memory,简称 cache 或者 高速缓存),作为暂时的集结区域,存放处理器近期可能会需要的信息。

上图为一个高速缓存存储器。位于处理器芯片上的L1高速缓存的容量可达数万字节,访问的速度几乎和访问寄存器文件一样快。这是用静态随机访问存储器(SRAM)的硬件技术实现的。利用高速缓存的局部性原理,使得程序具有访问局部区域里的数据和代码的趋势,让访问速度更快。

1.6 存储设备形成层次结构

每个计算机系统中的存储设备都被组织成了一个存储器层次结构。在这个层次结构中,从上到下,设备的访问速度越来越慢、容量越来越大,每个字节的造价也越来越便宜。

思想:上一层是下一层的高速缓存。

在某些具有分布式文件系统的网络系统中,本地磁盘就是存储在其他系统中磁盘上的数据的高速缓存。

1.7 操作系统管理硬件

当 shell 加载和运行 hello 程序时,以及 hello 程序输出自己的消息时,shell 和 hello 程序都没有直接访问键盘、显示器、磁盘或主存。取而代之的是,它们依靠操作系统提供的服务。我们可以把操作系统看成是应用程序和硬件之间插入的一层软件。所有应用程序对硬件的操作都必须通过操作系统。

操作系统的功能:防止硬件被失控的应用程序滥用、向应用程序提供简单一致的机制来控制复杂而又通常大而不同低级硬件设备。

操作系统通过几个基本的抽象概念(进程、虚拟内存和文件)来实现这两个功能。

文件:对 I/O 设备的抽象表示。

虚拟内存:对主盘和磁盘 I/O 设备的抽象表示。

进程:对处理器、主存和 I/O 设备的抽象表示

1.7.1 进程

进程是操作系统对一个正在运行的程序的一种抽象。

并发运行:一个进程的指令和另一个进程的指令是交错执行的。

传统系统在一个时刻只能执行一个程序,而先进的多核处理器同时能够执行多个程序。无论是在单核还是多核系统中,一个CPU看上去都像是在并发地执行多个进程,这是通过处理器在进程间切换来实现的。操作系统实现这种交错执行的机制称为上下文切换。

操作系统保持跟踪进程运行所需的所有状态信息:有很多信息,如PC和寄存器文件的当前值,以及主存的内容。

上下文切换:通俗来说就是进程切换

从一个进程到另一个进程的转换是由操作系统内核(kernel)管理的。内核是操作系统代码常驻主存的部分。当应用程序需要操作系统的某些操作时,比如读写文件,它就执行一条特殊的系统调用(system call)指令,将控制权传递给内核。然后内核执行被请求的操作并返回应用程序。内核不是一个独立的进程,而是系统管理全部进程所用代码和数据结构的集合。

1.7.2 线程

在现代系统中,一个进程实际上可以由多个称为线程的执行单元组成,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。线程是重要的编程模型,多线程之间比多进程之间更容易共享数据,也更加高效。

1.7.3 虚拟内存

虚拟内存是一个抽象概念,它为每个进程提供了一个假象,即每个进程都在独占地使用主存。每个进程看到的内存都是一致的,称为虚拟地址空间

下图为:Linux 进程的虚拟地址空间

程序代码和数据:对所有进程来说,代码是从同一固定地址开始,紧接着的是和C全局变量相对应的数据位置。

堆:代码和数据区后紧随着的是运行时堆。代码和数据区的大小在程序运行时就已经被指定了,但是当调用像 malloc 和 free 这样的 C 标准库函数时,堆可以在运行时动态地扩展和收缩。

共享库:大约在地址空间的中间部分是一块用来存放像 C 标准库和数学库这样的共享库的代码和数据的区域。

栈:位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数调用。和堆一样,用户栈在程序执行期间可以动态地扩展和收缩。特别地,每次我们调用一个函数时,栈就会增长;从一个函数返回时,栈就会收缩。

内核虚拟内存:地址空间顶部的区域是为内核保留的。不允许应用程序读写这个区域的内容,或者直接调用内核代码定义的函数。但是,它们必须调用内核来执行这些操作。

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

1.7.4 文件

文件就是字节序列。每个 I/O 设备,包括磁盘、键盘、显示器,甚至网络,都可以看成是文件。系统中的所有输入输出都是通过使用一小组称为 Unix I/O 的系统函数调用读写文件来实现的。

1.8 系统之间利用网络通信

网络也是一种 I/O 设备。

当系统从主存复制一串字节到网络适配器时,数据流经过网络到达另一台机器,而不是比如说到达本地磁盘驱动器。相似地,系统可以读取从其他机器发送来的数据,并把数据复制到自己的主存。

当我们在 telent 客户端键入 “hello” 字符串并敲下回车键后,客户端软件就会将这个字符串发送到 telnet 的服务器。telnet 服务器从网络上接收到这个字符串后,会把它传递给远端 shell 程序。接下来,远端 shell 运行 hello 程序,并将输出行返回给 telnet 服务器。最后,telnet 服务器通过网络把输出串转发给 telnet 客户端,客户端就将输出串输出到我们的本地终端上。

注:本文主要是博主在阅读《深入理解计算机系统》一书中觉得重要的内容,对于书中比较好的描述进行了保留,同时对一些其他概念进行了总结,如果又错误还望评论区指正,相互学习,共同进步。

  • 32
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
中文名: 深入理解计算机系统(第2版) 原名: Computer Systems: A Programmer's Perspective ,2nd Edition 作者: Randal E. Bryant David R. O'Hallaron 资源格式: PDF 版本: 英文文字版 出版社: Addison Wesley书号: 0136108040发行时间: 2010年02月14日 地区: 美国 语言: 英文 简介: 内容介绍:   本书从程序员的视角详细阐述计算机系统的本质概念,并展示这些概念如何实实在在地影响应用程序的正确性、性能和实用性。全书共12章,主要内容包括信息的表示和处理、程序的机器级表示、处理器体系结构、优化程序性能、存储器层次结构、链接、异常控制流、虚拟存储器、系统级i/o、网络编程、并发编程等。书中提供子大量的例子和练习题,并给出部分答案,有助于读者加深对正文所述概念和知识的理解。   本书适合作为高等院校计算机及相关专业本科生、研究生的教材,也可供想要写出更快、更可靠程序的程序员及专业技术人员参考。 作者介绍:   Randal E. Bryant 1973年于密歇根大学(University of Michigan)获得学士学位,随即就读于麻省理工学院(Massachusetts Institute of Technology)的研究生院,并在1981年获计算机博士学位。他在加州理工学院(California Institute of Technology)做了三年助教,从1984年 内容截图: 目录: 第1章 计算机系统漫游 第一部分 程序结构和执行 第2章 信息的表示和处理 第3章 程序的机器级表示 第4章 处理器体系结构 第5章 优化程序性能 第6章 存储器层次结构 第二部分 在系统上运行程序 第7章 链接 第8章 异常控制流 第9章 虚拟存储器 第三部分 程序间的交互和通信 第10章 系统级i/o 第11章 网络编程 第12章 并发编程 附录a 错误处理 参考文献
深入理解计算机系统》的目录如下: 第一部分 计算机系统漫游 第 1 章 计算机系统漫游 1.1 信息就是位+上下文 1.2 程序被其他程序翻译成不同的格式 1.3 了解编译系统如何工作是大有益处的 1.4 处理器读并解释存储在内存中的指令 1.5 系统中的 I/O 操作会以编程方式访问硬件 1.6 系统之间利用网络进行通信 第二部分 程序结构与执行 第 2 章 信息的表示和处理 2.1 信息存储 2.2 整数表示 2.3 整数运算 2.4 浮点数 2.5 代码优化 第 3 章 程序的机器级表示 3.1 程序编码 3.2 访问信息 3.3 操作系统管理信息的方式 3.4 异常控制流 3.5 虚拟内存 3.6 内存管理 第三部分 系统级 I/O 第 4 章 处理器体系结构 4.1 处理器组成 4.2 指令执行和处理器性能 4.3 流水线处理器 4.4 处理器调度 4.5 多处理器系统 第 5 章 存储器层次结构 5.1 存储器技术 5.2 存储器层次结构 5.3 缓存性能优化 5.4 虚拟存储器 第 6 章 系统级 I/O 6.1 通信原理 6.2 I/O 设备 6.3 客户端-服务器模型 6.4 网络编程 6.5 并发编程 第四部分 网络编程 第 7 章 链接 7.1 静态链接 7.2 目标文件 7.3 动态链接 7.4 重定位 第 8 章 异常控制流 8.1 异常 8.2 进程 8.3 信号 8.4 异常控制流在 UNIX 系统中的实现 第 9 章 虚拟内存 9.1 地址空间 9.2 页表 9.3 页面置换算法 9.4 高级虚拟内存技术 第五部分 存储器层次结构 第 10 章 系统级 I/O 的例子:以 Linux 为例 10.1 Linux I/O 简介 10.2 操作系统支持的 I/O 设备 10.3 I/O 设备编程 10.4 总线、I/O 设备和驱动程序之间的交互 10.5 系统级 I/O 软件的架构 第 11 章 系统级 I/O 的例子:以 Windows 为例 11.1 Windows I/O 简介 11.2 操作系统支持的 I/O 设备 11.3 I/O 设备编程 11.4 总线、I/O 设备和驱动程序之间的交互 11.5 系统级 I/O 软件的架构 附录A ASCII 码表 附录B 编译器和库函数 附录C x86 指令集简介 附录D 常用的 C 库函数 附录E 无符号加法的实现 附录F 数组乘法的实现 附录G 格式化输出的实现 附录H Makefile 的基础知识和技巧 附录I 延伸阅读 附录J 题解

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值