10

I have an application in Go that reroutes the STDIN and STDOUT of binaries and then runs them. In a nutshell I'm doing:

- create command object with the binary path (lets call the object command A) - create command object with the binary path (calling it command B) - set the stdout of command B to the stdin of Command A - start command A - start command B

I noticed whenever the process for command B exits while command A is running, it becomes a zombie process in the process table.

Here's an example:

commandA := exec.Command("samplebin")
commandB := exec.Command("sample2bin")

cmdAStdin := commandA.StdinPipe()

commandB.Stdout = cmdAStdin

commandA.Start()
commandB.Start()

Why does commandB become a Zombie if it exits while commandA is still running? I'm running Go 1.5 on Ubuntu 14.

Anfernee
  • 1,445
  • 1
  • 15
  • 26
  • 1
    @chris-dodd says right, and if you just do not want zombie, you can ignore the SIGCHLD by adding code: `signal.Ignore(syscall.SIGCHLD)` – AnonymousX Dec 22 '20 at 11:02

2 Answers2

21

When a process exits, it ALWAYS becomes a zombie, regardless of what other processes are running. That's just the way process termination works. The process will remain a zombie until its parent calls wait to get its exit status, or indicates that it is uninterested in children by ignoring SIGCHLD (which may have been before the child exited). It will remain a zombie until that happens, lest the exit status get lost.

In your example, it would seem that your process (the one creating the processes) is the parent, so both A and B will remain as zombies until your process collects them.

If a process exits while it still has children (either running or zombies), those children will be reparented to the exiting process's parent, which will generally ignore the exit status (clearing up the zombies).

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • So, SIGCHLD is sent by the child processes right before they become Zombies? Then how does one "ignore" SIGCHLD? By catching the signal and doing nothing? – Anfernee Mar 17 '16 at 14:01
  • SIGCHLD is sent by the kernel when a child as part of making it a zombie. If you want to ignore SIGCHLD and still get zombies, set the SIGCHLD action to SIG_DFL (the default) rather than SIG_IGN -- the default action is to do nothing, but still get zombies. – Chris Dodd Mar 18 '16 at 03:55
  • 1
    I don't want the Zombies, I want the exited processes to be cleaned up. I tried setting up signals in the main application to ignore SIGCHLD and that still made zombies so I end up calling Wait(). – Anfernee Mar 18 '16 at 11:56
  • If you're running the process (even if you've called `wait` finally) inside the docker container with pid:1, it will also lead to a zombie. https://github.com/krallin/tini/ will be helpful in this case. – mckelvin Mar 08 '17 at 11:34
  • @Anfernee I'm not too sure what Chris is talking about, but ignoring the SIGCHLD means you don't get the signal, not that the children processes don't become zombies, as you've discovered. – Alexis Wilke Jan 04 '20 at 04:23
3

Agree with the first answer that exiting processes becomes zombies until the process is waited for by another process. Here's how I handle things in go.

package main

import (
    "bytes"
    "io"
    "os"
    "os/exec"
)

func main() {
    c1 := exec.Command("samplebin")
    c2 := exec.Command("sample2bin")

    r, w := io.Pipe()
    c1.Stdout = w
    c2.Stdin = r

    var b2 bytes.Buffer
    c2.Stdout = &b2

    // Writing without a reader will deadlock so write in a goroutine
    go func() {
        // Close the writer or the pipe will not be closed for c2
        defer w.Close()
        defer c1.Wait()
        c1.Start()
    }()
    defer c2.Wait()
    c2.Start()
    io.Copy(os.Stdout, &b2)
}
WeakPointer
  • 3,087
  • 27
  • 22