docs: clang 插件开发

This commit is contained in:
杭城小刘
2024-04-27 13:01:58 +08:00
parent 6e47061735
commit 851797d133
257 changed files with 9060 additions and 239 deletions

View File

@@ -1,4 +1,297 @@
# 虚拟内存
- 进程隔离的必要性
- 虚拟内存是如何实现进程隔离的
- 线性地址和物理地址是如何转换的
# Swift 枚举值内存布局
> enum 使用很简单,那大家有没有思考过系统针对枚举的实现是怎么样的?接下去会针对不同情况的枚举,结合汇编来窥探下系统实现原理。
### 基础枚举
```swift
enum Season {
case spring
case summer
case antumn
case winter
}
var season: Season = Season.spring
print(Mems.ptr(ofVal: &season))
season = Season.summer
season = Season.antumn
print("over")
```
- `var season: Season = Season.spring` 基础枚举默认值是0。
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/EnumBaseMemoryLayoutDemo1.png" style="zoom:25%">
- `season = Season.summer`此时可以看到第一个字节的位置是1.
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/EnumBaseMemoryLayoutDemo2.png" style="zoom:25%">
- `season = Season.antumn` 此时可以看到第一个字节的位置是2
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/EnumBaseMemoryLayoutDemo3.png" style="zoom:25%">
结论查看内存信息可以看到基础枚举只占1个字节大小空间且值为默认值。
### 只有原始值
```swift
enum Season:Int {
case spring = 1
case summer = 2
case antumn = 3
case winter = 4
}
//print(MemoryLayout<Season>.size)
//print(MemoryLayout<Season>.stride)
//print(MemoryLayout<Season>.alignment)
var season: Season = Season.spring
print(Mems.ptr(ofVal: &season))
season = .summer
season = .winter
print("over")
```
- `var season: Season = Season.spring` 基础枚举变量默认值可以看到第一个字节的位置是0
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/EnumWithRawValueMemoryLayoutDemo1.png" style="zoom:25%">
- `season = .winter` 基础枚举,当赋值为 winter 的时候可以看到第一个字节的位置是3
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/EnumWithRawValueMemoryLayoutDemo2.png" style="zoom:25%">
结论带有原始值的枚举同样只占用1个字节该字节的值为枚举的位置比如 case1 case2
### 带有关联值的枚举
```swift
enum Season {
case spring(Int, Int, Int)
case summer(Int, Int)
case antumn(Int)
case winter(Bool)
case unknown
}
print(MemoryLayout<Season>.size)
print(MemoryLayout<Season>.stride)
print(MemoryLayout<Season>.alignment)
var season: Season = Season.spring(1, 2, 3)
print(Mems.ptr(ofVal: &season))
season = Season.summer(4, 5)
season = Season.antumn(6)
season = Season.winter(true)
season = Season.unknown
print("over")
```
- `var season: Season = Season.spring(1, 2, 3)` 带有关联值的枚举,`.spring` 有3个 Int单个 Int 占8个字节空间所以红色框代表 spring 的1蓝色框代表 spring 的2绿色框代表 spring 的3黄色框代表枚举的第1个 case剩余7个字节为空。
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/AssociatedEnumMemoryLayoutDemo1.png" style="zoom:25%">
其内存信息如下8字节为1组对应上图
```shell
01 00 00 00 00 00 00 00
02 00 00 00 00 00 00 00
03 00 00 00 00 00 00 00
00
00 00 00 00 00 00 00
```
这段内存信息怎么看?我划分了下
```
关联值: 01 00 00 00 00 00 00 00
关联值: 02 00 00 00 00 00 00 00
关联值: 03 00 00 00 00 00 00 00
位置值: 00
内存对齐占用00 00 00 00 00 00 00
```
下面的几组一样
- `season = Season.summer(4, 5)` 带有关联值的枚举,`.summer` 有2个 Int单个 Int 占8个字节空间所以红色框代表 summer 的4蓝色框代表 summer 的5绿色框为空黄色框代表枚举的第2个 case剩余7个字节为空。
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/AssociatedEnumMemoryLayoutDemo3.png" style="zoom:25%">
其内存信息如下8字节为1组对应上图
```shell
04 00 00 00 00 00 00 00
05 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
01
00 00 00 00 00 00 00
```
- `season = Season.antumn(6) 带有关联值的枚举,`. `antumn` 有1个 Int单个 Int 占8个字节空间所以红色框代表 antumn 的6蓝色框为空绿色框为空黄色框代表枚举的第3个 case剩余7个字节为空。
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/AssociatedEnumMemoryLayoutDemo4.png" style="zoom:25%">
其内存信息如下8字节为1组对应上图
```shell
06 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
02
00 00 00 00 00 00 00
```
- `season = Season.winter(true)` 带有关联值的枚举,`. `winter` 有1个 Bool单个 Int 占1个字节空间所以红色框代表 winter 的 true蓝色框为空绿色框为空黄色框代表枚举的第4个 case剩余7个字节为空。
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/AssociatedEnumMemoryLayoutDemo5.png" style="zoom:25%">
其内存信息如下8字节为1组对应上图
```shell
01 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
03
00 00 00 00 00 00 00
```
- `season = Season.unknown` 带有关联值的枚举,`unknown` 没有关联值所以红色框为空蓝色框为空绿色框为空黄色框代表枚举的第5个 case剩余7个字节为空。
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/AssociatedEnumMemoryLayoutDemo6.png" style="zoom:25%">
其内存信息如下8字节为1组对应上图
```shell
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
04
00 00 00 00 00 00 00
```
- `MemoryLayout<Season>.size` 3个 Int 最大为3*81个字节用来表达位置信息`3*8 + 1 = 25`
- `MemoryLayout<Season>.stride` :获取系统分配给数据类型的内存大小,也就是实际内存大小(对齐后的)
- `MemoryLayout<Season>.alignment` 内存对齐系数以8 Byte 为单位,对象分配的内存必须是该值的整数倍
### 只有一个 case 的枚举
```swift
enum SimpleEnum {
case one
}
var caseOne = SimpleEnum.one
print(MemoryLayout<SimpleEnum>.size) // 0
print(MemoryLayout<SimpleEnum>.stride) // 1
print(MemoryLayout<SimpleEnum>.alignment) // 1
```
为什么 size 为0看上去是一个变量但根本不占内存。因为枚举里面就一个 case所以里面根本不需要存储值来区分是哪个 case。
```swift
enum SimpleEnum {
case one
case two
}
var caseOne = SimpleEnum.one
print(MemoryLayout<SimpleEnum>.size) // 1
print(MemoryLayout<SimpleEnum>.stride) // 1
print(MemoryLayout<SimpleEnum>.alignment) // 1
```
现在好理解2个 case 需要存储1个 Byte 的值来区分是哪个 case1 Byte 可以代表最多256个 case
### 只有1个 case 且带关联值的枚举
```swift
enum SimpleEnum {
case one(Int)
}
var caseOne = SimpleEnum.one(4)
print(MemoryLayout<SimpleEnum>.size) // 8
print(MemoryLayout<SimpleEnum>.stride) // 8
print(MemoryLayout<SimpleEnum>.alignment) // 8
```
带有关联值且只有1个 case 的枚举因为有1个 Int 的关联值但只有1个 case所以只需要8 Byte 存储关联值即可。
请看下面的对照实验
```swift
enum SimpleEnum {
case one(Int)
case two
}
var caseOne = SimpleEnum.one(4)
print(MemoryLayout<SimpleEnum>.size) // 9
print(MemoryLayout<SimpleEnum>.stride) // 16
print(MemoryLayout<SimpleEnum>.alignment) // 8
```
2个 case其中一个 case 有关联值 Int所以需要8 Byte 存 Int 值1 Byte 区分是哪个 case实际需要占用 8 + 1 = 9 Byte内存对齐单位是89向上为16.
### 用汇编验证下内存
```
enum Season {
case spring(Int, Int, Int)
case summer(Int, Int)
case antumn(Int)
case winter(Bool)
case unknown
}
var season: Season = Season.spring(1, 2, 3)
print(Mems.ptr(ofVal: &season))
season = Season.summer(4, 5)
season = Season.antumn(6)
season = Season.winter(true)
season = Season.unknown
print("over")
```
断点停到 `var season: Season = Season.spring(1, 2, 3)` 位置
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/AssociatedEnumMemoryLayoutExplore.png" style="zoom:25%">
将断点处的汇编单独摘出来研究
```assembly
0x10000334b <+11>: movq $0x1, 0x8eaa(%rip) ; demangling cache variable for type metadata for Swift.Array<Swift.UInt8> + 4
0x100003356 <+22>: movq $0x2, 0x8ea7(%rip) ; SwiftDemo.season : SwiftDemo.Season + 4
0x100003361 <+33>: movq $0x3, 0x8ea4(%rip) ; SwiftDemo.season : SwiftDemo.Season + 12
0x10000336c <+44>: movb $0x0, 0x8ea5(%rip) ; SwiftDemo.season : SwiftDemo.Season + 23
0x100003373 <+51>: movl $0x1, %edi
```
`rip` 存储的说指令的地址。CPU 要执行的下一条指令地址就存储在 rip 中。所以在执行第一行的时候rip 寄存器的值。
所以第一句汇编代码的意思是rip 为 `0x100003356`,再加上 `0x8eaa`,得到一个地址值(用 Mac 自带的计算器可以算出)`0X10000C200`,然后 movq 是将十六进制的1赋值给 `0X10000C200` 这个地址。
第二句汇编代码类似,此时 rip 为 `0x100003361`,再加上 `0x8ea7`,得到一个地址值 `0X10000C208`,然后 movq 将十六进制的2赋值给 `0X10000C208` 这个地址。
第三句汇编代码类似,此时 rip 为 `0x10000336c`,再加上 `0x8ea4`,得到一个地址值 `0X10000C210`,然后 movq 将十六进制的3赋值给 `0X10000C210` 这个地址。
第四句汇编代码类似,此时 rip 为 `0x100003373`,再加上 `0x8ea5`,得到一个地址值 `0X10000C218`,然后 movq 将十六进制的0赋值给 `0X10000C218` 这个地址。
此时断点走到下一行,拿到 season 的内存地址 `0X10000C200` ,查看内存发现和上面理论分析一直
```shell
01 00 00 00 00 00 00 00
02 00 00 00 00 00 00 00
03 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
```
结论:如果枚举存在关联值,内存大小为:
- 1个字节用来存储成员值
- n个字节用来存储关联值n取占用内存最大的关联值任何一个 case 的关联值都共用这 n 个字节
- 且存在内存对齐,所以占用大小为 n 和 1 的最大值,再结合内存对齐。
- 如果枚举的定义非常简单系统会用1个字节来存放值最大范围是256个 case。
- 枚举定义如果有原始值,也不会影响内存布局。