Golang: Child Processes become Zombies
Asked Answered
D

2

11

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.

Demivolt answered 17/3, 2016 at 2:17 Comment(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)Valene
W
22

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).

With answered 17/3, 2016 at 2:32 Comment(5)
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?Demivolt
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.With
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().Demivolt
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. github.com/krallin/tini will be helpful in this case.Overmodest
@Demivolt 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.Experimentation
D
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)
}
Designate answered 17/3, 2016 at 19:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.