同步信号是由程序错误触发的信号 执行:SIGBUS,SIGFPE和SIGSEGV。这些仅被考虑 由程序执行引起的同步,而不是在使用时发送 os.Process.Kill或kill程序或一些类似的机制。在 一般情况下,除了下面讨论过,Go程序将转换为 同步信号进入运行时恐慌。
然而,似乎recover()
没有抓住这一点。
程序:
package main
import (
"fmt"
"unsafe"
"log"
)
func seeAnotherDay() {
defer func() {
if p := recover(); p != nil {
err := fmt.Errorf("recover panic: panic call")
log.Println(err)
return
}
}()
panic("oops")
}
func notSoMuch() {
defer func() {
if p := recover(); p != nil {
err := fmt.Errorf("recover panic: sigseg")
log.Println(err)
return
}
}()
b := make([]byte, 1)
log.Println("access some memory")
foo := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(9999999999999999)))
fmt.Print(*foo + 1)
}
func main() {
seeAnotherDay()
notSoMuch()
}
输出:
2017/04/04 12:13:16 recover panic: panic call
2017/04/04 12:13:16 access some memory
unexpected fault address 0xb01dfacedebac1e
fatal error: fault
[signal SIGSEGV: segmentation violation code=0x1 addr=0xb01dfacedebac1e pc=0x108aa8a]
goroutine 1 [running]:
runtime.throw(0x10b5807, 0x5)
/usr/local/go/src/runtime/panic.go:596 +0x95 fp=0xc420043ea8 sp=0xc420043e88
runtime.sigpanic()
/usr/local/go/src/runtime/signal_unix.go:297 +0x28c fp=0xc420043ef8 sp=0xc420043ea8
main.notSoMuch()
/Users/kbrandt/src/sigseg/main.go:32 +0xca fp=0xc420043f78 sp=0xc420043ef8
main.main()
/Users/kbrandt/src/sigseg/main.go:37 +0x25 fp=0xc420043f88 sp=0xc420043f78
runtime.main()
/usr/local/go/src/runtime/proc.go:185 +0x20a fp=0xc420043fe0 sp=0xc420043f88
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:2197 +0x1 fp=0xc420043fe8 sp=0xc420043fe0
exit status 2
有没有办法以局部化到代码某些部分的方式处理SIGSEGV?
答案 0 :(得分:2)
是的,您将希望使用debug.SetPanicOnFault将意外(非零)地址处的故障转换为可从中恢复的紧急情况。从文档中:
SetPanicOnFault控制程序在意外(非nil)地址处发生错误时的运行时行为。此类错误通常是由诸如运行时内存损坏之类的错误引起的,因此默认响应是使程序崩溃。在不太动态的情况下,使用内存映射文件或不安全地操作内存的程序可能会在非空地址处引起错误; SetPanicOnFault允许此类程序请求运行时仅触发紧急事件,而不触发崩溃。 SetPanicOnFault仅适用于当前goroutine。它返回以前的设置。
对于影响的本地化,请注意,SetPanicOnFault是在goroutine级别设置的,因此单个goroutine可以处理已知的不安全访问。
答案 1 :(得分:1)
当你遇到一个sigsegv时,你真的处于一个关于程序状态的全押的情况。唯一通常安全的做法是停止所有操作,并可能让系统将内存转储到文件进行调试,这就是Go所做的。实际上没有任何方法可以保护主运行时间#34;在这种情况下。
如果您的运行时运行的代码不受信任或不安全,那么您应该将其隔离到单独的进程中。如果你是运行从用户(而不是用户自己)收到的代码的那个,那么这个过程绝对应该被沙箱化。
所以我的建议是,执行以下任一操作: