How to keep track of child processes - linux

So my program spawns a number of child processes in response to certain events, and I'm doing something ike this to keep track and kill them upon program exit (Perl syntax):
my %children = ();
# this will be called upon exit
sub kill_children {
kill 'INT' => keys %children;
exit;
}
# main code
while(1) {
...
my $child = fork();
if ($child > 0) {
$children{$child} = 1;
} elsif ($child == 0) {
# do child work ...
exit();
} else {
# handle the error
}
}
So the idea is as above. However, there's a blatant race condition there, in that a given child can start and terminate before the father has a chance to run and record its pid in the %children hash. So the father may end up thinking that a given pid belongs to an active child, even if this child has terminated.
Is there a way to do what I'm trying to accomplish in a safe way?
Edit: To better keep track of children, the code can be extended as follows (which however also suffer of the exact same race condition, so that's why I didn't write it fully in the first place):
my %children = ();
sub reap {
my $child;
while (($child = waitpid(-1, WNOHANG)) > 0) {
#print "collecting dead child $child\n";
delete $children{$child};
}
}
$SIG{CHLD} = \&reap;
# this will be called upon exit
sub kill_children {
local $SIG{CHLD} = 'IGNORE';
kill 'INT' => keys %children;
exit;
}
# main code
while(1) {
...
my $child = fork();
if ($child > 0) {
$children{$child} = 1;
} elsif ($child == 0) {
# do child work ...
exit();
} else {
# handle the error
}
}
Even in this case, the contents of %children may not reflect the actual active children.
Edit 2: I found this question, which is exactly about the same problem. I like the solution suggested in there.

On UNIX it's not a race condition. This is the standard way to handle fork(). When the child process exits, its status is changed to "terminated"; it becomes a zombie. It still has an entry in the process table until the parent process calls one of the wait functions. Only after that is the dead process really removed.
Even if the parent sets itself up to ignore SIGCHLD, it still wouldn't qualify as a race condition; the parent would just have a PID that's not valid anymore. In that case, wait() would return ECHILD. But setting SIGCHLD would free up a child's PID, possibly leading to the parent trying to kill a process that is not a child.
On Windows, which doesn't have a fork call, it is emulated by creating a thread in the perl process. See perlfork. I'm not knowlegable enough about Windows to opinionate about if that could cause a race condition, but I suspect not.

Related

syncronizing forked threads

I'd like to call fork() from within a signal handler, where the child thread needs to wait for the parent thread to finish a task before completing its tasks. So for example:
lock=init_lock();
grab_lock(&lock);
pid_t child_pid = fork();
if (child_pid != 0) {
/* we are the parent */
build_house();
release_lock(&lock);
} else {
/* we are the child */
play_in_yard();
wait_for_lock(&lock);
enter_house();
}
In this case, you don't want the child entering the house before the parent finishes building it. (The parent would have to lock before forking, to avoid a race condition where the child enters the house before the parents have a chance to grab the lock). I'm wondering what type of lock I could use. I'm not seeing any of the locking/unlocking api's in the async-safe list of functions. Is there any way to safely do this?

How to kill "fast fork()" process without reboot?

