c - 为什么程序挂在孩子与父母的沟通上?
问题描述
我试图了解为什么我的程序挂起。父程序将其读取的文件中的输入发送给子程序,子程序将其计算结果发送回其父程序。但是,我无法通过第二个管道发送回消息。从管道读取时,父级似乎挂起。
从其他帖子中,我读到似乎表明父母应该使用wait
or等待孩子完成waitpid
(在我的情况下,他们都不能解决我的问题)。
我通过添加打印声明注意到父母或孩子都没有完成..有人可以向我解释为什么会这样吗?
为什么这不起作用?
int main(int argc,char** argv) {
char buffer[1];
int i;
int fd1[2]; int fd2[2];
pipe(fd1); pipe(fd2);
pid_t pid;
// FIRST PROCESS.
// -------------------
pid = fork();
if(pid == 0) {
int cnt;
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
for (i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
while(read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {
fprintf(stderr, "( %s )", buffer);
cnt = cnt + *buffer - 48;
}
write(STDOUT_FILENO, &cnt, sizeof(cnt));
exit(0);
}
// PARENT.
// ------------------------
int file = open(argv[1], O_RDONLY);
// READ THE FILE.
while(read(file, buffer, 1) > 0) {
if (48 <= *buffer && *buffer <= 57) {
// PIPE TO CHILD.
write(fd1[1], buffer, 1);
}
}
// WAIT FOR CHILD TO FINISH SENDING BACK.
// int status = 0;
// waitpid(pid, &status, 0);
// THIS BLOCK DOESN'T RESOLVE ANYTHING. IT HANGS AT WAIT OR WAITPID.
// **** THIS IS THE PART WHERE IT DOESN'T WORK.
while(read(fd2[0], buffer, 1) > 0) {
fprintf(stderr, "RESULT : %s", buffer);
}
// CLOSING PIPES
for (i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
close(file);
exit(0);
}
解决方案
您没有足够快地关闭父级中的足够文件描述符。
经验法则:如果您
将管道的一端连接到标准输入或标准输出,请
尽快dup2()
关闭返回的两个原始文件描述符
。pipe()
特别是,您应该在使用任何
exec*()
函数系列之前关闭它们。
如果您使用
dup()
或
复制描述符,该规则也fcntl()
适用F_DUPFD
现在,您的子进程完美地遵循了 RoT。但是父进程的必然结果是它们需要关闭管道的未使用端,并且它们必须关闭它们用来向该管道的读取端发出 EOF 信号的管道的写入端。这是您的代码失败的地方。
可以说,在读取文件之前,父进程应该关闭它用来写入子进程的管道的读取端,并且它应该关闭它用来从子进程读取的管道的写入端。
然后,在读取整个文件之后,它应该关闭管道的写入端到子节点,然后再进入“从子节点读取”循环。该循环永远不会终止,因为父级仍然打开管道的写入端,因此有一个进程可以(但不会)写入管道。
此外,由于子进程将整数字节写入管道,父进程应该读取整数字节。char buffer[1];
与%s
格式一起使用是没有意义的;您需要字符串的空终止符,并且单个字符缓冲区不能同时保存空字节和任何数据。
除了各种其他改进(例如,'0'
而不是48
),您最终可能会得到:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
int fd1[2];
int fd2[2];
char buffer[1];
pipe(fd1);
pipe(fd2);
pid_t pid = fork();
if (pid == 0) {
int cnt = 0;
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
for (int i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
while (read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {
fprintf(stderr, "(%c)", buffer[0]); // Changed
cnt = cnt + buffer[0] - '0';
}
putc('\n', stderr); // Aesthetics
write(STDOUT_FILENO, &cnt, sizeof(cnt));
exit(0);
}
int file = open(argv[1], O_RDONLY);
if (file < 0) {
fprintf(stderr, "failed to open file '%s' for reading\n", argv[1]);
exit(EXIT_FAILURE);
}
close(fd1[0]); // Added
close(fd2[1]); // Added
while (read(file, buffer, sizeof(buffer)) > 0) {
if ('0' <= buffer[0] && buffer[0] <= '9') {
write(fd1[1], buffer, sizeof(buffer));
}
}
close(file); // Moved
close(fd1[1]); // Added
// Rewritten
int result;
while (read(fd2[0], &result, sizeof(result)) == sizeof(result)) {
fprintf(stderr, "RESULT : %d\n", result);
}
close(fd2[0]); // Added
// Close loop removed
return 0;
}
如果将其存储在文件中pipe71.c
并编译,则在运行时会得到以下输出:
$ ./pipe71 pipe71.c
(2)(0)(1)(2)(2)(2)(1)(1)(2)(0)(0)(2)(1)(0)(2)(2)(1)(0)(2)(1)(2)(0)(0)(0)(0)(0)(1)(0)(1)(1)(0)(2)(1)(0)(0)(0)(0)(9)(1)(1)(1)(1)(2)(0)(2)(0)(0)
RESULT : 49
$ ./pipe71 pipe71
(0)(0)(8)(0)(0)(2)(2)(0)(8)(1)(1)(5)(1)(1)(1)(1)(5)(1)(1)(1)(8)(5)(1)(9)(8)(5)(1)(1)(0)(4)(4)(4)(6)(0)(2)(8)(0)(0)(0)(2)(7)(1)(3)(8)(3)(0)(4)(3)(0)(4)(9)(0)(0)(0)(0)(7)(1)(9)(8)(1)(3)(0)
RESULT : 178
$
推荐阅读
- java - 谷歌云功能(Alpha),用于 gradle 的 Spring 云?
- python - 无法在 chromebook 上安装 auto-py-to-exe
- nginx - Tableau 使用 nginx 规则将 https://tabserver.com/#/signin 重定向到 https://myserver.com
- node.js - npm 错误!当我运行 vue 创建项目时代码 EINTEGRITY
- python - 条形图+折线图合并而不是并排?海博恩
- python - 如何合并字典中的值而不重复
- python - 如何根据资源使用情况跟踪和终止池的进程?
- c++ - OpenXR - 未解析的外部符号
- javascript - 使用 JavaScript 将另一个 HTML 文件形式的数据写入一个 HTML 文件
- c# - 为什么 HashAlgorithm.ComputeHash 从相同的数据返回不同的值?