Skip to content

Commit 2a2eb51

Browse files
committed
update
1 parent 0345984 commit 2a2eb51

10 files changed

+264
-12
lines changed

computer/MMU.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# 内存管理单元 MMU
2+
3+
内存管理单元 (MMU),或称存储器管理单元,或称分页内存管理单元 (PMMU: Paged Memory Management Unit)。MMU 是一种负责 CPU 的内存访问请求的计算机硬件设备。是 CPU 的一部分。
4+
5+
MMU 的功能包括虚拟地址到物理地址的转换(即虚拟内存管理)、内存保护、中央处理器高速缓存的控制
6+
MMU 使得每个用户进程拥有自己的地址空间,并通过内存访问权限的检查保护每个进程所用的内存不被其他进程破坏。
7+
8+
页表一般都很大,并且存放在内存中,所以 CPU 引入 MMU 后,读取指令、数据需要访问两次内存:首先通过查询页表得到物理地址,然后访问该物理地址读取指令、数据。CPU 每次访问虚拟内存,虚拟地址都必须转换为对应的物理地址。从概念上说,这个转换需要遍历页表,页表是三级页表,就需要 3 次内存访问。就是说,每次虚拟内存访问都会导致 4 次物理内存访问。
9+
10+
为了减少 CPU 访问内存所需平均时间,又引入了 [TLB](./TLB.md)[CPU 多级缓存](./cpu-cache.md)

computer/TLB.md

+40-8
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,43 @@
1-
## TLB
1+
# TLB
22

3-
内存管理单元 (MMU),或称存储器管理单元,或称分页内存管理单元 (PMMU: Paged Memory Management Unit)。MMU 是一种负责 CPU 的内存访问请求的计算机硬件设备。是 CPU 的一部分。
4-
MMU 的功能包括虚拟地址到物理地址的转换(即虚拟内存管理)、内存保护、中央处理器高速缓存的控制
5-
MMU 使得每个用户进程拥有自己的地址空间,并通过内存访问权限的检查保护每个进程所用的内存不被其他进程破坏。
6-
7-
页表一般都很大,并且存放在内存中,所以 CPU 引入 MMU 后,读取指令、数据需要访问两次内存:首先通过查询页表得到物理地址,然后访问该物理地址读取指令、数据。CPU 每次访问虚拟内存,虚拟地址都必须转换为对应的物理地址。从概念上说,这个转换需要遍历页表,页表是三级页表,就需要 3 次内存访问。就是说,每次虚拟内存访问都会导致 4 次物理内存访问。
8-
为了减少因为 MMU 导致的处理器性能下降,引入了 TLB。
9-
TLB (Translation Lookaside Buffer) 是内存管理单元 (MMU: Memory Management Unit) 用于改进虚拟地址到物理地址转换速度的缓存。TLB 又称“快表”,直译为旁路快表缓冲,也可以理解为页表缓冲,地址变换高速缓存。
3+
TLB (Translation Lookaside Buffer) 是[内存管理单元 (MMU)](./MMU.md) 用于改进虚拟地址到物理地址转换速度的缓存。
4+
TLB 又称“快表”,直译为旁路快表缓冲,也可以理解为页表缓冲,地址变换高速缓存。
105

