Process exit on Windows and Linux
-
If you have
class Foo { // stuff private: Bar* bar; Baz* baz; }
I would expect ~Foo to delete bar and baz. I consider that roughly analogous to launching a child process to do something for you and then getting it to exit via some mechanism when it's done. Or stronger, if you had this:
That depends heavily on ownership semantics, which are ambiguous from that class definition. Are
bar
andbaz
owned components ofFoo
, or do they point to external objects? That distinction must be encoded in~Foo
in order for the above behavior to occur, just as parent and child processes must have some understanding about what should happen if either the parent or a child terminates unexpectedly.
-
Foo allocates bar and baz itself, just as the child process starts its own children itself. All internal, invisible to the external world unless it starts groveling through data structures it's got very little business touching.
-
So why use pointers at all? Why not embed the child in the parent? You don't need another human to digest your food. You have a stomach for that.
-
friggin' everything is already in a job in win7.
While true it suffices to break away the initially launched process from the parent job, as presumably the processes themselves won't be using jobs. It is actually not difficult and works like a charm.
-
I was looking at windows process management recently. Looks like wmi is the way to go these days; here's the first hit on a search for wmi kill child processes. Probably not immune to race-conditions, but still doable.
-
Wait, I thought the term "child process" was purely cosmetic? I didn't realize some people might think there was an actual relationship between the processes...
-
On Linux, there is.
-
That's great when discussing Windows
-
Here. Better?
-
You can't do this guaranteed clean on linux either (without virtualization). Although the default action on parent death is to send a signal that kills the client, the client can request a different or no signal, or it can handle the signal without exiting (immediately or at all).
You can explicitly kill a process group, but if you use a signal like SIGTERM that is not always fatal (clean exit is merely a convention) there is no guarantee all processes will die. And any process is free to change its own process group, or start a new one.
-
I'm going to jump on the "and why the fuck would it" bandwagon. Just because Process A spawned Process B does not mean that when Process A shuts down so should Process B (and shut down cleanly, no less).
Perhaps you are conflating "process" with "thread".
-
Threads don't work that way either, unless you mean that exiting a process destroys all threads in said process.
On this note I always found it odd that the "main thread" has to be the last thread to exit.
-
That's exactly what I mean, because it sounds like that is what he thinks should happen with processes.
-
Perhaps you are conflating "process" with "thread".
Processes are hard Yo.
INB4: giggity
-
I didn't realize some people might think there was an actual relationship between the processes...
On Linux, there is.
And Windows.
root 1272 0.0 0.0 287464 2880 ? Sl Jan05 0:28 /usr/lib/accountsservice/accounts-daemon root 1429 0.0 0.1 613848 5024 ? Ssl Jan05 4:07 /usr/bin/docker -d root 1743 0.0 0.1 226104 5820 ? Sl Jan05 0:17 \_ docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 2222 -container-ip 172.17.0.1 -container-port 22 root 1752 0.0 0.0 226104 2004 ? Sl Jan05 0:17 \_ docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 444 -container-ip 172.17.0.1 -container-port 443 root 1761 0.0 0.0 160568 1940 ? Sl Jan05 0:17 \_ docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 81 -container-ip 172.17.0.1 -container-port 80 root 1767 0.0 0.0 21092 1708 pts/1 Ss+ Jan05 0:00 \_ /bin/bash /sbin/boot root 1871 0.0 0.0 196 32 pts/1 S+ Jan05 0:21 \_ /usr/bin/runsvdir -P /etc/service root 1872 0.0 0.0 176 0 ? Ss Jan05 0:00 \_ runsv nginx root 1881 0.0 0.0 125612 2476 ? S Jan05 0:00 | \_ nginx: master process /usr/sbin/nginx www-data 1912 0.0 0.0 126452 3244 ? S Jan05 1:35 | \_ nginx: worker process www-data 1913 0.0 0.0 126460 3088 ? S Jan05 0:55 | \_ nginx: worker process www-data 1914 0.0 0.0 125816 1448 ? S Jan05 0:08 | \_ nginx: cache manager process root 1873 0.0 0.0 176 0 ? Ss Jan05 0:00 \_ runsv sshd root 1878 0.0 0.0 61376 2544 ? S Jan05 0:00 | \_ /usr/sbin/sshd -D -e root 1874 0.0 0.0 176 0 ? Ss Jan05 0:00 \_ runsv redis landsca+ 1880 0.6 0.0 42452 3584 ? Sl Jan05 63:30 | \_ /usr/bin/redis-server *:6379 root 1875 0.0 0.0 176 0 ? Ss Jan05 0:00 \_ runsv unicorn pjh 1877 0.1 0.0 30708 3620 ? S Jan05 15:19 | \_ /bin/bash config/unicorn_launcher -E production -c config/unicorn.conf.rb pjh 1982 0.3 3.2 402184 129084 ? Sl Jan05 30:40 | \_ unicorn master -E production -c config/unicorn.conf.rb pjh 3070 1.0 5.1 1223148 201952 ? Sl Jan05 102:04 | | \_ sidekiq 3.5.0 discourse [0 of 5 busy] pjh 3078 0.3 3.9 994688 154580 ? Sl Jan05 32:14 | | \_ unicorn worker[0] -E production -c config/unicorn.conf.rb pjh 3101 0.3 4.5 1042816 177520 ? Sl Jan05 33:16 | | \_ unicorn worker[1] -E production -c config/unicorn.conf.rb pjh 3114 0.3 4.7 1040744 184908 ? Sl Jan05 34:19 | | \_ unicorn worker[2] -E production -c config/unicorn.conf.rb pjh 13003 0.0 0.0 19976 3444 ? S 15:53 0:00 | \_ sleep 1 root 1876 0.0 0.0 176 0 ? Ss Jan05 0:00 \_ runsv rsyslog syslog 1879 0.0 0.0 180152 2828 ? Sl Jan05 0:04 | \_ rsyslogd -n root 1882 0.0 0.0 176 0 ? Ss Jan05 0:00 \_ runsv cron root 1885 0.0 0.0 26780 1756 ? S Jan05 0:02 | \_ cron -f root 1884 0.0 0.0 176 0 ? Ss Jan05 0:00 \_ runsv postgres message+ 1886 0.0 0.1 386252 6108 ? S Jan05 0:59 \_ /usr/lib/postgresql/9.3/bin/postmaster -D /etc/postgresql/9.3/main message+ 1977 0.0 0.1 386384 6216 ? Ss Jan05 0:06 \_ postgres: checkpointer process message+ 1978 0.0 0.0 386384 2252 ? Ss Jan05 0:13 \_ postgres: writer process message+ 1979 0.0 0.0 386384 2312 ? Ss Jan05 0:29 \_ postgres: wal writer process message+ 1980 0.0 0.1 387124 4004 ? Ss Jan05 0:47 \_ postgres: autovacuum launcher process message+ 1981 0.0 0.0 104100 2496 ? Ss Jan05 2:05 \_ postgres: stats collector process kernoops 1486 0.0 0.0 37148 1368 ? Ss Jan05 0:24 /usr/sbin/kerneloops root 1681 0.0 0.0 253044 3696 ? Ss Jan05 1:03 /usr/sbin/apache2 -k start www-data 24844 0.0 0.0 253556 3232 ? S Jan10 0:00 \_ /usr/sbin/apache2 -k start www-data 24845 0.0 0.0 253556 3136 ? S Jan10 0:00 \_ /usr/sbin/apache2 -k start www-data 24846 0.0 0.0 253556 3192 ? S Jan10 0:00 \_ /usr/sbin/apache2 -k start www-data 24847 0.0 0.0 253556 3144 ? S Jan10 0:00 \_ /usr/sbin/apache2 -k start www-data 24848 0.0 0.0 253556 3044 ? S Jan10 0:00 \_ /usr/sbin/apache2 -k start www-data 28744 0.0 0.0 253556 3628 ? S Jan10 0:00 \_ /usr/sbin/apache2 -k start www-data 6436 0.0 0.0 253556 3528 ? S Jan10 0:00 \_ /usr/sbin/apache2 -k start www-data 18163 0.0 0.0 253556 3652 ? S Jan11 0:00 \_ /usr/sbin/apache2 -k start www-data 20743 0.0 0.0 253548 3632 ? S 11:25 0:00 \_ /usr/sbin/apache2 -k start www-data 20745 0.0 0.0 253548 3672 ? S 11:25 0:00 \_ /usr/sbin/apache2 -k start root 1728 0.0 0.0 15824 1456 tty1 Ss+ Jan05 0:00 /sbin/getty -8 38400 tty1
-
Wait, I thought the term "child process" was purely cosmetic? I didn't realize some people might think there was an actual relationship between the processes...
There is.
Not sure about on Windows, but on UNIX the processes are considered related until you use the
setsid
system call on the child to move a process to a new process group. Usually this is done immediately after you fork the new process.
-
Well, at least you're not sending SIGKILL to it...
SIGKILL
isn't so much about sending a signal to the process as it is sending a note toinit
to take the process out back and shoot it.
-
@LB_ said:
I didn't realize some people might think there was an actual relationship between the processes...
On Linux, there is.
And Windows.
Yes, but what does it actually mean, apart from when you want to go into Task Manager and "End Process Tree"? It remembers the relationship, but I don't think it automatically gives the two processes any kind of special parent-child bond.
-
The only thing that comes to mind is the child process inherits the parent's security descriptor
-
The only thing that comes to mind is the child process inherits the parent's security descriptor
unless of course the parent explicitly asks for the child process to get a different one of course.
although that is a bit of an odd case...
-
The only thing that comes to mind is the child process inherits the parent's security descriptor
In the default case, yes, although there's nothing unique about it. And after the initial process creation is completed, it doesn't really matter anymore.
unless of course the parent explicitly asks for the child process to get a different one of course.
although that is a bit of an odd case...
Isn't that how elevation normally takes place?
-
IIUC, an elevated process doesn't lose its original security descriptor; it just operates under a different one on a temporary basis.
-
Isn't that how elevation normally takes place?
yes, ish?
elevation is the process assuming a more permissive security descriptor rather than spawning a child one with a more permissive descriptor.
because the child can ask for a different security descriptor too, it might not get it (security), but it can ask for it
-
Ah... maybe more like "Run as...", then?
-
And Windows.
IIRC that is only the process creation metadata and isn't really significant. Or that's what I've been told.
There is.
Are they just siblings or actually child and parent? I was under the impression they were siblings, but I don't work with *nix very often.
The only thing that comes to mind is the child process inherits the parent's security descriptor
That's not really a long term relationship though, changing that for the parent doesn't change it for existing children (I assume).
-
@RaceProUK said:
The only thing that comes to mind is the child process inherits the parent's security descriptor
That's not really a long term relationship though, changing that for the parent doesn't change it for existing children (I assume).
Correct; I guess I should have said
the child process inherits a copy of the parent's security descriptor
which is more technically correct.
-
FWIW, when I start Task Manager (using Ctrl-Shift-Esc; if you use the Run window, it starts under explorer.exe but other than that it's the same)
Then click "Show processes from all users", which launches an elevated instance of the process, and if you're quick you'll see this happen:
Then the non-elevated process (PID 6044 in those screenshots) exits, orphaning the new process (PID 8972):
Edit: the screenshots were taken from a separate instance of Process Explorer.
-
Yeah, Windows processes can't change their uid IIRC. That's why taskmgr (and other programs that need to do the same thing) do that
-
Are they just siblings or actually child and parent? I was under the impression they were siblings, but I don't work with *nix very often.
henke37 mentioned this earlier:
https://what.thedailywtf.com/t/process-exit-on-windows-and-linux/53959/37?u=powerlord
Specifically, if you don't break the relationship using
setsid
(orsetpgrp
, but you should never use that one), the parent process gets sent aSIGCHLD
signal when the child exits.
-
From the MSDN page on ExitProcess :
Exiting a process does not cause child processes to be terminated.
Who designed this shitty operating system?!
Sounds like a good design to me.
Would you really want a system where process termination forces termination of all its children?
Would you want all your apps (and maybe even your login session) to terminate because the Explorer process crashes? If you launch a GUI-based app from a console window, would you want it to be forcibly terminated when you quit the terminal app?
There are plenty of reasons why you may want to launch a child process that is capable of outliving its parent.
... Distributed batch processing ...
Stuff like this, when it starts failing, is nasty evil and ugly under all circumstances. Figuring out how to kill a huge set of distributed child processes is only the tip of the iceberg here, I'm afraid.
-
Would you want all your apps (and maybe even your login session) to terminate because the Explorer process crashes? If you launch a GUI-based app from a console window, would you want it to be forcibly terminated when you quit the terminal app?
... I feel like you're only reading the Windows half of this conversation.
If you did this, you would have a system call to sever the connection between the child and parent process for programs that are not intended to be child processes belonging to a parent.
On UNIX, this is
void setsid()
, which I mentioned twice in this thread already.
-
I'd like to point the blame at the POSIX design instead. The entire concept of a parent being interrupted in the middle of work to be notified that it has to go and collect the exit code of the child process is moronic.
If I want to be notified, I call a function such as MsgWaitForMultipleObjects in my main loop. Then I'm prepared to deal with the situation. None of this nonsense with surprise control flow hijacking.
To be fair, the default action for SIGCHLD is to ignore the signal, so you have to ask for your control flow to be hijacked by changing the signal's handler.
Then again, I wish for somewhat more unified event handling in various POSIX and *nix APIs. Waiting on multiple different objects (e.g., file/socket plus a condition variable for example) is a mess. OTOH MsgWaitForMultipleObjects seems nice but has (had?) a ridiculously low maximum number on the number of objects it can wait on (like 64 or something silly).
-
... I feel like you're only reading the Windows half of this conversation.
... I feel like you ignored the fact that my post was in reply to two specific messages and not the entire thread, and that you believe I wrote something that disagrees with what you wrote.
-
Err... it isn't? I assume that's intended to be rhetorical and/or socratic, but I don't see the difference. When you destroy things, their children should go with them.
How very north korea of you.
-
Well, you can wait for file descriptors and a signal at the same time with
pselect
orppoll
. And you can get certain things redirected to file descriptors. But indeed AFAIK you can't easily add a condition to the mix. Easiest solution is probably to use a pipe instead...
-
we have no idea if they've spawned children themselves.
(someone may have mentioned this already, I haven't caught up yet)
SysInternals Process Explorer shows a tree of processes, so it would appear that it is possible to know the children a process has spawned.
-
Well, you can wait for file descriptors and a signal at the same time with
pselect
orppoll
. And you can get certain things redirected to file descriptors. But indeed AFAIK you can't easily add a condition to the mix. Easiest solution is probably to use a pipe instead...Yea, the pipe is what I do now. IIRC using signals seemed surprisingly tricky w.r.t. race conditions - although I can't remember now what the problem was exactly.
Futexes seemed like a solution at some point, with FUTEX_FD, but that was removed again because it was "inherently racy".
I used to wish for a unified way for waiting on various events, but .. the whole thing becomes pointless as soon as you start adding third party libraries. Few of them expose the underlying primitives anyway, so ... meh.
-
SysInternals Process Explorer shows a tree of processes, so it would appear that it is possible to know the children a process has spawned.
Yes, it does this by reading the process creation metadata, which I have been told is not used for anything other than being meta. Unfortunately I am having a hard time finding the source of this info, I don't remember what I was searching for at the time that led me to this possibly not true statement.
-
SysInternals Process Explorer shows a tree of processes, so it would appear that it is possible to know the children a process has spawned.
Race conditions.
-
The fundamental unit is usually the file descriptor. I never closely looked into futexes, but they are inherently hard to use because they assume a certain part of the work is done in userspace via atomic instructions.
Signals require using
pselect
orppoll
and supplying a signal mask. For example, if you rely on SIGHUP to trigger a configuration load, you'll have it blocked normally but unblocked using the signal mask option topselect
andppoll
. This ensures arrival of that signal always interrupts the call.Relying on signals while using normal
select
orpoll
(orsleep
/nanosleep
, if no file descriptors are involved) is racy because the signal might arrive between unblocking the signal (or testing the condition) and going into theselect
(or whatever) call.
-
Signals require using
pselect
orppoll
and supplying a signal mask. For example, if you rely on SIGHUP to trigger a configuration load, you'll have it blocked normally but unblocked using the signal mask option topselect
andppoll
. This ensures arrival of that signal always interrupts the call.Yeah, as said, I don't really remember what the problem was. It seems like
ppoll
plus one of theSIGRT*
signals (inter-thread) should have done the trick.
-
Ended up reading a bit more about this, and seems like there's
-
@dcon said:
SysInternals Process Explorer shows a tree of processes, so it would appear that it is possible to know the children a process has spawned.
Yes, it does this by reading the process creation metadata, which I have been told is not used for anything other than being meta. Unfortunately I am having a hard time finding the source of this info, I don't remember what I was searching for at the time that led me to this possibly not true statement.
Yeah, and the main problem with this is that in Windows, processes can be orphaned, because unlike unixoid systems, the existence of a child process doesn't prevent the termination of a process, not even the destruction of a process object.
This is, of course, a blessing, because otherwise it would be impossible for a program to explicitly wait for its parent to terminate (as is required for, say, an automatic update mechanic).
-
-
```
class Foo {
// stuff
private:
Bar* bar;
Baz* baz;
}class Foo {
// stuff
private:
std::unique_ptr<Bar> bar;
std::unique_ptr<Baz> baz;
};HTH, HAND, ZOMG, BBQ, ETC
-
```
class Foo {
// stuff
private:
std::unique_ptr<Bar> bar;
std::unique_ptr<Baz> baz;
};```cpp class Foo { // stuff private: Bar bar; Baz baz; };
No need to use any pointers of any kind.
-
...unless
Bar
orBaz
is abstract.
-
...Or is the output of a Factory, some kind of ProcessFactory maybe...
-
...Or is the output of a Factory, some kind of ProcessFactory maybe...
I thought dependency injection was all the rage these days? My point still holds if
bar
andbaz
are move-constructed or copy-constructed from constructor parameters. (However, if polymorphism is involved, then yeah, a smart pointer is needed.)
-
Could also be intended to use the Pimpl idiom (hide implementation details behind a pointer so including the class header doesn't include implementation specific headers).