程序员的自我修养【正式版】
2022-03-12 10:14:12 23 举报
AI智能生成
程序员的自我修养正式版,是一本专为程序员打造的提升指南。本书深入剖析了程序员应具备的核心技能和素质,如代码质量、团队协作、沟通能力等。通过阅读本书,程序员可以不断提升自己的编程水平,更好地适应不断变化的技术环境。此外,本书还提供了丰富的实践案例和经验分享,帮助程序员在实际工作中避免陷阱,提高工作效率。总之,程序员的自我修养正式版是每位程序员必备的进阶宝典,值得一读。
作者其他创作
大纲/内容
静态链接
程序构建过程
预处理
处理"#"开头的预编译指令,-> .i
编译
词法分析
语法分析
语义分析
中间语言生成
目标代码生成 & 优化
汇编
将汇编代码翻译成机器代码
链接
地址和空间分配、符号决议、重定位
链接目标文件和库
目标文件
定义
源代码编译后但未进行链接的中间文件
ELF结构
File header
ELF文件版本 | 目标机器型号 | 程序入口地址
.text
.data
已初始化的全局变量 & 局部静态变量
.rodata
只读数据
.bss
未初始化的全局变量 & 局部静态变量
Other sections
Sysbol Table
记录了目标文件中所有用到的符号
Section header table
描述了ELF包含的所有段的信息
链接的接口-符号
概念
符号:函数和变量的统称
链接:将多个不同目标文件相互“粘”到一起
符号分类
定义在本文件的全局变量
外部符号:在本文件中引用的全局符号,却没有定义在本文件中
局部符号:只在编译单元内部可见
信号信息:目标文件指令与源代码中代码行的对应关系,(可选)
符号表结构
特殊符号
一些符号并没有在程序中定义,但是可以直接声明并引用
符号修饰 & 函数签名
C++中如何实现函数重载
函数签名可以识别不同的函数,包括名称,参数类型,命名空间等
如何解决多模块符号冲突问题
使用符号修饰,在编译以后,相应的符号加上下划线,以避免和库里的函数冲突
强符号 & 弱符号
强符号:函数名 & 已初始化的全局变量
弱符号:未初始化的全局变量
强弱符号都只是针对定义来说的,引用不存在这个概念
链接器处理符号规则
1. 不允许强符号被多次定义
2. 如果一个符号在某个文件是强符号,在其他文件中为弱符号,则选择强符号
3. 如果一个符号在所有文件中都是弱符号,则选择占用空间最大的一个
调试信息
GCC "-g" 参数,就会在目标文件里放入调试信息
静态链接
目标文件如何合并
按序叠加
相似段合并
符号地址的确定
符号解析与重定位
重定位表
记录了需要重定位的符号
结构:r_offset、r_info
符号解析
连接器查找目标文件里的符号表
指令修正方式
绝对寻址修正
相对寻址修正
COMMON块
作用:处理多个不同类型的弱符号
原因:链接器无法判断符号类型
静态库链接
可以看成一组目标文件的集合
链接过程控制
链接控制脚本
默认控制脚本:ld -verbose
自定义控制脚本:ld -T link.script
链接脚本语法(参考P153)
BFD库
Windows PE/COFF
库与运行库
内存
i386基本内存布局
内核空间
用户空间
栈
堆
可执行映像文件
动态链接库映射区
保留区
栈
栈帧
函数调用惯例
参数传递顺序和方式
栈的维护方式
案例:cdecl方式:从右至左压入参数 | 将返回地址压栈 | 转到调用函数
函数传递返回值
如何返回结构体
- 调用者在栈上开辟一片空间用于存放返回值
- 调用者将首地址作为“隐式参数”传递
- 被调用者将数据拷贝到指定位置,并通过eax传出
- 调用者将eax指向的对象拷贝走
将规模很大的结构体作为返回值 is unadvisable
堆
堆分配算法
空闲链表
以双向链表为基础
位图
对象池
Linux系统堆
分配方式
brk():设置进程数据段的结束地址
mmap():向操作系统申请一段虚拟地址空间
Windows 系统堆
运行库
入口函数
概念
在main函数之前,已有函数准备好main函数需要的环境,并负责调用main函数
在main函数结束后,回到入口函数,进行清理工作
实现
Linux: Glibc
以静态glibc用于可执行文件为例
动态glibc(没介绍)
Windows: MSVC CRT
初始化和OS版本有关全局变量
初始化堆
初始化IO
获取命令行参数 & 环境变量
初始化C库一些数据
调用main并记录返回值
检查错误并将main的返回值返回
C运行库构造
C语言运行库
概念:代码集合 | 至少包括入口函数、标准库函数
C语言标准库
边长参数:stdarg.h
通过宏实现
非局部跳转: setjmp.h
C运行库与并发的关系
标准库通常不支持多线程
多线程和具体的平台有关
C运行库改进
使用线程局部存储j(TLS)
TLS类型的变量每个线程都会有一个备份,修改不会影响其他线程
加锁
在线程不安全的函数前加锁
改进函数调用方式
C++运行库实现全局构造与析构
glibc全局构造与析构
构造
1. 将所有全局构造函数映射到.ctor段,然后放入_CTOR_LIST_数组中
2. 然后在_start -> _libc_start_main -> _libc_csu_int -> _init -> _do_global_ctors_aux中统一执行
void foo(void) _attribute_ ((constructor))
析构
与构造类似(具体参考P386)
MSVC CRT全局构造与析构
在 mainCRTStartup -> _cdecl _initterm(void * pfbegin, void * pfend)中执行
fread实现
系统调用与API
系统调用概念
应用程序通过系统调用来操纵系统资源
系统调用弊端
使用不便
各个操作系统之间系统调用不兼容
系统调用原理
特权级原理
通过中断切换用户态和内核态
中断原理
重要属性
中断向量表(Interrupt Vector Table)
中断处理程序(Interrupt Service Routine)
Linux系统调用(P422)
API(P424)
API历史与成因
API组织形式
API实现原理
子系统
运行库实现
计算机基础知识
CPU与外围设备连接方式
PCI Bridge & PCI bus
ISA Bridge & ISA bus
SMP & 多核
CPU工艺方面已到达物理极限,开始增加数量横向提高CPU速度
软硬件层次体系结构
接口
操作系统内核
开发库 & 运行库
CPU调度
内存
空间隔离:每个进程拥有独立的运行空间
地址映射:虚拟地址 -> MMU -> 物理地址
👆内存使用率:分页 & 分段
线程
访问权限
私有:局部变量 | 函数的参数 | TLS数据
公有:全局变量 | 静态变量 | 堆 | 代码 | 文件列表
线程调度
优先级调度(Priority Schedule)
轮转法(Round Robin)
每个线程轮流执行一段时间片(Time Slice)
Linux 多线程
frok:复制当前进程-COW
exec: 将新代码覆盖当前的
clone:创建子进程从指定位置执行
线程安全
线程模型
一对一模型
多对一模型
多对多模型
装载与动态链接
Chapter 6 可执行文件的装载与进程
装入方式
覆盖装入(几乎淘汰)
方式:程序员手工将程序分块,同时编写辅助代码管理模块在内存驻留情况
关键:如果两个函数没有依赖关系,则可以共享内存
页映射
方式:将内存和磁盘数据和指令按照“页”为单位划分
进程的创建
1. 创建虚拟地址空间
实质:虚拟空间 <-> 物理内存
2. 读取文件头,建立虚拟空间和可执行文件的映射
实质:虚拟空间 <-> 可执行文件
3. 将CPU指令寄存器设置成可执行文件入口
页错误处理
进程虚存空间分布
问题:当Section的数量增多时,就会有空间浪费的问题(对齐等)
解决:将多个权限相同的Section映射到同一Segment
程序头表:目标文件不需要被装载,没有程序头表
一个进程VMA区域
代码VMA
数据VMA
堆VMA
栈VMA
堆的最大申请数量
Linux下虚拟地址空间给进程有3G
影响因素:操作系统版本、程序本身大小、共享库数量等
段地址对齐
Intel 80x86上页大小为4096,则空间长度必须是4096的整数倍
进程栈初始化
动态链接
优势:将程序的模块分割开来,形成独立的文件,等到程序运行时才进行链接,节省空间 | 易于更新
装载时重定位
思想:在链接时对所有绝对地址的引用不作重定位,推迟到装载时再完成
缺点:无法共享代码段
优点:运行速度稍快
地址无关代码
思想
缺点:运行速度稍慢
优点:可以实现代码段在各进程共享
ELF延时绑定技术
思想:当函数第一次被用到时才经行绑定
实现
.got保存全局变量引用的地址
.got.plt保存函数引用的地址
和动态链接有关的ELF结构
.interp
保存动态连接器的路径
.dynamic
保存了动态连接器所需要的基本信息:所依赖的共享对象,动态链接符号表位置等
动态符号表
模块之间的符号导入导出关系
重定位表
动态连接器
自举
动态连接器本身所需要的全局和静态变量的重定位工作本身完成
装载共享对象
重定位 & 初始化
重新遍历重定位表,进行修正
动态链接
显示动态链接
dlopen
dlsym
dlerror
dlclose
Linux 共享库的组织
Windows 共享库的组织
0 条评论
下一页