《程序员的自我修养(链接、装载与库)》学习笔记一(稳固而知新)

温故而知新 | 静态链接 | 装载与动态链接 | 库与运行库 |
---|---|---|---|
计算机发展 | 编译和链接 | 可执行文件的装载与进程 | 内存 |
软件体系结构 | 目标文件里有什么 | 动态链接 | 运行库 |
操作系统 | 静态链接 | Linux 共享库的组织 | 系统调用与 API |
内存、线程 | Windows PE/COFF | Windows 下的动态链接 | 运行库实现 |
在这书里有一句话,是之前认识的一个研发长者经常挂在嘴边的,今天在书中看到了感触颇多:
经常听很多人谈起,IT 技术日新月异,其实真正核心的东西数十年都没怎么变化,变化的仅仅是它们外在的表现,大体也是换汤不换药吧。
本书的作者介绍之所以想写这本书,其实主要也是因为不满足于技术的表面,想探索问题的根源。就像上面写的,技术发展日新月异,但是核心的东西却是相对稳定不变的,那么对于从事软件开发的工程师,研究人员,学习这些底层的知识就很有必要了,很多技术都是相通的,认识了底层才更能看清事情的表象,达到触类旁通的效果,毕竟只会写代码不是好程序员,这也是我想学习这本书的原因之一。
除此之外,这本书被大家评价为国人难得写的比较不错的一本计算机技术书籍,并且成为很多大厂人员的必读书籍,肯定是有其魅力所在的,那么是时候认真阅读一下了。
温故而知新
本书的第一章主要分为五个部分,除了第一部分主要是抛出几个问题让大家一起思考之外,剩下部分分别为计算机的发展,软件体系结构,内存和线程。具体知识点分布如图所示:

Hello World引发的思考
#include <stdio.h>
int main() {
printf("Hello World\n");
return 0;
}
针对这小段代码,本书抛出了一些问题。
- 为什么程序编译了才能运行?
- 编译器将 C 代码转化为机器码,做了什么?
- 编译出的可执行文件中有什么,存放机制是什么?
- ....
关于上述问题,本书会从基本的编译、链接开始讲解,然后到装载程序、动态链接等。
计算机基本结构以及CPU的发展
结构
计算机基本结构:CPU、内存、I/O 控制芯片,如下图:

CPU 的发展史
- 早期 CPU 的核心频率很低,几乎等于内存频率,每个设备都会有一个 I/O 控制器,链接在一条总线(Bus)上。
- 随着 CPU 核心频率的提升,内存访问速度低于 CPU,于是增加了处理高速 I/O 的北桥芯片和处理低速 I/O 的南桥芯片。
- CPU 速度达到极限后,又增加了多核处理器 SMP。
软件体系结构
下图为计算机的软件体系结构分层:

