我尝试使用管道将一些数据发送到子进程。分叉后,我调用execve,它可以正常工作,但我仍然无法从管道接收任何字节。我不知道我错过了什么。我尝试在不使用execve的情况下构建相同的程序,它可以正常工作。
家长代码:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include <sys/wait.h>
#include <errno.h>
#include <time.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
char *args_0[] = {"./child.out", "", (char *)0};
struct INFO
{
int a;
int b;
int c;
};
pid_t child_pid;
int i, j, status;
int main()
{
struct INFO *info;
ssize_t num_bytes;
char buf[30];
int my_pipe[2];
info = malloc(sizeof(struct INFO));
if (pipe(my_pipe) == -1)
{
perror("error on pipe");
};
for (i = 0; i < 2; i++)
{
write(1, "fork\n", 5);
switch (fork())
{
case -1:
write(1, "Error1\n", 7);
exit(EXIT_FAILURE);
case 0:
write(1, "forked\n", 7);
if (execve(args_0[0], args_0, NULL) == -1)
{
write(1, "Error\n", 6);
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
break;
default:
break;
}
}
info->a = 10;
info->b = 20;
info->c = 30;
close(my_pipe[0]);
for (size_t i = 0; i < 2; i++)
{
write(my_pipe[1], info, sizeof(struct INFO));
}
close(my_pipe[1]);
write(1, "writed\n", 7);
/* Now let's wait for the termination of all kids */
while ((child_pid = wait(&status)) != -1)
{
/*printf("PARENT: PID=%d. Got info of child with PID=%d, status=0x%04X\n", getpid(), child_pid,status);*/
}
exit(EXIT_SUCCESS);
}
字符串
儿童代码:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include <sys/wait.h>
#include <errno.h>
#include <time.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
struct INFO
{
int a;
int b;
int c;
};
int main()
{
struct INFO* info;
ssize_t num_bytes;
char buf[30];
int my_pipe[2];
if (pipe(my_pipe) == -1)
{
perror("error on pipe");
};
close(my_pipe[1]);
num_bytes = read(my_pipe[0], info, sizeof(struct INFO));
if (num_bytes >= 0)
{
bzero(buf, 30);
snprintf(buf, 30, "num byte %ld\n",num_bytes);
write(1, buf, 30);
bzero(buf, 30);
snprintf(buf, 30, "%d %d %d\n", info->a, info->b, info->c);
write(1, buf, 30);
}
close(my_pipe[0]);
write(1, "pausing\n", 8);
pause();
}
型
我应该得到
10 20 30
但我得到了
0 0 0
1条答案
按热度按时间3pmvbmvn1#
你的代码中的主要问题是,父级和子级都打开了两个独立的管道,它们不会以任何方式相互干扰。因此,父级写入一个从未被读取的管道,而子级从另一个从未被写入任何内容的管道读取。
首先,这是设计缺陷的结果。如果两个进程紧密相关,需要从同一个匿名管道中读取数据,那么它们(通常)应该托管在同一个应用程序中(也就是说,你可以fork,但不是通过
exec[...]
家族的任何函数启动另一个应用程序,而是直接执行子进程)。一个例外是,当你想通过标准IO与第三方应用程序通信时;然后你会创建 * 两个 * 管道,fork,
dup2
相应的管道结束于标准IO文件描述符:字符串
并关闭子进程中的所有管道末端(相关的管道末端已经被复制了!),而在父进程中只有两个未使用的管道末端,
in_pipe[1]
和out_pipe[0]
。注意:上面的命名反映了父进程的观点(它仍然会重用管道),因此看起来从in到out的Map是冲突的,反之亦然。实际上,你也可以在你的双应用程序场景中使用这种方法,子应用程序将使用标准输入/输出与父应用程序进行通信(
printf
,scanf
,puts
都可以使用,但read(STDIN_FILENO, ...)
和write(STDOUT_FILENO, ...)
也可以用于二进制数据)。最后,如果您不能或不想将文件描述符复制到标准IO,(也许你需要另一个目的),那么你需要让子进程知道父进程的文件描述符。(您将获得父进程映像的副本,其中只有调用
fork
的结果不同),然而,所有这些信息在调用exec[...]
家族的任何函数时都会丢失,其中包含当前进程的映像的映像完全被要加载的应用程序之一替换。(尽管文件描述符保持打开,这就是为什么复制标准IO工作,见上文,工作)。有几种方法,包括命令行参数,环境变量或临时文件-其中提到的第一个对我来说似乎是最狭窄的一个,例如:
型
然后在子进程中,将命令行参数解析回整数,并使用这些参数进行阅读和写入(跳过与创建自己的管道相关的所有部分)。(不完整)示例演示了双向通信;两个方向中的每一个都需要两个单独的管道(父到子和子到父);当然,对于单向通信,您可以删除不必要的管道。
请注意,您的代码在子级中显示了另一个致命错误,在父级中显示了内存泄漏:在子元素中,您为数据定义一个指针(
struct INFO* info;
),但你从来没有给它分配任何有意义的值,而是将read
分配给未初始化的指针,产生未定义的行为。(可能是程序崩溃,但也可能发生其他任何事情,例如程序显示出一些不合理的行为,可能在一些看起来完全不相关的地方,甚至-看起来-运行良好),而在父对象中,你不需要再次free
你之前malloc
ed的内存。你可以通过简单地使用本地分配的对象来避免这两种情况:型
最后一点:这里提供的所有代码都是完全未经测试的;如果你发现一个bug,请自行修复。