annidy / notes

0 stars 0 forks source link

defer的实现 #295

Open annidy opened 1 month ago

annidy commented 1 month ago

defer是如何实现的,我们用通过汇编来看看

func main() {
    defer println("hello")

    println("world")
}

打印汇编go build -gcflags="-S" main.go

不建议用 go tool compile -S main.go , see issue

main.main STEXT size=123 args=0x0 locals=0x28 funcid=0x0 align=0x0
        0x0000 00000 (/Users/annidy/Temp/hello_go/defer/main.go:3)      TEXT    main.main(SB), ABIInternal, $40-0
        0x0000 00000 (/Users/annidy/Temp/hello_go/defer/main.go:3)      CMPQ    SP, 16(R14)
        0x0004 00004 (/Users/annidy/Temp/hello_go/defer/main.go:3)      PCDATA  $0, $-2
        0x0004 00004 (/Users/annidy/Temp/hello_go/defer/main.go:3)      JLS     116
        0x0006 00006 (/Users/annidy/Temp/hello_go/defer/main.go:3)      PCDATA  $0, $-1
        0x0006 00006 (/Users/annidy/Temp/hello_go/defer/main.go:3)      SUBQ    $40, SP
        0x000a 00010 (/Users/annidy/Temp/hello_go/defer/main.go:3)      MOVQ    BP, 32(SP)
        0x000f 00015 (/Users/annidy/Temp/hello_go/defer/main.go:3)      LEAQ    32(SP), BP
        0x0014 00020 (/Users/annidy/Temp/hello_go/defer/main.go:3)      MOVQ    $0, R13
        0x001b 00027 (/Users/annidy/Temp/hello_go/defer/main.go:3)      MOVQ    R13, 24(SP)
        0x0020 00032 (/Users/annidy/Temp/hello_go/defer/main.go:3)      FUNCDATA        $0, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB)
        0x0020 00032 (/Users/annidy/Temp/hello_go/defer/main.go:3)      FUNCDATA        $1, gclocals·CnDyI2HjYXFz19SsOj98tw==(SB)
        0x0020 00032 (/Users/annidy/Temp/hello_go/defer/main.go:3)      FUNCDATA        $4, main.main.opendefer(SB)
        0x0020 00032 (/Users/annidy/Temp/hello_go/defer/main.go:3)      MOVB    $0, main..autotmp_0+23(SP)
        0x0025 00037 (/Users/annidy/Temp/hello_go/defer/main.go:4)      LEAQ    main.main.func1·f(SB), AX
        0x002c 00044 (/Users/annidy/Temp/hello_go/defer/main.go:4)      MOVQ    AX, main..autotmp_1+24(SP)
        0x0031 00049 (/Users/annidy/Temp/hello_go/defer/main.go:4)      MOVB    $1, main..autotmp_0+23(SP)
        0x0036 00054 (/Users/annidy/Temp/hello_go/defer/main.go:6)      PCDATA  $1, $1
        0x0036 00054 (/Users/annidy/Temp/hello_go/defer/main.go:6)      CALL    runtime.printlock(SB)
        0x003b 00059 (/Users/annidy/Temp/hello_go/defer/main.go:6)      LEAQ    go:string."world\n"(SB), AX
        0x0042 00066 (/Users/annidy/Temp/hello_go/defer/main.go:6)      MOVL    $6, BX
        0x0047 00071 (/Users/annidy/Temp/hello_go/defer/main.go:6)      CALL    runtime.printstring(SB)
        0x004c 00076 (/Users/annidy/Temp/hello_go/defer/main.go:6)      CALL    runtime.printunlock(SB)
        0x0051 00081 (/Users/annidy/Temp/hello_go/defer/main.go:7)      MOVB    $0, main..autotmp_0+23(SP)
        0x0056 00086 (/Users/annidy/Temp/hello_go/defer/main.go:7)      CALL    main.main.func1(SB)
        0x005b 00091 (/Users/annidy/Temp/hello_go/defer/main.go:7)      MOVQ    32(SP), BP
        0x0060 00096 (/Users/annidy/Temp/hello_go/defer/main.go:7)      ADDQ    $40, SP
        0x0064 00100 (/Users/annidy/Temp/hello_go/defer/main.go:7)      RET

