So I have a project to do but I am completely stumped. I have spent ten hours and have gotten nowhere. I don't specifically want the code to the answer, but some pseudocode and good hints in the right direction would help a heap!!
所以我有一个项目要做,但我完全难过了。我已经花了十个小时而且无处可去。我没有特别想要代码的答案,但一些伪代码和正确方向的良好提示将有助于堆!
It forks a number of processes, k - a command-line argument, connected by pipes - each process is connected to the next, and the last process is connected to the first. Process number k sends its message on to process number (k+1)%n.
它分叉了许多进程,k - 一个命令行参数,通过管道连接 - 每个进程连接到下一个进程,最后一个进程连接到第一个进程。进程号k将其消息发送到进程号(k + 1)%n。
Process 0 reads a line from stdin
. It then transmits it to process 1. Each other process reads the line, increments the first byte of the string by 1, and then relays the line to the next process. As it relays, it prints a status message (shown below).
进程0从stdin读取一行。然后它将它发送到进程1.每个其他进程读取该行,将字符串的第一个字节递增1,然后将该行中继到下一个进程。在中继时,它会打印状态消息(如下所示)。
When the message gets back to process 0, it is output to the standard output as well. When a process receives EOF
(either from pipe, if its a process other than 0, or from stdin
, for process 0), it prints the final string. This will close all pipes.
当消息返回到进程0时,它也会输出到标准输出。当一个进程收到EOF(来自管道,如果它的进程不是0,或者来自stdin,进程0),它会打印最终的字符串。这将关闭所有管道。
The expected output is:
预期的输出是:
$ ./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
$
What I have written so far:
我到目前为止所写的内容:
#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;
}
What it does currently is read in a string from stdin, pass it to the first process and print process 0 (can't actually get the 0 though, simply printing 0), it then pipes the string to process 1 which distorts byte 1 and then writes to a pipe again and then outside of the pipes, the string is read and outputs the distorted string.
它当前的作用是从stdin中读取一个字符串,将其传递给第一个进程并打印进程0(实际上不能得到0,只需打印0),然后将字符串传递给进程1,这会扭曲字节1和然后再次写入管道然后在管道外部,读取字符串并输出失真的字符串。
$ ./ring
hello
process #0 (6677) sending message: hello
process #1 (6678) relaying message: iello
I hear -6678 iello
^C
$
I don't know where to go from here. Thank you in advance, anything will help!!
我不知道从哪里开始。提前谢谢,一切都会有所帮助!!
Given some help this is what I have now:
给予一些帮助,这就是我现在所拥有的:
#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: %s n, 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;
}
2 个解决方案
#1
8
This is a diagram I drew for myself showing how the processes are to be interconnected:
这是我自己绘制的图表,展示了如何互连流程:
p4
C5 <--------- C4
/ \
p5 / p3 \
/ \
o----> C0 ---->o C3
\ /
p0 \ p2 /
\ /
C1 ---------> C2
p1
The Cn represent the processes; C0 is the parent process. The pn represent the pipes; the other two lines are standard input and standard output. Each child has a simple task, as befits children. The parent has a more complex task, mainly ensuring that exactly the right number of file descriptors are closed. In fact, the close()
is so important that I created a debugging function, fd_close()
, to conditionally report on file descriptors being closed. I used that too when I had silly mistakes in the code.
Cn代表过程; C0是父进程。 pn代表管道;另外两行是标准输入和标准输出。每个孩子都有一个简单的任务,就像孩子一样。父级具有更复杂的任务,主要是确保关闭正确数量的文件描述符。实际上,close()非常重要,我创建了一个调试函数fd_close(),以有条件地报告关闭的文件描述符。当我在代码中遇到愚蠢的错误时,我也使用过它。
The err_*()
functions are simplified versions of code I use in most of my programs. They make error reporting less onerous by converting most error reports into a one-line statement, rather than requiring multiple lines. (These functions are normally in 'stderr.c' and 'stderr.h', but those files are 750 lines of code and comment and are more comprehensive. The production code has an option to support prefixing each message with a PID, which is also important with multi-process systems like this.)
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);
}
Example output:
$ ./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
$
#2
1
It seems to me that you are pretty close, as this works for two processes. What you need to do now is loop to create more processes from the parent.
在我看来,你非常接近,因为这适用于两个过程。您现在需要做的是循环以从父级创建更多进程。
(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
Once created with fork(), the child processes (i.e. apart from proc0) close the input of the pipe (the output of the other is already closed!), read the message on one, write on the other and exit.
一旦用fork()创建,子进程(即除proc0之外)关闭管道的输入(另一个的输出已经关闭!),在一个上读取消息,在另一个上写入并退出。
Some remarks on your current code:
对您当前代码的一些评论:
- The child shouldn't execute that list bit, when you read on child_parent.
- You don't need that many buffers (you only need one, that will turn into one per process after the fork).
- Put some terminating null bytes before printing :)
- It's good practice to close the ends that you're not going to need
当您阅读child_parent时,孩子不应该执行该列表位。
您不需要那么多缓冲区(您只需要一个缓冲区,在fork之后每个进程将变成一个缓冲区)。
在打印之前放一些终止空字节:)
关闭你不需要的目标是一种好习惯
#1
8
This is a diagram I drew for myself showing how the processes are to be interconnected:
这是我自己绘制的图表,展示了如何互连流程:
p4
C5 <--------- C4
/ \
p5 / p3 \
/ \
o----> C0 ---->o C3
\ /
p0 \ p2 /
\ /
C1 ---------> C2
p1
The Cn represent the processes; C0 is the parent process. The pn represent the pipes; the other two lines are standard input and standard output. Each child has a simple task, as befits children. The parent has a more complex task, mainly ensuring that exactly the right number of file descriptors are closed. In fact, the close()
is so important that I created a debugging function, fd_close()
, to conditionally report on file descriptors being closed. I used that too when I had silly mistakes in the code.
Cn代表过程; C0是父进程。 pn代表管道;另外两行是标准输入和标准输出。每个孩子都有一个简单的任务,就像孩子一样。父级具有更复杂的任务,主要是确保关闭正确数量的文件描述符。实际上,close()非常重要,我创建了一个调试函数fd_close(),以有条件地报告关闭的文件描述符。当我在代码中遇到愚蠢的错误时,我也使用过它。
The err_*()
functions are simplified versions of code I use in most of my programs. They make error reporting less onerous by converting most error reports into a one-line statement, rather than requiring multiple lines. (These functions are normally in 'stderr.c' and 'stderr.h', but those files are 750 lines of code and comment and are more comprehensive. The production code has an option to support prefixing each message with a PID, which is also important with multi-process systems like this.)
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);
}
Example output:
$ ./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
$
#2
1
It seems to me that you are pretty close, as this works for two processes. What you need to do now is loop to create more processes from the parent.
在我看来,你非常接近,因为这适用于两个过程。您现在需要做的是循环以从父级创建更多进程。
(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
Once created with fork(), the child processes (i.e. apart from proc0) close the input of the pipe (the output of the other is already closed!), read the message on one, write on the other and exit.
一旦用fork()创建,子进程(即除proc0之外)关闭管道的输入(另一个的输出已经关闭!),在一个上读取消息,在另一个上写入并退出。
Some remarks on your current code:
对您当前代码的一些评论:
- The child shouldn't execute that list bit, when you read on child_parent.
- You don't need that many buffers (you only need one, that will turn into one per process after the fork).
- Put some terminating null bytes before printing :)
- It's good practice to close the ends that you're not going to need
当您阅读child_parent时,孩子不应该执行该列表位。
您不需要那么多缓冲区(您只需要一个缓冲区,在fork之后每个进程将变成一个缓冲区)。
在打印之前放一些终止空字节:)
关闭你不需要的目标是一种好习惯