Chapter 6 UNIX Special Files Source: Robbins and Robbins, UNIX Systems Programming, Prentice Hall, 2003.
6.1 Pipes
3 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 int pipe(int fileIDs[2]); // An array with two members If successful, the function returns zero; otherwise, it returns –1 and sets errno
4 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 child reads a message from the pipe
5 Example use of pipe() #include #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)
6 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
7 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 size uxb2% ls –l >directory.txt uxb2% 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 file ls –l | sort –n +4 Below is another example using pipes ps –ef | grep "user1" | sort
6.3 FIFOs
9 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
10 mkfifo() Function The mkfifo() function creates a new FIFO special file corresponding to the path name specified in the path parameter #include 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 directory int status; status = unlink("client.fifo"); if (status == -1) perror("Failed to unlink FIFO");
6.4 Simple Client/Server Model
12 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
13 Example of Server use of FIFO #include #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)
14 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
15 Example of Client use of FIFO #include #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)
16 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