- 计算机软件体系结构是分层的,层与层之间通信的协议,称之为接口。
- 开发工具与应用程序都使用操作系统的应用程序编程接口。
- 运行库使用操作系统的系统调用接口。
- 接口需精心设计,尽量保持稳定,基于接口,具体实现层可以被任意替换。
- 中间层作为下面层级的包装和扩展,中间层的存在,保证了软硬件的相对独立。
- 操作系统提供抽象接口,管理软件、硬件资源。操作系统内核作为硬件接口的使用者,需定制硬件规格,硬件逐渐被抽象成一套接口,交给厂商,厂商写各自的驱动程序,硬件交互细节交给操作系统(驱动),程序员无需和硬件打交道。
分层
分层设计的思想,其实渗透在计算机的各个领域。其中有我们最熟悉的 OSI 七层网络模型,它从低到高分别是:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。参考计算机的软件体系结构,OSI 网络模型同样是通过制定层层的通信协议,界定各个分层的具体责任和义务。
OSI七层网络模型 | TCP/IP四层概念模型 | 对应网络协议 |
应用层 | 应用层 | HTTP、TFTP、FTP、NFS、WAIS、SMTP |
表示层 | Telnet、Riogin、SNMP、Gopher | |
会话层 | SMTP、DNS | |
传输层 | 传输层 | TCP、UDP |
网络层 | 网络层 | IP、ICMP、ARP、RARP、AKP、UUCP |
数据链路层 | 数据链路层 | FDDI、Ethernet、Arpanet、PDN、SLIP、PPP |
物理层 | IEEE 802.1A、IEEE 802.2 到 IEEE 802.11 |
那么为什么在架构设计的时候,采用分层设计的实现方案呢?
之所以要设计分层,主要有以下几点考虑:
- 降低复杂度,上层不需要关注下层细节。
- 提高灵活性,可以灵活替换某层的实现。
- 减小耦合度,将层次间的依赖减到最低。
- 有利于重用,同一层次可以有多种用途。
- 有利于标准化。
中间层
除了分层设计,中间层的设计,也是非常巧妙的存在。
在计算机软件体系结构中,中间层作为下面层级的包装和扩展,中间层的存在,保证了软硬件的相对独立。 中间层的强大之处在 LLVM 设计的过程中也深有体现。
首先解释下 LLVM:
LLVM 是构架编译器(compiler)的框架系统,以 C++ 编写而成,用于优化以任意程序语言编写的程序的编译时间(compile-time)、链接时间(link-time)、运行时间(run-time)以及空闲时间(idle-time),对开发者保持开放,并兼容已有脚本。
LLVM 的大体结构设计如下图:
它的设计主要可以分为编译器前端(Frontend)、优化器(Optimizer)、后端和代码生成器(Backend And CodeGenerator)。
笔者理解优化器(Optimizer)不仅仅作为编译过程中的一道工序(做各种优化并且改善代码的运行时间,减少冗余计算),优化器还作为 LLVM 设计最为精妙的地方--中间层
。
为什么这么说呢?
前端语法种类繁多,后端硬件架构种类繁多,而正是中间层的存在,使得 LLVM 的架构即可以为各种语言独立编写前端,也可以为任意硬件架构编写后端,实现了开发语言和硬件架构之间相对独立,这才是其真正的强大之处。
类似软件的设计原则的体现
虽说是大的软件体系的结构设计,但是也能让笔者感触到一些软件设计原则的体现,毕竟万物皆对象,而面向对象设计原则如下:
设计原则名称 | 简单定义 |
---|---|
开闭原则 | 对扩展开放,对修改关闭 |
单一职责原则 | 一个类只负责一个功能领域中的相应职责 |
里氏替换原则 | 所有引用基类的地方必须能透明地使用其子类的对象 |
依赖倒置原则 | 依赖于抽象,不能依赖于具体实现 |
接口隔离原则 | 类之间的依赖关系应该建立在最小的接口上 |
合成/聚合复用原则 | 尽量使用合成/聚合,而不是通过继承达到复用的目的 |
迪米特法则 | 一个软件实体应当尽可能少的与其他实体发生相互作用 |
感受到了哪些设计原则?这列举一二,当然应该还会有更多。
- 单一职责 分层设计,每层只负责特定的职责,拥有清晰的职责范围。
- 单一职责 层与层之间交互应该依赖抽象,任何满足每层协议的实体,都可以进行层的替换。
- 开闭原则 采用类似工厂设计原则,增加一种硬件类型,仅需要增加一种符合硬件规格厂商即可。
总结:我们进行日常软件架构设计的时候,其实也可以参考计算机软件设计的一些思想,做一些合适的分层,制定层与层之间的协议,制定合适的中间层。
内存
早期内存采用扇形内存分区,磁盘中所有的扇区从0开始编号,直到最后一个扇区,编号为逻辑扇区号,设备会将逻辑扇区号,转换成真实的盘面、磁道位置。
早期程序直接运行在物理内存,存在的问题
- 地址空间
不隔离
,一个程序内容容易被另一个程序修改。
- 内存使用
效率低
,使用中的内存需要等到释放了,才能继续被使用。
- 程序运行的地址
不固定
,程序重新装载时,内存地址变化了。
虚拟地址&物理地址
为了解决地址空间不隔离
的问题,引入了虚拟地址的概念,于是地址就分为了两种,虚拟地址空间和物理地址空间。
MMU是内存管理单元,有时也称作分页内存管理单元,MMU在操作系统的控制下负责将虚拟内存实际翻译成物理内存,其与CPU以及物理内存的关系如下图:

- 物理地址空间是由地址总线条数决定的。
- 虚拟地址是想象出来的,每个进程都拥有独立的虚拟空间,这样做到了进程的地址隔离。
- 将一段程序所需要的虚拟空间,映射到某个实际的物理地址空间,映射函数由软件完成,实际转换由硬件完成。
分段和分页
仅仅增加虚拟地址只能解决地址空间不隔离的问题,剩下两个问题还没解决,于是又引入了分段和分页。
- 分段的基本思路是把一段与程序所需要的内存空间大小的虚拟空间映射到某个地址空间,映射关系如下图所示,通过使用分段,可以解决不隔离和不固定的问题,因为程序A和程序B被映射到了两块不同的物理空间。

- 但是分段内存使用效率低下,内存映射以程序为单位,如果内存不足,被换出的是整个程序,其实程序内的很多数据,都不会被频繁用到,没必要被一起移除内存。
- 分页就是将地址空间分为固定大小的页,进程的虚拟地址空间按页分隔,不常用的放入磁盘,用到时取出来即可,内存使用效率低下的问题得到了解决。

内存共享的实现机制
虚拟空间页称之为虚拟页,物理内存的页为物理页,磁盘中的页为磁盘页,不同虚拟页被同时映射到同一个物理页,即可实现内存共享。
Page Fault
虚拟页不在内存中,当需要用到时,就会捕获 Page Fault。
对于 iOS 开发来说,虚拟内存也是通过分页管理的,当访问到某些数据并没有加载到内存时,操作系统就会阻塞当前线程,新加载一页到物理内存,并且将虚拟内存与之对应,这个阻塞的过程就叫做缺页中断,App 启动的时候 Page Fault次数多了会影响启动速度,而我们优化启动速的方式之一就是通过二进制重排,减少 Page Fault 的次数。




