《深入理解计算机系统》(原书第二版)粗读笔记

《深入理解计算机系统》

  • 深入理解计算机系统
  • 日记
    • 2016年10月27日095534
    • 2016年10月28日090501
    • 2016年10月31日094406
    • 2016年11月01日070636
    • 2016年11月02日071720
    • 2016年11月03日074037
    • 2016年11月04日071244
    • 2016年11月06日095639
    • 2016年11月07日072930
    • 2016年11月08日073209
    • 2016年11月09日073201
    • 2016年11月10日074305
    • 2016年11月11日083820
    • 2016年11月12日082103
    • 2016年11月14日075750
    • 第一遍粗读完毕2016年11月14日095535
  • 检视阅读三问
    • 这是一本什么样的书
    • 整本书在谈的是什么
    • 作者是借着怎样的整体架构来发展他的观点或陈述他对这个主题的理解
  • 读书四问
    • 整体来说这本书到底在谈些什么
    • 作者都细说了什么怎么说的
    • 这本书跟你有什么关系
    • 难度系数
  • 第一部分 程序结构和执行
    • 第二章 信息的表示和处理
      • 概念
      • 1 信息存储
        • 11 十六进制
        • 12 字
        • 13 数据大小
        • 14 寻址和字节顺序
        • 15 表示字符串
        • 16 表示代码
        • 17 布尔代数简介
        • 18 C语言中的位级运算
        • 19 C语言中的逻辑运算
        • 110 C语言中的移位运算
      • 2 整数表示
        • 21 整形数据类型
        • 22 无符号数的编码
        • 23 补码编码
        • 24 有符号数和无符号数之间的转换
        • 25 C语言中的有符号数和无符号数
        • 26 扩展一个数字位的表示
        • 27 截断数字
        • 28 关于有符号数无符号数的建议
      • 3 整数运算
        • 31 无符号加法
        • 32 补码加法
        • 33 补码的非
        • 34 无符号乘法
        • 35 补码乘法
        • 36 乘以常数
        • 37 除以 2 的幂
        • 38 关于整数运算的最后思考
      • 4 浮点数
        • 41 二进制小数
        • 42 IEEE 浮点表示
        • 43 数字示例
        • 44 舍入
        • 45 浮点运算
        • 46 C语言中的浮点数
      • 5 小结
    • 第3章 程序的机器级表示
      • 1 历史观点
      • 2 程序编码
        • 21 机器级代码
        • 22 代码示例
        • 23 关于格式的注解
      • 3 数据格式
      • 4 访问信息
        • 41 操作数指示符
        • 42 数据传送指令
        • 43 数据传送示例
      • 5 算术和逻辑操作
        • 51 加载有效地址
        • 52 一元操作和二元操作
        • 53 移位操作
        • 54 讨论
        • 55 特殊的算术操作
      • 6 控制
        • 61 条件码
        • 62 访问条件码
        • 63 跳转指令及其编码
        • 64 翻译条件分支
        • 65 循环
        • 66 条件传送指令
        • 67 switch 语句
      • 7 过程
        • 71 栈桢结构
        • 72 转移控制
        • 73 寄存器使用惯例
        • 74 过程示例
        • 75 递归过程
      • 8 数组分配和访问
        • 81 基本原则
        • 82 指针运算
        • 83 嵌套的数组
        • 84 定长数组
        • 85 变长数组
      • 9 异质的数据结构
        • 91 结构
        • 92 联合
        • 93 数据对齐
      • 10 综合理解指针
      • 11 应用使用GDB调试器
      • 12 存储器的越界引用和缓冲区溢出
      • 13 X86-64将 IA32 扩展到64位
        • 131 x86-64 的历史和动因
        • 132 x86-64 简介
          • 数据类型
          • 汇编代码示例
        • 133 访问信息
          • 算术指令
        • 134 控制
          • 过程
          • 参数传递
          • 栈桢
          • 寄存器保存惯例
        • 135 数据结构
        • 136 关于 x86-64 的总结性评论
      • 14 浮点程序的机器级表示
      • 15 小结
    • 第4章 处理器体系结构
      • 1 Y86指令集体系结构
        • 11 程序员可见状态
        • 12 Y86指令
        • 13 指令编码
        • 14 Y86 的异常处理
        • 15 Y86 程序
        • 16 一些 Y86 指令的详情
      • 2 逻辑设计和硬件控制语言 HCL
        • 21 逻辑门
        • 22 组合电路和 HCL 布尔表达式
        • 23 字级的组合电路和HCL整数表达式
        • 24 集合关系
        • 25 存储器和时钟
      • 3 Y86的顺序实现
        • 31 将处理组织成阶段
        • 32 SEQ 硬件结构
        • 33 SEQ 的时序
      • 4 流水线的通用原理
        • 41 计算流水线
      • 5 Y86 流水线实现
      • 6 小结
    • 第5章 优化程序性能
      • 1 优化编译器的能力和局限
      • 2 表示程序性能
      • 3 程序示例
      • 4 消除循环的抵效率
      • 5 减少过程调用
      • 6 消除不必要的存储器引用
      • 7 理解现代处理器
      • 8 循环展开
      • 9 提高并行性
      • 10 优化合并代码的结果小结
      • 11 一些限制因素
      • 12 理解存储器性能
        • 121 加载的性能
        • 122 存储的性能
      • 13 性能提高技术
      • 14 确认和消除性能瓶颈
      • 15 小结
    • 第6章 存储器层次结构
      • 1 存储技术
        • 11 随机访问存储器
        • 12 磁盘存储
        • 13 固态硬盘
        • 14 存储技术趋势
      • 2 局部性
      • 3 存储器层次结构
        • 31 存储器层次结构中的缓存
        • 32 存储器层次结构概念小结
      • 4 高速缓存存储器
      • 5 编写高速缓存友好的代码
      • 6 综合高速缓存对程序性能的影响
        • 61 存储器山
        • 62 重新排列循环以提高空间局部性
        • 63 在程序中利用局部性
      • 7 小结
  • 第二部分 在系统上运行程序
    • 第7章 链接
      • 1 编译器驱动程序
      • 2 静态连接
      • 3 目标文件
      • 4 可重定位目标文件
      • 5 符号和符号表
      • 6 符号解析
        • 61 链接器如何解析多重定义的全局符号
        • 62 与静态库链接
        • 63 链接器如何使用静态库来解析引用
      • 7 重定位
      • 8 可执行目标文件
      • 9 加载可执行目标文件
      • 10 动态链接共享库
      • 11 从应用程序中加载和链接共享库
      • 12 与位置无关的代码PIC
      • 13 处理目标文件的工具
      • 14 小结
    • 第8章 异常控制流
      • 1 异常
      • 2 进程
      • 3 系统调用错误处理
      • 4 进程控制
      • 5 信号
      • 6 非本地跳转
      • 7 操作进程的工具
      • 8 小结
    • 第9章 虚拟存储器
  • 第三部分 程序间的交互和通信
    • 第10章 系统级 IO
      • 名词解释
    • 第11章 网络编程
      • 1 客户端-服务器编程模型
      • 2 网络
      • 3 全球 IP 因特网
      • 4 套接字接口
      • 5 Web服务器
      • 7 小结
    • 第 12 章 并发编程
      • 8 小结

