最后,在这里回顾并提出问题之后,我能够编写这个代码,生成4个子进程并通过4个不同的管道进行通信。 4个子进程中的每一个都写入一个管道(fd_log
),但是当父进程从管道读取时,它只读取进程A
中的第一条消息,来自进程{{1来自流程C
的所有消息,来自流程B
的所有消息。这是代码:
D
当我查看输出时,我知道所有消息都被读取(在输出中看到返回的字节数int main(int argc, char *argv[]) {
printf("\nWritten by Nawar Youssef\n");
int i, x=0, fd_log[2], fd_A_B[2], fd_B_C[2], fd_B_D[2], pipe_size=500;
char ch, msg_from_A[pipe_size], msg_to_B[pipe_size], msg_to_log[pipe_size],
msg_to_C[pipe_size], msg_to_D[pipe_size], msg_B_C[pipe_size], msg_B_D[pipe_size],
msg_from_log[pipe_size], temp_char[pipe_size], msg_C_log[pipe_size], msg_D_log[pipe_size];
pipe(fd_log);
pipe(fd_A_B);
pipe(fd_B_C);
pipe(fd_B_D);
if (fork()==0) { //child A
for (i=0; i < 10; i++) {
x = (rand() % 2);
if (x == 1)
ch='C';
else
ch='D';
//write records to A-B pipe
close(fd_A_B[READ]);
sprintf(msg_to_B, "%c %d", ch, i);
write(fd_A_B[WRITE], msg_to_B, strlen(msg_to_B)+1);
//write records to log pipe
close(fd_log[READ]);
sprintf(msg_to_log, "A sent to B: %c %d", ch, i);
write(fd_log[WRITE], msg_to_log, strlen(msg_to_log)+1);
}//for
close(fd_A_B[WRITE]);
close(fd_log[WRITE]);
_exit(1);
}//if
if (fork()==0) { //child B
//read A-B pipe
close(fd_A_B[WRITE]);
int n_bytes = read(fd_A_B[READ], msg_from_A, sizeof(msg_from_A));
for(i=0; i < pipe_size; i++) {
if ( msg_from_A[i] == 'C') {
//write the message from B to C pipe
sprintf(msg_to_C, "%c %c", msg_from_A[i], msg_from_A[i+2]);
//printf("--%s\n", msg_to_C);
close(fd_B_C[READ]);
write(fd_B_C[WRITE], msg_to_C, strlen(msg_to_C)+1);
//write C message to log pipe
close(fd_log[READ]);
sprintf(msg_to_log, "B sent to C: %s", msg_to_C);
write(fd_log[WRITE], msg_to_log, strlen(msg_to_log)+1);
sleep(1);
}else if (msg_from_A[i] == 'D') {
//write the message from B to D pipes
sprintf(msg_to_D, "%c %c", msg_from_A[i], msg_from_A[i+2]);
//printf("--%s\n", msg_to_D);
close(fd_B_D[READ]);
write(fd_B_D[WRITE], msg_to_D, strlen(msg_to_D)+1);
//write D message to log pipe
close(fd_log[READ]);
sprintf(msg_to_log, "B sent to D: %s", msg_to_D);
write(fd_log[WRITE], msg_to_log, strlen(msg_to_log)+1);
sleep(5);
}else
continue;
}//for
close(fd_B_C[WRITE]); close(fd_B_D[WRITE]); close(fd_log[WRITE]);
_exit(1); //process B
}//if
if (fork()==0) { //child C
//read from B-C pipe
close(fd_B_C[WRITE]);
int n_bytes = read(fd_B_C[READ], msg_B_C, sizeof(msg_B_C));
for (i=0; i < pipe_size; i++) {
//write to log pipe
if (msg_B_C[i] == 'C') {
sprintf(msg_C_log, "%c %c", msg_B_C[i], msg_B_C[i+2]);
close(fd_log[READ]);
sprintf(msg_to_log, "C sent to log: %s", msg_C_log);
write(fd_log[WRITE], msg_to_log, strlen(msg_to_log)+1);
}else
continue;
}
_exit(1); //process C
}
if (fork()==0) { //child D
//read from fd_B_D
close(fd_B_D[WRITE]);
int n_bytes = read(fd_B_D[READ], msg_B_D, sizeof(msg_B_D));
for (i=0; i < pipe_size; i++) {
//write to log pipe
if (msg_B_D[i] == 'D') {
sprintf(msg_D_log, "%c %c", msg_B_D[i], msg_B_C[i+2]);
close(fd_log[READ]);
sprintf(msg_to_log, "D sent to log: %s", msg_D_log);
write(fd_log[WRITE], msg_to_log, strlen(msg_to_log)+1);
}
}
_exit(1);
}//if
//parent
close(fd_log[WRITE]);
int n_bytes;
while( (n_bytes = read(fd_log[READ], msg_from_log, sizeof(msg_from_log)-1)) > 0){
printf("Log pipe reads (%d) bytes: %s\n", n_bytes, msg_from_log);
sleep(1);
}
close(fd_log[READ]);
return (0);
}
)。所以看起来问题在于显示消息。我遇到了类似问题的更简单的代码,我通过使read
更大来解决它。但这里没有用。
这是(#)之间的输出数,是每次while循环时读取的字节数:
由Nawar Youssef撰写
日志管道读取(187)字节:A发送到B:C 0
日志管道读取(36)字节:C发送到日志:C 0
日志管道读取(17)字节:B发送到C:C 2
日志管道读取(35)字节:B发送到D:D 3
日志管道读取(17)字节:B发送到D:D 4
日志管道读取(17)字节:B发送到D:D 5
日志管道读取(17)字节:B发送到D:D 6
日志管道读取(17)字节:B发送到D:D 7
日志管道读取(17)字节:B发送到C:C 8
日志管道读取(17)字节:B发送到C:C 9
答案 0 :(得分:1)
代码有点令人困惑,因为你刚决定将所有内容都放在main()
中。一些辅助功能会有用,但我不是来判断它。
一些更重要的错误:
read(2)
,write(2)
,close(2)
,pipe(2)
和fork(2)
都可以返回错误。你永远不会检查,你应该。如果你这样做,你会发现close(2)
在大多数情况下都会返回EBADF
,因为你在一个循环中调用它,并且反复使用相同的文件描述符。例如,查看子A的代码。它关闭循环内的两个管道。一旦循环到达第二次迭代,close(2)
就会开始返回错误,因为您已经关闭了该描述符。read(2)
中保持活动状态并被阻止,因为管道(父进程)中仍有一个可以(但不会)写入的活动编写器。您必须在父级中关闭管道写入结束。在分配将使用它们的子项之前立即在父项中创建管道总是一个好主意,并在分叉后立即关闭父项中未使用的通道。儿童过程也是如此。每个孩子都应该读取一个类似于父对日志的循环中的管道。否则,您只需阅读一次并丢弃同时写入的其他消息。您必须继续读取,直到管道的写入通道被另一端关闭(这在read(2)
返回0时发生)。
子句中用于解析从管道读取的消息的循环条件应为i+2 < nbytes
,而不是i < pipe_size
- 当消息小于{{{}时,后者将超出范围1}}字节(非常可能)。更新条件也应该是pipe_size
,因为每次迭代消耗3个字节。
i+= 3
完全没用。
请在启用警告的情况下编译代码。你会看到你有一个从未使用的变量else continue;
。
儿童D应该从temp_char
而不是msg_B_D
读取数字。我相信这是一个复制/粘贴错误。
您无法通过管道发送空终止符。您正在执行此操作,因此,当您将其打印到输出时,您不会看到发送到日志的每条消息 - 请记住msg_B_C
只打印一个字符串,直到它看到终止空字符。相反,我建议您使用换行符将消息发送到日志,父母可以打印它而无需担心。
以下是解决所有这些错误的代码。我注释了睡眠以快速获得输出:
printf()
仍有很大的改进空间。您不需要这么多字符数组变量 - 请记住,父进程和子进程之间不共享内存。在写入之前,您可以使用1或2个缓冲区来打印中间消息。但似乎有效:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#define READ 0
#define WRITE 1
int main(int argc, char *argv[]) {
printf("\nWritten by Nawar Youssef\n");
int i, x=0, fd_log[2], fd_A_B[2], fd_B_C[2], fd_B_D[2], pipe_size=500;
char ch, msg_from_A[pipe_size], msg_to_B[pipe_size], msg_to_log[pipe_size],
msg_to_C[pipe_size], msg_to_D[pipe_size], msg_B_C[pipe_size], msg_B_D[pipe_size],
msg_from_log[pipe_size], msg_C_log[pipe_size], msg_D_log[pipe_size];
if (pipe(fd_log) < 0) {
fprintf(stderr, "%d: pipe(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (pipe(fd_A_B) < 0) {
fprintf(stderr, "%d: pipe(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
pid_t pid;
if ((pid = fork()) < 0) {
fprintf(stderr, "%d: fork(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (pid == 0) {
//child A
for (i = 0; i < 10; i++) {
x = (rand() % 2);
if (x == 1)
ch = 'C';
else
ch = 'D';
sprintf(msg_to_B, "%c %d", ch, i);
size_t to_write = strlen(msg_to_B);
if (write(fd_A_B[WRITE], msg_to_B, to_write) < 0) {
fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
sprintf(msg_to_log, "A sent to B: %c %d\n", ch, i);
to_write = strlen(msg_to_log);
if (write(fd_log[WRITE], msg_to_log, to_write) < 0) {
fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
}
exit(EXIT_SUCCESS);
}
if (close(fd_A_B[WRITE]) < 0) {
fprintf(stderr, "%d: close(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (pipe(fd_B_C) < 0) {
fprintf(stderr, "%d: pipe(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (pipe(fd_B_D) < 0) {
fprintf(stderr, "%d: pipe(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if ((pid = fork()) < 0) {
fprintf(stderr, "%d: fork(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (pid == 0) {
// Child B
int n_bytes;
while ((n_bytes = read(fd_A_B[READ], msg_from_A, sizeof(msg_from_A)-1)) > 0) {
size_t to_write;
for (i = 0; i+2 < n_bytes; i += 3) {
if (msg_from_A[i] == 'C') {
sprintf(msg_to_C, "%c %c", msg_from_A[i], msg_from_A[i+2]);
to_write = strlen(msg_to_C);
if (write(fd_B_C[WRITE], msg_to_C, to_write) != to_write) {
fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
sprintf(msg_to_log, "B sent to C: %s\n", msg_to_C);
to_write = strlen(msg_to_log);
if (write(fd_log[WRITE], msg_to_log, to_write) < 0) {
fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
//sleep(1);
} else if (msg_from_A[i] == 'D') {
sprintf(msg_to_D, "%c %c", msg_from_A[i], msg_from_A[i+2]);
to_write = strlen(msg_to_D);
if (write(fd_B_D[WRITE], msg_to_D, to_write) != to_write) {
fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
sprintf(msg_to_log, "B sent to D: %s\n", msg_to_D);
to_write = strlen(msg_to_log);
if (write(fd_log[WRITE], msg_to_log, to_write) < 0) {
fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
//sleep(5);
}
}
}
if (n_bytes < 0) {
fprintf(stderr, "%d: read(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
if ((pid = fork()) < 0) {
fprintf(stderr, "%d: fork(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (close(fd_B_C[WRITE]) < 0) {
fprintf(stderr, "%d: close(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (close(fd_B_D[WRITE]) < 0) {
fprintf(stderr, "%d: close(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (pid == 0) {
// Child C
int n_bytes;
while ((n_bytes = read(fd_B_C[READ], msg_B_C, sizeof(msg_B_C))) > 0) {
for (i = 0; i+2 < n_bytes; i += 3) {
if (msg_B_C[i] == 'C') {
sprintf(msg_C_log, "%c %c", msg_B_C[i], msg_B_C[i+2]);
sprintf(msg_to_log, "C sent to log: %s\n", msg_C_log);
size_t to_write = strlen(msg_to_log);
if (write(fd_log[WRITE], msg_to_log, to_write) != to_write) {
fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
}
}
}
if (n_bytes < 0) {
fprintf(stderr, "%d: read(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
if ((pid = fork()) < 0) {
fprintf(stderr, "%d: fork(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (pid == 0) {
// Child D
int n_bytes;
while ((n_bytes = read(fd_B_D[READ], msg_B_D, sizeof(msg_B_D))) > 0) {
for (i = 0; i+2 < n_bytes; i += 3) {
if (msg_B_D[i] == 'D') {
sprintf(msg_D_log, "%c %c", msg_B_D[i], msg_B_D[i+2]);
sprintf(msg_to_log, "D sent to log: %s\n", msg_D_log);
size_t to_write = strlen(msg_to_log);
if (write(fd_log[WRITE], msg_to_log, to_write) != to_write) {
fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
}
}
}
if (n_bytes < 0) {
fprintf(stderr, "%d: read(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
// Parent
if (close(fd_log[WRITE]) < 0) {
fprintf(stderr, "%d: close(): %s\n", __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
int n_bytes;
while ((n_bytes = read(fd_log[READ], msg_from_log, sizeof(msg_from_log)-1)) > 0) {
msg_from_log[n_bytes] = '\0';
printf("Log pipe reads (%d) bytes:\n%s", n_bytes, msg_from_log);
//sleep(1);
}
return 0;
}