main.main.func1 STEXT size=93 args=0x0 locals=0x18 funcid=0x15 align=0x0
        0x0000 00000 (/Users/annidy/Temp/hello_go/defer/main.go:4)      TEXT    main.main.func1(SB), WRAPPER|ABIInternal, $24-0
        0x0000 00000 (/Users/annidy/Temp/hello_go/defer/main.go:4)      CMPQ    SP, 16(R14)
        0x0004 00004 (/Users/annidy/Temp/hello_go/defer/main.go:4)      PCDATA  $0, $-2
        0x0004 00004 (/Users/annidy/Temp/hello_go/defer/main.go:4)      JLS     69
        0x0006 00006 (/Users/annidy/Temp/hello_go/defer/main.go:4)      PCDATA  $0, $-1
        0x0006 00006 (/Users/annidy/Temp/hello_go/defer/main.go:4)      SUBQ    $24, SP
        0x000a 00010 (/Users/annidy/Temp/hello_go/defer/main.go:4)      MOVQ    BP, 16(SP)
        0x000f 00015 (/Users/annidy/Temp/hello_go/defer/main.go:4)      LEAQ    16(SP), BP
        0x0014 00020 (/Users/annidy/Temp/hello_go/defer/main.go:4)      MOVQ    32(R14), R12
        0x0018 00024 (/Users/annidy/Temp/hello_go/defer/main.go:4)      TESTQ   R12, R12
        0x001b 00027 (/Users/annidy/Temp/hello_go/defer/main.go:4)      JNE     76
        0x001d 00029 (/Users/annidy/Temp/hello_go/defer/main.go:4)      NOP
        0x001d 00029 (/Users/annidy/Temp/hello_go/defer/main.go:4)      FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
        0x001d 00029 (/Users/annidy/Temp/hello_go/defer/main.go:4)      FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
        0x001d 00029 (/Users/annidy/Temp/hello_go/defer/main.go:4)      PCDATA  $1, $0
        0x001d 00029 (/Users/annidy/Temp/hello_go/defer/main.go:4)      NOP
        0x0020 00032 (/Users/annidy/Temp/hello_go/defer/main.go:4)      CALL    runtime.printlock(SB)
        0x0025 00037 (/Users/annidy/Temp/hello_go/defer/main.go:4)      LEAQ    go:string."hello\n"(SB), AX
        0x002c 00044 (/Users/annidy/Temp/hello_go/defer/main.go:4)      MOVL    $6, BX
        0x0031 00049 (/Users/annidy/Temp/hello_go/defer/main.go:4)      CALL    runtime.printstring(SB)
        0x0036 00054 (/Users/annidy/Temp/hello_go/defer/main.go:4)      CALL    runtime.printunlock(SB)
        0x003b 00059 (/Users/annidy/Temp/hello_go/defer/main.go:4)      MOVQ    16(SP), BP
        0x0040 00064 (/Users/annidy/Temp/hello_go/defer/main.go:4)      ADDQ    $24, SP
        0x0044 00068 (/Users/annidy/Temp/hello_go/defer/main.go:4)      RET

原来defer的语句是一个函数,在函数退出时调用它。

如果defer的个数不是静态的,怎么调用?

func main() {
    for i := 0; i < 10; i++ {
        defer println("hello")
    }
    println("world")
}

再看看main函数的汇编

