noblog 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/cmds/generate.d.ts +2 -0
- package/dist/cmds/generate.d.ts.map +1 -0
- package/dist/cmds/generate.js +46 -0
- package/dist/cmds/generate.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cvtmd.d.ts +38 -0
- package/dist/lib/cvtmd.d.ts.map +1 -0
- package/dist/lib/cvtmd.js +111 -0
- package/dist/lib/cvtmd.js.map +1 -0
- package/dist/lib/nomd/cvter.d.ts +38 -0
- package/dist/lib/nomd/cvter.d.ts.map +1 -0
- package/dist/lib/nomd/cvter.js +111 -0
- package/dist/lib/nomd/cvter.js.map +1 -0
- package/dist/lib/nomd/nomd.d.ts +20 -0
- package/dist/lib/nomd/nomd.d.ts.map +1 -0
- package/dist/lib/nomd/nomd.js +73 -0
- package/dist/lib/nomd/nomd.js.map +1 -0
- package/dist/lib/nomd.d.ts +8 -0
- package/dist/lib/nomd.d.ts.map +1 -0
- package/dist/lib/nomd.js +50 -0
- package/dist/lib/nomd.js.map +1 -0
- package/dist/lib/nosm/default.d.ts +2 -0
- package/dist/lib/nosm/default.d.ts.map +1 -0
- package/dist/lib/nosm/default.js +3 -0
- package/dist/lib/nosm/default.js.map +1 -0
- package/dist/lib/nosm/nosm.d.ts +2 -0
- package/dist/lib/nosm/nosm.d.ts.map +1 -0
- package/dist/lib/nosm/nosm.js +2 -0
- package/dist/lib/nosm/nosm.js.map +1 -0
- package/dist/lib/notion/client.d.ts +6 -0
- package/dist/lib/notion/client.d.ts.map +1 -0
- package/dist/lib/notion/client.js +48 -0
- package/dist/lib/notion/client.js.map +1 -0
- package/dist/lib/notion/object.d.ts +148 -0
- package/dist/lib/notion/object.d.ts.map +1 -0
- package/dist/lib/notion/object.js +2 -0
- package/dist/lib/notion/object.js.map +1 -0
- package/dist/lib/notion/request.d.ts +36 -0
- package/dist/lib/notion/request.d.ts.map +1 -0
- package/dist/lib/notion/request.js +2 -0
- package/dist/lib/notion/request.js.map +1 -0
- package/output/00097769-2aac-4b6f-8f37-d9b658d46b2e +435 -0
- package/output/1707063b-7c0a-4572-9526-f7db2f7bbc49 +162 -0
- package/output/1ed588e1-2604-4e33-9685-722b03f66374 +89 -0
- package/output/2b1c0d09-40f7-4151-a053-11391f775ece +33 -0
- package/output/336b6a5a-f383-4d6c-8fe5-54c9a918c4c0 +264 -0
- package/output/37b77a0a-e233-405a-93f5-a60a808eee35 +72 -0
- package/output/3ac660cb-9503-4e88-8939-5aee30c98351 +24 -0
- package/output/5bc1b19a-8350-48b3-aa19-8073561ad45b +109 -0
- package/output/765976b5-7790-497b-8634-4be1d4770e16 +111 -0
- package/output/90f2cb44-90ab-40b7-a736-02bfb83a314c +1167 -0
- package/output/99a7b0c5-f9de-403c-95ec-eba3d700a195 +49 -0
- package/output/c844b66d-0bf3-4f89-bbe1-5974f2de454b +193 -0
- package/output/cd6299f7-54bb-4160-8735-16e629422a57 +193 -0
- package/output/d462cfca-0a39-4b65-8430-4eae35edbb76 +27 -0
- package/output/daafaad2-2570-43da-8e9e-f95a1b29ca17 +12 -0
- package/output/e45ef6bf-6c30-4fb2-9efc-a1984e5dc530 +84 -0
- package/output/sub/05009ca9-cb39-4356-8f24-c0db39cb4661 +64 -0
- package/output/sub/0b903359-2337-4ad9-adef-e77b69142581 +328 -0
- package/output/sub/18f0fae1-b7ca-40ea-b725-4e69404429dc +109 -0
- package/output/sub/232ae8ce-5ec3-4737-a18e-077774b84b1e +940 -0
- package/output/sub/34f4d4af-812e-4e56-a6dd-deb96e7f605c +3 -0
- package/output/sub/44331753-a264-40a4-b86f-eafd676b3da0 +509 -0
- package/output/sub/4bac355b-24f0-49e7-a317-e4f1cad63c79 +65 -0
- package/output/sub/4d6663a2-e93a-402e-89f8-a47cccc0c8cf +76 -0
- package/output/sub/5771c956-25bf-4714-bc2e-65959c9156d9 +52 -0
- package/output/sub/6ff91a18-494e-4ffc-a33b-b9fd2c36f43f +42 -0
- package/output/sub/81e4a2b0-05e2-4013-bd41-14b144979d91 +0 -0
- package/output/sub/99ef1497-0b94-490f-b9a0-f54b0e64194d +6 -0
- package/output/sub/9a33b8cc-101a-4f6a-9852-115595cd166b +172 -0
- package/output/sub/9c060d29-39a2-432b-b06b-b0bd91989a52 +1159 -0
- package/output/sub/a45b383c-d894-4480-8322-9c5d028722e3 +9 -0
- package/output/sub/b875bfe4-3c1e-4a8f-a56c-27a48ab874ca +438 -0
- package/output/sub/b8cbb8f9-f702-4a43-943c-02a6e39683d5 +222 -0
- package/output/sub/e339a686-bbb1-41d9-b6b5-18111e72a68c +1392 -0
- package/output/sub/e7931a8e-45ca-444a-8d6f-e6fed9415c26 +110 -0
- package/output/sub/e859552b-c597-49ed-9ede-fab320fafd87 +150 -0
- package/output/sub/f4237e72-fdce-44a4-b62c-659dce24b1ee +107 -0
- package/output/sub/ff7ea97c-55ac-471c-902f-b7a7bd101ee6 +0 -0
- package/package.json +31 -0
- package/src/cmds/generate.ts +54 -0
- package/src/index.ts +22 -0
- package/src/lib/nomd/cvter.ts +143 -0
- package/src/lib/nomd/nomd.ts +88 -0
- package/src/lib/nosm/default.ts +0 -0
- package/src/lib/nosm/nosm.ts +3 -0
- package/src/lib/notion/client.ts +59 -0
- package/src/lib/notion/object.ts +176 -0
- package/src/lib/notion/request.ts +42 -0
- package/tsconfig.json +35 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# Overview
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Go scheduler 在源码中的结构体为 `schedt`,保存调度器的状态信息、全局的可运行 G 队列等(也就是之前提到过的 global goroutine queue)。源码如下:
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
```go
|
|
12
|
+
// 保存调度器的信息
|
|
13
|
+
type schedt struct {
|
|
14
|
+
// accessed atomically. keep at top to ensure alignment on 32-bit systems.
|
|
15
|
+
// 需以原子访问访问。
|
|
16
|
+
// 保持在 struct 顶部,以使其在 32 位系统上可以对齐
|
|
17
|
+
goidgen uint64
|
|
18
|
+
lastpoll uint64
|
|
19
|
+
|
|
20
|
+
lock mutex
|
|
21
|
+
|
|
22
|
+
// 由空闲的工作线程组成的链表
|
|
23
|
+
midle muintptr // idle m's waiting for work
|
|
24
|
+
// 空闲的工作线程数量
|
|
25
|
+
nmidle int32 // number of idle m's waiting for work
|
|
26
|
+
// 空闲的且被 lock 的 m 计数
|
|
27
|
+
nmidlelocked int32 // number of locked m's waiting for work
|
|
28
|
+
// 已经创建的工作线程数量
|
|
29
|
+
mcount int32 // number of m's that have been created
|
|
30
|
+
// 表示最多所能创建的工作线程数量
|
|
31
|
+
maxmcount int32 // maximum number of m's allowed (or die)
|
|
32
|
+
|
|
33
|
+
// goroutine 的数量,自动更新
|
|
34
|
+
ngsys uint32 // number of system goroutines; updated atomically
|
|
35
|
+
|
|
36
|
+
// 由空闲的 p 结构体对象组成的链表
|
|
37
|
+
pidle puintptr // idle p's
|
|
38
|
+
// 空闲的 p 结构体对象的数量
|
|
39
|
+
npidle uint32
|
|
40
|
+
nmspinning uint32 // See "Worker thread parking/unparking" comment in proc.go.
|
|
41
|
+
|
|
42
|
+
// Global runnable queue.
|
|
43
|
+
// 全局可运行的 G队列
|
|
44
|
+
runqhead guintptr // 队列头
|
|
45
|
+
runqtail guintptr // 队列尾
|
|
46
|
+
runqsize int32 // 元素数量
|
|
47
|
+
|
|
48
|
+
// Global cache of dead G's.
|
|
49
|
+
// dead G 的全局缓存
|
|
50
|
+
// 已退出的 goroutine 对象,缓存下来
|
|
51
|
+
// 避免每次创建 goroutine 时都重新分配内存
|
|
52
|
+
gflock mutex
|
|
53
|
+
gfreeStack *g
|
|
54
|
+
gfreeNoStack *g
|
|
55
|
+
// 空闲 g 的数量
|
|
56
|
+
ngfree int32
|
|
57
|
+
|
|
58
|
+
// Central cache of sudog structs.
|
|
59
|
+
// sudog 结构的集中缓存
|
|
60
|
+
sudoglock mutex
|
|
61
|
+
sudogcache *sudog
|
|
62
|
+
|
|
63
|
+
// Central pool of available defer structs of different sizes.
|
|
64
|
+
// 不同大小的可用的 defer struct 的集中缓存池
|
|
65
|
+
deferlock mutex
|
|
66
|
+
deferpool [5]*_defer
|
|
67
|
+
|
|
68
|
+
gcwaiting uint32 // gc is waiting to run
|
|
69
|
+
stopwait int32
|
|
70
|
+
stopnote note
|
|
71
|
+
sysmonwait uint32
|
|
72
|
+
sysmonnote note
|
|
73
|
+
|
|
74
|
+
// safepointFn should be called on each P at the next GC
|
|
75
|
+
// safepoint if p.runSafePointFn is set.
|
|
76
|
+
safePointFn func(*p)
|
|
77
|
+
safePointWait int32
|
|
78
|
+
safePointNote note
|
|
79
|
+
|
|
80
|
+
profilehz int32 // cpu profiling rate
|
|
81
|
+
|
|
82
|
+
// 上次修改 gomaxprocs 的纳秒时间
|
|
83
|
+
procresizetime int64 // nanotime() of last change to gomaxprocs
|
|
84
|
+
totaltime int64 // ∫gomaxprocs dt up to procresizetime
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
在程序运行过程中,`schedt` 对象只有一份实体,它维护了调度器的所有信息。
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
**在 proc.go 和 runtime2.go 文件中,有一些很重要全局的变量:**
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
```go
|
|
96
|
+
// 所有 g 的长度
|
|
97
|
+
allglen uintptr
|
|
98
|
+
|
|
99
|
+
// 保存所有的 g
|
|
100
|
+
allgs []*g
|
|
101
|
+
|
|
102
|
+
// 保存所有的 m
|
|
103
|
+
allm *m
|
|
104
|
+
|
|
105
|
+
// 保存所有的 p,_MaxGomaxprocs = 1024
|
|
106
|
+
allp [_MaxGomaxprocs + 1]*p
|
|
107
|
+
|
|
108
|
+
// p 的最大值,默认等于 ncpu
|
|
109
|
+
gomaxprocs int32
|
|
110
|
+
|
|
111
|
+
// 程序启动时,会调用 osinit 函数获得此值
|
|
112
|
+
ncpu int32
|
|
113
|
+
|
|
114
|
+
// 调度器结构体对象,记录了调度器的工作状态
|
|
115
|
+
sched schedt
|
|
116
|
+
|
|
117
|
+
// 代表进程的主线程
|
|
118
|
+
m0 m
|
|
119
|
+
|
|
120
|
+
// m0 的 g0,即 m0.g0 = &g0,即全局的 g0
|
|
121
|
+
g0 g
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
在程序初始化时,这些全局变量都会被初始化为零值:指针被初始化为 nil 指针,切片被初始化为 nil 切片,int 被初始化为 0,结构体的所有成员变量按其类型被初始化为对应的零值。
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
> 因此程序刚启动时 allgs,allm 和allp 都不包含任何 g,m 和 p。
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
不仅是 Go 程序,系统加载可执行文件大概都会经过这几个阶段:
|
|
132
|
+
|
|
133
|
+
1. 从磁盘上读取可执行文件,加载到内存
|
|
134
|
+
2. 创建进程和主线程
|
|
135
|
+
3. 为主线程分配栈空间
|
|
136
|
+
4. 把由用户在命令行输入的参数拷贝到主线程的栈
|
|
137
|
+
5. 把主线程放入操作系统的运行队列等待被调度
|
|
138
|
+
|
|
139
|
+
# 浅尝辄止
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
## 调整SP(stack pointer)到一个地址是 16 的倍数的位置
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
```assembly
|
|
146
|
+
SUBQ $(4*8+7), SP // 2args 2auto
|
|
147
|
+
// 调整栈顶寄存器使其按 16 个字节对齐
|
|
148
|
+
ANDQ $~15, SP
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+

|
|
153
|
+
|
|
154
|
+
|
|
155
|
+

|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
上面两张图中,左侧用箭头标注了 16 字节对齐的位置。第一步表示向下移动 39 B,第二步表示与 `~15` 相与。
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
存在两种情况,这也是第一步将 SP 下移的时候,多移了 7 个 Byte 的原因。第一张图里,与 `~15` 相与的时候,SP 值减少了 1,第二张图则减少了 9。最后都是移位到了 16 字节对齐的位置。
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
两张图的共同点是 SP 与 argc 中间多出了 16 个字节的空位。
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
至于为什么进行 16 个字节对齐,就比较好理解了:因为 CPU 有一组 SSE 指令,这些指令中出现的内存地址必须是 16 的倍数。
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
## 初始化g0
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
g0 栈的作用就是为运行 runtime 代码提供一个“环境”。
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
这里直接叠甲过,看初始化g0完以后是啥样子:
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+

|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
stack.hi和stack.lo分别指向栈的高位和低位。
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
## 主线程绑定m0
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
因为 m0 是全局变量,而 m0 又要绑定到工作线程才能执行。我们又知道,runtime 会启动多个工作线程,每个线程都会绑定一个 m0。而且,代码里还得保持一致,都是用 m0 来表示。这就要用到线程本地存储的知识了,也就是常说的 TLS(Thread Local Storage)。简单来说,TLS 就是线程本地的私有的全局变量。
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
如果需要在一个线程内部的各个函数调用都能访问、但其它线程不能访问的变量(被称为 static memory local to a thread,线程局部静态变量),就需要新的机制来实现。这就是 TLS。
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
设置 tls 的函数 runtime·settls(SB) 位于源码 `src/runtime/sys_linux_amd64.s` 处,主要内容就是通过一个系统调用将 fs 段基址设置成 m.tls[1] 的地址,而 fs 段基址又可以通过 CPU 里的寄存器 fs 来获取。
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
> 而每个线程都有自己的一组 CPU 寄存器值,操作系统在把线程调离 CPU 时会帮我们把所有寄存器中的值保存在内存中,调度线程来运行时又会从内存中把这些寄存器的值恢复到 CPU。
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
这样,工作线程代码就可以通过 fs 寄存器来找到 m.tls。
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
**问题:为什么一定要将g0存储到tls中,明明还有一个**
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
最终,
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
```go
|
|
210
|
+
tls[0] = g0
|
|
211
|
+
m0.g0 = &g0
|
|
212
|
+
g0.m = &m0
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
就把 m0,g0,m.tls[0] 串联起来了。通过 m.tls[0] 可以找到 g0,通过 g0 可以找到 m0(通过 g 结构体的 m 字段)。并且,通过 m 的字段 g0,m0 也可以找到 g0。于是,主线程和 m0,g0 就关联起来了。
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
> 从这里还可以看到,保存在主线程本地存储中的值是 g0 的地址,也就是说工作线程的私有全局变量其实是一个指向 g 的指针而不是指向 m 的指针。
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
> 目前这个指针指向g0,表示代码正运行在 g0 栈。
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
于是,前面的图又增加了新的玩伴 m0:
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+