日记

2016年10月27日09:55:34

前两天读了《如何阅读一本书》,感觉在看这本书《深入理解计算机系统》的时候,需要先做一个检视阅读,即:15分钟的有系统的略读,3~5天的粗读。

2016年10月28日09:05:01

系统略读本书。
明后天粗读。

2016年10月31日09:44:06

周六 29号 团建,半宿都在玩,休息了4个小时左右
周日 30号 上午打游戏,下午扛不住了,睡了2小时左右,晚上22点睡。
今早7点半才醒。
这一天天过的。

从10.9日开始,计划学这本书。到现在为止整整20天过去了,第一部分都还没看完,看来自己的毛病还挺多的。各种坚持不下去。
最近借口还挺充足的,早上很冷,没空调和暖气,不愿起来。周六团建,状态不佳,最近事多压力大。等等。

今天晚上开始,21:30躺下,准备睡觉。早6点起,步行或骑车到公司。路上取711看看有没有早餐,没有就买点零食。7~10点三个小时的时间,来看书。

可能前一两天都没精神,毕竟突然早起,但此事,功在当代,利在千秋,所以要坚持。能不能完成这个计划,能不能让自己找回正能量,能否积极向上的良性循环,就看今晚能不能睡着,明早能不能起来了。

起来,前途就有一点点亮光,起不来,以后就得卷铺盖卷回去了。

[end]2016年10月31日09:56:47

2016年11月01日07:06:36

早上6点的闹钟,被闹钟叫起来了,脑子里完全没有昨天发过的誓言,躺了二十分钟,还是起来了。冷的要死,冻成狗。
洗刷后来到公司,6点59,哈哈。明天继续。
今天暂时不能做粗读,原因是下午有一个分享,屁屁踢写的不是很ok,在优化一下,以确保每月一次的组内分享有点干货,尽量让大家节省出来的时间有些收获。
[end]2016年11月01日07:10:13

2016年11月02日07:17:20

今天稍微晚了点。一般来说,坚持早起的前三天最难,但鉴于自己之前有过这种经历,所以只要有一天早起了,剩下的就简单多了。
昨天的分享比较成功,虽然时间短,但达到想要的效果了。
近期没啥需要花费时间的事情了,以后的早上时间,就可以用来读书了。很开心。再做一个规定,晚上下班回去的时间,不开电脑。除非有线上问题需要处理,否则不开电脑。因为开电脑不是必要的事情,完全可以用其他事情替代。

深入理解计算机系统,粗读第一天。

2016年11月03日07:40:37

懒床了,就一小会,然后就完了这么久。。。好吧
继续搞。
得稍微提升下速度,不然得看到啥时候才能搞定呀。
粗读,针对每个章节以及章节下的小标题,提出问题,知道这节在说什么即可。分析阅读时再好好看细节。粗读的目的在于掌握整本书的架构。

实践:每个章节只读一遍,如果没能理解在说什么,则标识【不懂】,然后继续阅读下一章节。如果能理解,则写出概要。

2016年11月04日07:12:44

6点的闹钟,6点30多才穿衣服。。。 太冷了。。。 6点就起还真是个挑战。
继续搞,得稍微加快点进度了。看看一早上能不能搞一章。

2016年11月06日09:56:39

昨天加班,昨晚很晚才睡,早上起不来。。。
继续

2016年11月07日07:29:30

昨晚对自己最近的行为进行反思,为什么指定的计划老是失败,这本书从十月就开始计划学习,三个月内搞定。到现在已经40天了,才看完第三章,还是粗读的(只知道章节在说啥,不知道细节)。
回头又看了下关于自控力的一些东西,其中知乎有一篇帖子写的很好,点这里,最高票回答。里面提到了哈弗公开课的一个视频。找到视频看了看,搞懂一个事情。


每个人的自控力跟时间一样是有限的。每天就这么多。每次强迫自己都会消耗一些。想要真正做一件事情,最好的方法是养成习惯。把它变成例行公事,而不是每次强迫自己去做。


