動機

偶然找到Green Threads Explained很讚,做點筆記

thread要有?

  • scheduler
    • 因為要schedule,所以要context switch
      • context switch要換的是當下執行的狀態,所以要看
        • arch(x86, amd64, arm)的ABI
        • arch的reg

實作?

這裡就看我覺得重要的地方

struct gt

thread的狀態,兩件事

  1. 紀錄當下執行的狀態,或是說在cpu運算會用到的reg
  • reg其實可以當成cpu的狀態
  • 同理,可以把這個概念推廣成在一般自己的function會用到的參數都是function的狀態
  1. 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

這要同時看gtgogtswtch

gtgo負責

  1. 設給cpu用的stack,這
  2. 對pc(program counter)動手腳
  • 我們沒辦法直接改pc,所以要利用stack動手腳
    • stack第一層放gtstop,第二層放f
    • 第二層會在gtswtch(context switch)的ret,被拿出來跑到f去
    • stack第一層會在f直接call時利用到,return就直接到gtstop
*(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?

  1. 看gtswtch怎麼被call的
  • gtswtch(old, new);
  1. 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?