Live data from Hacker News

Killing a process and all of its descendants

morningcoffee.io

41–50 of 76 posts

Re: Killing a process and all of its descendants

#41
post #24
post #2

Even waiting for a process to exit is surprisingly hard (impossible?) in Linux, unless it's your child.

Isn’t netlink’s PROC_EVENT_EXIT pretty straightforward?

The proc connector has a few problems:

1. It's effectively unmaintained (which I found out when I started mailing around asking if there was interest in me sending patches that fix the rest of the issues listed).

2. It requires privileges to use, making it useless when compared to other alternatives that can help solve some of the other issues (pidfds or just the good old /proc/$pid fstatat(2) trick).

3. It doesn't work in containers at all.

4. It has several pretty serious bugs which could even be argued to be security bugs. But since it has effectively zero users now, I'd be surprised if anyone would be interested in such bugs.

I wanted to fix these issues quite desperately, because it would allow for init systems that don't suffer from the cgroup or ptrace downsides. Unfortunately, it uses netlink and so any changes are mind-bogglingly complicated (especially if you want to tie it to PID namespaces because then you're really SoL since netlink is fundamentally tied to network namespaces).

Re: Killing a process and all of its descendants

#42
post #24
post #2

Even waiting for a process to exit is surprisingly hard (impossible?) in Linux, unless it's your child.

Isn’t netlink’s PROC_EVENT_EXIT pretty straightforward?

If you're a privileged process. Usually I, a non-root user, want to wait for another process I started in another process tree.

Re: Killing a process and all of its descendants

#43
post #2

Even waiting for a process to exit is surprisingly hard (impossible?) in Linux, unless it's your child.

You could do it for a very long time. Folks are mentioning the new pidfd stuff, but that interface is built on much older tricks.

In particular, what you could do is grab a handle to /proc/$pid. This is now called a pidfd, but this works on old kernels too. Then, to check if the process has died you just do a fstatat(2) and see if you get ESRCH -- if you do, the process has died and this will work even if the pid is reused. I think you could use inotify to avoid polling, but I'm not sure.

The main benefit for the new poll support for pidfds is that you can get the exit status. And obviously CLONE_PIDFD has other benefits as well as the incredibly useful feature of pidfd_send_signal(2) which was the first patch sent for Linux 5.1.

Re: Killing a process and all of its descendants

#44
post #14
post #8

Earlier quoted context omitted.

That is getting considerably easier with the addition of pidfds, though: https://lwn.net/SubscriberLink/794707/93ffb35438fd3710/

> Beyond the ability to unambiguously specify which process should be waited for, this change will eventually enable another interesting feature: it will make it possible to wait for a process that is not a child — something that waitid() cannot do now. Since a pidfd is a file descriptor, it can be passed to another process via an SCM_RIGHTS datagram in the usual manner. The recipient of a pidfd will, once this funct…

You can get a pidfd by opening /proc/$pid. That's all a pidfd is. The proposed pidfd_open(2) is for separate and more specialised use-cases where /proc isn't available and other restrictions apply.

Re: Killing a process and all of its descendants

#45
post #36
post #13

Earlier quoted context omitted.

Can freezing be done atomically?

Yes, the freezer cgroup can be used to "atomically" put an entire cgroup tree into a frozen mode. However, unless you're using cgroupv2, the process might be stopped in an unkillable state (defeating the purpose). So this is not an ideal solution. Really the best way to do it is to put it inside a PID namespaces and then kill the pid1. Unfortunately, most processes don't act correctly as a pid1 (the default signal ma…

Wait, what does cgroupv2 do with unkillable processes?

Maybe I'm misreading - is it that cgroupv1's freezer puts processes in an unkillable state? Or does cgroupv2's freezer have a way of rescuing processes already in uninterruptible sleep?

Re: Killing a process and all of its descendants

#46
post #34
post #32

Earlier quoted context omitted.

[CGroups original developer] Yes, for tracking processes and reliable resource control. Prior to cgroups, in Google's Borg cluster management daemon the best strategy I was able to come up with for reliably and efficiently tracking all the processes in a job was: - assign each job a supplementary group id from a range reserved for Borg, and tag any processes that were forked into that job with that group id - use a k…

Was giving each job its own UID not an option? users are the original privilege separation after all and kill -1 respects that.

No, because multiple jobs being run by the same end-user could share data files on the machine, in which case they needed to share the same uid. (Or alternatively we could have used the extra-gid trick to give shared group access to files, but that would have involved more on-disk state and hence be harder to change, versus the job tracking which was more ephemeral.) It's been a while now, but I have a hazy memory that in the case where a job was the only one with that uid running on a particular machine, we could make use of that and avoid needing to check the extra groups.

Re: Killing a process and all of its descendants

#47
post #20

Earlier quoted context omitted.

Unfortunately, as Linux has matured, many of its developers refuse to adapt technologies for the sole reason that it "stinks" of Microsoft. Cutler and his team were pretty forward thinking given that NT is over 30 years old. See also: the async i/o headaches and the inability to WaitForSingleObject()/WaitForMultipleObjects() (or an analogue) in Linux. It's a shame really.

re: WaitForMultipleObjects, finally something like that is likely coming to Linux: https://www.reddit.com/r/linux/comments/ck77gm/valve_propose... > It's a shame really. Well, yes, and of course, yet at the same time it's FOSS, so ... if someone really needed it, they should have proposed a patch. ¯\_(ツ)_/¯

RHEL had an entire async i/o subsystem. It was rejected upstream.

Re: Killing a process and all of its descendants

#48
post #45
post #36

Earlier quoted context omitted.

Yes, the freezer cgroup can be used to "atomically" put an entire cgroup tree into a frozen mode. However, unless you're using cgroupv2, the process might be stopped in an unkillable state (defeating the purpose). So this is not an ideal solution. Really the best way to do it is to put it inside a PID namespaces and then kill the pid1. Unfortunately, most processes don't act correctly as a pid1 (the default signal ma…

Wait, what does cgroupv2 do with unkillable processes? Maybe I'm misreading - is it that cgroupv1's freezer puts processes in an unkillable state? Or does cgroupv2's freezer have a way of rescuing processes already in uninterruptible sleep?

The if you freeze a cgroupv1 feeezer, the processes may be frozen at a point within their in-kernel execution such that they are in an uninterruptible sleep. The reason is that the cgroupv1 freezer basically tried to freeze the process immediately without regard to it's in-kernel state.

Fixing this, and making the freezer cgroup more like SIGSTOP on steroids (where the processes were put into a killable state upon being frozen, if possible) was the main reason why cgroupv2 support for freezer was delayed for so many years.

So the answer is "both, kinda". I'm not sure how it'd deal with legit uninterruptible sleep (dead-or-live locked) processes but I'll look into it.

Re: Killing a process and all of its descendants

#49
post #32
post #3

This is why cgroups were invented. They solve this problem. Start a process in its own cgroup, and you can later confidently kill the process and all of its descendants. Container "technologies" use cgroups extensively, as does systemd service management.

[CGroups original developer] Yes, for tracking processes and reliable resource control. Prior to cgroups, in Google's Borg cluster management daemon the best strategy I was able to come up with for reliably and efficiently tracking all the processes in a job was: - assign each job a supplementary group id from a range reserved for Borg, and tag any processes that were forked into that job with that group id - use a k…

cgroups was extremely useful for a system I built that ran on Borg, Exacycle, which needed to reliably "kill all child processes, recursively, below this process". I remember seeing the old /proc scanner and the new cgroups approach and being able to get the list of pids below a process and realizing- belatedly, that UNIX had never really made this easy.
Post reply on HN