CENG460
Operating Systems
Spring 2021 -2022
Interprocess communications
(IPC) (2)
[Operating Systems Concepts, 8th Edition, by Silberschatz, Galvin,
and Gagne, 2009]
1
B.2 Pipes
O Pipes are one of the most ancient, yet simple and useful, IPC
mechanisms provided by UNIX
O They’ve also been available in MS-DOS from the beginning
O In UNIX, a pipe is mono-directional, one end for read and
one end for write.
O Two pipes must be used for bi-directional communication
O Pipes can be used to create communication
channel between related processes (What is
related processes !! (next slides)
2
B.2 Pipes
O Example call to pipe() If successful the call will
return two integer file
int fd[2]; descriptors that reference
if (pipe(fd)) two data streams: fd[0] and
{ fd[1]
perror(“pipe()”);
exit(1); fd[0] is the read-end
} fd[1] is the write-end
O A pipe is used to establish communications between a pair of processes.
The system call to create a pipe is pipe()
O . The array pipe fd is used to return two file descriptors referring to the
ends of the pipe.
O Pipe fd[0] refers to the read end of the pipe.
O Pipe fd[1] refers to the write end of the pipe
O Data written to the write end of the pipe is buffered by the kernel until it
is read from the read end of the pipe.
O On success, 0 is returned. On error, -1 is returned, and errno is set
,3
appropriately.
B.2 Pipes Using pipes
O Creating a pipe
O int pfd[2];
O pipe(pfd);
Pipes
B.2 Fork and pipe
O A process is duplicated using fork() and two identical
processes exist.
O A pipe created before the fork() call becomes shared
between the two processes and can be used for
inter-process communication
After
fork
Before fork
Pipes
B.2 Fork and pipe
O As result there are two read ends and two write
ends.
O Either process can write into the pipe, and
either can read from it.
O One of the processes must close its read end,
and the other must close its write end. The
result is a simple pipeline again.
Pipes
B.2 Fork and pipe
O Suppose the parent wants to communicate
with the client and writes down a pipeline.
O The parent closes its read end, and writes into
the other end.
O The child closes its write end and reads from
the other end.
O In this manner the information is clearly sent
from the parent to the child.
Steps to communicate via pipes
with Fork
O Create the pipes needed
O Generate the child process(es)
O Close/duplicate file descriptors to properly
associate the ends of the pipe.
O Close the unneeded ends of the pipe
Perform the communication activities
O Close the remaining open file descriptors
O If appropriate, wait for the child process to
terminate
Pipes
B.2 Pipes Example
int fd[2];
pipe(fd);
int i = fork();
if (i==0)
{
char buffer[64] = “ Hello Father ”;
close(fd[0]);
printf(“ I am the child my pid is %d \n”, getpid());
write(fd[1], buffer, 64);
close(fd[1]);
}
else
{
char buffer[64] ;
close(fd[1]);
printf(“ I am the father my pid is %d \n”, getpid());
read(fd[0], buffer, 64);
prinf(“My son wrote %s \n “,buffer);
close(fd[0]); 9
}
exit(0);
B.2 Pipes and exec()
• A pipeline works because both processes know the file
descriptor (fd) of each end of the pipe.
⇒ As the processes have a stdin (fd=0),
a stdout (fd=1) and a stderr (fd=2) open.
• Sometimes, we need to communicate with a child process
that execs some executables
• In this case the process replace itself with new process
that use stdin and stdout respectively by calls to execlp()
⇒ the new processes have file descriptors 0, 1, 2, 3 & 4
open.
10
B.2 Pipes and exec()
Sometimes, we need to communicate with a child process
that execs some executables
– e.g., fork of a child that runs “/bin/ls” and retrieve its output
In this case, one can’t put code in that executable to tell it to
write its output to the write-end of a pipe
• So: how can we establish communication?
• The problem can be solved by overriding some
standard file descriptors with the end of a pipe
• A standard way to do I/O for a process is to use stdin and
stdout (system programs do this)
• These file descriptors are set as follows
– stdin: 0
– stdout: 1
– stderr: 2 11
B.2 Pipes and exec()
There is a system call to duplicate a fd (file descriptor)
O Another number through which to access the “file”
O dup (int oldfd) generates a new number for
oldfd
O e.g., dup(1) returns a new fd number for the stdout fd.
The process can now write to the new fd number to
generate output.
O dup2 (int oldfd, int newfd) specifies
the number of the new fd
O e.g. dup2(40, 0): the process’ stdin now comes from
fd 40
O e.g. dup2(30, 1); the process’ stdout now goes to fd 30
12
B.2 Pipes and exec()
O We can now do communication either way:
O create a pipe: fd[0] (read-end) and fd[1] (write-end)
O Fork a child
O Either parent or child process can do one of the following:
O dup2(fd[1], 1); ; the process’ stdout now goes to fd[1] (write to
the pipe)
O dup2(fd[0], 0); ; the process’ stdin now comes from fd[0] (read
from pipe)
13
Example
Child process
#include <sys/types.h>
#include <unistd.h> if (!pid)
{ Duplicate stdout
#include <stdio.h>
#include <stdlib.h> dup2(fd[1], 1);
#include <string.h> close(fd[0]);
int main() execl("/bin/date", "date",
Create pipes
{ NULL);
exit(0); Run a command
pid_t pid; Create a buffer
int fd[2];
}
and set to 0 End of child
if (pipe(fd) == -1)
close(fd[1]);
char buffer[64]; process
{
bzero(buffer,64);
perror("pipe()");
if (read(fd[0], buffer, 64) == -1)
exit(1);
{
} Create child perror("read()");
pid = fork(); Read what the
exit(1);
if (pid < 0) child wrote
}
{ fprintf(stdout,"Output from child:
perror("fork()"); %s",buffer);
exit(1); 14
close(fd[0]);
} exit(0);
}