What I need : My perl program is spawning a few external programs. Then it will monitor the external programs and restart them if they fail for any reason. The perl program cannot wait for the processes it started. I do not care about the STDOUT or STDERR of the spawned programs and would like to close any of their output completely
My problems so far :
First I don't know much beyond the basics of the process abstraction and process management. Different scenarios of process termination of my perl program and its children will cause 'zombies
' (programs that were once children of my perl program will become adopted by init
)
using open
to create the external child programs
# please note I have simplified the shell commands for this example
$SIG{CHLD}='IGNORE';
open(my $ph, "-|", "./sc_serv --options") or die $!;
open(my $ph, "-|", "(ffmpeg --opt1 --opt2) | vlc --many-options --etc") or die $!;
If you look at the output of pstree -p 6947
below you can see that my perl program created two children processes sc_serv(6948)
and sh(6949)
. sh(6949)
<- this shell was created because ffmpeg(6950)
was piped into vlc(6952)
( atleast I belive this is why a shell was needed, I am not 100% sure. )
perl(6947)─┬─sc_serv(6948)─┬─{sc_serv}(6964)
│ ├─{sc_serv}(6965)
│ └─{sc_serv}(6966)
└─sh(6949)─┬─ffmpeg(6950)
└─vlc(6952)─┬─{vlc}(6980)
├─{vlc}(6983)
└─{vlc}(6985)
Now if I kill perl(6947)
the entire family tree will be properly terminated and cleaned up, though killing sh(6949)
will leave its children to init creating a bit of a mess.
Please note that if I do not set $SIG{CHLD}
to IGNORE
, killing sh(6949)
will leave its children to init
and will also make sh(6949)
a defunct
process. I do not understand why this is. so,
Question 1.
Why do I have to set $SIG{CHLD}
to IGNORE
in this situation?
Question 2.
How do I make sure that if I need to kill sh(6949)
that all of its children will be killed as well?
I have also tried using fork
along with system
or exec
. The outcome of fork
has been very similar to the outcome of using open
. Here is a sub using system
I have tried.
sub start_vlc {
my $pid = fork();
if( $pid == 0 ) {
close STDOUT;
close STDERR;
system "(ffmpeg --opt1 --opt2) | vlc --many-options --etc";
}
else {
return $pid;
}
}
Using this sub ends up with almost the same problems as above, but now the children are the cloned perl programs. Killing perl(25991)
will leave all of its children to init creating the same mess as above. Also killing perl(25982)
, the Parent of all of the processes, will not kill all of its children properly. It will kill some but not all, creating more of a mess than my attempts with open.
perl(25982)─┬─perl(25989)───sc_serv(25990)─┬─{sc_serv}(25992)
│ ├─{sc_serv}(25993)
│ └─{sc_serv}(25994)
└─perl(25991)───sh(25995)─┬─ffmpeg(25996)
└─vlc(25997)─┬─{vlc}(26042)
├─{vlc}(26044)
└─{vlc}(26046)
Question 3.
How can I use fork
along with system
or exec
to spawn external programs and have control over their termination without leaving orphans to init?
Please be as detailed as you would like, I am not opposed to trying modules but I would rather learn how to do this without them to better my understanding of process control with perl.
references :
run process in background without being adopted by init, Perl
How can i get process id of UNIX command i am triggering in a Perl script?
threads
? – amphetamachinesetsid
to you can control your children using process groups. – Mark Setchell