Process control and parallel programs with perl
What I need: My perl program creates several external programs. It will then monitor external programs and restart them if they fail for any reason. The perl program cannot wait for started processes. I don't care about STDOUT or STDERR from generated programs and would like to completely close any of them.
My problems so far: First, I am not very good at the principles of process abstraction and process control. Various termination scripts of my perl program and its children will call " zombies
" (programs that were after the children of my perl program are adopted init
)
using open
to create 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 pstree -p 6947
below, you can see that my perl program has created two child processes sc_serv(6948)
and sh(6949)
. sh(6949)
<- this shell was created because it ffmpeg(6950)
was passed to vlc(6952)
(at least I believe it needs a shell, I'm 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 completed and cleaned, although killing sh(6949)
its children will start to create a bit of a mess.
Note that if I don't set $SIG{CHLD}
to IGNORE
, then killing sh(6949)
will leave its children init
and also make the process sh(6949)
a defunct
. I don't understand why this is the case. so,
Question 1. Why in this situation do I need to install $SIG{CHLD}
in IGNORE
?
Question 2. How can I make sure that if I need to kill sh(6949)
that all his children will also be killed?
I have also tried using fork
along with system
or exec
. The result fork
was very similar to the use result open
. Here's sub using system
. I 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 section ends up with almost the same problems as above, but the children are now cloned perl programs. The killing perl(25991)
will cause all of his children to start creating the same disorder as above. Also, when killing perl(25982)
, the Parent of all processes will not kill all of its children properly. It will kill some, but not all, creating more confusion than my open attempts.
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 it fork
with system
or exec
to run external programs and control their termination without leaving init?
Please be as detailed as you'd like, I don't mind trying modules, but I'd rather learn how to do it without them to better understand process control with perl.
links:
start a process in the background without being accepted by init, Perl
How can I get the process id of the UNIX command I'm running in a Perl script?
source to share
Question 1. Why do I have to set $SIG{CHLD} to IGNORE in this situation?
When you install this, it tells your child processes not to hang, so that your parent gets them with wait
or waitpid
. This does not mean zombies. The main thing is that he also cascaded to the kids when you fork()
. This way your process sh
inherits it.
When set to IGNORE
, yours sh
will exit immediately after his death, and children will retreat from init
. If you don't install it, then there sh
will be a zombie, waiting for the parent to receive it with help wait()
and collect the return code. You may want this because the parent can define the exit condition and do whatever he wants.
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?
Either: Kill the negative process id which will send the same signal to the whole tree. Or: use a signal handler and delay the signal - for example, SIGHUP
or SIGUSR1
- and then use that handler to propagate the signal to the appropriate child processes. Potentially a combination of both. (See: Best way to kill all child processes )
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?
What are you trying to avoid? All "reparations" of the process will do, which means it init
will clear them when they go out and go zombies, automatically. (Which can be avoided by installing $SIG{'CHLD'}
)
As someone mentioned in the comments - it might be worth a look threads
instead fork
. This is a different IPC model - it is probably less efficient, but makes some types of programming much easier and clearer - in particular, anything that uses operating models such as shared memory.
source to share