Lectures‎ > ‎

Week06


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
      • pipe(filedes)
    • fork() a child
    • close(filedes[0]) (don't leave open ends)
    • write to child with
      • write(filedes[1], ...)
    • 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
      • read(filedes[0], ...)
    • 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 Examples

pipe.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;
}

Comments