why is permission denied when calling DebugActiveProcessStop on windows after some time?

huangapple go评论86阅读模式
英文:

why is permission denied when calling DebugActiveProcessStop on windows after some time?

问题

在Windows上,我需要一个Go程序来挂起和恢复一个进程(使用exec.Command生成)。

该进程运行5秒钟,然后使用DebugActiveProcess挂起。
另外5秒后,使用DebugActiveProcessStop重新启动。

以这种方式它可以无缺地工作...问题出现在我等待60分钟或更长时间(有时甚至更短)后:在这段时间之后,DebugActiveProcessStop失败并显示Permission denied

以下是示例代码:

package main

import (
	"bufio"
	"fmt"
	"os/exec"
	"path/filepath"
	"syscall"
	"time"
)

var (
	// DebugActiveProcess function (debugapi.h)
	// https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-debugactiveprocess
	modkernel32                = syscall.NewLazyDLL("kernel32.dll")
	procDebugActiveProcess     = modkernel32.NewProc("DebugActiveProcess")
	procDebugActiveProcessStop = modkernel32.NewProc("DebugActiveProcessStop")
)

func main() {
	dir := "C:\\path\\to\\folder"
	execName := "counter.bat" //使用批处理文件作为长时间运行的程序示例
	cmd := exec.Command(filepath.Join(dir, execName))
	cmd.Dir = dir

	cmd.SysProcAttr = &syscall.SysProcAttr{
		CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
	}

	cmdprinter(cmd)

	cmd.Start()

	// 等待5秒
	time.Sleep(time.Duration(5) * time.Second)

	fmt.Println("--- STOPPING ---")
	// https://github.com/go-delve/delve/blob/master/pkg/proc/native/zsyscall_windows.go#L177-L199
	r1, _, e1 := syscall.Syscall(procDebugActiveProcess.Addr(), 1, uintptr(cmd.Process.Pid), 0, 0)
	if r1 == 0 {
		fmt.Println("error stopping process tree:", e1.Error())
	}
	fmt.Println("STOPPING done")

	// 等待15分钟
	for n := 0; n < 3600; n++ {
		fmt.Print(n, " ")
		time.Sleep(time.Second)
	}

	fmt.Println("\n--- STARTING ---")
	r1, r2, e1 := syscall.Syscall(procDebugActiveProcessStop.Addr(), 1, uintptr(cmd.Process.Pid), 0, 0)
	if r1 == 0 {
		fmt.Println("error starting process tree:", e1.Error(), "r2:", r2, "GetLastError:", syscall.GetLastError())
	}
	fmt.Println("STARTING done")

	cmd.Wait()
	fmt.Println("exiting")
}

func cmdprinter(cmd *exec.Cmd) {
	outP, _ := cmd.StdoutPipe()
	errP, _ := cmd.StderrPipe()
	go func() {
		scanner := bufio.NewScanner(outP)
		for scanner.Scan() {
			line := scanner.Text()
			fmt.Println(line)
		}
	}()
	go func() {
		scanner := bufio.NewScanner(errP)
		for scanner.Scan() {
			line := scanner.Text()
			fmt.Println(line)
		}
	}()
}

这是counter.bat的内容:

echo off
set /a n=0
:START
ping 127.0.0.1 -n 2 > nul
echo counter says %n%
set /a "n=%n%+1"
if %n%==30 (EXIT 0)
GOTO START
  • 这可能是什么原因?我该如何解决?
  • 在Windows上是否有其他挂起和恢复进程的方法(类似于Linux上的kill -STOP)?(我听说过Windows API的挂起函数,但无法使其工作)
英文:

On windows I need to have a go program that suspends and unsuspends a process (generated with exec.Command).

This process is run for 5 seconds and then suspended with DebugActiveProcess.
After other 5 seconds it is restarted with DebugActiveProcessStop.

It works flawlessly in this way... the problem arises when I wait for 60min or more (sometimes even less): after that amount of time the DebugActiveProcessStop fails with Permission denied.

this is the code of example:

