《程序员的自我修养》--读书笔记

印象

拿到这本书,肯定会想到星爷的《喜剧之王》中的《演员的自我修养》;没错,可能我们都是一些死烂演员,但是如果我们坚持,打好最基础的根基,总有一天,我们会成为优秀的程序员。
书的副标题是:链接、装载与库,没错,这三个连贯的主题构成了文章的核心内容,目前也有这方面的开源代码(轮子),作为程序员,我们并不鼓励重复造轮子,但是我们应该了解轮子的构造,特别是很基础层次的轮子。文中反复重复的一个观点是:一切问题都可使用中间层来解决,通过中间层次逐步屏蔽此层的差异化。了解了底层存在的问题,我们会理解中间层使用了什么方法解决了什么问题。

必备知识

C/C++、x86汇编语言基础、操作系统基本概念、计算机系统概念

内容

简介

温故知新

本章对计算机的软硬件基础结构进行回顾。

  • 计算机硬件的3个关键部件:中央处理器CPU、内存、I/O控制芯片;北桥,高速,协调CPU、内存和高速图形设备之间高速交换数据;南桥,连接低速设备如磁盘、USB、键盘、鼠标等,南桥将这些汇总到北桥。

  • SMP与多核:CPU的频率似乎不满足摩尔定律,限制于4GHz,于是从另外一个角度上提高CPU速度:多核。SMP:对称多处理器;

  • 计算机软件体系结构:应用程序(操作系统API)运行时库(系统调用)操作系统内核(硬件特性)硬件,引出了中间层的概念;

  • 操作系统的功能:提供抽象接口、管理硬件资源;比如CPU调度、设备驱动等;

  • 内存:怎么将有限的物理内存分配给多个程序使用?几个问题:地址空间隔离(虚拟地址)、内存使用效率低(段页式管理);

  • 线程调度、线程安全、线程的几种模型。

静态链接

编译和链接

