Inter-Process Communication
On Unix-like operating systems, several means of inter-process communication (IPC) are available, for instance:
In modern Fortran, we have access to further IPC mechanisms, such as:
Signals
On POSIX-compliant operating systems, signals are used to interrupt,
terminate, or control running processes. A process can react to specific signals
by implementing signal handlers for them. For example, an application may catch
SIGINT
to terminate when the user presses CTRL
+
C
. Common POSIX signals are listed in table 1. See
signal(3)
for a more comprehensive overview.
-
# Name Default Action Description 1 SIGHUP
Terminate process. Terminal line hang-up. 2 SIGINT
Terminate process. Interrupt program. 3 SIGQUIT
Create core image. Quit program. 9 SIGKILL
Terminate process. Kill program. - Table 1: Selection of POSIX signals on FreeBSD
GNU Fortran
GNU Fortran provides a compiler-dependent interface to POSIX signals. A
signal handler is registered by calling the signal()
routine. The
signal number and the handler function are passed as arguments:
! signal.f90
module handlers
implicit none
contains
subroutine sigint_handler()
print '("Process interrupted (SIGINT), exiting ...")'
end subroutine sigint_handler
end module handlers
program main
use :: handlers
implicit none
! Register signal handler.
call signal(2, sigint_handler)
print '("Press CTRL + C to send SIGINT.")'
do
print '("zzz ...")'
call sleep(1)
end do
end program main
Press CTRL
+ C
to send SIGINT
to the
Fortran program. The signal handler must be an external or module routine.
POSIX
The library fortran-unix provides an ISO C binding interface to signal(3) for Fortran 2008, without depending on non-standard compiler extensions:
! signal.f90
program main
use :: unix
implicit none
integer :: rc
type(c_funptr) :: ptr
! Register signal handler.
ptr = c_signal(SIGINT, c_funloc(sigint_handler))
print '("Press CTRL + C to send SIGINT.")'
do
print '("zzz ...")'
rc = c_usleep(10**6)
end do
contains
subroutine sigint_handler(signum) bind(c)
!! Signal handler for SIGINT.
integer(kind=c_int), intent(in), value :: signum
print '("Received SIGINT (", i0, "). Terminating ...")', signum
stop
end subroutine sigint_handler
end program main
Link the example with libfortran-unix.a
:
$ gfortran13 -o signal signal.f90 libfortran-unix.a
$ ./signal
We can run kill -s PID
, with s
being the signal
number and PID
the process id, to send an arbitrary signal. The
Unix command-line tool
ps(1) returns the ids of
all processes. The PID of process signal
is piped to
kill
, using signal 1
(SIGHUB
):
$ ps -A | grep signal | awk '{ print $1 }' | xargs kill -1 $1
The Fortran program has to register a SIGHUB
handler to catch
the signal.
Anonymous Pipes
Unix pipes allow processes to communicate with other processes without having
been designed explicitly for this task. The input and output streams of
processes can be chained together, providing a one-way flow of data. Anonymous
or unnamed pipes are a way of inter-process communication that let the output
of one program to become the input of another. The output can be redirected by
using the pipe symbol |
on the command-line.
The following example program reads given values in degrees Fahrenheit (°F)
from stdin
and writes the converted values in degrees Celsius (°C)
to stdout
:
! f2c.f90
program main
implicit none
integer :: rc
real :: f
do
read (*, *, iostat=rc) f ! Read degrees Fahrenheit (without unit) from stdin.
if (rc /= 0) exit ! Exit on input error.
print '(f0.2, " °C")', (f - 32) * 5 / 9 ! Write degrees Celsius (with unit) to stdout.
end do
end program main
After compilation, just pipe the value in degrees Fahrenheit to the Fortran program:
$ gfortran13 -o f2c f2c.f90
$ echo "60.45" | ./f2c
15.81 °C
The <
symbol allows to redirect the input to come from a
file, while >
writes the output to a file:
$ cat fahrenheit.txt
24.6
74.3
79.8
30.0
$ ./f2c < fahrenheit.txt
-4.11 °C
23.50 °C
26.56 °C
-1.11 °C
Furthermore, we can read the input values from file
fahrenheit.txt
and write the output to file
celsius.txt
:
$ ./f2c < fahrenheit.txt > celsius.txt
$ cat celsius.txt
-4.11 °C
23.50 °C
26.56 °C
-1.11 °C
The output of a Fortran program can be piped to other applications as well, for instance, to format the current time:
! time.f90
program main
implicit none
integer :: dt(8)
call date_and_time(values=dt)
print '(3(i0, ":"), i0)', dt(5), dt(6), dt(7), dt(8)
end program main
Pipe the output of program time
to
FIGlet:
$ gfortran13 -o time time.f90
$ ./time | figlet -f mini
_ _ _ _ _ __
/||_|_o|_ / \o_)|_ o/||_ /
| | o _)\_/o_) _)o ||_)/
Named Pipes
A further kind of pipes are named pipes, also knows as FIFO
(first in, first out). The pipe name is simply an arbitrary file name
on the file system. On Unix, named pipes can be created with
mkfifo
, and removed with rm
or
unlink
.
$ mkfifo ./pipe
$ echo "Hello, World!" > ./pipe
Another process may then read from the named pipe:
$ tail -f ./pipe
Hello, World!
In Fortran, the read
and write
statements can be
utilised to interchange data through named pipes.
! example.f90
program main
implicit none
character(len=*), parameter :: FIFO_PATH = '/tmp/fifo.tmp'
character(len=32) :: buf
integer :: fu, rc
logical :: file_exists
inquire (file=FIFO_PATH, exist=file_exists)
if (.not. file_exists) then
print '("Error: File ", a, " not found")', FIFO_PATH
stop
end if
do
! Open named pipe as formatted stream.
open (access='stream', &
action='read', &
file=FIFO_PATH, &
form='formatted', &
iostat=rc, &
newunit=fu, &
status='old')
if (rc /= 0) then
print '("Error: Opening ", a, " failed")', FIFO_PATH
exit
end if
do
read (fu, '(a)', iostat=rc) buf
if (is_iostat_end(rc)) then
! End of file.
exit
else if (rc /= 0) then
! Input error.
cycle
end if
print '(a)', trim(buf)
end do
close (fu)
end do
end program main
The Fortran 2003 function is_iostat_end(i)
returns whether the
given I/O status variable has the value of end of file. The function
is equivalent to comparing the variable with the IOSTAT_END
parameter of the intrinsic Fortran module iso_fortran_env
.
Compile the source code, create a new named pipe with
mkfifo
, and then run the example:
$ gfortran13 -o example example.f90 libfortran-unix.a
$ mkfifo /tmp/fifo.tmp
$ ./example
The Fortran program will output any data it receives trough the named pipe from another process:
$ echo "Hello" > /tmp/fifo.tmp
Be aware that the input data is read in record-based in this example
(form='formatted'
), i. e., spaces and commas separate
input values. Make sure to remove the named pipe when done:
$ unlink /tmp/fifo.tmp
References
< Command-Line Execution | [Index] | Key Capture > |