搞清楚这一点之后,剩下的就稍微简单点了。养成习惯的平均周期大概是30天左右。
本月要养成的习惯是:早起+读书。
早起:06:30起
读书:早上2小时,晚上半小时。
每天打卡,月底总结。在朋友圈发出,只要在发出去了,你自己就会不断的鞭策自己,很简单。

继续搞。


2016年11月08日07:32:09

打卡。。。
继续搞。

2016年11月09日07:32:01

打卡
继续

2016年11月10日07:43:05

昨晚11点睡的。。。
早上差点没起来。。。 贵在坚持
继续

2016年11月11日08:38:20

早上不到6点就醒了,睡不着。。。 难道是每天早起习惯了?
不过没看书,双十一,买了些东西。 所以来公司就晚了。
继续搞,搞完。感觉自己不会读书,或者说知识体系为零,所以读一本技术书籍难得要死。坚持多读,总有一天会建立起知识体系的。

2016年11月12日08:21:03

早上6点20,闹钟吵醒的。昨晚跟同事一起去看海贼王剧场版,回家后又下了个游戏《王者荣耀》类lol 的手游,稍微一玩,就快0点了。。。早上起床后,洗刷完毕,依然有点困,坐在床边,打开《王者荣耀》没敢玩,只是看了下英雄、技能、装备等东西,回过神来,又半个小时过去了。
在脑袋不清醒的时候很容易被诱惑,保持清醒很重要。
再就是,不要把诱惑摆在自己面前,脑子清醒的时候可以抵抗,但总有累的时候吧。所以,手游删掉了。哈哈

再就是读书,缺乏自己的知识体系,从头啃很难,先啃完这本,之后再找路子。依然坚持:“读书百遍,其义自现”。

赶紧搞完,今天还得加班搞需求。
再就是今天是走路上班的,没骑车。

2016年11月14日07:57:50

周天也是早起的,但没看书。。。
今天也早起了,走过来的,没骑车,所以有点晚了。
继续搞把,搞完之后换一本。这本书讲述的内容有点不太适合自己,原计划是了解linux 系统的,而这本书讲述的东西感觉有点偏硬件了。

第一遍粗读,完毕!(2016年11月14日09:55:35)

太难读了,基础差太多。硬着头皮啃下来的。收获不多,只知道了一些模糊的概念。
更新了下下面的检视阅读三问。 这本书暂且封存,换一本来读。如果想了解原理的时候再过来翻这本书。
[end] 2016年11月14日10:04:36

检视阅读三问:

这是一本什么样的书?

分类:计算机科学类
目的:以程序员的视角详细阐述计算机系统的本质概念,并展示这些概念如何影响程序的性能、正确性和实用性。

整本书在谈的是什么?

阐述计算机系统的核心概念,包括:
- 第2章:信息的表示和处理
- 第3章:程序的机器级表示
- 第4章:处理器体系结构
- 第5章:优化程序性能
- 第6章:存储器层次结构
- 第7章:链接
- 第8章:异常控制流
- 第9章:虚拟存储器
- 第10章:系统级I/O
- 第11章:网络编程
- 第12章:并发编程

作者是借着怎样的整体架构,来发展他的观点或陈述他对这个主题的理解?

主要分了三部分:程序结构和执行、在系统上运行程序、程序间的交互和通信

读书四问:

整体来说,这本书到底在谈些什么?

介绍计算机的整体结构,例如,如何表示信息,如何运行程序,CPU 的结构,存储器的结构,异常如何处理,如何实现网络、并发编程等等。以C语言描述的。

作者都细说了什么,怎么说的?

  • 作者的主要声明与论点

这本书跟你有什么关系?

大神进阶必读,但目前阶段不太适合自己,现有阶段的目标是理解 linux 系统,能排查线上问题,偏应用。这本书偏向介绍C语言与操作系统之间的一些实现,偏原理

难度系数

超高


第一部分 程序结构和执行

第二章 信息的表示和处理

概念

无符号、补码、浮点数、溢出

2.1 信息存储

虚拟存储器、地址、虚拟地址空间、

2.1.1 十六进制

2.1.2 字

决定虚拟地址空间的大小,32位字长,可用内存是4GB。

2.1.3 数据大小

C里面的 各种类型大小

2.1.4 寻址和字节顺序

跨多字节的程序对象,两个规则:对象的地址,在内存中如何排列。
关于排列:介绍了大端法、小端法。

2.1.5 表示字符串

C中,字符串是一个以 null 为字符结尾的字符数组。

2.1.6 表示代码

程序是字节序列,每个机器的编码不同。展示了windows、linux32、linux64之间的差异

2.1.7 布尔代数简介

计算机编码、存储和操作都是用的二进制。
布尔代数可以对0和1进行逻辑运算。
衍生出显示器 多色光源来控制显示。

2.1.8 C语言中的位级运算

或、与、取反、异或

2.1.9 C语言中的逻辑运算

|| 、&& 和 !

2.1.10 C语言中的移位运算

左右移位

2.2 整数表示

整数的两种编码方式:
- 只能表示非负数(无符号的)
- 能表示负数、零和整数

2.2.1 整形数据类型

介绍了C中 各种数据类型,根据两种不同的编码,所能表示的最大最小范围。
如:

C数据雷系 最小值 最大值
char -128 128
unsigned char 0 255
int -21 4748 3648 21 4748 3648
unsigned int 0 42 9496 7295

C和C++都支持有符号(默认)和无符号数,java只支持有符号数。

2.2.2 无符号数的编码

没符号而已,没啥好说的

2.2.3 补码编码

表示负数的一种方式,最高位 1 时为负,最高位 0 时为证。
如:

+7 0111
+6 0110
+5 0101
+4 0100
+3 0011
+2 0010
+1 0001
+0 0000
-0 1000
-1 1001
-2 1010
-3 1011
-4 1100
-5 1101
-6 1110
-7 1111
-8 超出4个bit所能表达范围 超出4个表达范围 1000

