分叉的ptraced进程挂起

时间:2013-08-29 04:19:31

标签: linux unix go

我试图在从Go调用程序时拦截系统调用,但是我遇到了两个问题。

孩子似乎挂了,这也挂起了父进程。似乎wait4(2)阻塞似乎很奇怪,孩子最终不会叫exit(2)退出吗?

我到达stdout的系统调用不一致,有时最后的系统调用是3,有时是6192。我的代码中是否存在竞争条件?为什么会这样?

我试着听父母的信号,但我没有收到任何东西..

我已经用/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 个答案:

答案 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尝试找到你的例行程序所在的线程。