英文:
Forked ptraced process hangs
问题
我正在尝试在调用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)
}
希望这对你有帮助!
英文:
I am trying to intercept syscalls when calling a program from Go, however I'm running into two issues.
The child seems to hang, which hangs the parent process as well. It seems wait4(2)
is blocking which seems weird, wouldn't the child finally call exit(2)
to exit?
The syscalls I get to stdout
are not consistent, sometimes the last syscall is 3
, other times it's 6
or 192
. Do I have a race condition in my code? Why does this happen?
I tried listening for signals on the parent, but I don't receive anything..
I've substituted the program I usually run with /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, &regs);
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)
}
答案1
得分: 1
简短回答是,使用Go拦截系统调用会非常困难,而且使用ptrace可能不起作用。
Go具有将go-routine多路复用到操作系统线程上的运行时。系统调用是一个调度点,因此在系统调用返回后,你可能会在不同的线程上,而我认为ptrace只跟踪单个线程。
假设你正在跟踪的线程正在运行你的主要go-routine。然后你调用fmt.Println(它执行syscall.Write),因此Go运行时将你的go-routine从该线程中移除,并在不同的操作系统线程中运行系统调用(系统调用总是在不同的线程中运行)。当系统调用返回时,你的主要go-routine将被放回可运行例程的调度器列表中,并且它将继续在任何可用的操作系统线程上运行,这可能不是你正在跟踪的线程。
这也是为什么你不能使用gdb
逐步执行Go程序的原因。
如果你只想执行外部程序(比如/bin/ls),你可以使用标准库中的os/exec。
最接近你尝试做的事情的程序可能是delve。我认为它在每个单步操作时在每个线程上设置断点,然后根据go-routine的ID尝试找到你的go-routine现在在哪个线程上。
英文:
The short answer is that intercepting syscalls with Go is going to be very difficult, and anything ptrace will probably not work.
Go has a runtime which multiplexes go-routines onto OS threads. A syscall is a scheduling point, so after the syscall returns you could be on a different thread, whereas I think ptrace follows a single thread.
Say the thread you are ptrace-ing is running your main go-routine. Then you call fmt.Println (which does syscall.Write), so the Go runtime takes your go-routine off that thread, and runs the syscall in a different os thread (syscalls always run in different threads). When the syscall returns, your main go-routine is put back on the schedulers list of runnable routines, and it will continue on whichever os thread is available, which may not be the one you are ptrace-ing.
This is also the reason you cannot step through a Go program with gdb
.
If you just wanted to execute an external program (like /bin/ls) you could use os/exec from the standard library.
The program that comes closes to what you are trying to do is probably delve. I think that sets a breakpoint on every thread at each single-step, then tries to find which thread your go-routine is now on based on the go-routine id.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论