c

Definition

pipe (C)

#include <unistd.h>
 
int pipe(int pipefd[2]);

pipe() creates a pipe, a unidirectional data channel that can be used for inter-process communication. The array pipefd is used to return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe.

Includes: unistd.h

Examples

Example: forksort

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
 
typedef struct lines {
  char **lines;
  int len;
  int cap;
} lines_t;
 
void lines_append(lines_t *lines, char *str, int len) {
  if (lines->len == lines->cap) {
    lines->cap = (lines->cap == 0) ? 1 : lines->cap * 2;
    lines->lines = realloc(lines->lines, lines->cap * sizeof(char *));
  }
  lines->lines[lines->len] = strdup(str);
  lines->len++;
}
 
void lines_free(lines_t *lines) {
  for (int i = 0; i < lines->len; i++) {
    free(lines->lines[i]);
  }
  free(lines->lines);
}
 
typedef struct process_data {
  pid_t pid;
  int to_child[2];   // parent writes to [1], child reads from [0]
  int from_child[2]; // child writes to [1], parent reads from [0]
} process_data_t;
 
bool spawn(process_data_t *ptr) {
  process_data_t created = {0};
 
  if (pipe(created.from_child) == -1 || pipe(created.to_child) == -1) {
    return false;
  }
 
  created.pid = fork();
  if (created.pid == -1) {
    return false;
  }
 
  if (created.pid == 0) {
    // inside child
 
    dup2(created.to_child[0], STDIN_FILENO);    // redirect stdin
    dup2(created.from_child[1], STDOUT_FILENO); // redirect stdout
 
    // process now uses stdin/out => can close pipe channels
    close(created.to_child[0]);
    close(created.to_child[1]);
    close(created.from_child[0]);
    close(created.from_child[1]);
 
    execlp("./forksort", "forksort", NULL);
 
    exit(EXIT_FAILURE);
    return false;
  } else {
    // inside parent
 
    close(created.to_child[0]);   // parent doesn't read from child's stdin
    close(created.from_child[1]); // parent doesn't write to child's stdout
  }
 
  *ptr = created;
  return true;
}
 
int main(int argc, char *argv[]) {
	if (argc > 1) {
		fprintf(stderr, "Usage: %s\n", argv[0]);
		return EXIT_FAILURE;
	}
 
	lines_t lines = {0};
 
	char *line = NULL;
	size_t len = 0;
	ssize_t read;
	while ((read = getline(&line, &len, stdin)) != -1) {
		lines_append(&lines, line, (int)read);
	}
	free(line);
 
	if (lines.len == 0) {
		lines_free(&lines);
		return EXIT_SUCCESS;
	}
 
	if (lines.len == 1) {
		fputs(lines.lines[0], stdout);
		lines_free(&lines);
		return EXIT_SUCCESS;
	}
 
	process_data_t child1, child2;
	if (!spawn(&child1) || !spawn(&child2)) {
		fprintf(stderr, "%s: failed to spawn children\n", argv[0]);
		lines_free(&lines);
		return EXIT_FAILURE;
	}
 
	// split and write to children
	FILE *out1 = fdopen(child1.to_child[1], "w");
	FILE *out2 = fdopen(child2.to_child[1], "w");
 
	if (out1 == NULL || out2 == NULL) {
		fprintf(stderr, "%s: fdopen failed\n", argv[0]);
		lines_free(&lines);
		return EXIT_FAILURE;
	}
 
	int split = lines.len / 2;
	for (int i = 0; i < split; i++) {
		fputs(lines.lines[i], out1);
	}
	for (int i = split; i < lines.len; i++) {
		fputs(lines.lines[i], out2);
	}
 
	fclose(out1);
	fclose(out2);
 
	// read and merge from children
	FILE *in1 = fdopen(child1.from_child[0], "r");
	FILE *in2 = fdopen(child2.from_child[0], "r");
 
	if (in1 == NULL || in2 == NULL) {
	fprintf(stderr, "%s: fdopen failed\n", argv[0]);
	lines_free(&lines);
	return EXIT_FAILURE;
	}
 
	char *l1 = NULL, *l2 = NULL;
	size_t n1 = 0, n2 = 0;
	ssize_t r1 = getline(&l1, &n1, in1);
	ssize_t r2 = getline(&l2, &n2, in2);
 
	while (r1 != -1 && r2 != -1) {
		if (strcmp(l1, l2) <= 0) {
			fputs(l1, stdout);
			r1 = getline(&l1, &n1, in1);
	} else {
			fputs(l2, stdout);
			r2 = getline(&l2, &n2, in2);
		}
	}
 
	while (r1 != -1) {
		fputs(l1, stdout);
		r1 = getline(&l1, &n1, in1);
	}
	while (r2 != -1) {
		fputs(l2, stdout);
		r2 = getline(&l2, &n2, in2);
	}
 
	// cleanup
	free(l1);
	free(l2);
	fclose(in1);
	fclose(in2);
 
	int status1, status2;
	if (waitpid(child1.pid, &status1, 0) == -1 ||
		waitpid(child2.pid, &status2, 0) == -1) {
		fprintf(stderr, "%s: waitpid failed\n", argv[0]);
		lines_free(&lines);
		return EXIT_FAILURE;
	}
 
	lines_free(&lines);
 
	if (!WIFEXITED(status1) || WEXITSTATUS(status1) != EXIT_SUCCESS ||
	    !WIFEXITED(status2) || WEXITSTATUS(status2) != EXIT_SUCCESS) {
		return EXIT_FAILURE;
	}
 
	return EXIT_SUCCESS;
}