Golang面试中经常会遇到defer-panic-recover相关的题目,其底层工作原理是什么呢?

快速要点:

// builtin.go#234
// The recover built-in function allows a program to manage behavior of a
// panicking goroutine. Executing a call to recover inside a deferred
// function (but not any function called by it) stops the panicking sequence
// by restoring normal execution and retrieves the error value passed to the
// call of panic. If recover is called outside the deferred function it will
// not stop a panicking sequence. In this case, or when the goroutine is not
// panicking, or if the argument supplied to panic was nil, recover returns
// nil. Thus the return value from recover reports whether the goroutine is
// panicking.
func recover() interface{}

// runtime/panic.go#1043, p is a panic
      p.argp = unsafe.Pointer(getargp())

			if goexperiment.RegabiDefer {
				fn := deferFunc(d)
				fn()
			} else {
				// Pass a dummy RegArgs since we'll only take this path if
				// we're not using the register ABI.
				var regs abi.RegArgs
				reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz), uint32(d.siz), &regs)
			}
		}

// runtime/panic.go#1159
// The implementation of the predeclared function recover.
// Cannot split the stack because it needs to reliably
// find the stack segment of its caller.
//
// TODO(rsc): Once we commit to CopyStackAlways,
// this doesn't need to be nosplit.
//go:nosplit
func gorecover(argp uintptr) interface{} {
   // Must be in a function running as part of a deferred call during the panic.
   // Must be called from the topmost function of the call
   // (the function used in the defer statement).
   // p.argp is the argument pointer of that topmost deferred function call.
   // Compare against argp reported by caller.
   // If they match, the caller is the one who can recover.
   gp := getg()
   p := gp._panic
   if p != nil && !p.goexit && !p.recovered && argp == uintptr(p.argp) {
      p.recovered = true
      return p.arg
   }
   return nil
}