尝试终止时使用管道的 C 程序挂起


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#define MSGSIZE 64
char msgbuf[MSGSIZE];
int main() {
int p1[2];
int p2[2];
int nread;
int choice = 0;
pid_t child_a, child_b;
if (pipe(p1) == -1) {
printf("error in creating pipen");
exit(-1);
}
if (pipe(p2) == -1) {
printf("error in creating pipen");
exit(-1);
}
child_a = fork();
if (child_a == 0) {
while (1) {
dup2(p1[0], STDIN_FILENO);
read(STDIN_FILENO, msgbuf, MSGSIZE);
printf("%d receives message: %sn", getpid(), msgbuf);
close(p1[0]);
close(p1[1]);
}
} else {
child_b = fork();
if (child_b == 0) {
while (1) {
dup2(p2[0], STDIN_FILENO);
read(STDIN_FILENO, msgbuf, MSGSIZE);
printf("%d receives message: %sn", getpid(), msgbuf);
close(p2[0]);
close(p2[1]);
}
} else {
while (1) {
printf("<child_to_receive_msg> <message>n");
scanf("%d %s", &choice, msgbuf);
switch (choice) {
case 1:
usleep(250);
write(p1[1], msgbuf, MSGSIZE);
break;
case 2:
usleep(250);
write(p2[1], msgbuf, MSGSIZE);
break;
default:
printf("Process does not exist");
break;
case -1:
close(p1[0]);
close(p2[0]);
printf("parent waiting");
wait(NULL);
exit(0);
}
}
}
}
return 0;
}

在上面的程序中,我有一个父进程创建属于同一父级的两个子进程。用户写入父进程,该父进程通过管道传输要由子项 1 或子项 2 读取的消息。除非用户输入 -1,否则它会一直这样做。

问题是我的switch语句中的情况没有得到执行,而是程序挂起了。我想我的管道在正确的地方关闭了。

您需要向子进程发送一些信号以通知然后终止,然后再等待它们退出。您应该定义一些预定义的消息,这意味着子级终止的时间。检查下面的代码。这里预定义的消息是"-1"。您应该选择自己的,它不会与应用程序的实际数据冲突。

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define MSGSIZE 64
char msgbuf[MSGSIZE];
int main() {
int p1[2];
int p2[2];
int nread;
int choice = 0;
pid_t child_a, child_b;
if (pipe(p1) == -1) {
printf("error in creating pipen");
exit(-1);
}
if (pipe(p2) == -1) {
printf("error in creating pipen");
exit(-1);
}
child_a = fork();
if (child_a == 0) {
while (1) {
dup2(p1[0], STDIN_FILENO);
read(STDIN_FILENO, msgbuf, MSGSIZE);
printf("%d receives message: %sn", getpid(), msgbuf);
close(p1[0]);
close(p1[1]);
if (strcmp(msgbuf, "-1") == 0) { // check if time to end
break;
}
}
} else {
child_b = fork();
if (child_b == 0) {
while (1) {
dup2(p2[0], STDIN_FILENO);
read(STDIN_FILENO, msgbuf, MSGSIZE);
printf("%d receives message: %sn", getpid(), msgbuf);
close(p2[0]);
close(p2[1]);
if (strcmp(msgbuf, "-1") == 0) {  // check if time to end
break;
}
}
} else {
while (1) {
printf("<child_to_receive_msg> <message>n");
scanf("%d %s", &choice, msgbuf);
switch (choice) {
case 1:
usleep(250);
write(p1[1], msgbuf, MSGSIZE);
break;
case 2:
usleep(250);
write(p2[1], msgbuf, MSGSIZE);
break;
default:
printf("Process does not existn");
break;
case -1:
strcpy(msgbuf, "-1");
write(p1[1], msgbuf, MSGSIZE); // send message to end
close(p1[0]);
close(p2[0]);
printf("parent waitingn");
wait(NULL);
exit(0);
}
}
}
}
return 0;
}

首先,您需要开始执行错误检查。检查所进行的调用的手册页。在代码中添加检查以检测错误。当他们返回错误时,请使用perrorexit(EXIT_FAILURE);


其次,您需要开始关注readwrite返回的值,因为它们可能低于预期。这些需要在循环中调用。

例如,对于read,您将使用以下方法:

#include <errno.h>
#include <limits.h>
// Returns the number of bytes read.
// EOF was reached if the number of bytes read is less than requested.
// On error, returns -1 and sets errno.
ssize_t read_fixed_amount(int fd, char *buf, size_t size) {
if (size > SSIZE_MAX) {
errno = EINVAL;
return -1;
}
ssize_t bytes_read = 0;
while (size > 0) {
ssize_t rv = read(fd, buf, size); 
if (rv < 0)
return -1;
if (rv == 0)
return bytes_read;
size -= rv;
bytes_read += rv;
buf += rv;
}
return bytes_read;
}

它将像这样使用:

ssize_t bytes_read = read_fixed_amount(fd, buf, size);
if (bytes_read < 0) {
perror("read");
exit(EXIT_FAILURE);
}
if (bytes_read == 0) {
printf("EOF reachedn");
exit(EXIT_SUCCESS);
}
if (bytes_read != size) {
fprintf(stderr, "read: Premature EOF.n");
exit(EXIT_FAILURE);
}

第三,只有在管道写入端的所有文件描述符都已关闭后,从管道读取才会返回 EOF。

在分叉之后,父母应该做

close(p1[0]); 
close(p2[0]);

分叉后,孩子 1 应该做

close(p1[1]);
close(p2[0]);
close(p2[1]);

分叉后,孩子 2 应该做

close(p1[0]); 
close(p1[1]);
close(p2[1]);

第四,有这个怪物:

while (1) {
dup2(p1[0], STDIN_FILENO);
read(STDIN_FILENO, msgbuf, MSGSIZE); 
...
close(p1[0]); 
close(p1[1]);
}

真?无限循环。尝试反复使 STDIN 成为 p1[0] 的重复。封闭描述符的重复。

这应该出现在循环之前:

dup2(p1[0], STDIN_FILENO);
close(p1[0]);

或者您可以跳过这两个电话,只需从p1[0]而不是STDIN_FILENO中读取。

至于无限循环,它回到了第二点。检查read返回的值。


第五,你只等一个孩子完成,但有两个孩子要等待。您需要wait两次电话。

最新更新