|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
## `schedinit`
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
首先回顾go程序初始化的过程:
|
|
235
|
+
|
|
236
|
+
- call osinit。初始化系统核心数。
|
|
237
|
+
- call schedinit。初始化调度器。
|
|
238
|
+
- make & queue new G。创建新的 goroutine。
|
|
239
|
+
- call runtime·mstart。调用 mstart,启动调度。
|
|
240
|
+
- The new G calls runtime·main。在新的 goroutine 上运行 runtime.main 函数。
|
|
241
|
+
|
|
242
|
+
### 初始化 allm
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
`schedt`是一个全局变量,所有的线程都可以操作它(要加锁),同时,会在`schedinit`中完成对`m0`的初始化。
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
```go
|
|
249
|
+
// 初始化 m
|
|
250
|
+
func mcommoninit(mp *m) {
|
|
251
|
+
// 初始化过程中_g_ = g0
|
|
252
|
+
_g_ := getg()
|
|
253
|
+
|
|
254
|
+
// g0 stack won't make sense for user (and is not necessary unwindable).
|
|
255
|
+
if _g_ != _g_.m.g0 {
|
|
256
|
+
callers(1, mp.createstack[:])
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// random 初始化
|
|
260
|
+
mp.fastrand = 0x49f6428a + uint32(mp.id) + uint32(cputicks())
|
|
261
|
+
if mp.fastrand == 0 {
|
|
262
|
+
mp.fastrand = 0x49f6428a
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
lock(&sched.lock)
|
|
266
|
+
// 设置 m 的 id
|
|
267
|
+
mp.id = sched.mcount
|
|
268
|
+
sched.mcount++
|
|
269
|
+
// 检查已创建系统线程是否超过了数量限制(10000)
|
|
270
|
+
checkmcount()
|
|
271
|
+
|
|
272
|
+
// ………………省略了初始化 gsignal
|
|
273
|
+
|
|
274
|
+
// Add to allm so garbage collector doesn't free g->m
|
|
275
|
+
// when it is just in a register or thread-local storage.
|
|
276
|
+
mp.alllink = allm
|
|
277
|
+
|
|
278
|
+
atomicstorep(unsafe.Pointer(&allm), unsafe.Pointer(mp))
|
|
279
|
+
unlock(&sched.lock)
|
|
280
|
+
|
|
281
|
+
// ………………
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
因为 sched 是一个全局变量,多个线程同时操作 sched 会有并发问题,因此先要加锁,操作结束之后再解锁。
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
```go
|
|
290
|
+
mp.id = sched.mcount
|
|
291
|
+
sched.mcount++
|
|
292
|
+
checkmcount()
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
可以看到,m0 的 id 是 0,并且之后创建的 m 的 id 是递增的。`checkmcount()` 函数检查已创建系统线程是否超过了数量限制(10000)。
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
```go
|
|
300
|
+
mp.alllink = allm
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
将 m 挂到全局变量 allm 上,allm 是一个指向 m 的的指针。
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
```go
|
|
308
|
+
atomicstorep(unsafe.Pointer(&allm), unsafe.Pointer(mp))
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
这一行将 allm 变成 m 的地址,这样变成了一个循环链表。之后再新建 m 的时候,新 m 的 alllink 就会指向本次的 m,最后 allm 又会指向新创建的 m。
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+

