Optional experiments

advertisement
CMPE-343: SYSTEMS PROGRAMMING
Experiments on Laboratory Works
Using unnamed pipes for interprocess communication in UNIX
INTRODUCTION
A pipe is a unidirectional communication channel between two processes in UNIX. Any pipe has two ends:
upstream (for writing) and downstream (for reading). Each of these two ends of a pipe is characterized by the
corresponding integer descriptor (like a file descriptor). The both descriptors are results of creation of the pipe by the
pipe() system call. Using a pipe, one process can put a flow of bytes in the upstream end of the pipe, and some other
process can get this flow of bytes from the downstream end of the pipe. Read more on pipes in the textbooks of Curry
D. (pp. 353 – 363) and Haviland, K. (pp. ).
Unnamed pipes are used commonly to exchange messages between related processes (for example, between two
child processes which have the same parent). It is possible to communicate via a pipe between the parent and its child,
between a grandparent and a grandchild, etc. From the logical point of view, a pipe is a special file, and if a pipe was
created by some process, then this pipe can be used by any pair of processes, including the creator process and all its
descendants.
An unnamed pipe is created using the system call pipe():
ret = pipe (fd);
where ret is a returned code of integer type , and fd is a two-element array int fd[2], which gets values as a result of
this system call. The system call pipe() returns 0 on success, and -1 on some failure.
If the system call is successful, then the first entry fd[0] is a descriptor of the downstream end (for reading) and
fd[1] is a descriptor of the upstream end (for writing) of the created pipe. In this laboratory work, it is necessary to
create an unnamed pipe at the beginning of a parent process, then create two child processes. One of these child
processes should execute some shell command (for example, “who”) and write the results of this command into the
upstream end of the pipe. Another child process should execute some other shell command (for example, “sort”)
taking its input from the downstream end of the same pipe. Therefore, the complete effect of executing the two child
processes can be expressed as the sequence of two shell commands
who | sort,
where “|” is a symbol of the pipe operation in the shell. See the corresponding picture below.
Output
of who
Input
for sort
The pipe
who
sort
Upstream end
of the pipe
(for writing)
Downstream end
of the pipe
(for reading)
To the
screen
Make the following experiments:
1. Create a separate subdirectory for this lab work. Input and create executable file, corresponding to the following C
program:
/* A program to experiment with an unnamed pipe */
#include <stdio.h>
#include <unistd.h>
main ()
{
int fd[2]; /* Array of two descriptors for an unnamed pipe */
int pid; /* Variable for a process identifier */
/* A pipe should be created before any fork() */
/* Do you understand why? */
if (pipe(fd) < 0)
{perror ("PIPE CREATION ERROR");
exit (1);
}
pid = fork (); /* Parent: creating the first child process */
if (pid == 0) /* The first child process starts here */
{
dup2 (fd[0],0); /* Standard input will be taken from the downstream of the pipe */
close (fd[1]); /* Upstream end of the pipe is closed for this process (not used) */
execlp ("sort", "sort", 0); /* Running “sort” command taking input from the pipe */
}
else /* Here the parent process continues */
pid = fork (); /* Parent: creating the second child process */
if (pid == 0) /* The second child process starts here */
{dup2 (fd[1],1); /* Standard output will be put to the upstream end of the pipe */
close (fd[0]); /* Downstream end is closed for this process (not used) */
execlp ("who", "who", 0); /* Running the command “who” which outputs to the pipe */
}
else /* Parent process closes for itself both ends of the pipe and waits for children to terminate */
{
close (fd[0]);
close (fd[1]);
wait (0);
wait (0);
}
}
Note: The library function dup2(fd1, fd2) causes the file descriptor fd1 to be the duplicate of the file descriptor fd2.
If fd2 refers to an already-open file, then that file is closed first. To understand this function in more detail, apply to OS
manual (use shell command man dup2), or see the textbook of Curry, D., pp. 69 and 312).
2. Run the created executable file and compare the results of running this file with the results of executing the following
shell commands:
who
and then
who | sort
3. Replace, in the source C program, the statement
execlp (“who”, “who”, 0);
by a few write() system calls, which will write into the pipe a number of words (like “this\n”, “is\n”, “a\”, “message\n”,
‘from\n”, “sending\n”, “process\”). To write these words into the pipe, the system calls write() should be used as for an
regular file, with the file descriptor fd[1]. After writing all the words into the pipe, close fd[1] and exit. Create the
executable file and trace its execution.
Note: For this modification, dup2(fd[1],1) is NOT necessary and should be excluded.
4. Replace, in the program, created in the Experiment 3, the statement
execlp(“sort”, “sort”, 0);
by a fragment, which reads the words from the pipe and prints them on its standard output device (i.e. on the screen).
To read data from the pipe, the system call read() should be used as for an regular file (possibly in a loop), with the file
descriptor fd[0]. After reading and printing all the words from the pipe, close fd[0] and exit. Create an executable file
and trace its execution.
Note: For this modification, dup2(fd[0], 0) is NOT necessary and should be excluded.
5. Trace all the programs carefully and try to explain their work.
Optional experiments
(see [2], p.157)
6. Generally, more than one process can read from the pipe, and more than one process can write into the pipe. Try to
develop a program that creates three child processes. Two of these child processes will write each one message into the
pipe, and one child process will read these messages from the pipe and print them.
7. To establish two-way communications between processes, two pipes operating in different directions can be created.
Develop a possible conversation between two processes and implement it using two pipes.
The sources of information:
1. Curry, D., UNIX Systems Programming for SVR4, O’Reilly & Assoc., 1995, pp. 353 - 363.
2. Haviland, K., et al, UNIX System Programming, 2nd ed., 1999, pp. 151 - 171.
3. Lecture notes.
QUESTIONS
1. What is the purpose of this lab work?
2. What is the purpose of pipes?
3. What types of pipes are used in UNIX?
4. Explain the parameter of system call pipe()?
5. Has the parameter of system call pipe() a meaningful value before execution of the system call? What is the value (or
values) of this parameter after successful execution of the system call?
6. What are upstream and downstream ends of a pipe?
7. How can you connect, in a program, the ends of the created pipe to the standard input and output?
8. Is it possible to use unnamed pipes for communication between unrelated processes?
9. Does a pipe continue to exist after the termination of processes which used it?
10. How can pipes be used to implement two-way communication between processes?
11. Assume that two child processes of the same parent process want to communicate using an unnamed pipe. Explain
the main steps in the corresponding program to implement this communication. Is it possible that one child process
creates the pipe, and then both child processes communicate using the created pipe? Why yes/no?
--------------------------------------------------------------------------------------------------© This lab work was prepared by A.Kostin
Download