動機
偶然找到Green Threads Explained很讚,做點筆記
thread要有?
- scheduler
- 因為要schedule,所以要context switch
- context switch要換的是當下執行的狀態,所以要看
- arch(x86, amd64, arm)的ABI
- arch的reg
- context switch要換的是當下執行的狀態,所以要看
- 因為要schedule,所以要context switch
實作?
這裡就看我覺得重要的地方
struct gt
thread的狀態,兩件事
- 紀錄當下執行的狀態,或是說在cpu運算會用到的reg
- reg其實可以當成cpu的狀態
- 同理,可以把這個概念推廣成在一般自己的function會用到的參數都是function的狀態
- thread的狀態
struct gt {
struct gtctx {
uint64_t rsp; // Stack pointer for top address of the stack
uint64_t r15; // I dont know
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t rbx; // Base index (for use with arrays)
uint64_t rbp; // Stack base pointer for holding the address of the current stack frame.
} ctx;
enum {
Unused,
Running,
Ready, // has rest of works
} st;
};
context switch
這要同時看gtgo
與gtswtch
gtgo負責
- 設給cpu用的stack,這
- 對pc(program counter)動手腳
- 我們沒辦法直接改pc,所以要利用stack動手腳
- stack第一層放
gtstop
,第二層放f
- 第二層會在gtswtch(context switch)的
ret
,被拿出來跑到f去 - stack第一層會在f直接call時利用到,return就直接到
gtstop
- stack第一層放
*(uint64_t *)&stack[StackSize - 8] = (uint64_t)gtstop;
*(uint64_t *)&stack[StackSize - 16] = (uint64_t)f;
p->ctx.rsp = (uint64_t)&stack[StackSize - 16];
gtswtch就是把reg存起來,但問題是從哪到哪? 怎麼看old與new?
- 看gtswtch怎麼被call的
gtswtch(old, new);
- arch的ABI
- ABI會說,reg要怎麼用,像function的參數要怎麼帶
- 這裡
rdi
是第一個參數,rsi
是第二個參數
gtswtch:
mov %rsp, 0x00(%rdi)
mov %r15, 0x08(%rdi)
mov %r14, 0x10(%rdi)
mov %r13, 0x18(%rdi)
mov %r12, 0x20(%rdi)
mov %rbx, 0x28(%rdi)
mov %rbp, 0x30(%rdi)
mov 0x00(%rsi), %rsp
mov 0x08(%rsi), %r15
mov 0x10(%rsi), %r14
mov 0x18(%rsi), %r13
mov 0x20(%rsi), %r12
mov 0x28(%rsi), %rbx
mov 0x30(%rsi), %rbp
ret
整體邏輯
一個array去放thread,之後在yield
被call時,換(這裡用RR去選)到下一個thread
Goroutines
Goroutines也是cooperative thread,與Green Threads Explained一樣,但感覺怪怪的?
Goroutines沒有yield
yield是runtime叫的
- Channel send and receive operations, if those operations would block.
- The Go statement, although there is no guarantee that new goroutine will be scheduled immediately.
- Blocking syscalls like file and network operations.
- After being stopped for a garbage collection cycle.
syscall不會block其他threadㄟ
所以go會把出事的thread放到其他OS thread去跑,畢竟syscall會block
這也是Goroutines要分成下面三個的理由
- G (green thread)
- M (OS thread,真正做事的)
- P (運行G的context)
如果G發syscall就會讓M被卡,所以需要P讓其他G可以避難到其他M去
Goroutines的context switch比較快?
go只保留需要的reg
since it is invoked implicitly in the code e.g. during sleep or channel wait, the compile only needs to safe/restore the registers which are alive at these points. In Go, this means only 3 registers i.e. PC, SP and DX (Data Registers) being updated during context switch rather than all registers
Ref
The Go scheduler Why goroutines are not lightweight threads?