0000 = 0
1111 = -1 * 23 + 22 + 21 + 20 = -1
1111 = 0000 - 0001 = -1

有符号数还有其他两种标准方式表示:
- 反码
- 原码

2.2.4 有符号数和无符号数之间的转换

介绍了下C语言在各种数据类型之间如何做强制类型转换的。原则是:底层的位保持不变

2.2.5 C语言中的有符号数和无符号数

C语言,几乎所有的机器都采用补码。大多数数字都默认为无符号的。
C允许无符号和有符号数之间进行转换,原则是底层的位保持不变
[end 2016年11月02日10:04:31]

2.2.6 扩展一个数字位的表示

较小的数据类型扩展到较大数据类型时怎么处理。
如:short 类型 改成 int 类型的。
无符号数,直接在前面加零即可,叫零扩展
有符号数,比较麻烦。

2.2.7 截断数字

int 强转 short 时如何处理的,直接丢弃高位。

2.2.8 关于有符号数无符号数的建议

有符号数到无符号数的隐式强制类型转换会导致某些非直观行为,这些行为有时会让程序出错。并且程序员经常忽略。
除了C以外很少语言支持无符号整数。
无符号的用途在于:
- 把字仅仅看成位的集合,并且没有数字意义时。
- 某些其他情况

2.3 整数运算

解释计算机如何做运算的

2.3.1 无符号加法

会溢出

2.3.2 补码加法

也会溢出,如-8 + -5 = -13

2.3.3 补码的非

不懂

2.3.4 无符号乘法

不懂

2.3.5 补码乘法

不懂

2.3.6 乘以常数

计算机做乘法较慢,编译器做了一个重要的优化:
把乘法计算 –> 改成移位运算 和 加法运算。
如:
x14=x(23+22+21)=(x<<3)+(x<<2)+(x<<1)

2.3.7 除以 2 的幂

大多数机器上,除法比乘法更慢,但也可以用位移来实现。只不过用的是向右位移。

2.3.8 关于整数运算的最后思考

计算机执行整数运算,实际上是一种模运算形式。
表示数字的字长限制了取值范围,计算可能溢出。
补码表示提供了一种既能表示负数也能表示正数的灵活方法。
C语言中的某些规定会产生意想不到的结果,如:unsigned数据类型,虽然概念简单,却会导致即使是资深程序员都想不到的行为。

2.4 浮点数

对有理数编码,无论是非常大的数字还是接近零的数字。

2.4.1 二进制小数

十进制小数如何用二进制小数表示。
如:十进制 5.75 的二进制表示
5.7510=534=101.112
但,因为位有限,所以精度有限,如不能表示 13

2.4.2 IEEE 浮点表示

上一节的定点表示法不能有效的表示大数字,
如: 5×2100 需要用 101 后面跟100个零才能表示。
IEEE 采用类科学计数法的形式来表示。公式如下:

V=(1)s×M×2E

其中 s 表示 是负数还是正数
M 是一个二进制小数,有范围。
E 是对浮点数加权。权重是2的E次幂。

2.4.3 数字示例

展示了 用上一节 的公式能表示啥样的数字,多大的数字,都一一举例了。

2.4.4. 舍入

因为浮点数的表示方式限制了精度和范围,所以浮点运算只能近似的表示实数运算,由此产生了 舍入 这一运算的任务。
IEEE 规定了4中舍入方式:
- 向偶数舍入
- 向零舍入
- 向下舍入
- 向上舍入

2.4.5 浮点运算

IEEE 指定了一个简单的规则,来确定加法乘法运算的结果:
按照实数运算,对结果进行舍入。
实际中,浮点单元的设计者使用一些小技巧来避免这种精确运算,只要能保证结果是一个正确的舍入结果即可。

2.4.6 C语言中的浮点数

C语言中两种浮点数:float 和 double,分别对应IEEE 的单精度和双精度。
但C语言不要求使用 IEEE 规定的浮点数。
int 和 float double 之间强转时,出现的一些情况

浮点溢出引发的惨案:
Ariane 5 火箭因为浮点溢出问题,发射失败,5亿美元的通信卫星报废。好多钱
[end]2016年11月03日10:09:37

2.5 小结

大多数机器对整数使用补码编码。
对浮点数使用IEEE浮点编码,类科学计数法的形式。
相同长度无符号和有符号整数的强制类型转换,遵循的原则是底层位不变。补码机器上转换是由: T2Uw U2Tw 来描述的 (w表示位)
编码长度有限,超出范围时会溢出。

第3章 程序的机器级表示

汇编是机器代码的文本表示。
为什么要看懂汇编代码?
- 对高层语言不可见的一些特性,将在汇编中体现出来。
- 可以通过汇编了解到一些编译器的优化性能。
本章将详细学习两种汇编语言:
- 编译器产生的汇编代码

讲解之前会先了解 C、汇编、机器码之间的关系。
之后介绍 IA32,在之后介绍x86-64

3.1 历史观点

介绍历代 intel 芯片的晶体管数量,性能。
摩尔定律的改版,晶体管数目从1978年开始,每18个月翻一倍。

3.2 程序编码

介绍 gcc 的运作流程。C预处理器、编译器、汇编器、链接器。

3.2.1 机器级代码

介绍了下机器级代码的构成。

3.2.2 代码示例

展示一段代码用 gcc 编译后的结果,以及如何查看。

3.2.3 关于格式的注解

3.3 数据格式

intel 用”字”表示16位数字
双字 表示32位数字
四字 表示64位数字

3.4 访问信息

IA32 的 CPU 包含8个32位的寄存器。寄存器用来存数据和指针。
介绍这个寄存器的结构

