Skip to content

Instantly share code, notes, and snippets.

@zed
Last active December 29, 2015 05:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zed/7623724 to your computer and use it in GitHub Desktop.
Save zed/7623724 to your computer and use it in GitHub Desktop.
/** 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