Interprocess Communication (IPC)- It is often useful and necessary to allow different processes to communicate with each other
- There are two basic types of IPC
- Explicit mechanisms: System call interface such as pipes, sockets, message passing, and remote procedure call (RPC)
- Shared memory: Using virtual memory, allow processes to share a portion of memory. Processes communicate by accessing shared variables.
- The UNIX pipe is a form of local IPC.
- Using pipes, processes that are on the same machine can communicate with each other.
- UNIX Sockets allow for IPC among processes that do not necessarily reside on the same machine.
UNIX Pipes- A UNIX pipe is an IPC mechanism
- A pipe allows for the exchange of data between processes
- A pipe is used to send a stream of character data
- Basic idea
- Create a pipe using the pipe() system call
- At the user level, a pipe is just a pair of file descriptors
- One for reading (0)
- One for writing (1)
- Use read() and write() system calls to receive and send data
- Order: FIFO (First in, first out)
- Implementation: a circular buffer that resides in the kernel
Uses for Pipes- General mechanism to allow related processes running on the same machine to communicate
- Note: we need something else to allow inter-machine communication (i.e., sockets)
- Use by the shell to "connect" programs
- ls | wc
- Gives the number of files in current directory
- who | wc
- Gives the number of users logged in
- ls | sort -r | head
- gives the last ten files in a directory in alphabetical order
- Note that shell pipes are unidirectional
- System pipes can be bidirectional
The Pipe System Call- int pipe(int fildes[2])
- Check return value for possible errors
- int fildes[2] is just an array of two ints
- fildes[0] is for reading (the "read end")
- fildes[1] is for writing (the "write end")
- Remember the read end is 0 (like stdin) and the write end is 1 (like stdout)
- Usage
int fildes[2]; pipe(fildes);
- Draw picture of a pipe with "hello world"

Connecting Processes- Consider a parent that wants to send data to a child
- In Parent:
- create a pipe
- fork() a child
- close(filedes[0]) (don't leave open ends)
- write to child with
- close(filedes[1]) on completion (important)
- In Child:
- close(filedes[1]) (don't leave open ends)
- optional: redirect stdin and use execl()
- read from parent with
- close(filedes[0]) when done
Notes on Pipes- Can setup many configurations
- Parent writes to a child (1 pipe)
- Child writes to a parent (1 pipe)
- Parent writes to a child and child writes to parent (2 pipes)
- Parent connects to child (1 pipe, like the shell)
- Parent connects two children (1 pipe)
- One processes with a common parent can communicate with a standard pipe
- There is a "named" pipe that can exist in the filesystem
- You need to close the ends of the pipe to ensure correct termination
- Use dup() to redirect stdin or stdout to a pipe
Pipe Examplespipe.c/* pipe.c - example of using a pipe */
/* Parent writes to child via pipe. */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char **argv)
{
pid_t id;
int count;
char buf[100];
int fildes[2];
pipe(fildes);
id = fork();
if (id == 0) {
/* We are in the child. */
/* Close write end of pipe. */
close(fildes[1]);
/* Read data from pipe. */
if ((count = read(fildes[0], buf, 100)) < 0) {
/* Write error message to stderr. */
write(2, "Cannot read from pipe\n", 22);
exit(1);
}
printf("buf[] = %s\n", buf);
/* Close read end of pipe. */
close(fildes[0]);
exit(0);
} else {
/* We are in the parent. */
/* Close read end of pipe. */
close(fildes[0]);
/* Write data to pipe. */
if (write(fildes[1], "Hello child!", 13) < 0) {
write(2, "Cannot write to pipe\n", 21);
}
/* Close write end of pipe. */
close(fildes[1]);
id = wait(NULL);
}
return 0;
}
pipe-exec.c/* pipe-exec.c - use a pipe to send "input" to another program */
/* Redirect stdin of a process to the read end of a pipe */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char **argv)
{
pid_t id;
int count;
int fildes[2];
/* Create a pipe. */
/* This will create two file descriptors in the file descriptor table. */
pipe(fildes);
if ((id = fork()) == 0) {
/* We are in the child */
/* Close stdin. */
close(0);
/* Put the read end of the pipe into stdin (0) file descriptor slot. */
dup(fildes[0]);
/* Close write end of pipe. */
close(fildes[1]);
if (execlp("sort", "sort", NULL) < 0) {
write(2, "execlp failed\n", 14);
exit(1);
}
} else {
/* We are in the parent. */
/* Close read end of pipe. */
close(fildes[0]);
/* Send some data to sort. */
write(fildes[1], "bbb\n", 4);
write(fildes[1], "ttt\n", 4);
write(fildes[1], "aaa\n", 4);
/* Close write end to tell sort we are done */
close(fildes[1]);
/* Wait for child to exit. */
id = wait(NULL);
}
return(0);
}
Working with Directories- opendir(), readdir(),closedir()
- man readdir (on Linux)
- man 5 dir (on Mac)
- struct dirent
readdir-example.c/* readdir-example.c - UNIX system calls to read directory contents. */
/* Look for name from argv[1] in current directory. */
#include<dirent.h>
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char **argv)
{
DIR *dirp;
struct dirent *dp;
char *name = argv[1];
int len;
bool found = false;
/* Open the current directory. */
dirp = opendir(".");
if (dirp == NULL) {
printf("Cannot opendir()\n");
exit(-1);
}
len = strlen(name);
/* Read all directory entires lookinf for name */
while ((dp = readdir(dirp)) != NULL) {
if (strlen(dp->d_name) == len && strcmp(dp->d_name, name) == 0) {
found = true;
}
}
closedir(dirp);
if (found) {
printf("Found %s in current directory.\n", name);
} else {
printf("Cannot find %s in current directory.\n", name);
}
return 0;
}
|
|