这是我为自己绘制的图表,显示了流程如何互连:
p4
C5 <--------- C4
/ \
p5 / p3 \
/ \
o----> C0 ---->o C3
\ /
p0 \ p2 /
\ /
C1 ---------> C2
p1
The Cn代表流程; C0 是父进程。这pn代表管道;另外两行是标准输入和标准输出。每个孩子都有一项适合孩子的简单任务。父进程有一个更复杂的任务,主要是确保关闭正确数量的文件描述符。事实上,close()
非常重要,所以我创建了一个调试功能,fd_close()
,有条件地报告正在关闭的文件描述符。当我在代码中出现愚蠢的错误时,我也使用了它。
The err_*()
函数是我在大多数程序中使用的代码的简化版本。它们通过将大多数错误报告转换为一行语句而不是需要多行语句来减少错误报告的繁重。 (这些函数通常位于“stderr.c”和“stderr.h”中,但这些文件有 750 行代码和注释,并且更全面。生产代码有一个选项支持为每条消息添加 PID 前缀,即对于这样的多进程系统也很重要。)
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
enum { BUFFER_SIZE = 1024 };
typedef int Pipe[2];
static int debug = 0;
static void fd_close(int fd);
/* These functions normally declared in stderr.h */
static void err_setarg0(const char *argv0);
static void err_sysexit(char const *fmt, ...);
static void err_usage(char const *usestr);
static void err_remark(char const *fmt, ...);
static void be_childish(Pipe in, Pipe out)
{
/* Close irrelevant ends of relevant pipes */
fd_close(in[1]);
fd_close(out[0]);
char buffer[BUFFER_SIZE];
ssize_t nbytes;
while ((nbytes = read(in[0], buffer, sizeof(buffer))) > 0)
{
buffer[0]++;
if (write(out[1], buffer, nbytes) != nbytes)
err_sysexit("%d: failed to write to pipe", (int)getpid());
}
fd_close(in[0]);
fd_close(out[1]);
exit(0);
}
int main(int argc, char **argv)
{
err_setarg0(argv[0]);
int nkids;
if (argc != 2 || (nkids = atoi(argv[1])) <= 1 || nkids >= 10)
err_usage("n # for n in 2..9");
err_remark("Parent has PID %d\n", (int)getpid());
Pipe pipelist[nkids];
if (pipe(pipelist[0]) != 0)
err_sysexit("Failed to create pipe #%d", 0);
if (debug)
err_remark("p[0][0] = %d; p[0][1] = %d\n", pipelist[0][0], pipelist[0][1]);
for (int i = 1; i < nkids; i++)
{
pid_t pid;
if (pipe(pipelist[i]) != 0)
err_sysexit("Failed to create pipe #%d", i);
if (debug)
err_remark("p[%d][0] = %d; p[%d][1] = %d\n", i, pipelist[i][0], i, pipelist[i][1]);
if ((pid = fork()) < 0)
err_sysexit("Failed to create child #%d", i);
if (pid == 0)
{
/* Close irrelevant pipes */
for (int j = 0; j < i-1; j++)
{
fd_close(pipelist[j][0]);
fd_close(pipelist[j][1]);
}
be_childish(pipelist[i-1], pipelist[i]);
/* NOTREACHED */
}
err_remark("Child %d has PID %d\n", i, (int)pid);
}
/* Close irrelevant pipes */
for (int j = 1; j < nkids-1; j++)
{
fd_close(pipelist[j][0]);
fd_close(pipelist[j][1]);
}
/* Close irrelevant ends of relevant pipes */
fd_close(pipelist[0][0]);
fd_close(pipelist[nkids-1][1]);
int w_fd = pipelist[0][1];
int r_fd = pipelist[nkids-1][0];
/* Main loop */
char buffer[BUFFER_SIZE];
while (printf("Input: ") > 0 && fgets(buffer, sizeof(buffer), stdin) != 0)
{
int len = strlen(buffer);
if (write(w_fd, buffer, len) != len)
err_sysexit("Failed to write to children");
if (read(r_fd, buffer, len) != len)
err_sysexit("Failed to read from children");
printf("Output: %.*s", len, buffer);
}
fd_close(w_fd);
fd_close(r_fd);
putchar('\n');
int status;
int corpse;
while ((corpse = wait(&status)) > 0)
err_remark("%d exited with status 0x%.4X\n", corpse, status);
return 0;
}
static void fd_close(int fd)
{
if (debug)
err_remark("%d: close(%d)\n", (int)getpid(), fd);
if (close(fd) != 0)
err_sysexit("%d: Failed to close %d\n", (int)getpid(), fd);
}
/* Normally in stderr.c */
static const char *arg0 = "<undefined>";
static void err_setarg0(const char *argv0)
{
arg0 = argv0;
}
static void err_usage(char const *usestr)
{
fprintf(stderr, "Usage: %s %s\n", arg0, usestr);
exit(1);
}
static void err_vsyswarn(char const *fmt, va_list args)
{
int errnum = errno;
fprintf(stderr, "%s:%d: ", arg0, (int)getpid());
vfprintf(stderr, fmt, args);
if (errnum != 0)
fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
}
static void err_sysexit(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
err_vsyswarn(fmt, args);
va_end(args);
exit(1);
}
static void err_remark(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
输出示例:
$ ./pipecircle 9
Parent has PID 34473
Child 1 has PID 34474
Child 2 has PID 34475
Child 3 has PID 34476
Child 4 has PID 34477
Child 5 has PID 34478
Child 6 has PID 34479
Child 7 has PID 34480
Child 8 has PID 34481
Input: Hello
Output: Pello
Input: Bye
Output: Jye
Input: ^D
34474 exited with status 0x0000
34477 exited with status 0x0000
34479 exited with status 0x0000
34476 exited with status 0x0000
34475 exited with status 0x0000
34478 exited with status 0x0000
34480 exited with status 0x0000
34481 exited with status 0x0000
$