|
Pipes
and FIFOs |
|
Prof. David Bernstein |
| Computer Science Department |
| bernstdh@jmu.edu |
PIP_BUF bytes) are guaranteed
to be atomic (hence, if multiple processes are writing to
the same pipe/FIFO their bytes won't be "intermingled")SIGPIPE signal#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int
main(void)
{
char msg[20];
int fd_pipe[2];
int n, status;
pid_t pid;
// Construct the pipe
pipe(fd_pipe);
pid = fork();
switch(pid)
{
case -1:
exit(1);
case 0:
// The child is "receiving" so must close the write end
close(fd_pipe[1]);
// Receive the message
read(fd_pipe[0], msg, 20); // Block until the parent is done
// Close the read end
close(fd_pipe[0]);
write(STDOUT_FILENO, msg, 20);
exit(0);
default:
// The parent is "sending" so must close the read end
close(fd_pipe[0]);
// Send the message
write(fd_pipe[1], "Because I said so!\n\0", 20);
// Close the write end
close(fd_pipe[1]);
wait(&status); // Reap the child
exit(0);
}
}
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define FIELD_WIDTH 11
float
update_mean(char *value)
{
static float total = 0.0;
static int count = 0;
count++;
total += atof(value);
if (count > 0) return total/count;
else return -1.0;
}
int
index_of(char *needle, char *haystack)
{
char *r = strstr(haystack, needle);
if (r == NULL) return -1;
else return r - haystack;
}
int
main(void)
{
char grade[FIELD_WIDTH+1]; // 7 for ID, 1 for comma, 3 for grade
float gpa;
int fd_pipe[2], fd_data, n;
pipe(fd_pipe);
switch(fork())
{
case -1:
exit(1);
case 0: // Code to execute in the child process
// The child is reading from the pipe so must close the write end
close(fd_pipe[1]);
// Read from the pipe and update the GPA
while ((n = read(fd_pipe[0], grade, FIELD_WIDTH)) == FIELD_WIDTH)
{
grade[FIELD_WIDTH] = '\0';
gpa = update_mean(grade+8);
}
// Done reading from the the pipe so close it
close(fd_pipe[0]);
// Print the CS GPA
printf("CS GPA: %4.2f\n", gpa);
exit(0);
default: // Code to execute in the parent process
// The parent is writing to the pipe so must close the read end
close(fd_pipe[0]);
// Open the data file
fd_data = open("grades.dat", O_RDONLY);
// Read from the data file, write to the pipe, and update the GPA
while ((n = read(fd_data, grade, FIELD_WIDTH)) == FIELD_WIDTH)
{
grade[FIELD_WIDTH] = '\0';
// Write CS courses (only) to the pipe
if (index_of("CS", grade) == 0)
{
write(fd_pipe[1], grade, FIELD_WIDTH);
}
// Update the GPA for all courses
gpa = update_mean(grade+8);
}
// Done reading from the file so close it.
close(fd_data);
// Done writing to the pipe so close it
close(fd_pipe[1]);
// Reap the child process
wait(NULL);
// Print the overall GPA
printf("Overall GPA: %4.2f\n", gpa);
exit(0);
}
}
int mkfifo(const char *name, mode_t mode)
name
|
the pathname of the FIFO |
mode
|
the permissions (as on files) |
| Return | 0 on success; -1 on error |
Since you need to have at least one writing process on one end and
one writing process on the other, by default a call to open() one
end (e.g., O_WRONLY will block until another process
calls open() for the other end (e.g., O_RDONLY).
This behavior can be modified with the O_NONBLOCK flag
(though there are many details to consider).
ls -A
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#define FIELD_WIDTH 11
int
main(int argc, char **argv)
{
char grade[FIELD_WIDTH+1]; // 7 for ID, 1 for comma, 3 for grade
int count, fd, n;
float total;
// Make the FIFO for the requested department
mkfifo(argv[1], S_IRUSR | S_IWUSR);
// Open the read end of the FIFO (blocking by default so that this program
// will wait until the splitter writes to the FIFO)
fd = open(argv[1], O_RDONLY);
count = 0;
total = 0.0;
// Read from the FIFO and update the GPA
while ((n = read(fd, grade, FIELD_WIDTH)) == FIELD_WIDTH)
{
grade[FIELD_WIDTH] = '\0';
count++;
total += atof(grade+8);
}
printf("\nGPA in %s: %4.2f\n", argv[1], total/count);
close(fd);
unlink(argv[1]);
return 0;
}
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#define DEPARTMENTS 3
#define FIELD_WIDTH 11
int
index_of(char *needle, char *haystack)
{
char *r = strstr(haystack, needle);
if (r == NULL) return -1;
else return r - haystack;
}
int
main(int argc, char **argv)
{
char grade[FIELD_WIDTH+1]; // 7 for ID, 1 for comma, 3 for grade
int fd_data, i, n;
int fd_fifo[DEPARTMENTS];
char *depts[] = {"CS", "ENG", "MUS"};
printf("Remember: First run averager for each department.\n");
for (i=0; i<DEPARTMENTS; i++)
{
// Create the FIFO for department i
mkfifo(depts[i], S_IRUSR | S_IWUSR);
// Open the write end of the FIFO in non-blocking mode
// so that the user can be get information only on those
// departments she/he is interested in
fd_fifo[i] = open(depts[i], O_WRONLY | O_NONBLOCK);
}
// Open the data file
fd_data = open("grades.dat", O_RDONLY);
// Read from the data file and write to the FIFOs
while ((n = read(fd_data, grade, FIELD_WIDTH)) == FIELD_WIDTH)
{
grade[FIELD_WIDTH] = '\0';
// Write to the appropriate FIFO
for (i=0; i<DEPARTMENTS; i++)
{
if (index_of(depts[i], grade) == 0)
{
write(fd_fifo[i], grade, FIELD_WIDTH);
break;
}
}
}
// Done reading from the file so close it.
close(fd_data);
// Close all of the file descriptors
for (i=0; i<DEPARTMENTS; i++)
{
close(fd_fifo[i]);
unlink(depts[i]);
}
return 0;
}
open() will block until both ends are
open.#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int
main(void)
{
char c;
int fd, n, status;
pid_t pid;
pid = fork();
switch(pid)
{
case -1:
exit(1);
case 0:
mkfifo(".continue", S_IRUSR);
fd = open(".continue", O_RDONLY); // Block until the parent is done
close(fd);
printf("Task 2\n");
unlink(".continue"); // Unlink the FIFO
exit(0);
default:
printf("Task 1\n");
mkfifo(".continue", S_IWUSR);
fd = open(".continue", O_WRONLY); // Let the child know the parent is done
close(fd);
wait(&status); // Reap the child
printf("Task 3\n");
unlink(".continue"); // Unlink the FIFO
exit(0);
}
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../signals/tax_lib.h"
int
main(void)
{
char c;
int fd_pipe[2];
int n, status;
pid_t pid;
// Construct the pipe
pipe(fd_pipe);
pid = fork();
switch(pid)
{
case -1:
exit(1);
case 0:
// The child is "receiving" so must close the write end
close(fd_pipe[1]);
read(fd_pipe[0], &c, 1); // Block until the parent is done
printf("Task 2\n");
// Close the read end
close(fd_pipe[0]);
exit(0);
default:
// The parent is "sending" so must close the read end
close(fd_pipe[0]);
printf("Task 1\n");
close(fd_pipe[1]); // Let the child know I'm done
wait(&status); // Reap the child
printf("Task 3\n");
exit(0);
}
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../signals/tax_lib.h"
int
main(void)
{
char should;
float rate, sales, tax;
int fd_pipe[2];
int n, status;
pid_t pid;
// Construct the pipe to be used for synchronization
pipe(fd_pipe);
rate = 0.05;
pid = fork();
switch(pid)
{
case -1:
exit(1);
case 0:
// The child is writing to the pipe so must close the read end
close(fd_pipe[0]);
if (should_tax()) write(fd_pipe[1], "?", 1); // "Signal"
close(fd_pipe[1]); // Don't "Signal"
record_status(); // This takes a long time
exit(0);
default:
// The parent is reading from the pipe so must close the write end
close(fd_pipe[1]);
sales = total_sales();
n = read(fd_pipe[0], &should, 1); // Blocks until the child is done
if (n == 1) tax = sales * rate; // The child "signaled"
else tax = 0.0; // The child didn't "signal"
// Close the read end
close(fd_pipe[0]);
printf("Tax: %5.2f\n", tax);
wait(&status); // Reap the child
exit(0);
}
}
Note: The "sender" transmits one bit of information (as in the similar example that used signals) by either writing or not. This approach is more subtle than necessary since the pipe can be used to transmit information more directly.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../signals/tax_lib.h"
int
main(void)
{
char should;
float rate, sales, tax;
int fd_pipe[2];
int n, status;
pid_t pid;
// Construct the pipe to be used for communication and synchronization
pipe(fd_pipe);
rate = 0.05;
pid = fork();
switch(pid)
{
case -1:
exit(1);
case 0:
// The child is writing to the pipe so must close the read end
close(fd_pipe[0]);
if (should_tax()) write(fd_pipe[1], "1", 1); // Send "should tax"
else write(fd_pipe[1], "0", 1); // Send "shouldn't tax"
// Close the write end
close(fd_pipe[1]);
record_status(); // This takes a long time
exit(0);
default:
// The parent is reading from the pipe so must close the write end
close(fd_pipe[1]);
sales = total_sales();
n = read(fd_pipe[0], &should, 1); // Blocks until the child is done
if (should == '1') tax = sales * rate;
else tax = 0.0;
// Close the read end
close(fd_pipe[0]);
printf("Tax: %5.2f\n", tax);
wait(&status); // Reap the child
exit(0);
}
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../signals/tax_lib.h"
int
main(void)
{
char c;
int c2p_pipe[2], p2c_pipe[2];
int n, status;
pid_t pid;
// Construct the pipes
pipe(c2p_pipe);
pipe(p2c_pipe);
pid = fork();
switch(pid)
{
case -1:
exit(1);
case 0:
// The child is writing to c2p_pipe so must close the read end
close(c2p_pipe[0]);
// The child is reading from p2c_pipe so must close the write end
close(p2c_pipe[1]);
printf("A1\n");
write(c2p_pipe[1], "?", 1); // Let the parent know A1 is done
read(p2c_pipe[0], &c, 1); // Block until B1 is done
printf("A2\n");
// Close the other ends
close(c2p_pipe[1]);
close(p2c_pipe[0]);
exit(0);
default:
// The parent is reading from c2p_pipe so must close the write end
close(c2p_pipe[1]);
// The parent is writing to p2c_pipe so must close the read end
close(p2c_pipe[0]);
printf("B1\n");
write(p2c_pipe[1], "?", 1); // Let the child know B1 is done
read(c2p_pipe[0], &c, 1); // Block until A1 is done
printf("B2\n");
// Close the other ends
close(c2p_pipe[0]);
close(p2c_pipe[1]);
wait(&status); // Reap the child
exit(0);
}
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../signals/tax_lib.h"
int
main(void)
{
char c;
int c2p_pipe[2], p2c_pipe[2];
int n, status;
pid_t pid;
// Construct the pipes
pipe(c2p_pipe);
pipe(p2c_pipe);
pid = fork();
switch(pid)
{
case -1:
exit(1);
case 0:
// The child is writing to c2p_pipe so must close the read end
close(c2p_pipe[0]);
// The child is reading from p2c_pipe so must close the write end
close(p2c_pipe[1]);
read(p2c_pipe[0], &c, 1); // Block until B1 is done
printf("A1\n");
write(c2p_pipe[1], "?", 1); // Let the parent know A1 is done
printf("A2\n");
// Close the other ends
close(c2p_pipe[1]);
close(p2c_pipe[0]);
exit(0);
default:
// The parent is reading from c2p_pipe so must close the write end
close(c2p_pipe[1]);
// The parent is writing to p2c_pipe so must close the read end
close(p2c_pipe[0]);
printf("B1\n");
write(p2c_pipe[1], "?", 1); // Let the child know B1 is done
read(c2p_pipe[0], &c, 1); // Block until the A1 is done
printf("B2\n");
// Close the other ends
close(c2p_pipe[0]);
close(p2c_pipe[1]);
wait(&status); // Reap the child
exit(0);
}
}
Note: With this protocol B1 will always be completed before A1 (which is not required).