Killing a process and all of its descendants
morningcoffee.io
morningcoffee.io
It depends what you need. If you don't care that the kill operation might not complete without unfreezing the cgroup, then you're right that it's not an issue. But if the signal was lost (assuming this can happen with SIGKILL), unfreezing means that the number of processes might not decrease over time and you'll have to retry several times. Yeah, it'd be hard to hit this race more than ~5 times in a row but it still makes userspace programs more complicated than they need to be.
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 mask is different for pid1, causing default "safe exit" signal behaviour to break for most programs). You could run a separate pid1 that just forwards signals (this is what Docker does with "docker run --init" and similar runtimes do the same thing). But now the solution has gotten significantly more complicated than "use PID namespaces".
Arguably the most trivial and workable solution is process groups and using a negative pid argument to kill(2), but that requires the processes to be compliant and not also require their own process groups. (I also haven't yet read TFA, it might say that this approach is also broken for reasons I'm not familiar with.)
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?
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.
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 kernel netlink connector socket to follow PROC_EVENT_FORK events to find new processes/threads, and assign them to a job based on the parent process; if the parent process wasn't found for some reason then query the process' groups in /proc to find the Borg-added group id to determine which job it's a part of.
- if the state gets out of sync (due to a netlink queue overflow, or a daemon restart) do a full scan of /proc (generally avoided since the overhead for continually scanning /proc got really high on a busy machine).
That way we always have the full list of pids for a given group. To kill a job, nuke all the known processes and mark the group id as invalid, so any racy forks will cause the new processes to show up with a stale Borg group id, which will cause them to be killed immediately.
This approach might would have had trouble keeping up with a really energetic fork bomb, but fortunately Borg didn't generally have to deal with actively malicious jobs, just greedy/misconfigured ones.
Once we'd developed cgroups this got a lot simpler.
https://www.maizure.org/projects/decoded-gnu-coreutils/timeo...
https://github.com/MaiZure/coreutils-8.3/issues/1#issuecomme...
For instance, when I want to stop conky, all I do is run `killall conky` and kills all of its processes at once.
Another longer way to do such thing is to run `kill -9 $(pidof conky)` which kills all returned processes.
Thankfully Raymond Chen wrote an article[2] about Job Objects which helped me figure out the last bits. I genuinely am not sure I could have gotten it right without that article. There’s so many subtle ways to fail!
[1] https://github.com/bazelbuild/bazel-watcher/pull/144/files
[2] https://devblogs.microsoft.com/oldnewthing/20130405-00/?p=47...
ResumeThread is a well documented API. As is TerminateJobObject. The one thing I find a bit baroque is the recently added way to make sure a process is in a job on creation, which I believe is part is the ProcThreadAttributes mechanism.
I’m sure it’s safe to rely on NtResumeProcess. I have used it since XP without issue. But I definitely wish there was a better way to go back from a process to a thread. The best I could find is using Toolhelp32 to iterate all the threads on the system, which I believe is just wrapping NtQuerySystemInformation. Would’ve worked but definitely wasn’t fast.
timeout 86400 ssh me@example.com
I was reading the man page for timeout, and it looks like you can throw some kill options, but I never needed them, so I never looked for them.
It covers everything that needs to be covered, but it also gets right to the point. Yet without being overly terse or dry.
And it explains everything clearly. So often the writer is good at understanding an idea but not conveying it. This lays it out where it's easy to pick up.
I don’t quite get what the implications are. The author also doesn’t seem to talk about their solution for it in the context.
Don't know about macOS, but session ID/pointer does present on FreeBSD and OpenBSD.
UNIX one-liner to kill a hanging Firefox process:
https://jugad2.blogspot.com/2008/09/unix-one-liner-to-kill-h...
wait [-fn] [jobspec or pid …]
https://www.gnu.org/software/bash/manual/html_node/Job-Contr...So, no. That's not it.
https://github.com/torvalds/linux/commit/b53b0b9d9a613c41805...
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.
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. ¯\_(ツ)_/¯
It's a bit like doing I/O that doesn't throw your data away... just use O_DIRECT, bring your own I/O manager and page cache and scheduling and simply implement all the low-level compatibility stuff yourself if you need such a weird thing ¯\_(ツ)_/¯
Because general purpose filesystems and I/O susbsystems are just that, general purpose.
PostgreSQL regularly woes about the problems of the Linux I/O and fs APIs, but ... to my knowledge they did not do much else. (Other than working around the problems in userspace, and taking the performance hit.)
If you want I/O that doesn't throw you data away, use the perfectly fine buffered I/O on let's say XFS. Fast, safe, sane. If you need more juice, add NVMe, and/or MD-RAID / LVM. And eventually you need to scale out to multiple nodes anyway, and then the performance of the cluster consistency subsystem will be the weak point.
But you also can't say that it's somehow impossible to get anything other than what Linus et al. think is the very epitome of perfect.
Upstreaming stuff is an effort. Contributing to such a complex and fragile pile of code/crap/C must be done through the kernel devs.
But if you want to add a new FS, I/O scheduler, bytecode VM, or whatever, it's possible. Look at Ceph, btrfs, bcachefs, all the SSD/NAND oriented FS-es, look at eBPF, and the XDP [eXpress Data Path] IO Visor network thingie, or WireGuard [which is still not merged as far as I know, because it tried to bring its own crypto library instead of using the already existing stuff]. All the stuff getting upstreamed from Android piece by piece.
A lot of kernel devs are happy about this or that, but they are pragmatists.
And Linus doesn't veto big ideas just because, but does not want to pull sub-par implementations. (See KDBUS for example.)
I don't where did you get that idea but it is not true at all. Linux developers have a history of ignoring (and reinventing) technologies and interfaces developed in other Unix systems, too. You can listen any Bryan Cantrill rant for more details. The claim that they refuse to adapt technologies that "stink" Microsoft is a mischaracterization.
So, to wait for a process that is not your child, do you have to get the relevant pidfd from its parent? In which case, this doesn't help all that much. Or is there some other way to get pidfds for arbitrary processes?
I find it bizarre they called it "pidfd_" rather than just "process_" or "proc_"... almost seems like they deliberately avoided the obvious?
In fact it's exactly equivalent to getting a file descriptor for /proc/$pid -- Christian (the person who developed the patchsets) quite cleverly solidified a trick that some folks knew about for several years (that you could use /proc/$pid as a race-free way of checking if a process has died if you grabbed a handle while it was still alive). Before pidfd_send_signal(2) there wasn't a way to use that "interface" nicely. But now it's a first-class citizen (and Christian had to fight a lot of battles to get this in over several releases).
It's really cool work and I have high hopes it will be used far and wide because it solves so many individual problems in one fell swoop.
It is, but you're opening the object, not the descriptor (which doesn't even exist yet). When you open a kernel object, you always get back a (new) file descriptor representing that object. It's against previous naming conventions too. It's not like mkfifo() was called mkfifofd() or socket() was called socketfd() or perf_event_open() was called perf_event_fd_open()...
I'm really amused that you're so excited about it and find it so cool. I mean, I'm not suggesting it isn't awesome that they added it, but to me, it's such a glaring obvious deficiency that I'm completely flabbergasting that a lot of battles had to be fought to include it. It should've been added and embraced with open arms over two decades ago...
pidfd_open(2) is still a proposed interface, and isn't in mainline yet (and if I'm remembering the ML discussions correctly, it might not even go in any time soon). The currently-available interfaces are pidfd_send_signal(2), CLONE_PIDFD, and the new pidfd_poll(2) support for pidfds. In that context, calling it "pidfd_" makes more sense (since you're operating on existing handles) and thus pidfd_open(2) also makes sense because otherwise the naming would be needlessly inconsistent.
There are also several pre-existing APIs that are called process_ (such as process_vm_{read,write}v(2)) which use pids and not pidfds -- so calling the new APIs process_ (or even proc_) could lead to confusion. From memory the first couple of iterations of the patchset changed the name several times until we landed on pidfd_ and nobody really complained much afterwards.
Also (and now I'm just nitpicking), mkfifo(3) doesn't give you an fd -- it's just a wrapper around mknod(2). But I do get your point.
> I'm really amused that you're so excited about it and find it so cool.
I might be a little bit more biased towards thinking it's cool, since the developer is a good friend of mine and we went back and forth on the design quite a lot (the fact he managed to get /proc/$pid fds to have these features is pretty remarkable and it's unbelievably cool that it didn't require having multiple classes of fds -- if you'd have asked me a year ago I would've said it'd be very hard to get right and would never be merged because it'd be so invasive). But thinking that it's neat isn't mutually exclusive with thinking that (something like) it should've been implemented a long time ago.
taskkill /t1. 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).
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.