I've found a Torjan on Linux system, it uses a method called "fast fork", like the code below
while(1)
{
count += 1;
pid_t pid = fork();
if (pid < 0)
{
printf("there is something wrong\n");
}
if (pid > 0) // father process
{
/* every 0x1000 times fork run the evil code once */
if (count & 0xfff)
{
exit(0);
}
/* stop the program if the job isn't done in XXs */
alarm(XX);
// evil code
}
}
It's really effective and you can't find it via ps aux, is there anyway to find the process and kill it without reboot?
P.S. The code runs as a normal user (not root) and I don't have root access either.
If the process name doesn't change after each fork, you can use a method called "fast kill", like this
$ while true; do killall -9 process_name; done
(but how to get the process name if the origin file has been removed? sorry I don't have enough reputation to add a comment)

How do I kill linux spawnProcess when the main process suddenly dies?

I have come across a problem with my application and the spawnProcess.
If the main application for some reason dies/is killed then the spawned processes seem to live on and I can't reach them unless I use terminal to kill them via their PIDs. My goal is if the main application dies then the spawned processes should be killed also, somehow.
My code is like this
auto appPid = spawnProcess("path/to/process");
scope(exit){ auto exitcode = wait(appPid);
stderr.writeln(...);}
And if I use the same approach when the main process dies, using wait(thisProcessID) I get an error. "No overload matches". Any ideas how to solve this problem?
Here's some code that will do it on Linux. It doesn't have all the features of the stdlib's spawnProcess, it just shows the bare basics, but expanding it from here isn't hard if you need more.
import core.sys.posix.unistd;
version(linux) {
// this function is Linux-specific
import core.stdc.config;
import core.sys.posix.signal;
// we can tell the kernel to send our child process a signal
// when the parent dies...
extern(C) int prctl(int, c_ulong, c_ulong, c_ulong, c_ulong);
// the constant I pulled out of the C headers
enum PR_SET_PDEATHSIG = 1;
}
pid_t mySpawnProcess(string process) {
if(auto pid = fork()) {
// this branch is the parent, it can return the child pid
// you can:
// import core.sys.posix.sys.wait;
// waitpid(this_ret_value, &status, 0);
// if you want the parent to wait for the child to die
return pid;
} else {
// child
// first, tell it to terminate when the parent dies
prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
// then, exec our process
char*[2] args;
char[255] buffer;
// gotta copy the string into another buffer
// so we zero terminate it and have a C style char**...
buffer[0 .. process.length] = process[];
buffer[process.length] = 0;
args[0] = buffer.ptr;
// then call exec to run the new program
execve(args[0], args.ptr, null);
assert(0); // never reached
}
}
void main() {
mySpawnProcess("/usr/bin/cat");
// parent process sleeps for one second, then exits
usleep(1_000_000);
}
So the lower level functions need to be used, but Linux does have a function that does what you need.
Of course, since it sends a signal, your child might want to handle that to close more gracefully than the default termination, but try this program and run ps while it sleeps to see cat running, then notice the cat dies when the parent exits.

Return a value from a Signal handler inside a perl daemon

How do I return a value to a process which initiated a daemon from a signal handler inside a daemon?
sub _fork
{
my $pid = fork;
$pid;
}
sub daemonize_monitor_sigio
{
_fork and return;
SIG{IO} = sub{
print "caught sigio";
$ret = {}
...#do some processing
#wants to return $ret here;
}
while(1)
{
;
}
}
daemoniz_monitor_sigio();
The thing about signal handlers, is they're pretty simple. They're basically an interrupt from the kernel, that's passed to the process.
The handler can alter state within the process. But because you've fork()ed beforehand, you have a parent process and a child process - the two don't have any shared state. So 'signaling' from one to the other, is an completely seperate IPC - at the simplest level - you can send another kill signal to the parent process - and get this pid via getpgrp.
For more complicated IPCs though, you're looking at... well, reading the perlipc doc, and figuring out what's most appropriate.

perl threads: is_joinable vs is_running

while ($thr->is_running() {
# do something
}
vs
while (! $thr->joinable()) {
# do something
}
Is there any difference between the two?
When would a programmer use one over the other and vice versa?
I am assuming you cant join a thread if it's running so arent they basically the same thing?
If so, why does perl provides two different ways to check the status of a thread?
is_joinable is not the same as !is_running.
is_joinable checks for
(thread->state & PERL_ITHR_FINISHED) &&
!(thread->state & PERL_ITHR_DETACHED) &&
!(thread->state & PERL_ITHR_JOINED)
is_running checks for
!(thread->state & PERL_ITHR_FINISHED)
So
A detached thread that finished is neither running nor joinable.
A thread that's already been joined is neither running nor joinable.
As per the documentation :
$thr->is_running()
Returns true if a thread is still running
(i.e., if its entry point function has not yet finished or exited).
$thr->is_joinable()
Returns true if the thread has finished running,
is not detached and has not yet been joined. In other words,
the thread is ready to be joined, and a call to $thr->join() will not block.
So the difference stems from the way detached threads are treated.
i.e $thread->is_running() would return true if the thread is running irrespective of whether it is detached or not
but "not $thread->is_joinable()" would return true even if a thread is detached but has stopped running.
Example:
1) detached thread
use strict;
use warnings;
use threads;
sub do_nothing {
print("in thread\n");
sleep(30);
return;
}
my $t = threads->create(\&do_nothing);
$t->detach();
while ($t->is_running()) {
print("is running\n");
sleep(4);
}
if ($t->is_joinable()) {
print("is joinable\n");
}
else {
print("not joinable\n");
}
exit;
Exampled : a non-detached thread
use strict;
use warnings;
use threads;
sub do_nothing {
print("in thread\n");
sleep(30);
return;
}
my $t = threads->create(\&do_nothing);
while ($t->is_running()) {
print("is running\n");
sleep(4);
}
if ($t->is_joinable()) {
print("is joinable\n");
}
else {
print("not joinable\n");
}
exit;
They are not the same.
A thread is "joinable" if it has not been joined or detached, and is no longer running. That is to say, it provides a poll interface to the condition that joining the thread would block on.
Finished running, not yet joined, not detached == joinable
Not yet finished running, not yet joined, not detached == running.
See Perl Threads.

Resources