Last active
December 29, 2015 05:39
-
-
Save zed/7623724 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Fork N children, each child writes his ID to the pipe, the parent | |
reads the IDs from the pipe. | |
http://stackoverflow.com/questions/20160438/fork-multiple-children-then-use-pipe-with-select | |
*/ | |
#include <errno.h> | |
#include <inttypes.h> | |
#include <signal.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/select.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <unistd.h> | |
/* number of processes to start */ | |
#ifndef N | |
#define N 3 | |
#endif | |
#define Close(FD) do { \ | |
const int Close_fd = (FD); \ | |
if (close(Close_fd) == -1) \ | |
fprintf(stderr, "%s:%d: close(" #FD ") %d: %s\n", \ | |
__FILE__, __LINE__, Close_fd, strerror(errno)); \ | |
}while(0) | |
static void report_error_and_exit(const char* msg) { | |
perror(msg); | |
exit(EXIT_FAILURE); | |
} | |
/** write the process ID to fd or return -1 on error (errno is set) */ | |
static ssize_t write_pid(int fd) { | |
pid_t pid = getpid(); | |
ssize_t size = sizeof(pid), n = size, m = 0; | |
while (n > 0) { /* until there is something to write */ | |
if ((m = write(fd, &pid + (size - n), n)) > 0) | |
n -= m; | |
else if (errno != EINTR) | |
return -1; /* error */ | |
} | |
return size; /* number of bytes written */ | |
} | |
/** read pid from fd; return -1 on error (errno is set) */ | |
static pid_t read_pid(int fd) { | |
pid_t pid = -1; | |
ssize_t size = sizeof(pid), n = size, m = 0; | |
while (n > 0) { /* until there is something to read */ | |
if ((m = read(fd, &pid + (size - n), n)) > 0) | |
n -= m; | |
else if (errno != EINTR) | |
return -1; /* error */ | |
} | |
return pid; | |
} | |
/** read pid from fd and report it; exit on error */ | |
static void process_pid(int fd) { | |
pid_t pid = -1; | |
if ((pid = read_pid(fd)) == -1) | |
report_error_and_exit("read"); | |
printf("%" PRIiMAX " got %" PRIiMAX "\n", (intmax_t)getpid(), (intmax_t)pid); | |
} | |
int main(void) { | |
const int nchildren = (N); | |
int i = -1, npids_read = nchildren; | |
int fd[2] = {-1}; /* pipe to communicate with children */ | |
pid_t pid = -1; | |
if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) /* POSIX.1-2001 reap children */ | |
report_error_and_exit("signal"); | |
/* use a single pipe to communicate with the children, assume a | |
child writes no more than PIPE_BUF (>=512 in POSIX) bytes at a | |
time, and the output from multiple `write` calls may interleave. | |
*/ | |
if (pipe(fd) == -1) | |
report_error_and_exit("pipe"); | |
/* run children */ | |
for (i = 0; i < nchildren; ++i) { | |
if ((pid = fork()) == -1) | |
report_error_and_exit("fork"); | |
else if (pid == 0) { /* child */ | |
Close(fd[0]); /* close unused read end of the pipe */ | |
if (write_pid(fd[1]) == -1) { | |
perror("write"); | |
_exit(EXIT_FAILURE); | |
} | |
_exit(EXIT_SUCCESS); | |
} | |
else { /* parent */ | |
const int pipe_read = fd[0]; | |
printf("started %d-th child, pid: %" PRIdMAX "\n", i, (intmax_t)pid); | |
/* try to read ids without waiting for all children to start */ | |
for ( ; ; ) { | |
int r = -1; | |
struct timeval tv = {0}; /* zero timeout */ | |
fd_set rd; FD_ZERO(&rd); | |
FD_SET(pipe_read, &rd); | |
r = select(pipe_read + 1, &rd, NULL, NULL, &tv); | |
if (r == -1 && errno == EINTR) | |
continue; /* try again */ | |
if (r == -1) | |
report_error_and_exit("select"); | |
if (FD_ISSET(pipe_read, &rd)) { | |
process_pid(pipe_read); | |
--npids_read; /* one less pid to read */ | |
continue; /* process more */ | |
} | |
break; /* not ready to read yet */ | |
} | |
} | |
} | |
printf("all children started\n"); | |
/* parent */ | |
Close(fd[1]); /* close unused write end of the pipe */ | |
/* read rest of pids */ | |
while (npids_read --> 0) | |
process_pid(fd[0]); | |
printf("done\n"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment