160 likes | 391 Views
Chapter 6 UNIX Special Files. Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003. 6.1 Pipes. pipe() Function. The simplest mechanism in UNIX for interprocess communication is the unnamed pipe, which is represented by a special file
E N D
Chapter 6UNIX Special Files Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.
pipe() Function • The simplest mechanism in UNIX for interprocess communication is the unnamed pipe, which is represented by a special file • The pipe() function creates a communication buffer that the caller can access through the two-entry array parameter ( i.e., fileDescriptors[2] ) • The data written to fileDescriptors[1] can be then read from fileDescriptors[0] on a first-in-first-out basis#include <unistd.h>int pipe(int fileIDs[2]); // An array with two members • If successful, the function returns zero; otherwise, it returns –1 and sets errno
pipe() Function (continued) • A pipe has no external or permanent name, so a program can access it only through its two descriptors • For this reason, a pipe can be used only by the process that created it and by descendants that inherit the descriptors by way of a fork() call • The pipe() function creates a unidirectional communication buffer • When a process calls read() on a pipe, the read() function returns immediately if the pipe is not empty • If the pipe is empty, the read() function blocks until something is written to the pipe, as long as some process has the pipe open for writing • On the other hand, if no process has the pipe open for writing, a read() call on an empty pipe returns zero, indicating end of file • Normally, a parent process uses one or more pipes to communicate with its children as shown on the next slide • A parent creates a pipe before calling fork() to create a child • The parent then writes a message to the pipe • The childreads a message from the pipe
Example use of pipe() #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #define BUFFER_SIZE 100 // ********************************************* int main(void) { char bufferIn[BUFFER_SIZE] = "empty"; char bufferOut[BUFFER_SIZE] = "empty"; int bytesIn; pid_t childPid; int fileDescriptors[2]; int status; bytesIn = strlen(bufferIn); status = pipe(fileDescriptors); if (status == -1) { perror("Failed to create the pipe"); return 1; } // End if (More on next slide)
Example use of pipe()(continued) childPid = fork(); if (childPid == -1) { perror("Fork call failed"); return 1; } // End if if (childPid != 0) /* parent code */ { strcpy(bufferOut, "Hello, child"); write(fileDescriptors[1], bufferOut, strlen(bufferOut)+1); } else /* child code */ { bytesIn = read(fileDescriptors[0], bufferIn, BUFFER_SIZE); } fprintf(stderr, "PID: %5d bufferIn: {%.*s} bufferOut: {%s}\n", getpid(), bytesIn, bufferIn, bufferOut); sleep(1); return 0; } // End main
Redirection and Pipes • Pipes can be combined with redirection in a command line to connect the standard output of one process to the standard input of another • The vertical bar ( | ) placed between two program names on the command line represents a pipe • As an example, the following shell commands use the sort filter utility in conjunction with the ls utility to output a directory listing sorted by sizeuxb2% ls –l >directory.txtuxb2% sort –n +4 < directory.txt(Note: The "-n" option means sort numerically; the “+4" option means to find the sort key by skipping over four fields) • An alternative approach for creating a sorted directory listing is to use a pipe, thereby eliminating the need for an intermediate filels –l | sort –n +4 • Below is another example using pipesps –ef | grep "user1" | sort
FIFOs • Pipes are temporary in the sense that they cease to exist when no process has them open • FIFOs or named pipes, are special files that persist even after all processes have closed them • A FIFO has a name and permissions just like an ordinary file and appears in a directory listing • Any process with the appropriate permissions can access a FIFO • A user creates a FIFO by executing the mkfifo command from a command shell or by calling the mkfifo() function from within a program
mkfifo() Function • The mkfifo() function creates a new FIFO special file corresponding to the path name specified in the path parameter#include <sys/stat.h>int mkfifo(const char *path, mode_t mode); • The mode parameter specifies the permissions for the newly created FIFO • If successful, the function returns zero; otherwise, it returns –1 and sets errno • A return value of –1 means that the FIFO was not created • The following code segment creates a FIFO in the current working directory#define FIFO_PERMISSIONS (S_IRUSR | S_IWUSR)int status;status = mkfifo("client.fifo", FIFO_PERMISSIONS);if (status == -1) perror("Failed to create FIFO"); • The following code segment deletes the FIFO from the current working directoryint status;status = unlink("client.fifo");if (status == -1) perror("Failed to unlink FIFO");
Simple Client/Server Model • The client/server model is a standard pattern for process interaction • One simple implementation approach is through the use of FIFOs • One type of client/server communication is simple-request, where the client sends information to the server in a one-way transmission through the FIFO • Another type of client/server communication is request-reply, where the client sends a request to the server through the FIFO and the server sends a reply back through the FIFO • The following set of slides shows the implementation of the simple-request communication approach • The server creates the FIFO and opens it for both reading and writing • When an attempt is made to open a FIFO for only reading, the open() call blocks until another process opens the FIFO for writing • Because the server opens the FIFO for both reading and writing, the open() call does not block • The client opens the FIFO for writing, sends a message to the server, and closes its connection to the FIFO • The server reads the message from the FIFO, displays the message, and closes its connection to the FIFO
Example of Server use of FIFO #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #define BUFFER_SIZE 100 // ********************************************* int main (int argc, char *argv[]) { int fifoDescriptor; char buffer[BUFFER_SIZE]; int status; int byteCount; if (argc != 2) { fprintf(stderr, "Usage: a.out fifo_name\n"); return 1; } (More on next slide)
Example of Server use of FIFO (continued) status = mkfifo(argv[1], (S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH) ); if (status == -1) { perror("Server failed to create a FIFO"); return 1; } fifoDescriptor = open(argv[1], O_RDWR); if (fifoDescriptor == -1) { perror("Server failed to open its FIFO"); return 1; } byteCount = read(fifoDescriptor, buffer, BUFFER_SIZE); if (byteCount <= 0) perror("Problem occurred when server tried reading the FIFO"); fprintf(stderr, "SERVER RECEIVED: %.*s\n", byteCount, buffer); close(fifoDescriptor); return 1; } // End main
Example of Client use of FIFO #include <errno.h> #include <fcntl.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> #include <sys/stat.h> #define BUFFER_SIZE 100 // *********************************** int main (int argc, char *argv[]) { time_t currentTime; char buffer[BUFFER_SIZE]; int fifoDescriptor; int byteCount; int bufferLength; if (argc != 2) { fprintf(stderr, "Usage: a.out fifo_name\n"); return 1; } (More on next slide)
Example of Client use of FIFO (continued) fifoDescriptor = open(argv[1], O_WRONLY); if (fifoDescriptor == -1) { perror("Client failed to open FIFO for writing"); return 1; } currentTime = time(NULL); snprintf(buffer, BUFFER_SIZE, "%d: %s", getpid(), ctime(¤tTime)); bufferLength = strlen(buffer); byteCount = write(fifoDescriptor, buffer, bufferLength); if (byteCount != bufferLength) { perror("Client failed to write to FIFO"); return 1; } else fprintf(stderr, "\nClient sent a message to the server\n"); close(fifoDescriptor); return 0; } // End main