18

I'm trying to build a process container. The container will trigger other programs. For example - a bash script that launches running background tasks with '&' usage.

The important feature I'm after is this: when I kill the container, everything that has been spawned under it should be killed. Not just direct children, but their descendants too.

When I started this project, I mistakenly believed that when you killed a process its children were automatically killed too. I've sought advice from people who had the same incorrect idea. While it's possible to catch a signal and pass the kill on to children, that's not what I'm looking for here.

I believe what I want to be achievable, because when you close an xterm, anything that was running within it is killed unless it was nohup'd. This includes orphaned processes. That's what I'm looking to recreate.

I have an idea that what I'm loooking for involves unix sessions.

If there was a reliable way to identify all the descendants of a process, it would be useful to be able to send them arbitrary signals, too. e.g. SIGUSR1.

  • Well, killing the parent process sends SIGHUP to it's direct children processes. The default handler of hang up signal aborts process execution, so the default route is to kill all the descendants. Read more on processes and process groups. – alex Jun 11 '11 at 13:27
  • Closing the xterm kills everything spawned in the xterm because the TTY is destroyed. If you can come up with a way to create a tty which child processes can use, you can then destroy the TTY and accomplish the same thing. Any process which didnt close that TTY (nohup & friends) will get a SIGHUP. – Patrick Jun 12 '11 at 10:34
21

If you send a signal to a process, that process gets killed. I wonder how the rumor that killing a process also kills other processes got started, it seems particularly counter-intuitive.

There are, however, ways to kill more than one process. But you won't be sending a signal to one process. You can kill a whole process group by sending a signal to -1234 where 1234 is the PGID (process group ID), which is the PID of the process group leader. When you run a pipeline, the whole pipeline starts out as a process group (the applications may change this by calling setpgid or setpgrp).

When you start processes in the background (foo &), they are in their own process group. Process groups are used to manage access to the terminal; normally only the foreground process group has access to the terminal. The background jobs remain in the same session, but there's no facility to kill a whole session or even to enumerate the process groups or processes in a session, so that doesn't help much.

When you close a terminal, the kernel sends the signal SIGHUP to all processes that have it as their controlling terminal. These processes form a session, but not all sessions have a controlling terminal. For your project, one possibility is therefore to start all the processes in their own terminal, created by script, screen, etc. Kill the terminal emulator process to kill the contained processes (assuming they haven't seceded with setsid).

You can provide more isolation by running the processes as their own user, who doesn't do anything else. Then it's easy to kill all the processes: run kill (the system call or the utility) as that user and use -1 as the PID argument to kill, meaning “all of that user's processes”.

You can provide even more isolation, but with considerably more setup by running the contained processes in an actual container.

  • If you program your own processes, then you can use something such as: prctl(PR_SET_PDEATHSIG, SIGHUP);, more info: man7.org/linux/man-pages/man2/prctl.2.html – Alexis Wilke Jul 19 '16 at 19:33
  • Gilles, you mention this - "When you close a terminal, the kernel sends the signal SIGHUP to all processes that have it as their controlling terminal". From this I'm surmising the terminal sends SIGHUP to the session leader (shell) who forwards it on to all process groups within the session. Which of the two would be right? – iruvar Oct 24 '18 at 4:35
3

Within the parent script trap the kill signal and have it kill all the children. For example,

#!/bin/bash
# kill the parent and children together
trap "kill 0" EXIT
# create all the children
for n in $(seq 1 100)
do
    ( echo "begin $n"; sleep 60; echo "end $n" ) &
done
# wait for the children to complete
wait
2

A reliable way to identiry all the descendants of a process is to use the command pstree <pid> where pid is your parent process id.

Read the man page on pstree here.

To signal all members of a process group: killpg(<pgrp>, <sig>);
where pgrp is the process group number and sig is the signal.

To wait for children in a specified process group: waitpid(-<pgrp>, &status, ...);

An alternative to what you're doing is to run your process container in a new bash shell. Create the new bash shell with the command bash and then run your processes. When you want all processes to be ended, exit the shell with the command exit.

  • I think killpg will allow me to do what I need to do. Thanks. – Craig Turner Jun 12 '11 at 12:24
1

Use

unshare -fp --kill-child -- yourprogram

If you kill unshare, all child processes (that yourprogram may have spawned) will be killed.

This is now possible with util-linux 2.32; I implemented this upstream. It requires either user namespaces (kernel config option CONFIG_USER_NS=y) or root privileges. See also here.

0

rkill command from pslist package sends given signal (or SIGTERM by default) to specified process and all its descendants:

rkill [-SIG] pid/name...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.