Chapter-3
FIFO stands for First In First Out. Named pipes exist as device
special files in the file system. [pipes] can be used for IPC only
between processes of common ancestors, whereas processes of different
ancestors can share data through named pipes. After the I/O operation
(IPC) by the processes, the FIFO remains in the file system for later
use unless explicitly removed. Like pipes, FIFO allows one way flow of
data. For both-way communication two FIFOs are used. Unlike pipes FIFO
does not have separate read-end and write-end.
Create a directory [ fifo ] in your home directory. The assignments
should be done in this directory.
$ mkdir $HOME/fifo
$ cd $HOME/fifo
$ ftp IP-address-of-laboratory-FTP-server
Bring the file [fifo.txt] in $HOME/fifo directory.
Creation of FIFO:
A FIFO can be created with [ mknod ] shell command.
$ /bin/mknod ./aaa -m 0666 p
Check creation of of a FIFO [aaa] in the current directory.
$ file ./aaa
A FIFO can also be created with shell command [ mkfifo ].
Create a FIFO [ ./xxx ] under [ /tmp/ ] directory with read and write
permissions for group and others
$ mkfifo /tmp/xxx -m 0606
The created FIFO is checked with two commands and their likely outputs
are shown below
$ ls -l /tmp/xxx
total 0
prw----rw- 1 _______ ______ 0 Sep 25 15:39 xxx
$ file /tmp/xxx
/tmp/xxx: fifo (named pipe)
Delete the FIFO [/tmp/xxx].
$ rm /tmp/xxx
FIFO can be created by system call [mknod(2)]. Read [ mknod(2) ]. This
system call is not in POSIX. In the following example library function
[ mkfifo(3) ] is used to create FIFO as this is POSIX compliant. Read
manual [ mkfifo(3) ].
A client-server model is described using a FIFO. In this example, one
way communication, from client to server, is described using a single
FIFO.
Copy the following code in a file named [f1.c].
-------------------------------------------------------------------
// file-name f1.c
// one way FIFO server - reads data from client
// and prints on standard output
#include
#include
#include
#include
#include
#include
#include
#define FIFOPATH "/tmp/your-name"
#define MODE ( S_IFIFO | S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH )
// it is a FIFO, user and others have read and write permissions
#define BUFFERSIZE 1024
extern int errno;
int main ( void )
{
int fdr,fdw,n;
ssize_t i;
char buffer[BUFFERSIZE];
// remove stale FIFO if any
n = unlink( FIFOPATH );
if( n == -1 ) { perror("unlink"); } // note absence of exit()
else printf("removed stale FIFO %s\n", FIFOPATH );
// file creation mask is cleared
umask(0000);
// a FIFO is tried to be created
n = mkfifo( FIFOPATH, MODE );
if( n == -1 ) { perror("mkfifo"); exit(1); }
printf("created FIFO <%s> \n", FIFOPATH );
// server reads data from client from this descriptor
fdr = open( FIFOPATH, O_RDONLY );
if( fdr == -1 ) { perror("open-rdonly"); exit(1); }
fdw = open( FIFOPATH, O_WRONLY );
if( fdw == -1 ) { perror("open-wronly"); exit(1); }
// this descriptor is not used by the server but it ensures
// that the FIFO has a writer during lifetime of the server
while ( 1 ) //endless loop.
{
printf("waiting for data from a client\n");
memset(buffer, '\0', BUFFERSIZE );
// server reads data from a client
i = read( fdr, buffer, BUFFERSIZE );
if( i == -1 ) { perror("read"); exit(1); }
write( STDOUT_FILENO, "server: from client-> ", 22 );
write( STDOUT_FILENO, buffer, i );
//printf("%s\n", buffer );
}
close(fdr); close(fdw);
return 0;
}
-------------------------------------------------------------------
A FIFO (/tmp/your-name) is created with [ mkfifo(3) ] library function
with read & write permissions for owner and others. The FIFO is
created in [/tmp/] directory, as this directory has execute permission
for all. Execute permission of a directory means search permission.
All users can access the FIFO in [/tmp/] directory.
The FIFO is opened twice by the server; first in read-only mode and
then in write-only mode.
The server-client arrangement is shown below
---------------------
| |
SERVER <---- fdr | /tmp/_________ | fdw <----- CLIENT
fdw | |
---------------------
When the server starts, it blocks in [open()] call in read-only mode,
as yet there is no other process (a client) which has the FIFO opened
for writing. This can be verified by using [strace] command to run the
server. When the first client starts, server's FIFO is opened in read-
only mode and then in write-only mode. The descriptor [fdw] is not
used further by the server. This open descriptor ensures that a
process has the FIFO opened for writing, even after a client exits.
When no process have the FIFO opened for writing, [read()] call on the
FIFO returns with zero, indicating end of file. This property of FIFO
and pipe, is described in Optional Assignment-2.
When any process have the FIFO opened for writing, [read()] call on
an empty FIFO blocks until there is data in the FIFO.
In the endless loop the server reads the FIFO, when data is written
by a client, prints it in standard output and blocks on [read()] call
for the next client.
Compile the program
$ gcc -Wall ./f1.c -o ./1way-fifo-server
Use the following command
$ strace ./1way-fifo-server
Did the program block in opening FIFO in read-only mode ( Y / n )
Stop the command with CTRL-C
Start the server
$ ./1way-fifo-server
Login as [ guest ] in another terminal. As the FIFO, created by the
server has write permission by others, [ guest ] is able to write into
the FIFO using shell command.
T-4 guest $ echo "I am guest" > /tmp/your-name
Was the server able to receive data from [guest] ? ( Y / n )
You may allow the server to run or terminate with CTRL-C.
Save the following as the FIFO client program.
------------------------------------------------------------------
// file-name f2.c
// One way FIFO client - writes a string to the FIFO server
#include
#include
#include
#include
#define FIFOPATH "/tmp/your-name"
// the FIFOPATH is known to client program. This need not be
// known to users, using the client program
int main( int argc, char *argv[] )
{
int fd;
ssize_t i;
// argument checking
if( argc != 2 )
{
printf("usage: %s \"any-string within quotes\" \n", argv[0] );
exit(1);
}
fd = open( FIFOPATH, O_WRONLY );
if( fd == -1 ) { perror("open"); exit(1); }
// client] writes the string in the server's FIFO
i = write( fd, argv[1], strlen( argv[1] ) );
if( i == -1 ) { perror("write"); exit(1); }
close(fd);
return 0;
}
------------------------------------------------------------------
Compile the program
$ gcc -Wall ./f2.c -o ./1way-fifo-client
Copy the executable in [/tmp/] directory so that all users may run the
client program.
Stale executable if any, has to be removed as [root] user
# rm /tmp/1way-fifo-client
$ cp ./1way-fifo-client /tmp/
Start the server from your account
T-3 you $ ./1way-fifo-server
From other terminals run the client as different users.
T-4 guest $ /tmp/1way-fifo-client "Hi server, this is guest"
T-4 guest $ /tmp/1way-fifo-client "My name is hizbizbiz"
T-5 sumit $ /tmp/1way-fifo-client "My name is Sumit"
Was the server able to receive data from unrelated processes ? (Y / n)
Terminate the server
T-3 you $ CTRL-C
Two FIFOs are used for two way communication between two processes,
if proper permissions of the FIFO exist.
The following is the FIFO server program.
--------------------------------------------------------------
// file-name f3.c Two way FIFO server
#include
#include
#include
#include
#include
#define FIFO1 "/tmp/XX"
#define FIFO2 "/tmp/YY"
#define BUFFERSIZE 4098
#define MODE 0606 // use symbolic constants !
int main ( void )
{
int XXr, XXw, YYw, YYr, n;
ssize_t i,j;
char buffer[BUFFERSIZE];
// remove stale FIFO if any
n = unlink(FIFO1);
if( n == -1 ) perror("unlink-1");
n = unlink(FIFO2);
if( n == -1 ) perror("unlink-2");
umask(0000);
// two FIFOs are created
n = mkfifo( FIFO1, MODE );
if( n == -1 ) { perror("mkfifo-1"); exit(1); }
n = mkfifo( FIFO2, MODE );
if( n == -1 ) { perror("mkfifo-2"); exit(1); }
XXr = open( FIFO1, O_RDONLY );
if( XXr == -1 ) { perror("open-XX-rdonly"); exit(1); }
XXw = open( FIFO1, O_WRONLY );
if( XXw == -1 ) { perror("open-XX-wronly"); exit(1); }
// XXw is not used by server
YYw = open( FIFO2, O_WRONLY );
if( YYw == -1 ) { perror("open-YY-wronly"); exit(1); }
YYr = open( FIFO2, O_RDONLY );
if( YYr == -1 ) { perror("open-YY-rdonly"); exit(1); }
// YYr is not used by server
while ( 1 ) //endless loop.
{
write(STDOUT_FILENO,"waiting for data from client\n", 29);
memset(buffer, '\0', BUFFERSIZE );
// server reads data from client
i = read( XXr, buffer, BUFFERSIZE );
if( i == -1 ) { perror("read"); exit(1); }
write(STDOUT_FILENO, "server: from client-> ", 22 );
j = write(STDOUT_FILENO, buffer, i );
if( j == -1 ) { perror("write-1"); }
// server writes data to client
i = write( YYw,"This is server \n", 16 );
if( i == -1 ) { perror("write-2"); }
}
close(XXr); close(XXw); close(YYw);
return 0;
}
--------------------------------------------------------------
In the above example, the server creates two FIFOs [ /tmp/XX ] and
[ /tmp/YY ] for both way communication.
The arrangement for communication between server and client, using the
two FIFOs is shown below:
------------- ------------
| | <--------- XXr (/tmp/XX) XXwc <---------- | |
| SERVER | | CLIENT |
| | ---------> YYw (/tmp/YY) YYrc ----------> | |
------------- -------------
XXw and YYr are not
used by server
The server opens both the FIFOs for reading and writing. So the server
has four descriptors of the two FIFOs. They are XXr, XXw, YYr and YYw.
The server reads data from a client from XXr and writes data to client
in YYw. Descriptors XXw and YYr are not used by the server.
XXw ensures that FIFO [ /tmp/XX ] always has a writer. The reason for
this was explained in connection with [f1.c].
YYr ensures that FIFO [/tmp/YY] always has a reader even when there is
no client process in existence. If the server writes to the FIFO
[/tmp/YY] when the FIFO has no reader, SIGPIPE signal is delivered to
the server. The default action of this signal is to terminate the
server process, if the signal is not handled. This is a property of
FIFO and pipe. This property is described in Optional Assignment-3.
The following is the client program.
--------------------------------------------------------------
// file-name f4.c Two way FIFO client
#include
#include
#include
#include
#include
#define FIFO1 "/tmp/XX"
#define FIFO2 "/tmp/YY"
#define BUFFERSIZE 4098
int main( void )
{
int XXwc,YYrc;
int n;
ssize_t i;
char buffer[BUFFERSIZE];
char *username;
//If opened in read only mode first, there would be deadlock
XXwc = open( FIFO1, O_WRONLY );
if( XXwc == -1 ) { perror("open-wronly"); exit(1); }
YYrc = open( FIFO2, O_RDONLY );
if( YYrc == -1 ) { perror("open-rdonly"); exit(1); }
username = getlogin();
// buffer is prepared
sprintf( buffer,"I am client <%s>, my uid = %u\n",
username, getuid() );
// buffer is sent to server
n = write( XXwc, buffer, strlen(buffer) );
if( n == -1 ) { perror("write"); exit(1); }
memset( buffer, '\0', BUFFERSIZE );
// reading data from server
i = read(YYrc, buffer, BUFFERSIZE );
write(STDOUT_FILENO, "from server-->", 14 );
write(STDOUT_FILENO, buffer, i );
close(XXwc); close( YYrc );
return 0;
}
--------------------------------------------------------------
Compile
$ gcc -Wall ./f4.c -o ./2way-fifo-client
Copy the executable under [/tmp/] directory
$ cp ./2way-fifo-client /tmp/
Start the server
T-3 you/fifo $ ./2way-fifo-server
Test the server from other terminals and observe the results in the
server's terminal.
T-4 you $ /tmp/2way-fifo-client
T-5 guest $ /tmp/2way-fifo-client
T-6 sumit $ /tmp/2way-fifo-client
Stop server with CTRL-C
T-3 you/fifo $ CTRL-C
Optional Assignment-1.
The [ make ] utility automatically determines
which pieces of a large program need to be recompiled, and issues
commands to recompile them.
The above line is reproduced from "GNU Make Manual". You may read the
manual.
$ info make
[make] utility is described with a VERY simple example. The above 2way
FIFO client-server programs are modified to for the example.
The headers needed for server and client programs are kept in a
file named [headers].
----------------------
// file-name headers
#include
#include
#include
#include
#include
----------------------
The definitions needed for server and client programs are kept in a
file named [definitions].
------------------------------------------------------------------
// file-name definitions
#define FIFO1 "/tmp/XX"
#define FIFO2 "/tmp/YY"
#define BUFFERSIZE 4098
#define MODE ( S_IFIFO | S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH )
// it is a FIFO, user and others have read and write permissions
------------------------------------------------------------------
Save the following lines in a file [f5.c]
--------------------------------------------------------------
// file-name f5.c FIFO server-duplex
#include "headers"
#include "definitions"
int main ( void )
{
int XXr, XXw, YYw, YYr, n;
ssize_t i;
char buffer[BUFFERSIZE];
// remove stale FIFO if any
n = unlink(FIFO1);
if( n == -1 ) perror("unlink-1");
n = unlink(FIFO2);
if( n == -1 ) perror("unlink-2");
umask(0000);
// two FIFOs are created
n = mkfifo( FIFO1, MODE );
if( n == -1 ) { perror("mkfifo-1"); exit(1); }
n = mkfifo( FIFO2, MODE );
if( n == -1 ) { perror("mkfifo-2"); exit(1); }
XXr = open( FIFO1, O_RDONLY );
if( XXr == -1 ) { perror("open-XX-rdonly"); exit(1); }
XXw = open( FIFO1, O_WRONLY );
if( XXw == -1 ) { perror("open-XX-wronly"); exit(1); }
// XXw is not used by server
YYw = open( FIFO2, O_WRONLY );
if( YYw == -1 ) { perror("open-YY-wronly"); exit(1); }
YYr = open( FIFO2, O_RDONLY );
if( YYr == -1 ) { perror("open-YY-rdonly"); exit(1); }
while ( 1 ) //endless loop.
{
write(STDOUT_FILENO,"waiting for data from client\n", 29);
memset(buffer, '\0', BUFFERSIZE );
// server reads data from client
i = read( XXr, buffer, BUFFERSIZE );
if( i == -1 ) { perror("read"); exit(1); }
write(STDOUT_FILENO, "server: from client-> ", 22 );
printf("%s\n", buffer );
// server writes data to client
n = write( YYw,"This is server \n", 16 );
}
close(XXr); close(XXw); close(YYw);
return 0;
}
--------------------------------------------------------------
Save the following lines in a file [f6.c]
--------------------------------------------------------------
// file-name f6.c FIFO client-duplex
#include "headers"
#include "definitions"
int main( void )
{
int XXw,YYr;
int n;
ssize_t i;
char buffer[BUFFERSIZE];
char *username;
//If opened in read only mode first, there would be deadlock
XXw = open( FIFO1, O_WRONLY );
if( XXw == -1 ) { perror("open-wronly"); exit(1); }
YYr = open( FIFO2, O_RDONLY );
if( YYr == -1 ) { perror("open-rdonly"); exit(1); }
username = getlogin();
// buffer is prepared
sprintf( buffer,"I am client <%s>, my uid = %u\n",
username, getuid() );
// buffer is sent to server
n = write( XXw, buffer, strlen(buffer) );
if( n == -1 ) { perror("write"); exit(1); }
memset( buffer, '\0', BUFFERSIZE );
// reading data from server
i = read(YYr, buffer, BUFFERSIZE );
write(STDOUT_FILENO, "from server-->", 14 );
write(STDOUT_FILENO, buffer, i );
close(XXw); close( YYr );
return 0;
}
--------------------------------------------------------------
The [ make ] utility needs a file [ Makefile ] to specify the targets,
prerequisites and rules to create the target. Save the following lines
in a file [Makefile]
--------------------------------------------------------------------
# file-name Makefile
# Using [makefile] as file-name should also work
# [fs] is the target. It depends on one object file [f5.o]
fs : ./f5.o
gcc -Wall ./f5.o -o ./fs
# The rule to make target [fs] is given by the above command
# NOTE: rules must start with a TAB
# target [f5.o] depends on files [f5.c], [headers] and [definitions]
f5.o : ./f5.c ./headers ./definitions
gcc -Wall -c ./f5.c
# Command [ gcc -Wall -c s2.c -o s.o ] should also work; but
# output file-name must not be used, when working with more than
# one input file
# target [fc] depends on the object file [f6.o].
fc : ./f6.o
gcc -Wall ./f6.o -o ./fc
# target [f6.o] depends on files [f6.c], [headers] and [definitions]
f6.o : ./f6.c ./headers ./definitions
gcc -Wall -c ./f6.c
install : ./fs ./fc
/bin/cp ./fs /tmp/fs
/bin/cp ./fc /tmp/fc
clean :
/bin/rm ./f5.o ./f6.o ./fs ./fc
all-clean :
/bin/rm ./f5.o ./f6.o ./fs ./fc /tmp/fs /tmp/fc
--------------------------------------------------------------------
The above [ Makefile ] has seven targets.
In target [install], installation is done in [/tmp/] directory, which
is not done in actual practice. This is done, as it is convenient for
laboratory experiment.
In the following [make] commands, typical outputs are shown.
$ make fs
gcc -Wall -c ./f5.c
gcc -Wall ./f5.o -o ./fs
As the first target depended on [f5.o], the second target [ f5.o ] was
created first and then first target was created.
Note: To examine the intermediate files created in a compilation, use
the following command
$ gcc --save-temp ./f1.c
where [f1.c] is a C source code
Try to make the first target again
$ make fs
make: `fs' is up to date.
Make the other targets:
$ make fc
gcc -Wall -c ./f6.c
gcc -Wall ./f6.o -o ./fc
$ make install
/bin/cp ./fs /tmp/fs
/bin/cp ./fc /tmp/fc
After the installations, the temporary files in the current directory
may be removed with target [clean].
$ make clean
/bin/rm ./f5.o ./f6.o ./fs ./fc
To remove the temporary files in the working directory and the
installed files may be removed with target [all-clean]
$ make all-clean
/bin/rm ./f5.o ./f6.o fs fc /tmp/fs /tmp/fc
/bin/rm: cannot remove `./f5.o': No such file or directory
/bin/rm: cannot remove `./f6.o': No such file or directory
/bin/rm: cannot remove `fs': No such file or directory
/bin/rm: cannot remove `fc': No such file or directory
make: *** [all-clean] Error 1
Try the following command
$ make install
gcc -Wall -c ./f5.c
gcc -Wall ./f5.o -o ./fs
gcc -Wall -c ./f6.c
gcc -Wall ./f6.o -o ./fc
/bin/cp ./fs /tmp/fs
/bin/cp ./fc /tmp/fc
$ make clean
/bin/rm ./f5.o ./f6.o ./fs ./fc
Start the FIFO server from your account
T-3 $ /tmp/fs
Run client from accounts of other users
T-4 guest $ /tmp/fc
T-5 sumit $ /tmp/fc
Was client-server communication possible ? ( Y / n )
Optional Assignment-2.
When a process reads from a FIFO, which has no
writer, [read()] call returns with zero, indicating end of file. This
is not an error. This is a property of FIFO ( and pipe ).
------------------------------------------------------------------
// file-name f7.c
// reading a FIFO with and without writer
//#include
#include
#include
#include
#include
#include
#define FIFO "./xx"
#define MODE 0606 // symbolic constants should be used !
extern int errno;
int main( void )
{
int fd;
int n;
ssize_t i;
pid_t m;
// removing stale FIFO
n = unlink( FIFO );
if( n == -1 ) perror("unlink");
// creating a FIFO
n = mkfifo( FIFO, MODE );
if( n == -1 ) perror("mkfifo");
m = fork();
if( m == -1 ) { perror("fork"); exit(1); }
if( m == 0 ) // child process block
{
sleep(10);
fd = open( FIFO, O_WRONLY );
if( fd == -1 ) { perror("open-child"); exit(1); }
sleep(12);
printf("child: FIFO writer is exiting !\n");
exit(0);
} // end of child process block
// rest is parent process
write( STDOUT_FILENO,"parent: opening FIFO for reading: ", 34 );
system("/bin/date");
fd = open( FIFO, O_RDONLY );
if( fd == -1 ) { perror("open-parent"); exit(1); }
write( STDOUT_FILENO,"parent: opened FIFO for reading: ", 34 );
system("/bin/date");
// errno is cleared
errno = 0;
for( n = 0; n<=3; n++ )
{
write( STDOUT_FILENO, "parent: reading FIFO : ", 24 );
system("/bin/date");
printf("parent: before read() call: errno = %u\n", errno );
i = read( fd, "Hello\n", 6);
if( i == -1 ) printf( "read() call failed\n" );
printf("parent: after read() call: errno = %u\n", errno );
write( STDOUT_FILENO, "parent: after reading : ", 24 );
system("/bin/date");
printf("parent: %u bytes read from FIFO\n\n", i );
}
close( fd );
return 0;
}
------------------------------------------------------------------
The parent blocks when it tries to open the FIFO in read-only mode.
This open() call returns, when the child opens the FIFO for writing
after 10 seconds. So the parent blocks in open() call for about 10
seconds. The parent then blocks in read() call, till data is available
in the FIFO or at least one writer of the FIFO exists. The child exits
after 12 seconds of opening the FIFO, for writing. The child does not
write any data in the FIFO. So the parent blocks in read() call for
about 12 seconds. Without a writer of the FIFO, the read() call of the
parent returns immediately with zero and this not an error.
Compile and run the program
$ gcc -Wall ./f7.c -o ./seven
$ ./seven
Optional Assignment-3.
When data is written in a FIFO without a reader,
error number is set to EPIPE and SIGPIPE signal is delivered to the
calling process. This is another property of FIFO ( and pipe ).
The default action of SIGPIPE signal is to terminate the process
unless this signal is handled.
------------------------------------------------------------------
// file-name f8.c
// writing a FIFO with and without reader
//#include
#include
#include
#include
#include
#include
#include
#include
#define FIFO "./xx"
#define MODE 0606 // symbolic constants should be used !
extern int errno;
// function prototype
void sighandler( int signum );
int main( void )
{
int fd;
int n;
ssize_t i;
pid_t m;
struct sigaction action;
char buffer[100];
// removing stale FIFO
n = unlink( FIFO );
if( n == -1 ) perror("unlink");
// creating a FIFO
n = mkfifo( FIFO, MODE );
if( n == -1 ) perror("mkfifo");
printf("EPIPE = %u\n", EPIPE );
m = fork();
if( m == -1 ) { perror("fork"); exit(1); }
if( m == 0 ) // child process block
{
sleep(10);
fd = open( FIFO, O_RDONLY );
if( fd == -1 ) { perror("open-child"); exit(1); }
memset(buffer,' ',100);
i = read(fd,buffer,100);
if( i == -1 ) { perror("read-child"); exit(1); }
write(STDOUT_FILENO,"child : received from FIFO-->",29);
write(STDOUT_FILENO, buffer,i );
sleep(12);
printf("child: FIFO reader is exiting !\n");
exit(0);
} // end of child process block
// rest is parent process
// a signal handler for SIGPIPE is installed
action.sa_handler = sighandler;
n = sigaction( SIGPIPE, &action, NULL );
if( n == -1 ) { perror("sigaction"); exit(1); }
printf("parent: my PID = %u\n", getpid() );
write( STDOUT_FILENO,"parent: opening FIFO for writing: ", 34 );
system("/bin/date");
fd = open( FIFO, O_WRONLY );
if( fd == -1 ) { perror("open-parent"); exit(1); }
write( STDOUT_FILENO,"parent: opened FIFO for writing: ", 34 );
system("/bin/date");
// errno is cleared
errno = 0;
while(1)
{
write( STDOUT_FILENO, "parent: writing FIFO : ", 24 );
system("/bin/date");
printf("parent: before write() call: errno = %u\n", errno );
i = write( fd, "This is parent\n", 15);
if( i == -1 )
{
printf("parent: write() call failed; errno = %u\n", errno);
exit(1);
}
printf("parent: after write() call: errno = %u\n", errno );
write( STDOUT_FILENO, "parent: after reading : ", 24 );
system("/bin/date");
printf("parent: %u bytes written in FIFO\n\n", i );
sleep(6);
}
close( fd );
return 0;
}
void sighandler( int signum )
{
printf("process-%u: received signal %u \n", getpid(),signum );
return;
}
------------------------------------------------------------------
The parent installs a signal handler for SIGPIPE. The purpose of this
signal handler is to confirm that SIGPIPE ( signal number 13 ) is
delivered to the process writing to a FIFO, which has no reader. The
parent blocks when it tries to open the FIFO in write-only mode. This
open() call returns when the child opens the FIFO for reading after 10
seconds. So the parent blocks in open() call for about 10 seconds. The
child blocks in read() call, till data is available in the FIFO. The
parent writes data in FIFO in an endless loop. It sleeps for 6 seconds
after a passes in the endless loop. The child reads data from FIFO and
prints it on standard output and exits after 12 seconds. When child
exits, no process has the FIFO opened for reading. When parent tries
to write in the FIFO, after the exit of the child, write() call fails
with [ errno ] set to EPIPE and SIGPIPE signal is delivered to the
parent. The program is terminated with explicit exit(1) call, when the
write() call returns minus one.
Compile and run the program
$ gcc -Wall ./f8.c -o ./eight
$./eight