package main
import (
&quot;bufio&quot;
&quot;fmt&quot;
&quot;os/exec&quot;
&quot;path/filepath&quot;
&quot;syscall&quot;
&quot;time&quot;
)
var (
// DebugActiveProcess function (debugapi.h)
// https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-debugactiveprocess
modkernel32                = syscall.NewLazyDLL(&quot;kernel32.dll&quot;)
procDebugActiveProcess     = modkernel32.NewProc(&quot;DebugActiveProcess&quot;)
procDebugActiveProcessStop = modkernel32.NewProc(&quot;DebugActiveProcessStop&quot;)
)
func main() {
dir := &quot;C:\\path\\to\\folder&quot;
execName := &quot;counter.bat&quot; //using a batch file as an example of a program that runs for long time
cmd := exec.Command(filepath.Join(dir, execName))
cmd.Dir = dir
cmd.SysProcAttr = &amp;syscall.SysProcAttr{
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
}
cmdprinter(cmd)
cmd.Start()
// wait 5 seconds
time.Sleep(time.Duration(5) * time.Second)
fmt.Println(&quot;--- STOPPING ---&quot;)
// https://github.com/go-delve/delve/blob/master/pkg/proc/native/zsyscall_windows.go#L177-L199
r1, _, e1 := syscall.Syscall(procDebugActiveProcess.Addr(), 1, uintptr(cmd.Process.Pid), 0, 0)
if r1 == 0 {
fmt.Println(&quot;error stopping process tree:&quot;, e1.Error())
}
fmt.Println(&quot;STOPPING done&quot;)
// wait 15 min
for n := 0; n &lt; 3600; n++ {
fmt.Print(n, &quot; &quot;)
time.Sleep(time.Second)
}
fmt.Println(&quot;\n--- STARTING ---&quot;)
r1, r2, e1 := syscall.Syscall(procDebugActiveProcessStop.Addr(), 1, uintptr(cmd.Process.Pid), 0, 0)
if r1 == 0 {
fmt.Println(&quot;error starting process tree:&quot;, e1.Error(), &quot;r2:&quot;, r2, &quot;GetLastError:&quot;, syscall.GetLastError())
}
fmt.Println(&quot;STARTING done&quot;)
cmd.Wait()
fmt.Println(&quot;exiting&quot;)
}
func cmdprinter(cmd *exec.Cmd) {
outP, _ := cmd.StdoutPipe()
errP, _ := cmd.StderrPipe()
go func() {
scanner := bufio.NewScanner(outP)
for scanner.Scan() {
line := scanner.Text()
fmt.Println(line)
}
}()
go func() {
scanner := bufio.NewScanner(errP)
for scanner.Scan() {
line := scanner.Text()
fmt.Println(line)
}
}()
}

this is counter.bat:

echo off
set /a n = 0
:START
ping 127.0.0.1 -n 2 &gt; nul
echo counter says %n%
set /a &quot;n=%n%+1&quot;
if %n%==30 (EXIT 0)
GOTO START
  • What might be the cause of this? how can I fix it?
  • Is there any other way to suspend a process and unsuspend it on windows (like kill -STOP on linux)? (i heard of the windows api suspend function but was unable to make it work)

答案1

得分: 2

这是因为DebugActiveProcessStop命令必须在与调用原始DebugActiveProcess的线程相同的线程中运行。一个非常古老的线程在这个方向上给出了一个提示。

现在在GO中这会带来一个问题,因为你无法直接控制goroutine在哪个线程上运行... GO将这个"琐碎的细节"抽象了起来。到目前为止,对我来说有效的解决方法是使用runtime.LockOSThread()

类似这样的代码:

func exampleSuspendResume(){

   var wg sync.WaitGroup
   runtime.LockOSThread()
   defer runtime.UnlockOSThread()

   // 调用DebugActiveProcess

   // 在不调用`go func`的情况下做更多的事情

   // 调用DebugActiveProcessStop

   
}
英文:

This is because the DebugActiveProcessStop command must be run in the same thread than the thread which called the original DebugActiveProcess. A very old thread gave a hint in this direction.

Now this presents a problem in GO because you do not have direct control over which thread a goroutine is run... GO abstracts this "nitty gritty" away. The workaround that has held true for me so far is using runtime.LockOSThread().

Something like:

func exampleSuspendResume(){

   var wg sync.WaitGroup
   runtime.LockOSThread()
   defer runtime.UnlockOSThread()

   // call DebugActiveProcess

   // do more stuff WITHOUT calling `go func`

   // call DebugActiveProcessStop
   

   
}

huangapple
  • 本文由 发表于 2022年1月31日 08:06:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/70920073.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定