Lab1 Pipes
Lab1 Pipes
Lab1 Pipes
Theory:
We can think of pipe as piece of plumbing that allows data to flow from one process to another.
• A pipe is byte stream. No boundaries maintained between two writes of sender process.
o One end is used for reading and the other for writing.
• Normally pipe is used for communication between two processes. So fork() follows pipe() system
call.
If there is need for both parent and child to read and write data, then
o Using single pipe leads to race conditions. Can be avoided using some synchronizations
mechanism.
This may lead to deadlock situation. Both parent and child blocked in reading but there is no data
in the pipes.
• Not only parent and child but any two processes having a common ancestor can use pipe provided
that common ancestor has created the pipe.
Process reading from pipe closes write end of the pipe. Why?
o While reading from pipe an EOF is encountered only if there are no more write ends open.
o If a process tries to write to a pipe for which there is no read end open, then kernel generates
SIGPIPE signal. This signal has default action, terminate process.
Although write() also returns an error EPIPE, but the generation of signal is meant for killing the
sender process because the processes using pipe do not know that they are using pipe.
o If the process doesn’t close read end, process will still be able to write to the pipe, once full it will
indefinitely blocked waiting for someone to read the pipe.
Fork a child
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#define MSGSIZE 16
main ()
{
int i;
char *msg = "How are you?";
char inbuff[MSGSIZE];
int p[2];
pid_t ret;
pipe (p);
ret = fork ();
if (ret > 0)
{
i = 0;
while (i < 10)
{
write (p[1], msg, MSGSIZE);
sleep (2);
read (p[0], inbuff, MSGSIZE);
printf ("Parent: %s\n", inbuff);
i++;
}
exit(1);
}
else
{
i = 0;
while (i < 10)
{
sleep (1);
read (p[0], inbuff, MSGSIZE);
printf ("Child: %s\n", inbuff);
write (p[1], "i am fine", strlen ("i am fine"));
i++;
}
}
exit (0);
}
Q?
a. Check the output of the above program. Observe that using one pipe we
can communicate both ways but in only one direction at a time.
b. Remove one of the sleep statements and see the output.
c. Remove both the sleep statements and see the output.
d. Try to make the above program synchronized i.e. only when the child
completes its writing, parent writes data; child doesn’t write until parent
completes writing.
2. Application of Pipe
The program needs to read only the upper case letters even though the user
may enter the lowercase letters. For that the following design is considered.
The filter program reads the characters from terminal and converts all of them
into upper case and writes to output. Filter program is invoked by the program
using popen().
/*filter.c*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
void
err_sys (char *str)
{
perror (str);
exit (-1);
}
int
main (void)
{
int c;
/*parent.c*/
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
void err_sys(char* str)
{
perror(str);
exit(-1);
}
#define MAXLINE 80
int
main (void)
{
char line[MAXLINE];
FILE *fpin;
Q?
1. Observe the usage of popen. Using pipes we can filter the data that is
coming from terminal into the program.
2. modify the program so that parent can accept only numbers. User may
enter anything. But the program should read only the numbers.
3. Pipelines
Consider the following program for executing ls –l|wc –l.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
main ()
{
int i;
int p[2];
pid_t ret;
pipe (p);
ret = fork ();
if (ret == 0)
{
close (1);
dup (p[1]);
close (p[0]);
execlp ("ls", "ls", "-l", (char *) 0);
}
if (ret > 0)
{
close (0);
dup (p[0]);
close (p[1]);
wait (NULL);
execlp ("wc", "wc", "-l", (char *) 0);
}
}
Q?
a. Is wait required here? Why or why not?
b. Is it required to close unused ends of pipe? Suppose if you don’t close p[1] in
parent will it have any effect? Find out.
c. dup2() system call can be used instead of dup(). Find out more on dup2() using
man dup2 command. Dup2 is considered safer version of dup(). Can you see
why is it so?
d. Modify the above program to execute ls –l| grep ^d |wc –l. The output of
this should be the number of directories in the current directory.
4. Coprocesses:
The diagram shows the use of a child process loading an executable. Here the
noticeable point is parent is supplying the input and the same parent is reading
the output from the child. This requires two pipes to make the duplex
communication possible. In such cases the child process is called as coprocess.
The co-process can do variety of tasks like spell checking, validation, sorting,
etc …
In the following example, we use a coprocess to validate emails. The emailValidate.c
program is follows.
#include <stdio.h>
#include <string.h>
#define MAXSIZE 100
main()
{
char buf[MAXSIZE];int n;
while((n=read(0, buf, MAXSIZE))>0)
{
buf[n]='\0';
if(strstr(buf,"@")>0)
if(strstr(buf,".")>0)
write(1,"1\n",2);
else write(1,"-2\n",3);
else write(1,"-3\n",3);
}
}
It takes the string from stdin and writes the result to stdout. Now consider the
following parent process.
main ()
{
int p1[2], p2[2];
int ret;
FILE *fpi, *fpo;
char line[MAXSIZE], result[MAXSIZE];
pipe (p1);
pipe (p2);
ret = fork ();
if (ret == 0)
{
close (p1[1]);
close (p2[0]);
dup2 (p1[0], 0);
dup2 (p2[1], 1);
execl ("./validateEmail", "validateEmail", (char *) 0);
}
else
{
close (p1[0]);
close (p2[1]);
fpi = fopen ("emails.txt", "r");
fpo = fopen ("emails_validation.txt", "w");
while (fgets (line, MAXSIZE, fpi) != NULL)
{
write (p1[1], line, strlen (line));
read (p2[0], result, MAXSIZE);
fprintf (fpo, "%s,%d\n", line, atoi (result));
}}}
The parent process takes help from the co-process validateEmail to check the
emails.
Q?
1. Execute the above program. First compile validateEmailCoprocess.c to
validateEmail executable. gcc validateEmailCoprocess.c –o
validateEmail Then compile coprocess_parent.c and run it . The
emails.txt sample input is available here. Double click it to open.
. Now parent is running, if we kill validateEmail child process by
Emails.txt
sending SIGKILL signal, find out what would happen? Modify the parent to
recognize such unexpected situations and respond positively.
What did you notice? Why the program doesn’t work? Find out. [hint: Effect
of using low-level system calls like read(), write() with standard i/o functions
like printf(), scanf() etc]