Interprocess communication essentials in Perl
Connections
Anyone who has written a fairly complex script has had to deal with interprocess communication (IPC) at one time or another. In this article, I talk about the use and structure of IPC by reading and writing from parent to child and from child to parent processes, and I'll look at bi-directional communication to achieve both at the same time.
A process is a task given to the operating system that has its own execution resources, including its own allocation of CPU time. A process can interact with other processes in a number of different ways, as you'll see in this article.
The support for IPC in Perl is considerable. At its most basic is the use of backticks to perform system commands (e.g., unlink
). When you submit a command with backticks, a new child process is created. When the task the child process has been given is complete, the child process reports back to the parent process and the parent script continues.
Perl supplies the fork
function to start a new process after setting up a pipe with the pipe
command. Although you can use the open
function in lieu of the pipe
function, I'll stick to piping at first.
It should be noted that the use of fork
is entirely operating system dependant. It is readily available on Unix or Linux machines, but you'll run into problems on a Windows system because the command is not supported on that platform.
Signal Handling
On Linux, processes can communicate with each other with signals. Perl implements an easy mechanism for catching signals sent to your processes. All you do is connect a signal-handling subroutine to the signal's entry in a predefined %SIG hash. To set up a machination to catch an INT
signal, use:
$SIG{INT} = \&handler_sub;
Note that a hard reference to the handler_sub
subroutine was used, but you could also use symbolic references, if required. The handler_sub
subroutine simply gets the name of the signal passed as an argument:
sub handler_sub { $forced_scalar = shift; die "The signal was $forced_scalar"; }
A signal handler like this gives you an idea of the ease with which Perl handles IPC. The die
and print make for a clean exit from the subroutine.
You should note here that Perl is currently not re-entrant. When a function is re-entrant, you can interrupt processing while still inside the function and call it again at any time, because the original state of the data is stored and is restored when the second call to the function exits. If a function is not re-entrant, an interrupt and subsequent call will overwrite the original data.
Functions
Another easy way to play with processes is with the exec
function, which executes your commands and doesn't return if the command is executed without error. For example, the exec
function will fail and return a false value if the program you called isn't found. That is, exec
returns only when an error is encountered and is the only time exec
will return a value. If you want a return value, you would use system
instead of exec
. I'll explore system
thoroughly later.
Parameters passed to exec
are treated in a very interesting way. The list of parameters passed is parsed, and the first element in the list is treated as the program to run. The next value is a function of the program being executed. For example, if you were to use the copy
command, you would specify the file name to be copied as a parameter. In the code
@thisarray = ("copy", "thisfile.txt"); if (exec(@thisarray)) { if ($?) { die qq{Error Encountered: $?}; } else { die qq{One File Copied. File Name Is: $thisarray[1]}; } }
the first element of the array passed is the program to run, and the next item within the array is the file name. The exec
is executed if @thisarray
exists, and the two parameters within @thisarray
are used to perform the copy
system command. If no errors are encountered, the program prints and exits. The program also reports whether an error has been placed in the $?
status variable, which then prints an error message and exits the program without copying the file.
This simple and intuitive way of creating processes isn't all that is available. To really get an idea of how to use processes, you must explore the system
function. When you call a program with exec
, it replaces the current program with the one called by exec
. If you were to use the system
command, however, you'd end up with a program that forks and creates a child process. The program is then executed, and any values you have need for can be returned to the parent.
Pipes
The means to send data to and from different processes is a necessarily simple task. To open a program with a pipe, I use:
open(THISFILEHANDLE, "thisfile.cgi |");
The pipe (|
) character sets the file to send its output to the calling program. To make the operation go the other way and send data to the program, simply move the pipe:
open(THISFILEHANDLE, "| thisfile.cgi");
The child process, then, can be piped to or from, depending on what is required. Pipes are fundamental to IPC. The entire read operation would be,
open(THISFILEHANDLE, "thisfile.cgi |"; while (<THISFILEHANDLE>) { print; } close(THISFILEHANDLE);
where the file that is opened and executed simply has a print
statement. This ease of reading and writing to different processes is a big reason why Perl has stayed alive all these years. To send data would be just a little different:
open(THISFILEHANDLE, "| thisfile.cgi"; print THISFILEHANDLE "Hey There!"; close(THISFILEHANDLE);
The example sends the string Hey There! to the program that was opened. What the program that was opened does with the string is entirely up to you.
Buy this article as PDF
(incl. VAT)