分叉和pipe道过程c

所以我有一个项目,但我完全难住。 我花了十个小时,没有任何地方。 我不特别想要代码的答案,但一些伪代码和良好的提示在正确的方向将帮助一堆!

它分叉了许多进程,k – 一个由pipe道连接的命令行参数 – 每个进程连接到下一个进程,最后一个进程连接到第一个进程。 进程号码k将其消息发送到进程号(k + 1)%n。

进程0从stdin读取一行。 然后它将它发送到进程1.每个其他进程读取该行,将该string的第一个字节递增1,然后将行中继到下一个进程。 随着中继,它打印一个状态消息(如下所示)。

当消息返回到进程0时,它也输出到标准输出。 当一个进程收到EOF (或者从pipe道,如果它不是一个进程,或者从stdin ,进程0),它打印最后一个string。 这将closures所有pipe道。

预期产出是:

 $ ./ring 4 hello process #0 (32768) sending message: hello process #1 (32769) relaying message: iello process #2 (32770) relaying message: jello process #3 (32767) relaying message: kello I hear kello ^C $ 

我到目前为止写的是:

 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define BUFFER_SIZE 80 #define READ_END 0 #define WRITE_END 1 int main(int argc, char *argv[]) { char readmsg[BUFFER_SIZE], readmsg2[BUFFER_SIZE], final[BUFFER_SIZE]; int pid, process; int parent_child[2], child_parent[2]; process = 0; if (pipe(child_parent) == -1) { fprintf(stderr, "Pipe failed"); return 1; } if (pipe(parent_child) == -1) { fprintf(stderr, "Pipe failed"); return 1; } pid = fork(); if (pid < 0) { fprintf(stderr, "Fork failed"); return 1; } else if (pid > 0) { /* PARENT */ read(0, &readmsg, BUFFER_SIZE); printf("process #%d (%d) sending message: %s", 0, getpid(), readmsg); write(parent_child[WRITE_END], &readmsg, BUFFER_SIZE); close(parent_child[WRITE_END]); } else { /* CHILD */ read(parent_child[READ_END], &readmsg2, BUFFER_SIZE); readmsg2[0] += 1; printf("process #%d (%d) relaying message: %s", 1, getpid(), readmsg2); process += 1; write(child_parent[WRITE_END], &readmsg2, BUFFER_SIZE); } read(child_parent[READ_END], &final, BUFFER_SIZE); printf("I hear %d %s", pid - getpid(), final); return 0; } 

它现在所做的是从stdin中读取一个string,将它传递给第一个进程并打印进程0(虽然不能实际得到0,只是打印0),然后将string传给进程1,从而使字节1和然后再次写入pipe道,然后再写入pipe道,读取string并输出扭曲的string。

 $ ./ring hello process #0 (6677) sending message: hello process #1 (6678) relaying message: iello I hear -6678 iello ^C $ 

我不知道该从哪里出发。 预先感谢您,任何事情都会有所帮助!

给一些帮助,这是我现在拥有的:

 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define BUFFER_SIZE 80 #define READ_END 0 #define WRITE_END 1 int main(int argc, char **argv) { char buf[BUFFER_SIZE]; int process, rings, pid, pid_n, pid_n1, pid_1, i; int Pn[2]; //Pipe for process n -> 0 int Pn_1[2]; //Pipe for process n-1 -> n int Pn_2[2]; //Pipe for process n-2 -> n-1 int P_0[2]; //Pipe for process 0 -> 1 process = 0; if (argc == 2) { rings = atoi(argv[1]); } else { fprintf(stderr, "Usage: %sn, where n is number of rings\n", argv[0]); exit(EXIT_FAILURE); } if ((pid = fork()) < 0) { fprintf(stderr, "Fork failed"); return 1; } else if (pid == 0) { if ((pid_n = fork()) < 0) { fprintf(stderr, "Fork failed"); return 1; } else if (pid_n == 0) { /* CHILD */ close(Pn[WRITE_END]); close(Pn_1[READ_END]); } else { /* PARENT */ close(Pn[READ_END]); close(Pn_1[WRITE_END]); } for (i = 0; i < rings; i++) { if ((pid_n1 = fork()) < 0) { fprintf(stderr, "Fork failed"); return 1; } else if (pid_n1 == 0) { /* CHILD */ close(Pn_1[WRITE_END]); close(Pn_2[READ_END]); } else { /* PARENT */ close(Pn_1[READ_END]); close(Pn_2[WRITE_END]); } } /* Not sure about these last ones */ if ((pid_1 = fork()) < 0) { fprintf(stderr, "Fork failed"); return 1; } else if (pid_1 == 0) { /* CHILD */ close(P_n2[WRITE_END]); close(P_0[READ_END]); } else { /* PARENT */ close(P_n2[READ_END]); close(P_0[WRITE_END]); } } else { /* PARENT */ read(0, &buf, BUFFER_SIZE); buf[BUFFER_SIZE - 1] = '\0'; printf("process first # (%d) sending message: %s", getpid(), buf); write(P_0[WRITE_END], &buf, BUFFER_SIZE); read(Pn[READ_END], &buf, BUFFER_SIZE); buf[BUFFER_SIZE - 1] = '\0'; printf("I hear %s", buf); } return 0; } 

这是我为自己绘制的图表,显示了这些过程如何相互关联:

  p4 C5 <--------- C4 / \ p5 / p3 \ / \ o----> C0 ---->o C3 \ / p0 \ p2 / \ / C1 ---------> C2 p1 

Cn代表过程; C0是父进程。 pn代表管道; 另外两行是标准输入和标准输出。 每个孩子都有一个简单的任务,就像适合孩子一样。 父母有一个更复杂的任务,主要是确保正确数量的文件描述符被关闭。 实际上, close()非常重要,所以我创建了一个调试函数fd_close()来有条件地报告正在关闭的文件描述符。 当我在代码中犯了愚蠢的错误时,我也是这样使用的。

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 $ 

在我看来,你是非常接近,因为这适用于两个进程。 你现在需要做的就是循环从父进程创建更多的进程。

 (k=N+1 processes: proc0 = parent, proc1, ..., procN) Create a pipe Pn, that will be for procN->proc0 Create a pipe Pn-1, that will be for procN-1->procN Create relaying fork procN fork closes Pn output and Pn-1 input parent closes Pn input and Pn-1 output (loop here) Create a pipe Pi-2, that will be for procI-2->procI-1 Create relaying fork procI-1 fork closes Pi-1 output and Pi-2 input parent closes Pi-1 input and Pi-2 output ... Create a pipe P0 that will be for proc0->proc1 Create relaying fork proc1 fork closes P1 output and P0 input parent closes P1 input and P0 output (end loop) (parent final code:) Read from stdin Write on P0 Read on Pn Write on stdout 

一旦用fork()创建,子进程(即除proc0之外)关闭管道的输入(另一个的输出已经关闭了!),读一个消息,写另一个消息并退出。

关于你当前的代码的一些评论:

  • 当你在child_parent上阅读时,孩子不应该执行这个列表位。
  • 你不需要那么多的缓冲区(你只需要一个 ,在fork之后每个进程就会变成一个 )。
  • 在打印之前放入一些终止的空字节:)
  • 关闭你不需要的目标是一个好习惯