3.4.1 操作数指示符

多数指令有一个或多个“操作数”,指出一个操作中要引用的原数值,以及存放结果的位置。
IA32 支持多种操作数:
- 立即数:常数值
- 寄存器:
- 存储器引用:

3.4.2 数据传送指令

将数据从一个位置复制到另一个位置的指令是最频繁使用的指令。
“操作数”的通用性用一条指令就可以替代其他系统多条指令才能完成的功能
我们把不同的指令,分成了指令类,一类做一种操作。
介绍move 类的指令

3.4.3 数据传送示例

用一段 C 赋值代码,演示传送指令。

3.5 算术和逻辑操作

基本每个指令都有对:字节、字、双字进行操作的的指令。
操作分为四类:
- 加载有效地址
- 一元操作:如,++,–
- 二元操作:如,x+=y
- 移位操作:如,x >> 2,x << 3

3.5.1 加载有效地址

加载有效地址实际是 movl 指令的变形,指令作用是从存储器读取数据到寄存器。

3.5.2 一元操作和二元操作

一元操作:一个操作数,既是原,又是目的,如 i++,i–类的运算。
二元操作:元操作数是一个,目的操作数是一个。如 x+=y

3.5.3 移位操作

x >> 2,x << 3

3.5.4 讨论

3.5.5 特殊的算术操作

双操作数乘法指令,没看懂

3.6 控制

C语言中的某些结构,要求有条件的执行。if else等,根据数据测试的结果来决定执行的操作顺序。
机器代码提供两种低级机制来实现条件行为:
- 测试数据值。
- 根据结果来改变控制流或数据流。
jump 指令可以用来跳转

3.6.1 条件码

除了整数寄存器,CPU还维护者一个 条件码寄存器
没看懂

3.6.2 访问条件码

条件码通常不会直接读取,常用方法有三种:
- 通过条件码组合,将一个字节设置为 0 或1
- 可以条件跳转到程序的某个地方
- 有条件的传送数据

3.6.3 跳转指令及其编码

介绍 jump 指令

3.6.4 翻译条件分支

条件语句从C语言翻译成机器码,最常用方式,结合有条件和无条件跳转。

3.6.5 循环

C中有循环语句,汇编中没有。用条件测试和跳转组合实现循环语句。
讲述 do while 循环,while 循环,for循环等
讲述逆向工程循环。

3.6.6 条件传送指令

实现条件的传统方法利用控制的条件转移。条件满足走一条路径,不满足走另一条路径。效率低下。
数据的条件转移是替代策略。

3.6.7 switch 语句

c是如何实现 switch 语句的,跳转表的用途

3.7 过程

过程调用,就是方法。需要将数据和控制从代码的一部分转移到另一部分,同事还要对过程的局部变量分配空间,释放空间。
IA32只提供 简单的指令
- 转移控制到过程
- 从过程中转移出控制

数据传递、局部变量分配和释放通过操纵程序栈来实现。

3.7.1 栈桢结构

程序用程序栈来实现过程调用。
栈用来传递过程参数、存储返回信息、保存寄存器用于以后恢复,以及本地存储。

3.7.2 转移控制

支持过程调用和返回的指令:

指令 描述
call Label 过程调用
call *Operand 过程调用
leave 为返回准备栈
ret 从过程调用中返回

3.7.3 寄存器使用惯例

寄存器组是唯一能被所有过程共享的资源。
为了保证一个过程调用另一个过程,后者不会覆盖掉前者将要用的寄存器的值。所以 IA32 约定了惯例。
- 调用者保存寄存器:P调用Q,Q可以覆盖。寄存器 %eax、%edx 和 %ecx
- 被调用者保存寄存器:P调用Q,Q在覆盖之前,需要先把值写入栈中,并在返回之前恢复。(寄存器 %ebx、%esi 和 %edi)

3.7.4 过程示例

演示一个过程

3.7.5 递归过程

过程是如何递归调用自身的。

3.8 数组分配和访问

C语言实现数组的方式很简单,很容翻译成机器码。
但,C有一个特点是可以产生指向数组中元素的指针,并且对这些指针进行运算。
优化编译器会简化数组索引所使用的地址计算,不过这样产生的机器码会很难立即。

3.8.1 基本原则

定义一个数组:

int A[N];

会有两个效果:
- 内存中分配一个连续区域,大小为 int 的大小 * N 个。
- 引入标识符 A,A作为指向数组开头的指针。

3.8.2 指针运算

3.8.3 嵌套的数组

3.8.4 定长数组

C语言编译器能优化定长多为数组上的操作代码。

3.8.5 变长数组

3.9 异质的数据结构

C语言 提供了两种不同类型的对象来创建数据类型的机制:
- 结构(struct):将多个对象集合到一个单位中
- 联合(union):允许用几种不同的类型来引用一个对象

3.9.1 结构

结果的所有组成部分都存放在存储器中一段连续的区域内。
指针指向结构第一个字节的地址。
编译器根据每个结构类型的信息,指示每个字段的字节偏移。

3.9.2 联合

联合:使几个不同类型的变量共占一段内存(相互覆盖)
语法跟 struct 一样,区别在于:
- struct 每个字段都有自己的内存。内存占用长度是所有类型之和。
- union 所有的字段是共享内存的,内存占用长度是最长类型的长度。

3.9.3 数据对齐

对齐:许多计算机系统对基本数据类型合法地址做出了一些限制:要求某种类型对象的地址必须是某个值 的倍数。
对齐可以简化处理器和存储系统之间接口的硬件设计。

3.10 综合:理解指针