|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
上图中,1 将 m0 挂在 allm 上。之后,若新创建 m,则 m1 会和 m0 相连。
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
### **初始化 allp**
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
跳过一些其他的初始化代码,继续往后看:
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
```go
|
|
328
|
+
procs := ncpu
|
|
329
|
+
if n, ok := atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 {
|
|
330
|
+
procs = n
|
|
331
|
+
}
|
|
332
|
+
if procs > _MaxGomaxprocs {
|
|
333
|
+
procs = _MaxGomaxprocs
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
这里就是设置 procs,它决定创建 P 的数量。ncpu 这里已经被赋上了系统的核心数,因此代码里不设置 GOMAXPROCS 也是没问题的。这里还限制了 procs 的最大值,为 1024。
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
来看最后一个核心的函数:
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
```go
|
|
345
|
+
// src/runtime/proc.go
|
|
346
|
+
|
|
347
|
+
func procresize(nprocs int32) *p {
|
|
348
|
+
old := gomaxprocs
|
|
349
|
+
if old < 0 || old > _MaxGomaxprocs || nprocs <= 0 || nprocs > _MaxGomaxprocs {
|
|
350
|
+
throw("procresize: invalid arg")
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ……………………
|
|
354
|
+
|
|
355
|
+
// update statistics
|
|
356
|
+
// 更新数据
|
|
357
|
+
now := nanotime()
|
|
358
|
+
if sched.procresizetime != 0 {
|
|
359
|
+
sched.totaltime += int64(old) * (now - sched.procresizetime)
|
|
360
|
+
}
|
|
361
|
+
sched.procresizetime = now
|
|
362
|
+
|
|
363
|
+
// 初始化所有的 P
|
|
364
|
+
for i := int32(0); i < nprocs; i++ {
|
|
365
|
+
pp := allp[i]
|
|
366
|
+
if pp == nil {
|
|
367
|
+
// 申请新对象
|
|
368
|
+
pp = new(p)
|
|
369
|
+
pp.id = i
|
|
370
|
+
// pp 的初始状态为 stop
|
|
371
|
+
pp.status = _Pgcstop
|
|
372
|
+
pp.sudogcache = pp.sudogbuf[:0]
|
|
373
|
+
for i := range pp.deferpool {
|
|
374
|
+
pp.deferpool[i] = pp.deferpoolbuf[i][:0]
|
|
375
|
+
}
|
|
376
|
+
// 将 pp 存放到 allp 处
|
|
377
|
+
atomicstorep(unsafe.Pointer(&allp[i]), unsafe.Pointer(pp))
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ……………………
|
|
381
|
+
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// 释放多余的 P。由于减少了旧的 procs 的数量,因此需要释放
|
|
385
|
+
// ……………………
|
|
386
|
+
|
|
387
|
+
// 获取当前正在运行的 g 指针,初始化时 _g_ = g0
|
|
388
|
+
_g_ := getg()
|
|
389
|
+
if _g_.m.p != 0 && _g_.m.p.ptr().id < nprocs {
|
|
390
|
+
// continue to use the current P
|
|
391
|
+
// 继续使用当前 P
|
|
392
|
+
_g_.m.p.ptr().status = _Prunning
|
|
393
|
+
} else {
|
|
394
|
+
// 初始化时执行这个分支
|
|
395
|
+
|
|
396
|
+
// ……………………
|
|
397
|
+
|
|
398
|
+
_g_.m.p = 0
|
|
399
|
+
_g_.m.mcache = nil
|
|
400
|
+
// 取出第 0 号 p
|
|
401
|
+
p := allp[0]
|
|
402
|
+
p.m = 0
|
|
403
|
+
p.status = _Pidle
|
|
404
|
+
// 将 p0 和 m0 关联起来
|
|
405
|
+
acquirep(p)
|
|
406
|
+
if trace.enabled {
|
|
407
|
+
traceGoStart()
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
var runnablePs *p
|
|
411
|
+
// 下面这个 for 循环把所有空闲的 p 放入空闲链表
|
|
412
|
+
for i := nprocs - 1; i >= 0; i-- {
|
|
413
|
+
p := allp[i]
|
|
414
|
+
// allp[0] 跟 m0 关联了,不会进行之后的“放入空闲链表”
|
|
415
|
+
if _g_.m.p.ptr() == p {
|
|
416
|
+
continue
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// 状态转为 idle
|
|
420
|
+
p.status = _Pidle
|
|
421
|
+
// p 的 LRQ 里没有 G
|
|
422
|
+
if runqempty(p) {
|
|
423
|
+
// 放入全局空闲链表
|
|
424
|
+
pidleput(p)
|
|
425
|
+
} else {
|
|
426
|
+
p.m.set(mget())
|
|
427
|
+
p.link.set(runnablePs)
|
|
428
|
+
runnablePs = p
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
stealOrder.reset(uint32(nprocs))
|
|
432
|
+
var int32p *int32 = &gomaxprocs // make compiler check that gomaxprocs is an int32
|
|
433
|
+
atomic.Store((*uint32)(unsafe.Pointer(int32p)), uint32(nprocs))
|
|
434
|
+
// 返回有本地任务的 P 链表
|
|
435
|
+
return runnablePs
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
代码比较长,**这个函数不仅是初始化的时候会执行到,在中途改变 procs 的值的时候,仍然会调用它**。所有存在很多一般不用关心的代码,因为一般不会在中途重新设置 procs 的值。我把初始化无关的代码删掉了,这样会更清晰一些。
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
函数先是**从堆上创建了 nproc 个 P**,并且把 P 的状态设置为 `_Pgcstop`,**现在全局变量 allp 里就维护了所有的 P**。
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
接着,调用函数 `acquirep` 将 p0 和 m0 关联起来。我们来详细看一下:
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
```go
|
|
451
|
+
func acquirep(_p_ *p) {
|
|
452
|
+
// Do the part that isn't allowed to have write barriers.
|
|
453
|
+
acquirep1(_p_)
|
|
454
|
+
|
|
455
|
+
// have p; write barriers now allowed
|
|
456
|
+
_g_ := getg()
|
|
457
|
+
_g_.m.mcache = _p_.mcache
|
|
458
|
+
|
|
459
|
+
// ……………………
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
先调用 `acquirep1` 函数真正地进行关联,之后,将 p0 的 mcache 资源赋给 m0。再来看 `acquirep1`:
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
```go
|
|
468
|
+
func acquirep1(_p_ *p) {
|
|
469
|
+
_g_ := getg()
|
|
470
|
+
|
|
471
|
+
// ……………………
|
|
472
|
+
|
|
473
|
+
_g_.m.p.set(_p_)
|
|
474
|
+
_p_.m.set(_g_.m)
|
|
475
|
+
_p_.status = _Prunning
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
可以看到就是一些字段相互设置,执行完成后:
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
```go
|
|
484
|
+
g0.m.p = p0
|
|
485
|
+
p0.m = m0
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
并且,p0 的状态变成了 `_Prunning`。
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
接下来是一个循环,它将除了 p0 的所有非空闲的 P,放入 P 链表 runnablePs,并返回给 procresize 函数的调用者,并由调用者来“调度”这些 P。
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
# 总结
|
|
496
|
+
|
|
497
|
+
1. 使用 make([]p, nprocs) 初始化全局变量 allp,即 allp = make([]p, nprocs)
|
|
498
|
+
2. 循环创建并初始化 nprocs 个 p 结构体对象并依次保存在 allp 切片之中
|
|
499
|
+
3. 把 m0 和 allp[0] 绑定在一起,即 m0.p = allp[0],allp[0].m = m0
|
|
500
|
+
4. 把除了 allp[0] 之外的所有 p 放入到全局变量 sched 的 pidle 空闲队列之中
|
|
501
|
+
|
|
502
|
+
说明一下,最后一步,代码里是将所有空闲的 P 放入到调度器的全局空闲队列;对于非空闲的 P(本地队列里有 G 待执行),则是生成一个 P 链表,返回给 procresize 函数的调用者。
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
最后我们将 allp 和 allm 都添加到图上:
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+

|
|
509
|
+
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
1. unsafe 包提供了 2 点重要的能力:
|
|
2
|
+
2. 任何类型的指针和 unsafe.Pointer 可以相互转换。uintptr 类型和 unsafe.Pointer 可以相互转换。
|
|
3
|
+
|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
pointer 不能直接进行数学运算,但可以把它转换成 uintptr,对 uintptr 类型进行数学运算,再转换成 pointer 类型。
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
```go
|
|
11
|
+
func main() {
|
|
12
|
+
p := Programmer{"stefno", 18, "go"}
|
|
13
|
+
fmt.Println(p)
|
|
14
|
+
|
|
15
|
+
lang := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Sizeof(int(0)) + unsafe.Sizeof(string(""))))
|
|
16
|
+
*lang = "Golang"
|
|
17
|
+
|
|
18
|
+
fmt.Println(p)
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
```go
|
|
24
|
+
// uintptr 是一个整数类型,它足够大,可以存储
|
|
25
|
+
type uintptr uintptr
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
如下,map从汇编上可以知道是`*hmap`类型的,但是语言层面可见的就是map结构。Pointer可以转化的必要条件是语言(语法检查工具)可见范围内要出现取引用符`&`
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
```go
|
|
33
|
+
package main
|
|
34
|
+
|
|
35
|
+
import (
|
|
36
|
+
"fmt"
|
|
37
|
+
"unsafe"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
type myInt int
|
|
41
|
+
type myIntPtr *int
|
|
42
|
+
|
|
43
|
+
func main() {
|
|
44
|
+
mp := make(map[string]int)
|
|
45
|
+
mp["qcrao"] = 100
|
|
46
|
+
mp["stefno"] = 18
|
|
47
|
+
|
|
48
|
+
count := *(**int)(unsafe.Pointer(&mp))
|
|
49
|
+
fmt.Println(count, len(mp)) // 2 2
|
|
50
|
+
|
|
51
|
+
var fooi myInt = 1
|
|
52
|
+
var i int = 5
|
|
53
|
+
var fooip myIntPtr = &i
|
|
54
|
+
|
|
55
|
+
fmt.Println(unsafe.Pointer(fooip))
|
|
56
|
+
fmt.Println(unsafe.Pointer(&fooi))
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
还有一点要注意的是,uintptr 并没有指针的语义,意思就是 uintptr 所指向的对象会被 gc 无情地回收。而 unsafe.Pointer 有指针语义,可以保护它所指向的对象在“有用”的时候不会被垃圾回收。
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
|
|
2
|
+
# Intro
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
This blog is written in eng cus deref, ref and concept related to them could be super confusing when translation happens.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# From top to bottom
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
fn from(args: impl Iterator<Item = String>) -> () {
|
|
13
|
+
let mut args = let mut args = args.peekable()
|
|
14
|
+
args.next();
|
|
15
|
+
|
|
16
|
+
while let Some(s) = args.next_if(|x: &String| x.starts_with("-")) {
|
|
17
|
+
match s.as_str() {
|
|
18
|
+
"-i" | "--ignore_case" => ignore_case = true,
|
|
19
|
+
_ => {
|
|
20
|
+
eprintln!("invalid parameter");
|
|
21
|
+
process::exit(1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
First of all, lets take a look at a simple case: we want to use iterator and walk through the vector. And for some reason, we want to check the next entry before advancing by calling `fn next`. A simple way is to use the official wrapper for all the objects that implement trait Iterator → `peekable`.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
```rust
|
|
32
|
+
fn peekable(self) -> Peekable<Self>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
All right, but why the f**k we should declare it as mutable? That’s cus the function next_if is defined as,
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
```rust
|
|
40
|
+
pub fn next_if(
|
|
41
|
+
&mut self,
|
|
42
|
+
func: impl FnOnce(&<I as Iterator>::Item) -> bool
|
|
43
|
+
) -> Option<<I as Iterator>::Item>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
OK, now lets look into this function, this function takes a closure func, and throw true or false according to value of the next variable.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
```rust
|
|
51
|
+
// official usage example of next_if
|
|
52
|
+
let mut iter = (0..5).peekable();
|
|
53
|
+
// The first item of the iterator is 0; consume it.
|
|
54
|
+
assert_eq!(iter.next_if(|&x| x == 0), Some(0));
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
Why `|&x| x == 0` instead of `|x| x == 0`?
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
To explain this question, we would have to throw another question: [**Do iterators return a reference to items or the value of the items in Rust?**](https://stackoverflow.com/questions/60780389/do-iterators-return-a-reference-to-items-or-the-value-of-the-items-in-rust)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
So actually the input of the& closure here is of type `&String`. And according to the definition `func: impl FnOnce(&<I as Iterator>::Item)`, the input of closure is `&<I as Iterator>::Item`. Guess what, we triggered a [irrefutable matching](https://stackoverflow.com/questions/70278831/why-does-the-compiler-allow-pattern-matching-an-enum-in-a-function-argument-if-i), and the reason we use `|&x| x == 0` is that, under this circumstance, x is dereferenced as `int32`. Don’t take it for granted, cus this is for the sake of the realization of trait copy on `int32`. So actually `assert_eq!(iter.next_if(|x| *x == 0), Some(0));` is legal as well.
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
So, back to the starting point,
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
```rust
|
|
71
|
+
args.next_if(|x: &String| x.starts_with("-"))
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
Yes, String has no trait copy. That’s the PUZZLE.
|
|
76
|
+
|