Hi everybody,

I am currently working on a project and I need parent process to fork
multiple children to do the actual work, but maintain a bidirectional
communication with each of the children to send them commands and
receive back, agregate and display summary results. 

According to the perlipc tutorial and other resources, the best in this
case is to use either pair of unidirectional pipes, or use anonymous
sockets created by socketpair function. I chose socketpair, but I
discovered very weird behaviour and could not find any workaround so
far. Consider the following code:

#!/usr/bin/perl -w

use strict;
use warnings;
use Socket;
use IO::Handle;
use POSIX 'WNOHANG';

my $kids = 0;                   # Number of kids alive
my %kids = ();                  # kid PID => Socket
my ($child, $parent);

foreach my $i (1..3)
{
        socketpair($child, $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC)
                or die "socketpair: $!";
        #pipe($parent, $child);
        $child->autoflush(1);
        $parent->autoflush(1);
        print "Created child socket: ". fileno($child) . "\n";
        print "Created parent socket: ". fileno($parent) . "\n";

        die "cannot fork: $!" unless defined(my $pid = fork);
        unless ($pid)
        {
                close $child;
                do_child($parent);
                exit(0);
        }
        else {
                close $parent;
                $kids++;
                $kids{$pid} = $child;
        }
}

$SIG{INT} = \&HUNTER;
$SIG{CHLD} = \&REAPER;

foreach my $kid (keys %kids)
{
        print "Sending message to the kid $kid\n";
        print {$kids{$kid}} "Parent Pid $$ is sending this\n";
}

map { close $_ } values(%kids);
sleep while($kids);

sub do_child {
        my $parent = shift;
        print "Child $$\n";
        chomp(my $line = <$parent>);
        print "Child Pid $$ just read this:
'".(defined($line)?$line:'undef')."'\n";
        close $parent;
}

sub HUNTER {
        print "Killing children\n";
        my $count = kill INT => keys(%kids);
        print "Killed $count children\n";
}

sub REAPER {
        while ((my $kid = waitpid(-1,WNOHANG)) > 0)
        {
                $kids--;
                print "Reaped kid $kid\n";
        }
        unless ($kids)
        {
                print "Parent exit\n";
                exit(0);
        }
}

This is the working, but simplified example. For simplicity, in this
snippet I am trying just to write from parent process to all children,
not in both directions. In the parent process, I create socket pair in
loop, fork and store the handle to communicate with the newly created
child in the hash. I then try to send message to all stored sockets (to
all children). If I run this example, I get the following output:

Created child socket: 3
Created parent socket: 4
Child 4230
Created child socket: 3
Created parent socket: 4
Use of uninitialized value $line in chomp at ./test_bidirectional.pl
line 52.
Child Pid 4230 just read this: 'undef'
Created child socket: 3
Created parent socket: 4
Sending message to the kid 4232
Sending message to the kid 4231
Sending message to the kid 4230
Child 4231
Use of uninitialized value $line in chomp at ./test_bidirectional.pl
line 52.
Child Pid 4231 just read this: 'undef'
Child 4232
Child Pid 4232 just read this: 'Parent Pid 4229 is sending this'
Reaped kid 4230
Reaped kid 4231
Reaped kid 4232
Parent exit

The socketpair() function called in loop returns the same pair of
sockets each call. So all children are reading from the same socket! Is
this the correct behavior? I would expect, that the socketpair function
creates and returns new pair of sockets each call. I need to have
different pair of sockets to communicate with each child, otherwise they
are competing for the incoming data and only one receves them. In the
code is commented pipe() function. If you comment the createsocketpair
and uncomment the pipe function, result is the same. Even the pipe
function does not create new pair of handles each call.

Does anyone know, what am I doing wrong, what am I missing? What is the
correct way to establish communication with multiple subprocesses? I
really need to get this working somehow. If needed, I will gladly
provide more information.

Thanks in advance for your time

Regards

Honza Mach

P.S. My system information:

Perl: v5.10.1 (*) built for x86_64-linux-gnu-thread-multi
WS: Linux Mint 10, Linux 2.6.35-28-generic x86_64 GNU/Linux

Attachment: smime.p7s
Description: S/MIME cryptographic signature

Reply via email to