本章回顾了编译的几个步骤及其相关部门。

  • GCC编译过程分解:预编译(处理源代码中得# 预编译指令)、编译(词法分析、语法分析、语义分析、优化)、汇编(将汇编代码变成机器码)、链接(多个目标文件链接成可执行文件);

  • 编译器的工作:扫描、语法分析、语义分析(静态语义、动态语义)、中间代码生成(编译器前端负责产生机器无关的中间代码,编译器后端将中间代码转换成目标机器代码)、目标代码优化;

  • 链接:模块的拼接工作、符号、重定位等;

  • 静态链接:把一些指令对其它符号地址的引用加以修正,包括:地址和空间分配、符号决议、重定位等;

目标文件有什么

本章主要介绍ELF文件的段结构及相关结构

  • 目标文件的格式:Windows的PE、Linux的ELF,都是COFF格式的变种

  • 目标文件将这些信息按照不同的属性以Section的形式存储,比如机器指令放在代码段、全局变量和局部静态变量放在数据段里。而把指令和数据分开,也是有原因的,比如提高缓存命中率、共享指令从而节省内存、安全等;

  • 反汇编看看代码段、数据段和只读数据段、bss段、其它段的内容;

  • ELF文件结构描述:文件头(魔数)、段表、重定位表、字符串表;

  • 链接的接口–符号:extern c的用法等;

静态链接

本章主要介绍目标文件中的段是如何合并的、链接器如何为它们分配在输出文件中的空间和地址。 地址确定之后,进行符号解析和重定位,以及“修补”共工作。

  • 空间与地址分配:按序叠加、相似度合并策略;

  • 符号解析与重定位;

  • 处理弱符号时的COMMON块;

  • C++相关问题:重复代码消除、全局构造与析构、C++和ABI;

  • 链接过程控制:链接控制脚本;

  • BFD库:一个GNU项目,希望通过一种统一的接口来处理不同的目标文件格式;

Windows PE/COFF

本章介绍了Windows下的可执行文件和目标文件格式PE/COFF

装载与动态链接

本章介绍程序运行时如何使用内存空间、如何被装载到内存中、进程虚拟地址空间的分布等。

可执行文件的装载与进程

  • 进程的虚拟地址空间;

  • 装载的方式:静态装入(指令和数据全部装入内存)、动态装入(利用局部性原理,把程序最常用的部分驻留在内存中,不常用的放在磁盘里)。 动态装载的方法:覆盖载入和页映射。页映射,选择哪些页,有多种算法,比如FIFO、LUR(最少使用算法)等;

  • 可执行文件的装载:建立进程、页错误;

  • 进程虚拟空间分布:ELF文件链接视图和执行试图、堆和栈;

  • Linux内核装载ELF;

  • WIndows PE的装载;

动态链接

本章介绍动态链接的优点、解决绝对地址引用问题、动态连接器的步骤以及实现。

  • 为什么需要动态链接?静态链接存在的两个问题:内存和磁盘空间浪费;程序开发、发布和更新比较麻烦;而动态链接是把链接的过程推迟到了运行时再进行,解决了静态链接存在的两个问题,当然动态链接也会带来一些问题,比如性能;

  • 程序模块在编译时目标地址不确定而需要在装载时将模块重定位,叫做装载时重定位,Windows下叫做基址重置。而动态模块中有绝对地址引用的话,指令部分如何在多个进程之间共享?基本想法是把指令中需要修改的部分分离出来,跟数据部分放在一起,这种方案叫做地址无关代码。

  • 动态链接的性能要低于静态链接,常用的优化手段:延迟绑定实现等;

  • 动态链接实现的相关结构;

  • 动态链接的步骤和实现:1.动态连接器自举;2.装载共享对象;3.重定位和初始化。

Linux共享库的组织

本章介绍了大量的共享库怎么在更新或者升级中兼容版本。如何管理和维护这些版本库。介绍了ELF共享库的版本命名方式、共享库的符号版本机制、共享库路径、查找过程、环境变量、共享库的创建与安装等。

  • 共享库的兼容性、共享库的版本命名、SO-NAME表示了一个库的接口(接口不向后兼容)

  • 共享库的路径:/lib,/usr/lib,/usr/local/lib

  • 共享库的创建和安装

库和运行库

内存

本章回顾程序的基本内存布局,然后介绍栈和堆

  • 程序的内存布局:栈、堆、可执行文件映像、保留区

  • 栈:寄存器操作、函数传参、函数返回值传递(返回数据结构)

  • 堆与内存管理:运行库一个算法来管理堆空间,比如空闲链表、位图、对象池等,而实际上,堆的分配算法是采取多种算法复合而成。

运行库

本章介绍运行库的各个方面:程序入口点的实现、CRT的初始化过程(比如IO初始化)、库函数的实现、运行库的构造、运行库与并发、C++运行库实现全局构造的方法。

  • 入口函数是什么? 怎么实现?

  • C语言运行库包含功能:启动与退出、标准函数、I/O、堆、语言实现、调试

  • 运行库与多线程:线程局部存储实现

  • C++全局构造与析构

  • fread函数的实现(缓冲、文本换行等)

系统调用与API

本章介绍了系统调用和API,并进而介绍了特权级、中断等系统调用相关的实现原理。同时介绍了Windows下的API的历史和成因、组织形式、实现原理等。

  • 很多资源是操作系统管理的,操作系统提供系统调用的接口给应用程序。而Windows上,在系统调用之上又封装了一个叫做API的东西,API提供接口给应用程序;

  • 系统调用是运行在内核态,操作系统通过中断从用户态切换到内核态。中断可以通过轮询或者信号。中断的两个属性:中断号和中断处理程序,内核中,有一个数组:中断向量表。中断有两种类型:硬件中断和软件中断,比如系统调用就是0x80号的软件中断。

  • 系统调用的实现:触发中断、切换堆栈、中断处理程序。

  • Windows APi是操作系统提供给应用程序开发者的最底层与Windows打交道的接口。API基于系统调用,系统调用在Windows上叫做系统服务。API之所以存在是因为,系统调用非常依赖硬件接口,Windows为了程序兼容性(商业操作系统),加了一个中间层:API。

运行库实现

本章实现了一个简易的CRT,接着添加了C++语言特性的支持。

  • C 语言运行库:入口函数、main函数、CRT初始化、结束部分、堆的实现、IO与文件操作、字符串相关操作、格式化字符串;

  • C++运行库实现:new/delete、C++全局构造与析构、入口函数修改、stream与string

你可能感兴趣的:(读书笔记)