main.main STEXT size=133 args=0x0 locals=0x20 funcid=0x0 align=0x0
        0x0000 00000 (/Users/annidy/Temp/hello_go/defer/main.go:3)      TEXT    main.main(SB), ABIInternal, $32-0
        0x0000 00000 (/Users/annidy/Temp/hello_go/defer/main.go:3)      CMPQ    SP, 16(R14)
        0x0004 00004 (/Users/annidy/Temp/hello_go/defer/main.go:3)      PCDATA  $0, $-2
        0x0004 00004 (/Users/annidy/Temp/hello_go/defer/main.go:3)      JLS     121
        0x0006 00006 (/Users/annidy/Temp/hello_go/defer/main.go:3)      PCDATA  $0, $-1
        0x0006 00006 (/Users/annidy/Temp/hello_go/defer/main.go:3)      SUBQ    $32, SP
        0x000a 00010 (/Users/annidy/Temp/hello_go/defer/main.go:3)      MOVQ    BP, 24(SP)
        0x000f 00015 (/Users/annidy/Temp/hello_go/defer/main.go:3)      LEAQ    24(SP), BP
        0x0014 00020 (/Users/annidy/Temp/hello_go/defer/main.go:3)      FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
        0x0014 00020 (/Users/annidy/Temp/hello_go/defer/main.go:3)      FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
        0x0014 00020 (/Users/annidy/Temp/hello_go/defer/main.go:3)      XORL    AX, AX
        0x0016 00022 (/Users/annidy/Temp/hello_go/defer/main.go:4)      JMP     32
        0x0018 00024 (/Users/annidy/Temp/hello_go/defer/main.go:4)      MOVQ    main.i+16(SP), AX
        0x001d 00029 (/Users/annidy/Temp/hello_go/defer/main.go:4)      INCQ    AX
        0x0020 00032 (/Users/annidy/Temp/hello_go/defer/main.go:4)      CMPQ    AX, $10
        0x0024 00036 (/Users/annidy/Temp/hello_go/defer/main.go:4)      JGE     79
        0x0026 00038 (/Users/annidy/Temp/hello_go/defer/main.go:4)      MOVQ    AX, main.i+16(SP)
        0x002b 00043 (/Users/annidy/Temp/hello_go/defer/main.go:5)      LEAQ    main.main.func1·f(SB), AX
        0x0032 00050 (/Users/annidy/Temp/hello_go/defer/main.go:5)      PCDATA  $1, $0
        0x0032 00050 (/Users/annidy/Temp/hello_go/defer/main.go:5)      CALL    runtime.deferproc(SB)
        0x0037 00055 (/Users/annidy/Temp/hello_go/defer/main.go:5)      TESTL   AX, AX
        0x0039 00057 (/Users/annidy/Temp/hello_go/defer/main.go:5)      JNE     64
        0x003b 00059 (/Users/annidy/Temp/hello_go/defer/main.go:5)      JMP     24
        0x003d 00061 (/Users/annidy/Temp/hello_go/defer/main.go:5)      NOP
        0x0040 00064 (/Users/annidy/Temp/hello_go/defer/main.go:5)      CALL    runtime.deferreturn(SB)
        0x0045 00069 (/Users/annidy/Temp/hello_go/defer/main.go:5)      MOVQ    24(SP), BP
        0x004a 00074 (/Users/annidy/Temp/hello_go/defer/main.go:5)      ADDQ    $32, SP
        0x004e 00078 (/Users/annidy/Temp/hello_go/defer/main.go:5)      RET
        0x004f 00079 (/Users/annidy/Temp/hello_go/defer/main.go:7)      CALL    runtime.printlock(SB)
        0x0054 00084 (/Users/annidy/Temp/hello_go/defer/main.go:7)      LEAQ    go:string."world\n"(SB), AX
        0x005b 00091 (/Users/annidy/Temp/hello_go/defer/main.go:7)      MOVL    $6, BX
        0x0060 00096 (/Users/annidy/Temp/hello_go/defer/main.go:7)      CALL    runtime.printstring(SB)
        0x0065 00101 (/Users/annidy/Temp/hello_go/defer/main.go:7)      CALL    runtime.printunlock(SB)
        0x006a 00106 (/Users/annidy/Temp/hello_go/defer/main.go:8)      CALL    runtime.deferreturn(SB)
        0x006f 00111 (/Users/annidy/Temp/hello_go/defer/main.go:8)      MOVQ    24(SP), BP
        0x0074 00116 (/Users/annidy/Temp/hello_go/defer/main.go:8)      ADDQ    $32, SP
        0x0078 00120 (/Users/annidy/Temp/hello_go/defer/main.go:8)      RET

这次循环把defer后面的函数放到runtime.deferproc(SB)中,然后在函数结尾调用runtime.deferreturn(SB)。

// Create a new deferred function fn, which has no arguments and results.
// The compiler turns a defer statement into a call to this.
func deferproc(fn func()) {
        // 获取当前的G
    gp := getg()
    if gp.m.curg != gp {
        // go code on the system stack can't defer
        throw("defer on system stack")
    }

    d := newdefer()
    if d._panic != nil {
        throw("deferproc: d.panic != nil after newdefer")
    }
        // 链表结构,将d插入到表头。先入后出
    d.link = gp._defer
    gp._defer = d  
    d.fn = fn
    d.pc = getcallerpc()
    // We must not be preempted between calling getcallersp and
    // storing it to d.sp because getcallersp's result is a
    // uintptr stack pointer.
    d.sp = getcallersp()

    // deferproc returns 0 normally.
    // a deferred func that stops a panic
    // makes the deferproc return 1.
    // the code the compiler generates always
    // checks the return value and jumps to the
    // end of the function if deferproc returns != 0.
    return0()
    // No code can go here - the C return register has
    // been set and must not be clobbered.
}

// deferreturn runs deferred functions for the caller's frame.
// The compiler inserts a call to this at the end of any
// function which calls defer.
func deferreturn() {
    gp := getg()
    for {
        d := gp._defer
        if d == nil {
            return
        }
        sp := getcallersp()
        // 链表保存了所有闭包,需要判断sp以防止调用到其它函数的defer方法
                 if d.sp != sp {
            return
        }
        if d.openDefer {
            done := runOpenDeferFrame(d)
            if !done {
                throw("unfinished open-coded defers in deferreturn")
            }
            gp._defer = d.link
            freedefer(d)
            // If this frame uses open defers, then this
            // must be the only defer record for the
            // frame, so we can just return.
            return
        }

        fn := d.fn
        d.fn = nil
        gp._defer = d.link
        freedefer(d)
        fn()
    }
}

当defer较多时,最终还是用链表来实现的