0

How do you kill child processes?

I have a long running application starting a new process with "exec.Command":

// ...I am a long running application in the background

// Now I am starting a child process, that should be killed togeter with the parent application.
cmd := exec.Command("sh", "-c", execThis)

// create a new process group
// cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

Now if I kill <pid of long running application in the background> it does not kill the child process - do you know how?

Chris G.
  • 23,930
  • 48
  • 177
  • 302
  • But why the child process should be killed if parent killed? – NobbyNobbs Mar 15 '21 at 14:40
  • I am handeling fswatch application events within my application(running in the background), and I need both to be killed when killing the parent – Chris G. Mar 15 '21 at 14:42
  • 1
    Handle `sigterm` and `sigint` signals in your application, and kill children process in signal handler. I think you can do nothing with kill 9 (`sigkill`). – NobbyNobbs Mar 15 '21 at 14:51
  • Thanks I will try killing the main application using signals, and then handle the child accordingly. – Chris G. Mar 15 '21 at 14:56
  • 2
    See also https://stackoverflow.com/questions/34730941/ensure-executables-called-in-go-process-get-killed-when-process-is-killed/34731014#34731014 – JimB Mar 15 '21 at 14:57

1 Answers1

5

There are quite a few things to be teased apart here.

First, there's what the OS itself does. Then, once we know what the OS is and what it does, there's what the program does.

What the OS does is, obviously, OS-dependent. POSIX-flavored OSes have two kinds of kill though: plain kill and process-group-based kill, or killpg. The killpg variety of this function is the only one that sends a signal to an entire process group; plain kill just sends a signal to a single process.

When a program is run from a controlling terminal, keyboard signals (^C, ^Z, etc) get sent to the foreground process group of that control terminal (see the linked page for a reasonably good description of these and note that BSD/macOS has ^T and SIGINFO as well). But if the signals are being sent from some other program, rather than from a controlling terminal, it is up to that program whether to call killpg or kill, and what signal(s) to send.

Some signals cannot be caught. This is the case for SIGKILL and SIGSTOP. These signals should not be sent willy-nilly; they should be reserved to a last resort. Instead, programs that want another program to stop should generally send one of SIGINT, SIGTERM, SIGHUP, or (rarely) SIGQUIT. Go tends to tie SIGQUIT to debug (in that the runtime on POSIX systems makes ^\ dump the stacks of the various goroutines) so that one is not a good choice. However, it's not up to the Go program you write here, which can only try to catch the signal. The choice of what to send is up to the sender.

The "Go way" to catch the signal is to use a goroutine and a channel. The signal.Notify function turns an OS-level signal into an event on the channel. What you do not (and cannot) know is whether the signal reached your process through kill or killpg (though if it came from a controlling terminal interaction, the POSIX-y kernel sent it via the equivalent of killpg). If you want to propagate that signal on your own, simply use the notification event to invoke code that makes an OS-level kill call. When using the os/exec package, use cmd.Process.Signal: note that this invokes the POSIX kill, not its killpg, but you would not want to use killpg here since we're assuming a non-process-group signal in the first place (a pgroup-based signal presumably needs no propagation).

There is no fully portable way to send a signal to a POSIX process group (which is not surprising, since this isn't portable to non-POSIX systems). Sadly, there's no direct Unix or POSIX specific way to do that either, it seems, in Go.

On non-POSIX systems, everything is quite different. See the discussion near the front of the os/signal package.

torek
  • 448,244
  • 59
  • 642
  • 775