指针的作用是帮助程序员避免寻址错误。
重点介绍指针和它们映射到机器代码的关键原则。
- 每个指针都对应一个类型
- 每个指针都有一个值
- 指针用 & 运算符创建
- 运算符 * 用于指针的间接引用
- 数组与指针紧密联系
- 将指针从一种类型强制转换成另一种类型,只需要改变他的类型,而不改变它的值。
- 指针也可以指向函数。

3.11 应用:使用GDB调试器

介绍这个玩意的玩法。

3.12 存储器的越界引用和缓冲区溢出

c 不对数组引用进行任何边界检查,而且局部变量和状态信息都存放在栈中。这两种情况可能导致很严重的问题。
如:蠕虫和病毒。
介绍了如何对抗缓冲区溢出攻击。
- 栈随机化
- 栈破坏检测
- 限制可执行代码区域

3.13 X86-64:将 IA32 扩展到64位

3.13.1 x86-64 的历史和动因

物理机很容易就能配备 4GB 以上的内存,但字长32位却用不了。
加上很多大型应用越来越多。

3.13.2 x86-64 简介

x86-64 代码与 IA32 机器生成的代码有极大不同。主要特性如下:
- 指针和长整数是64位长。
- 通用目的寄存器组从 8 个扩展到 16个。
- 许多程序状态都保存在寄存器上,而不是栈上。有些过程根本不需要访问栈。
- 如果可能,条件操作用条件传送指令实现。
- 浮点操作用面向寄存器的指令集实现,而不用 IA32 支持的基于栈的方法来实现。

1.数据类型

介绍了 64 位机器的C语言数据类型与 32位的区别

2.汇编代码示例

3.13.3 访问信息

跟32位的区别在于:
- 寄存器的数量翻倍至16个。
- 所以寄存器都是 64 位长。
- 可以直接访问每个寄存器的低32位、16位、8位。

算术指令

3.13.4 控制

实现控制转移的指令和方法与 IA32 一样。新增了一些指令。

1.过程
2.参数传递
3.栈桢
4.寄存器保存惯例

3.13.5 数据结构

3.13.6 关于 x86-64 的总结性评论

3.14 浮点程序的机器级表示

3.15 小结

我们窥视了C语言提供的抽象层下面的东西,以了解机器级编码。
通过让编译器产生机器及程序的汇编代码,我们了解编译器和它的优化能力,以及机器、数据类ing和指令级。

第4章 处理器体系结构

指令级体系结构(ISA):一个处理器支持的指令和指令的字节级编码。
不同的处理器“家族”,有不同的ISA。编译好的程序,在家族之间不通用。
ISA在 编译器编写者 和 处理器设计人员之间提供了一个抽象层概念。编写者只关心能用哪些指令,以及如何编码的,处理器设计者关心造出执行指令的处理器。

本章简要介绍处理器硬件的设计。

  • 本章先介绍一个简单的指令集 Y86
  • 之后提供一个数字硬件设计的背景,简单的硬件以及硬件的控制语言
  • 做一个顺序操作的处理器
  • 流水线处理器的原理
  • 做一个流水线处理器

4.1 Y86指令集体系结构

4.1.1 程序员可见状态

4.1.2 Y86指令

大概介绍了一些指令,其实是 IA32 的子集。

4.1.3 指令编码

介绍 Y86指令的一些编码

4.1.4 Y86 的异常处理

4.1.5 Y86 程序

4.1.6 一些 Y86 指令的详情

4.2 逻辑设计和硬件控制语言 HCL

4.2.1 逻辑门

数字电路的基本元素。AND(&&)、OR(||) 和 NOT(!) 门。不用C语言中的 &|~ 表示的原因是,前者只针对位进行运算,后者针对整数进行运算。

4.2.2 组合电路和 HCL 布尔表达式

组合电路如何构建的。

4.2.3 字级的组合电路和HCL整数表达式

4.2.4 集合关系

4.2.5 存储器和时钟

4.3 Y86的顺序实现

讲解一个顺序的处理器,最终目标是实现一个高效、流水线化的处理器,而这是第一步

4.3.1 将处理组织成阶段

4.3.2 SEQ 硬件结构

4.3.3 SEQ 的时序

讲解一个实现,分为6个阶段。
取指阶段
译码和回写阶段
执行阶段
访问存储阶段:读写程序数据
更新PC阶段:
SEQ小结。

4.4 流水线的通用原理

在做一个流水线的CPU之前,先介绍下原理。
流水线就是排着队干活,举个例子,做黄油面包。
顺序流程是:切片、抹黄油、对折面包。每次只做一个步骤。直到完成
流水线是:切片车间、抹黄油车间、对折车间。面包从头源源不断的送进去,各个车间负责处理自己的部分,从尾部获取搞好的面包。

4.4.1 计算流水线

4.5 Y86 流水线实现

4.6 小结

指令集体系结构,在处理器行为和如何实现处理器之间提供了一层抽象。
流水线化让不同的阶段并行操作,改进了系统的吞吐量。
本章的几个重要经验:
- 管理复杂性是首要问题。创建一个简单一致的框架,来处理所有不同的指令类型。
- 不需要直接实现ISA:ISA的直接实现意味着顺序的设计,我们可以采用流水线。
- 硬件设计人员必须小心谨慎:一旦芯片造出来,就几乎不能改正任何错误了。开始就设计正确非常重要。

第5章 优化程序性能

优化程序性能有三种方式:
- 选择合适的算法和数据结构
- 写出编译器能有效优化,转成高效可执行的代码。
- 运算量特别大的计算将一个任务分成多个部分,分别在其他机器上做。

本章不是很关心啊,快速过了把。

5.1 优化编译器的能力和局限

编译器必须很小心的对程序只是用安全的优化,必须得保证优化前后的程序有一样的行为。
大多数编译器可以定制优化级别,包括GCC。

5.2 表示程序性能

