首页 > 解决方案 > 为什么子进程“sleep 10”没有终止?

问题描述

我需要测试一个进程是否终止,而我所拥有的只是它的 pid 号。为此,我测试伪文件“/proc/<pid>”是否存在。

为这个函数写一个测试,我注意到进程没有按预期终止。

对于测试,我运行“sleep 10”作为应该运行 10 秒的子进程。启动此过程后,我轮询伪文件“/proc/<pid>”的消失。该伪文件永远不会消失,也不会检测到子进程的终止。

测试 golang playground 的代码重现了问题: https: //play.golang.org/p/fb4CbXkIjh3

我检查了该进程是否已创建,并且 pid 是否正确。在检查过程中可以看到它变成了<defunct>。它没有因此被删除。

问题如下:

  1. 为什么子进程不终止?
  2. 如何更改代码以使其终止?
package main

import (
    "fmt"
    "log"
    "os"
    "os/exec"
    "strconv"
    "time"
)

func main() {
    fmt.Println("Hello, playground")

    cmd := exec.Command("sleep", "10")
    if err := cmd.Start(); err != nil {
        log.Fatal("unexpected error:", err)
    }
    pidStr := strconv.Itoa(cmd.Process.Pid)
    log.Println("sleep pid:", pidStr)

    for {
        if _, err := os.Stat("/proc/" + pidStr); os.IsNotExist(err) {
            log.Println("detect termination of /proc/" + pidStr)
            return
        }
        log.Println("pgm /proc/" + pidStr + " is running")
        time.Sleep(3 * time.Second)
    }
}

标签: gounix

解决方案


在操作系统级别,在任何与 POSIX 兼容的操作系统(Unix、Linux、Darwin 等)中,已完成但尚未被其上级收集的进程处于“已失效”或“僵尸”状态。它仍然存在,但不能被杀死:它已经死了。它的存在正是为了让它的上级——可以调用操作系统级系统调用的进程——可以wait调用操作系统级wait系统调用并看到该进程现在已经死了。

一旦它的上级等待它,该进程就真正被删除了:不再有一个僵尸进程占用该进程 ID。如果你有一个/proc文件系统,这是进程从/proc.

在 Go 中,调用cmd.Wait()会调用操作系统级别的wait调用,这就是执行此操作的方法。如果您想收集 this 的结果cmd.Wait(),一个好方法是通过通道发送它。

(如果你想产生一个运行时间很长的进程而不是等待它,你可以放弃它,这样你就不再是它的上级了。正确执行此操作的细节充满了操作系统特定的小玩意儿,例如丢弃控制 tty , 设置会话, 使用procctlor prctl, 等等。)


推荐阅读