我试图在从Go调用程序时拦截系统调用,但是我遇到了两个问题。
孩子似乎挂了,这也挂起了父进程。似乎wait4(2)
阻塞似乎很奇怪,孩子最终不会叫exit(2)
退出吗?
我到达stdout
的系统调用不一致,有时最后的系统调用是3
,有时是6
或192
。我的代码中是否存在竞争条件?为什么会这样?
我试着听父母的信号,但我没有收到任何东西..
我已经用/bin/ls
代替我经常运行的程序。
package main
import (
"syscall"
"fmt"
"os/signal"
"os"
)
func main() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
go SignalListener(c)
attr := new(syscall.ProcAttr)
attr.Sys = new(syscall.SysProcAttr)
attr.Sys.Ptrace = true
pid, err := syscall.ForkExec("/bin/ls", nil, attr)
if err != nil {
panic(err)
}
var wstat syscall.WaitStatus
var regs syscall.PtraceRegs
for {
fmt.Println("Waiting..")
_, err := syscall.Wait4(pid, &wstat, 0, nil)
fmt.Printf("Exited: %d\n", wstat.Exited())
if err != nil {
fmt.Println(err)
break
}
syscall.PtraceGetRegs(pid, ®s);
fmt.Printf("syscall: %d\n", regs.Orig_eax)
syscall.PtraceSyscall(pid, 0)
}
}
func SignalListener(c <-chan os.Signal) {
s := <-c
fmt.Printf("Got signal %d\n", s)
}
答案 0 :(得分:0)
简短的回答是,使用Go拦截系统调用将非常困难,任何ptrace都可能无法正常工作。
Go有一个运行时,它将go-routines多路复用到OS线程上。系统调用是一个调度点,所以在syscall返回后你可能在不同的线程上,而我认为ptrace遵循一个线程。
假设您正在运行的线程正在运行您的主要例程。然后你调用fmt.Println(它执行syscall.Write),因此Go运行时将你的例程从该线程中删除,并在另一个os线程中运行syscall(syscalls总是在不同的线程中运行)。当系统调用返回时,你的主要例程被放回到可运行例程的调度程序列表中,并且它将继续在任何可用的os线程上,这可能不是你正在进行的那个。
这也是您无法使用gdb
单步执行Go程序的原因。
如果您只想执行外部程序(例如/ bin / ls),可以使用标准库中的os/exec。
关于你要做的事情的程序可能是delve。我认为在每个单步执行的每个线程上都设置一个断点,然后根据go-routine id尝试找到你的例行程序所在的线程。