引入一个量化标准:每元素的周期数。

5.3 程序示例

说明一个抽象的程序如何转换成系统代码的。

5.4 消除循环的抵效率

5.5 减少过程调用

5.6 消除不必要的存储器引用

5.7 理解现代处理器

到目前为止的优化:
- 降低了过程调用的开销
- 消除一些妨碍优化的“重大因素”

为了进一步优化,必须考虑处理器的微体系结构的优化。

5.8 循环展开

这是一种程序变换,增加每次迭代的数量,减少迭代次数。

5.9 提高并行性

不是并发,而是多存出一些变量,一起计算。

5.10 优化合并代码的结果小结

5.11 一些限制因素

5.12 理解存储器性能

5.12.1 加载的性能

5.12.2 存储的性能

5.13 性能提高技术

5.14 确认和消除性能瓶颈

5.15 小结

编译器可以生成高效的代码,但程序员可以协助编译器做这件事情。
使用正确的数据结构和算法是很重要的,编译器不能做这个事情。
妨碍优化的因素有一些,它会影响编译器的优化。

更高级的优化需要对处理器微体系结构的理解。

第6章 存储器层次结构

存储系统是一个具有不同容量、成本和访问时间的存储设备层次结构。
越靠近CPU 越小越快。
计算机系统基本持久的思想:理解数据在存储层次中上下移动,利用这个可以将数据存放在更高的层次,运行更快。

6.1 存储技术

介绍都有哪些存储技术

6.1.1 随机访问存储器

分为两类:
- 静态的(SRAM):比动态的快,但贵的多。
- 动态的(DRAM):

1.静态RAM
SRAM 将位存储在一个双稳态的存储单元里。每个单元使用一个六晶体管电路实现。
2.动态RAM
DRAM 将位存储为对一个电容充电,电容很小。

这里写图片描述

3.传统的DRAM
展示了下传统的DRAM
4.存储器模块
DRAM 包装在存储器模块里面,然后查到主板插槽上。
5. 增强的DRAM
6. 非易失性存储器
永久存储类的存储器,如:闪存,PROM(只能写一次)
7.访问主存
通过总线来访问主存,每次和CPU传送数据都是通过一系列步骤来实现的,称为总线事务。分为读事务和写事务。

6.1.2 磁盘存储

介绍磁盘的组成。

6.1.3 固态硬盘

介绍固态硬盘的一些基本构成:多个闪存芯片和一个闪存翻译层。
速度快,但会磨损,并且贵。

6.1.4 存储技术趋势

容量越来越大。访问速度也稍微变快

6.2 局部性

局部性原理:是指CPU访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中。

局部性分为两种形式:
- 时间局部性:命中率高,访问次数多。(在一个具有良好时间局部性的程序中,被引用过一次的存储器位置在将来也会被再次引用)
- 空间局部性:没命中的数据,在下一层cache 也会命中。

6.3 存储器层次结构

L1、L2、L3高速缓存,主内存,本地磁盘,远程磁盘。

6.3.1 存储器层次结构中的缓存

缓存命中
缓存不命中
缓存不命中的种类:
缓存管理

6.3.2 存储器层次结构概念小结

6.4 高速缓存存储器

通用的告诉缓存存储器结构
组相联高速缓存
全相联高速缓存
有关写的问题
一个真实的高速缓存层次结构的解剖
高速缓存参数的性能影响

6.5 编写高速缓存友好的代码

6.6 综合:高速缓存对程序性能的影响

6.6.1 存储器山

一个程序从存储系统读数据的速率称为读吞吐量,有时称为读带宽。
示例:用一个小程序,测试出 Core i7 处理器的存储器山。

6.6.2 重新排列循环以提高空间局部性

6.6.3 在程序中利用局部性

6.7 小结

基本存储技术包括随机存储器(RAM)、只读存储器(ROM)和磁盘。
RAM有两种:
- SRAM:静态存储器,速度快,但贵
- DRAM:动态存储器,主内存使用。

程序员通过编写有良好空间和时间局部性的程序来显著改进程序 的运行时间。

第二部分 在系统上运行程序

本书的第一部分,介绍了程序和硬件之间的交互关系。
本书的第二部分,介绍程序和操作系统之间的交互关系。
将学会如何利用操作系统提供的服务来构建系统级程序。如 unix 外壳和动态存储器分配包。

第7章 链接

链接:将各种代码和数据部分收集起来并组合成一个单一文件的过程。
链接是由叫做链接器的程序自动执行的。

为什么要了解链接知识?
- 可以构造大型程序:大型程序的程序员经常会遇到链接问题,如,缺少模块,缺少库或不兼容的版本。
- 可以避免一些危险的编程错误:Unix 链接器解析符引用时所做的决定会偷偷的干一些事情。
- 理解语言作用域规则是如何实现的:全局和局部变量的区别。
- 理解其他重要的系统概念:链接器产生的可执行文件,在重要的功能中,扮演重要的角色,如,加载和运行程序、虚拟存储器、分页和存储映射。,
- 能够利用共享库:共享库和动态链接越来越重要,链接也越来越复杂。

7.1 编译器驱动程序

这章在说啥:
给了两个函数,讲述如何使用 编译驱动程序 来把这两个函数编译成一个可执行程序的。

7.2 静态连接

这章在说啥:
介绍静态连接器:输入可重定位的目标文件、命令行参数,输出可执行文件。
链接器有两个任务:
- 符号解析:
- 重定位:

7.3 目标文件

目标文件有三种:
- 可重定位目标文件:可以在编译时与其他可重定位文件合并,创造可执行目标文件。
- 可执行目标文件:可被直接拷贝到内存并执行。
- 共享目标文件:特殊类型的可重定位目标文件,可以再加载或运行时被动态的加载到存储器并链接。

