Is there a way to prevent only a specific child from triggering a SIGCHLD?
Asked Answered
S

2

5

I'm writing a debugging utility, and I want to fork a child while preventing that child's termination from triggering a SIGCHLD to its parent. I still want other children to normally cause a SIGCHLD upon termination.

I want to do this because I don't want the fork to trigger an existing $SIG{CHLD} handler, but I still want other children to trigger it. That is, I want to isolate my new child and I don't want it to interfere with management of existing children.

I'm wary of locally installing a custom $SIG{CHLD} handler since I don't think I can properly detect when I should call the original handler. For instance, if I install a local $SIG{CHLD} handler, then I'm guaranteed to receive a SIGCHLD signal once I spawn a child and have the parent waitpid for it to terminate. However, that SIGCHLD won't indicate whether or not other children have terminated as well, so I can't be sure whether to call the original handler.

I researched that a process cannot change its parent pid. I'm not sure if changing the child's process group id or session id would be useful.

Is it even possible to prevent a specific child from triggering the SIGCHLD on the parent? Am I forced to rely on the existing $SIG{CHLD} handler to Do The Right Thing when it receives the SIGCHLD signal for a child that it did not expect?

Although there may be a better way to implement that debugging utility (let me know if there is), I'm still wondering whether POSIX offers such fine-grained control over children, and am looking for a Perl solution to my dilemma.

Sirree answered 5/3, 2014 at 15:5 Comment(0)
L
2

You can't portably prevent the SIGCHLD entirely, but if the signal handler is written properly you can prevent the waitpid from returning your debugging tool's pid by orphaning it.

use Signal::Mask;
sub start_background_child {
  local $Signal::Mask{CHLD} = 1;
  my $first = fork;
  croak "fork failed: $!" if not defined $first;
  if ($first) {
    waitpid $first, 0;
    return;
  }

  my $second = fork;
  croak "fork failed: $!" if not defined $second;
  exit if $second;

  your_stuff_here();
}
Leadwort answered 5/3, 2014 at 16:30 Comment(2)
This approach may or may not prevent that initial SIGCHLD (portability issue), but if it doesn't waitpid will never return the debugging process.Leadwort
As perfect as it can get! On my macbook (OS X 10.8.2), this works as desired. Also, I love your Signal::Mask and Signal::Pending modules; they made it very easy to explore the behavior on my system. :)Sirree
H
1

I think you could daemonize the special child -- fork twice -- to sever the parent-child relationship. You'd still receive a SIGCHLD when the process was created -- I don't know if that's acceptable for you.

sub start_special_child {
    return if fork;     # run rest of this routine in child process
    fork && exit;       # run rest of this routine in grandchild process
                        # the  exit  here triggers SIGCHLD in the parent
    ...                 # now run your special process
    exit;               # exit here does not trigger SIGCHLD
}

The other approach is to keep track of the process id's of your child processes, and use waitpid to figure out which process(es) triggered the SIGCHLD handler.

$SIG{CHLD} = \&sigchld_handler;
$pid1 = start_child_process();
$pid2 = start_child_process();
$pid3 = start_child_process();
$pidS = start_special_child_process();

sub sigchld_handler {
    $pid = waitpid -1, WNOHANG;     # use POSIX ':sys_wait_h'
    return if $pid == $pidS;
}
Harneen answered 5/3, 2014 at 16:4 Comment(2)
I'd like to avoid that initial SIGCHLD, so the first approach is unfortunately not acceptable for me (since it would still trigger the existing $SIG{CHLD} handler). The second approach would be ideal if I had control of the original $SIG{CHLD} handler as well as the other children. However, I'm looking for a generic solution that works regardless of the original $SIG{CHLD} and without knowledge of any other children. I'd also like to avoid reaping the other children (but I don't mind if the special child is reaped).Sirree
I don't usually like Perl's threads, but maybe a thread is more appropriate here than a fork.Harneen

© 2022 - 2024 — McMap. All rights reserved.