程序员的自我修养【正式版】
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全局构造与析构
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 条评论
回复 删除
下一页