我想使用ptrace跟踪输入和输出的进程,所以我决定使用exec.Com来启动进程而不是os.StartProcess。但令我困惑的事情发生了,我不确定它是否是由gorountines计划引起的。我睡了5秒钟,并且每百万秒发送一次报警信号,但测试结果表明它很快就会退出。 但是,如果我使用运行实际CPU时间的进程进行测试,它将通过测试。我找到exec.Com的源代码,并且有管道stdin和stdout的gorountines,这与os.StartProcess不同。如果我改为使用os.StartProcess,它将通过测试。所以我想知道运行睡眠的gorountines有问题。 我想我应该做一些事情来锁定某个线程的睡眠,但我不确定是什么导致了这个问题。
这是我的代码:
package sandbox
import (
"fmt"
"os"
"os/exec"
"runtime"
"syscall"
"time"
)
const (
AC uint64 = iota
PE
TLE
MLE
WA
RE
OLE
CE
SE
)
type Tracer struct {
Process *os.Process
}
func Attach(proc *os.Process) (*Tracer, error) {
err := syscall.PtraceAttach(proc.Pid)
if err == syscall.EPERM {
_, err := syscall.PtraceGetEventMsg(proc.Pid)
if err != nil {
return nil, err
}
} else if err != nil {
return nil, err
}
return &Tracer{
Process: proc,
}, nil
}
func (t *Tracer) Syscall(sig syscall.Signal) error {
err := syscall.PtraceSyscall(t.Process.Pid, int(sig))
if err != nil {
return err
}
return nil
}
type RunningObject struct {
Proc *os.Process
TimeLimit int64
MemoryLimit int64
Memory int64
Time int64
Status uint64
}
func (r *RunningObject) RunTick(dur time.Duration) {
ticker := time.NewTicker(dur)
for _ = range ticker.C {
r.Proc.Signal(os.Signal(syscall.SIGALRM))
}
}
func Run(src string, args []string, timeLimit int64, memoryLimit int64) *RunningObject {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var rusage syscall.Rusage
var runningObject RunningObject
runningObject.TimeLimit = timeLimit
runningObject.MemoryLimit = memoryLimit
cmd := exec.Command(src, args...)
err := cmd.Start()
if err != nil {
panic(err)
}
proc := cmd.Process
tracer, err := Attach(proc)
if err != nil {
panic(err)
}
runningObject.Proc = proc
go runningObject.RunTick(time.Millisecond)
if err != nil {
fmt.Println(err)
return &runningObject
}
for {
status := syscall.WaitStatus(0)
_, err := syscall.Wait4(proc.Pid, &status, syscall.WSTOPPED, &rusage)
if err != nil {
panic(err)
}
if status.Exited() {
fmt.Println("exit")
return &runningObject
}
if status.CoreDump() {
fmt.Println("CoreDump")
return &runningObject
}
if status.Continued() {
fmt.Println("Continued")
return &runningObject
}
if status.Signaled() {
return &runningObject
}
if status.Stopped() && status.StopSignal() != syscall.SIGTRAP {
fmt.Println(status.StopSignal())
}
err = tracer.Syscall(syscall.Signal(0))
if err != nil {
fmt.Println(err)
}
}
}
这是我的测试:
package sandbox
import (
"testing"
)
func TestTime(t *testing.T) {
obj := Run("/bin/sleep", []string{"sleep", "5"}, 1000, 10000)
//t.Log(obj.Status)
if obj.Status != TLE {
t.Fatal("time exceed test failed.")
}
}