3

I am using Linux and .sh is in tcsh.

I have made a very basic fork and exec, but I need help in implementing safeties to it.

Basically my perl script calls a .sh script in a child process. But when I do Ctrl+c to kill the parent, the signal gets ignored by the child.

1) How do I capture the SIGINT for the child process as well?

2) The child process that runs the .sh script still STDOUT to the screen of the xterm. How can I remove this? I was thinking of doing running the script in the background

 exec("shell.sh args &");  

But haven't tested as I need to figure out how to keep the child from going wild first.

3) The parent process(perl script) doesn't wait on the child(.sh script). So I've read a lot about the child becoming a zombie??? Will it happen after the script is done? And how would I stop it?

$pid = fork();
if($pid < 0){
    print "Failed to fork process... Exiting";
    exit(-1);
    }
elsif ($pid ==0) {
    #child process
    exec("shell.sh args");
    exit(1);
    }
else { #execute rest of parent}
user1539348
  • 513
  • 1
  • 7
  • 20

2 Answers2

10

But when I do ctrl+c to kill the parent, the signal gets ignored by the child.

The signal is sent to two both the parent and the child.

$ perl -E'
   if (my $pid = fork()) {
      local $SIG{INT} = sub { say "Parent got SIGINT" };
      sleep;
      waitpid($pid, 0);
   } else {
      local $SIG{INT} = sub { say "Child got SIGINT" };
      sleep;
   }
'
^CParent got SIGINT
Child got SIGINT

If that child ignores it, it's because it started a new session or because it explicitly ignores it.

The child procces that runs the .sh script still STDOUT to the screen of the xterm. How can I remove this?

Do the following in the child before calling exec:

open(STDOUT, '>', '/dev/null');
open(STDERR, '>', '/dev/null');

Actually, I would use open3 to get some error checking.

open(local *CHILD_STDIN,  '<', '/dev/null') or die $!;
open(local *CHILD_STDOUT, '>', '/dev/null') or die $!;
my $pid = open3(
   '<&CHILD_STDIN',
   '>&CHILD_STDOUT',
   '>&CHILD_STDOUT',
   'shell.sh', 'args',
);

The parent process(perl script) doesn't wait on the child(.sh script). So I've read alot about the child becoming a zombie???

Children are automatically reaped when the parent exits, or if they exit after the parent exits.

$ perl -e'
   for (1..3) {
      exec(perl => (-e => 1)) if !fork;
   }
   sleep 1;
   system("ps");
' ; ps
  PID TTY          TIME CMD
26683 pts/13   00:00:00 bash
26775 pts/13   00:00:00 perl
26776 pts/13   00:00:00 perl <defunct>       <-- zombie
26777 pts/13   00:00:00 perl <defunct>       <-- zombie
26778 pts/13   00:00:00 perl <defunct>       <-- zombie
26779 pts/13   00:00:00 ps
  PID TTY          TIME CMD
26683 pts/13   00:00:00 bash
26780 pts/13   00:00:00 ps
                                             <-- all gone

If the parent exits before the children do, there's no problem.

If the parent exits shortly after the children do, there's no problem.

If the parent exits a long time after the children do, you'll want to reap them. You could do that using wait or waitpid (possibly from a SIGCHLD handler), or you could cause them to be automatically reaped using $SIG{CHLD} = 'IGNORE';. See perlipc.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Improved answer to last question. – ikegami Sep 26 '12 at 18:49
  • So back to the 1st question. How do I kill the child process with ctrl+C, if they both still recieve the signit? Do I put this in the parent $SIG{INT} = sub{ kill -9 $pid}; ? also a related question what happens if a child has already finished before the parent executes the waitpid($pid, 0)? in the case of multiple childs forked at once(with a loop) then waited for at the end. – user1539348 Sep 26 '12 at 20:28
  • I can't install any modules, so should I just output the error to a file? – user1539348 Sep 26 '12 at 20:37
  • First, that's not true. If you can install a script, you can install a module. There's no difference. Secondly, `open3` is part of Perl. You also have the option of passing a shell command (`'shell.sh args >/dev/null 2>&1'`) although you then have to deal with properly escaping any argument you interpolate into it. – ikegami Sep 26 '12 at 20:40
  • I don't have access to install modules on the linux, school issues. If I used $pid = open3( '<&CHILD_STDIN', '>&CHILD_STDOUT', '>&CHILD_STDOUT', 'shell.sh', 'args', ); Does this spawn another process? Or does it replace the current one like exec. The reason I used Fork and Exec was because I didn't want the parent to wait on the child. But was afraid of creating zombie processes. – user1539348 Sep 26 '12 at 20:51
  • You don't need any special access. It does the same fork and exec, plus error checking. Already explained about zombies. – ikegami Sep 27 '12 at 00:40
0

Use waitpid in the parent thread: http://perldoc.perl.org/functions/waitpid.html

waitpid($pid, 0);

You can also redirect stdout of your exec to /dev/null:

exec("shell.sh args > /dev/null");
RobEarl
  • 7,862
  • 6
  • 35
  • 50