如何在Windows上使用golang验证父进程是否已退出?

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

How to verify that parent process has exited in golang on Windows?

问题

所以我正在编写一个小工具,应该能够更新自身(替换自己的二进制文件)。

在Windows上实现这个功能的最佳方法似乎是:

  1. 下载新版本的二进制文件并命名为my.exe.new。
  2. my.exe 运行 my.exe.new 并退出。
  3. my.exe.new 等待 my.exe 退出。
  4. my.exe.new 将自身复制为 my.exe。
  5. my.exe.new 启动另一个副本作为 my.exe 并退出。
  6. my.exe 等待 my.exe.new 退出。
  7. my.exe 删除 my.exe.new。

为了使所有这些工作正常运行,我必须能够在进程之间同步状态(即能够知道父进程何时退出),但是似乎在golang的Windows版本中 os.Getppid(或 syscall.Getppid)没有实现,因为它总是返回-1。

我看到有人正在进行补丁工作,但我不想对我的标准库进行补丁。

是否有一种简单的方法可以使 Getppid 在旧版本的Go上工作(也许重新实现它?),或者也许有人可以建议一种更好的进程状态同步方法?

我想到的方法是绑定一个套接字,但那是一个比较笨拙的方法。

也许可以将一个管道传递给子进程,然后子进程等待管道关闭?

谢谢。

英文:

So I am writing a small utility which should be able to update itself (replace it's own binary).

The best way to do this on Windows seems to be:

  1. Download the new version of the binary as my.exe.new
  2. my.exes runs my.exe.new and exits
  3. my.exe.new waits for my.exe to exit
  4. my.exe.new copies itself as my.exe
  5. my.exe.new starts another copy of itself as my.exe and exits
  6. my.exe waits for my.exe.new to exit
  7. my.exe removes my.exe.new

Now for all of this to work I have to be able to synchronize the state between the processes (being able to know when the parent has exited), but it seems that os.Getppid (nor syscall.Getppid) in golang Windows is not implemented as it always returns -1.

I've seen that patches are underway, but I am not willing to patch my standard libraries.

Is there an easy way to make Getppid working on even older versions of Go (perhaps reimplementing it?), or perhaps anyone can suggest a better method of synchronizing between the process state?

The thing which comes to mind is binding on a socket, but thats a big hacky.

Perhaps passing a pipe to the child process, and the child waiting for the pipe to close?

Thanks

答案1

得分: 2

你可以使用os.Stdinexec.Cmd,现在我没有访问Windows来测试这个,不过相同的概念应该也适用于Windows:

var child = flag.Bool("child", false, "damn children and their music")

func init() {
    flag.Parse()
}

func main() {
    if *child {
        fmt.Println("child start", time.Now())
        
        // 等待父进程退出并关闭标准输入
        ioutil.ReadAll(os.Stdin)
        fmt.Println("the parent is dead", time.Now())
        time.Sleep(5 * time.Second)
        fmt.Println("tada\n")

    } else {
        fmt.Fprintln(os.Stdout, time.Now())
        cmd := exec.Command(os.Args[0], "-child")
        cmd.Stdout = os.Stdout // 在真实程序中不需要这行

        // 这一行很重要,bufio会在父进程退出后关闭,与os.Stdin不同,后者在Linux上会出问题
        cmd.Stdin = bufio.NewReader(os.Stdin) 

        fmt.Println("giving painful birth:", cmd.Start())
        time.Sleep(2 * time.Second)
    }
}

希望对你有帮助!

英文:

You could use os.Stdin with exec.Cmd, now I don't have access to windows to test this, however the same concept should apply there just fine:

var child = flag.Bool("child", false, "damn children and their music")

func init() {
	flag.Parse()
}

func main() {
	if *child {
		fmt.Println("child start", time.Now())
        
        // wait until the parent dies and bufio closes the stdin
		ioutil.ReadAll(os.Stdin)
		fmt.Println("the parent is dead", time.Now())
		time.Sleep(5 * time.Second)
		fmt.Println("tada\n")

	} else {
		fmt.Fprintln(os.Stdout, time.Now())
		cmd := exec.Command(os.Args[0], "-child")
		cmd.Stdout = os.Stdout //not needed in a real program.

		//this is important, bufio will close after the parent exits, 
 		// unlike os.Stdin which screws up, at least on linux
		cmd.Stdin = bufio.NewReader(os.Stdin) 

		fmt.Println("giving painful birth:", cmd.Start())
		time.Sleep(2 * time.Second)
	}
}

答案2

得分: 1

父进程可以将其PID传递给子进程。

你可以使用命令行参数或环境变量来实现这一点。

英文:

The parent process can pass its PID to the child.

You could use a command-line parameter or an environment variable to do this.

huangapple
  • 本文由 发表于 2014年8月2日 20:19:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/25094958.html
匿名

发表评论

匿名网友

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

确定