116
- [TLB的作用及工作原理](https://www.cnblogs.com/alantu2018/p/9000777.html)
7+
- [虚拟内存的作用,虚拟地址,页表,TLB,缓存原理](https://wangjunstf.github.io/2021/11/09/xu-ni-nei-cun-de-zuo-yong-xu-ni-di-zhi-ye-biao-tlb-huan-cun-yuan-li/) ([链接备份](https://web.archive.org/web/20230311080315/https://wangjunstf.github.io/2021/11/09/xu-ni-nei-cun-de-zuo-yong-xu-ni-di-zhi-ye-biao-tlb-huan-cun-yuan-li/))
8+
9+
## TLB 物理结构
10+
11+
12+
## TLB 歧义问题
13+
14+
同进程的相同的虚拟地址可以映射不同的物理地址。这就会造成歧义 (ambiguity) 问题。
15+
例如,进程 A 将虚拟地址 0x2000 映射物理地址 0x4000。进程 B 将虚拟地址 0x2000 映射物理地址 0x5000。当进程 A 执行的时候将 0x2000 对应 0x4000 的映射关系缓存到 TLB 中。当切换 B 进程的时候,B 进程访问 0x2000 的数据,会由于命中 TLB 从物理地址 0x4000 取数据。这就造成了歧义。
16+
17+
如何消除这种歧义,我们可以借鉴 VIVT 数据 cache 的处理方式,在进程切换时将整个 TLB 无效。切换后的进程都不会命中 TLB,但是会导致性能损失。
18+
19+
## ASID
20+
21+
ASID (Address-Space Identifiers) 是为了解决多进程环境下 TLB 歧义问题而引入的一种机制,它可以标识当前访问的页面属于哪个进程的地址空间,从而避免不同进程之间的虚拟地址映射关系发生歧义。
22+
23+
ASID 需要 CPU 和操作系统共同支持。具体可以参考 [Linux 的 TLB 实现](https://github.com/torvalds/linux/blob/master/arch/x86/mm/tlb.c)
24+
25+
在 x86 架构中,这个功能又叫做 PCID (Process Context IDentifier)。
26+
27+
不是所有的 CPU 都支持 TLB ASID。目前,大多数现代 CPU 都支持 ASID。例如,ARMv8-A 和 MIPS64 架构都支持 ASID。
28+
在 x86 架构中,Intel 和 AMD 的最新处理器已经支持 ASID。但是一些旧的处理器可能不支持 ASID,如 Intel Pentium Pro,Pentium II 和 Pentium III。
29+
30+
需要注意的是,虽然 ASID 可以很好地解决 TLB 歧义问题,但是使用不当可能会导致安全隐患。
31+
如果恶意程序能够获取当前进程的 ASID 或者猜测其他进程的 ASID,那么它就可以绕过操作系统的地址空间隔离机制,对其他进程的内存进行读写操作。因此,在设计和实现 ASID 机制时,需要考虑到安全性和可靠性等方面的因素。
32+
33+
参考[这篇文章](https://www.cs.swarthmore.edu/~kwebb/cs31/s15/bucs/virtual_memory_hardware.html) ([链接备份](https://web.archive.org/web/20191001194242/https://www.cs.swarthmore.edu/~kwebb/cs31/s15/bucs/virtual_memory_hardware.html)) 里的 Flushing the TLB 章节。
34+
35+
### ASID 只有 0-255 个
36+
37+
因为 ASID 是 8 位的,所以它的范围只有 0-255。这对于大多数系统都没问题。因为系统的 CPU 核数不超过 256,最多只有 256 个进程占用 CPU。
38+
39+
参考[这篇文章](https://community.arm.com/support-forums/f/architectures-and-processors-forum/5229/address-space-identifier---asid) ([链接备份](https://web.archive.org/web/20220926202622/https://community.arm.com/support-forums/f/architectures-and-processors-forum/5229/address-space-identifier---asid))。
40+
41+
## VMID
42+
43+
> For virtual machines, there is a VMID - each guest OS has a VMID and each guest OS has tasks with an ASID. A cache lookup requires both to match a translation, so the ASID rollover code won't clobber entries for another guest OS.

git/proxy.md

+37-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
1-
## git proxy
1+
# git proxy
22

3-
[Configure Git to use a proxy](https://gist.github.com/evantoli/f8c23a37eb3558ab8765)
3+
## 对于 http 和 https 网络协议
4+
5+
```
6+
[http "http://github.com"]
7+
proxy = socks5://127.0.0.1:7890
8+
[https "https://github.com"]
9+
proxy = socks5://127.0.0.1:7890
10+
```
11+
12+
这里的 `127.0.0.1:7890` 改成你的代理监听的端口。
13+
14+
这个只会对 github.com 域名下的 git 操作起作用。
15+
如果要针对所有的 http/https 网站,可以这么设置。
16+
17+
```
18+
[http]
19+
proxy = socks5://127.0.0.1:7890
20+
[https]
21+
proxy = socks5://127.0.0.1:7890
22+
```
23+
24+
参考 [Configure Git to use a proxy](https://gist.github.com/evantoli/f8c23a37eb3558ab8765)
25+
26+
## 对于 git 网络协议
27+
28+
需要修改 `~/.ssh/config` 文件。
29+
30+
```
31+
Host github.com
32+
User git
33+
ProxyCommand nc -v -x 127.0.0.1:7890 %h %p
34+
```
35+
36+
这里的 `127.0.0.1:7890` 改成你的代理监听的端口。
37+
38+
参考 https://gist.github.com/chenshengzhi/07e5177b1d97587d5ca0acc0487ad677

go/go-assembler.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# GO 汇编
2+
3+
- https://go.dev/doc/asm
4+
- https://www.ipeapea.cn/post/go-asm/ ([链接备份](https://web.archive.org/web/20221005111356/https://ipeapea.cn/post/go-asm/))

go/linkname.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# //go:linkname
2+
3+
用法:`//go:linkname localname``//go:linkname localname importpath.name`
4+
5+
这在 go 源码中经常见到。
6+
7+
`//go:linkname` 指令并不作用于于后面的 Go 代码。而是指示编译器使用 `importpath.name` 作为名为 `localname`**变量或函数**的文件符号名 (file symbol name)。即外部文件可以通过名字 `importpath.name` 引用 `localname`
8+
如果省略了 `importpath.name` 参数,`importpath.name` 默认是 `localname`
9+
10+
举个例子,`sync.throw` 实际上是由 [sync_throw 函数](https://github.com/golang/go/blob/aee9a19c559da6fd258a8609556d89f6fad2a6d8/src/runtime/panic.go#L1024-L1027)实现的。通过 `//go:linkname sync_throw sync.throw` 生效。
11+
12+
使用这个指令之前必须要 `import "unsafe"`
13+
14+
详见 https://pkg.go.dev/cmd/compile

go/pitfalls.md

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Go 语言的几大坑
2+
3+
## nil slice 与 empty slice 的区别
4+
5+
```go
6+
func main() {
7+
var s1 []string
8+
s2 := []string{}
9+
10+
fmt.Printf("s1 == nil: %v\n", s1 == nil) // s1 == nil: true
11+
fmt.Printf("s2 == nil: %v\n", s2 == nil) // s2 == nil: false
12+
}
13+
```
14+
15+
阅读 [Golang: Nil vs Empty Slice (零值切片和空切片的坑)](https://qwqaq.com/2021/12/golang-slice-nil-vs-empty/) ([链接备份](https://web.archive.org/web/20221226232054/https://qwqaq.com/2021/12/golang-slice-nil-vs-empty/))。
16+
17+
## interface 的空值判断问题
18+
19+
```go
20+
func main(){
21+
var a interface{} = nil
22+
var b interface{} = (*int)(nil)
23+
24+
fmt.Println(a == nil) // true
25+
fmt.Println(b == nil) // false
26+
}
27+
```
28+
29+
阅读 [Go “一个包含nil指针的接口不是nil接口”踩坑](https://juejin.cn/post/6844903905797603335) ([链接备份](https://web.archive.org/web/20220901174714/https://juejin.cn/post/6844903905797603335))。
30+
31+
## append 一定返回新的 slice,但内部数组不一定是新的
32+
33+
得根据是否超出 cap 来判断。
34+
35+
```go
36+
func main(){
37+
arr := []int{0, 1, 2}
38+
func(v []int) {
39+
v[0] = 100 // modify origin array
40+
v = append(v, 4) // new array allocated, and new slice variable
41+
v[0] = 50 // not modify origin array
42+
}(arr)
43+
fmt.Println(arr) // [100 1 2]
44+
}
45+
```
46+
47+
```go
48+
func main() {
49+
var arr = make([]int, 0, 5)
50+
arr = append(arr, 0, 1, 2)
51+
52+
func(v []int) {
53+
v[0] = 100 // modify origin array
54+
v = append(v, 4) // no new array allocated, but new slice variable
55+
v[0] = 50 // modify origin array
56+
}(arr)
57+
58+
fmt.Println(arr) // [50 1 2]
59+
}
60+
```
61+
62+
## 数组是值传递
63+
64+
```go
65+
func main() {
66+
x := [3]int{1, 2, 3}
67+
68+
func(arr [3]int) {
69+
arr[0] = 7
70+
fmt.Println(arr) // [7 2 3]
71+
}(x)
72+
73+
fmt.Println(x) // [1 2 3]
74+
}
75+
```
76+
77+
## slice 循环问题
78+
79+
```go
80+
func main() {
81+
var out []*int
82+
for i := 0; i < 3; i++ {
83+
out = append(out, &i)
84+
}
85+
fmt.Println("value:", *out[0], *out[1], *out[2]) // value: 3 3 3
86+
fmt.Println("pointer:", out[0], out[1], out[2]) // pointer: 0x14000018178 0x14000018178 0x14000018178
87+
}
88+
```
89+
90+
```go
91+
func main() {
92+
var out []*int
93+
for _, i := range []int{1, 2, 3} {
94+
out = append(out, &i)
95+
}
96+
fmt.Println("value:", *out[0], *out[1], *out[2]) // value: 3 3 3
97+
fmt.Println("pointer:", out[0], out[1], out[2]) // pointer: 0x14000018178 0x14000018178 0x14000018178
98+
}
99+
```
100+
101+
因为 `i` 始终是同一个变量,地址没变。
102+
103+
## 默认零值
104+
105+
当声明变量时未初始化,默认会根据类型赋予零值。
106+
107+
- 数值类型(包括整数、浮点数和复数):`0`
108+
- 布尔类型:`false`
109+
- 字符串类型:`""` (空字符串)
110+
- 指针类型:`nil`
111+
- 接口类型:`nil`
112+
- 函数类型:`nil`
113+
- 切片类型:`nil`
114+
- 映射类型:`nil`
115+
- 通道类型:`nil`
116+
- 结构体类型:其所有成员变量的默认零值
117+
118+
当 UnmarshalJSON 时,比如结构体定义了布尔类型的字段,无法区分是传了 `false` 还是没有传值,因为默认值就是 `false`
119+
解决方案是定义 `type ConvertibleBoolean bool` 以及 `func (bit *ConvertibleBoolean) UnmarshalJSON(data []byte)`
120+
参考 https://stackoverflow.com/a/37214476/4622308
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# 给命令的每一行输出加前缀的方法
2+
3+
```sh
4+
#! /bin/bash
5+
exec > >(trap "" INT TERM; sed 's/^/foo: /')
6+
exec 2> >(trap "" INT TERM; sed 's/^/foo: (stderr) /' >&2)
7+
echo foo
8+
echo bar >&2
9+
date
10+
```
11+
12+
输出结果
13+
14+
```sh
15+
foo: foo
16+
foo: (stderr) bar
17+
foo: Fri Apr 27 20:04:34 IST 2018
18+
```
19+
20+
出自 https://unix.stackexchange.com/a/440439/373303

shell/bash-tricks.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,14 @@ $- 是一串字符串,由 set option 的简写标记组成。
142142

143143
## `=~` 的正则表达式
144144

145-
`[[ '-' =~ [._-] ]]` 是 true,但是 `[[ '-' =~ [._-a-zA-Z0-9] ]]` 是 false。
145+
### `-` 要放在正则表达式的首部或尾部
146+
147+
```sh
148+
[[ '-' =~ [._-] ]]
149+
echo $? # 0,即 true
150+
[[ '-' =~ [._-a-zA-Z0-9] ]]
151+
echo $? # 2,即 false
152+
```
146153

147154
因为正则表达式里的 `-`,如果不是第一个或者最后一个字符,或者没有写成转义的 `\-`,那 `-` 会被当成 range 的连字符。
148155
所以要写成 `[[ '-' =~ [-._a-zA-Z0-9] ]]``[[ '-' =~ [._a-zA-Z0-9-] ]]` 才是 true。

shell/file-descriptor.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Shell 操作文件描述符
2+
3+
- 创建文件描述符:(`fd` 填数字)
4+
- 创建读写的 `exec fd<> fileName`。如果文件不存在,会自动创建。
5+
- 创建只读的 `exec fd< fileName`
6+
- 关闭文件描述符:`exec fd>&-``exec fd<&-``>&``<&` 效果一样。
7+
- 写文件:`echo "hello" >&fd`
8+
- 读文件:`while read -r line 0<&fd; do ...``cat <&fd`

vm/kvm.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
### KVM 与 QEMU 与 Libvirt 的关系
44

5-
https://archive.ph/TwVct
5+
![.jpg](https://user-images.githubusercontent.com/1998490/223427418-84015b3d-b6ef-4da9-bf46-049e9bf86699.png)
6+
7+
- [KVM-Qemu-Libvirt三者之间的关系](https://blog.51cto.com/changfei/1672147) ([链接备份](https://archive.ph/TwVct))
68

79
### 硬件虚拟化
810

0 commit comments

Comments
 (0)