7.4 可重定位目标文件

展示了一可重定位的目标文件长啥样。

7.5 符号和符号表

每个可重定位目标模块 m 都有一个符号表,描述自己定义和引用的符号信息。
链接器上下文有三种类型的符号:
- 由m 定义,能被其他模块引用的全局符号:对应非静态的C函数和非静态的C全局变量。
- 由其他模块定义,能被m引用的外部符号,也是全局符号:对应其他模块中的C函数和变量。
- m自己定义,自己使用的本地符号:带status 的C函数和变量。

7.6 符号解析

链接器是如何解析符号的:
将每个引用和可重定位目标文件符号表中的符号联系起来。
本地符号很好解析。
全局符号的引用难解析。

7.6.1 链接器如何解析多重定义的全局符号

7.6.2 与静态库链接

7.6.3 链接器如何使用静态库来解析引用

7.7 重定位

链接器完成符号解析,它就把代码中的符号引用和符号定义联系起来。

7.8 可执行目标文件

前面说的是链接器将多个目标合成一个可执行文件。
现在介绍下可执行文件

7.9 加载可执行目标文件

如何加载的

7.10 动态链接共享库

静态库有一些缺点:
- 需要定期维护,如果有更新,程序员则要了解处理
- 有一些标准函数,如 printf 和 scanf,如果分散到个个代码里面,很浪费内存。
共享库是一个目标模块,解决了如上问题。可以加载到任意存储地址,并和存在存储器中的程序链接起来。

7.11 从应用程序中加载和链接共享库

之前说的是,程序执行之前,动态链接器加载和链接共享库的情景,现在了解程序执行后,如何加载的。

7.12 与位置无关的代码(PIC)

7.13 处理目标文件的工具

介绍各种工具

7.14 小结

完成链接的三种方式:静态编译器、加载时和运行时有动态链接器完成。
目标文件有三种形式:可重定位的、可执行的和共享的。

可重定位的目标文件由静态链接器合并成可执行文件,可以加载到内存中子星。
共享目标文件(共享库)在运行时由动态链接器链接和加载。

链接器主要任务:符号解析和重定位。

第8章 异常控制流

程序计数器中指令的地址的过渡称为控制转移,控制转移的序列称为处理器的控制流。最简单的是平滑流。跳转、调用和返回等指令会造成平滑流的突变,来对内部的程序状态中的变化做出反应。系统也需要能够对系统状态的变化做出反应,这些系统状态不能被内部程序变量捕获,系统通过使控制流突变来完成,这些突变称为异常控制流(ECF)。ECF发生在系统的各个层次,包括异常、系统调用、信号和非本地跳转等,本篇对它们一一总结。

8.1 异常

8.2 进程

8.3 系统调用错误处理

8.4 进程控制

8.5 信号

8.6 非本地跳转

8.7 操作进程的工具

8.8 小结

异常控制流(ECF)发生在计算机系统的各个层次,是计算机系统提供并发的基本机制。

第9章 虚拟存储器

系统中的进程共享CPU和主存资源,但存储器空间是有限的,而且还容易被破坏。现代系统提供了一种对主存的抽象,称为虚拟存储器,以更有效地管理存储器。虚拟存储器将主存看作磁盘上的地址空间的高速缓存,为每个进程提供了一致的地址空间,并保护进程的地址空间不被其他进程破坏。

第三部分 程序间的交互和通信

第10章 系统级 I/O

http://www.yeolar.com/note/2012/04/20/linux-io/

名词解释

  • 文件描述符
    • 内核通过文件描述符引用打开的文件,它是一个非负整数。 shell 中用0表示标准输入相关,1与标准输出相关,2与标准错误输出相关。这些幻术对应的符号常量 STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO,定义在

第11章 网络编程

大概介绍了,网络的基本概念。局域网、广域网的组成,TCP/IP 协议。
使用C语言中的函数如何做一个,类似于 java socket 的 web服务程序。

11.1 客户端-服务器编程模型

介绍这么一个模型概念,用于后期的讲解。实际上也是这种模型。
客户端负责请求、交互、展示。服务器负责 执行操作、返回资源。

11.2 网络

对于主机来说,网络只是又一种 I/O 设备。通过网络适配器对外发送数和接收数据。
大概介绍了下局域网、广域网是如何通信的。

11.3 全球 IP 因特网

介绍 TCP/IP 的功能。
因特网的域名,以及如何解析域名:早期,是放在 HOSTS文件中,从1988年之后放在 DNS 服务器中。DNS 就是一个大数据库,存放了 IP 和 域名的映射关系。

11.4 套接字接口

介绍C语言中套接字的相关函数。
socket函数、connect函数、open_clientfd函数、bind函数、listen函数、open_listenfd函数、accept函数、echo客户端和服务器的示例。

11.5 Web服务器

介绍web 服务器的一些基本知识。以及如何用C语言输出一个html页面。各种printf。

11.7 小结

第 12 章 并发编程

操作系统提供了三种基本的构造并发程序的方法:
- 进程:每个逻辑控制流都是一个进程,由内核来调度和维护。
- I/O多路复用:应用程序在一个进程的上下文中显式地从一个状态转换到另一个状态。
- 线程:运行在一个单一进程上下文中的逻辑流,由内核进行调度。

12.8 小结

一个并发程序是由在时间上重叠的一组逻辑流组成的。
无论哪种并发机制,同步对共享数据的并发访问都是一个困难的问题。提出对信号量的 P 和 V 操作就是为了帮助解决这个问题。
并发也引入了其他一些困难的问题,被线程调用的函数必须具有一种称为线程安全的属性。

你可能感兴趣的:(自学,深入理解计算机系统)