gpt4 book ai didi

c - C 中通过管道重定向 stdin 和 stdout 适用于外部程序,但不适用于递归调用

转载 作者:行者123 更新时间:2023-11-30 16:24:42 25 4
gpt4 key购买 nike

我正在尝试通过 C 中的 stdin 和 stdout 的管道重定向与 fork 子进程进行通信。我已经设法让它适用于在子进程中执行的 shell 命令(例如 ls )。但是,我无法递归执行相同的程序并通过管道将输出(由 printf()fprintf() 打印到 stdout ,...)重定向到父进程(在此测试中)到父级的 stdout ),尽管这对于 ls 效果很好。或类似的命令。

以下是我尝试解决此问题的方法:

  • 我创建了一个管道,读端是给父进程的,子进程应该写到写端。
  • 进程 fork ,两个进程分别关闭未使用的一端。
  • 管道的写入端被重定向到STDOUT_FILENO并关闭
  • 子进程递归执行程序(称为./to2)

如上所述,如果我执行 ls ,这确实有效。在子进程中,但如果我尝试递归调用同一程序则不会。这是我的测试程序,我试图让它发挥作用:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <netdb.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <fcntl.h>


static void usage(void){
fprintf(stderr,"RIP");
exit(EXIT_FAILURE);
}

int main(int argc, char *argv[]){
if(argc > 1){
dprintf(STDOUT_FILENO,"Please work\n");
printf("\n THIS IS A MESSAGE FROM THE CHILD \n");
fputs("Pretty Please!\n",stdout);
fflush(stdout);
exit(EXIT_SUCCESS);
}
int p1[2];
if(-1 == pipe(p1)) {
fprintf(stderr,"pipe\n");
fprintf(stderr,"%s\n",strerror(errno));
usage();
}
int f = fork();
if(f == 0){
close(p1[0]);
if(dup2(p1[1],STDOUT_FILENO) < 0){
fprintf(stderr,"dup2\n");
usage();
}
close(p1[1]);

//I want this to work:
//execlp("./to2", "./to2", "-e");

//This works fine:
execlp("ls", "ls");

exit(EXIT_SUCCESS);
} else if (f == -1) {
usage();
} else {
close(p1[1]);
int w = -1;
if(-1 == wait(&w)) usage();
char b[12];
memset(b,0,12);
read(p1[0],&b,12);

char reading_buf[1];

while(read(p1[0], reading_buf, 1) > 0){
write(1, reading_buf, STDOUT_FILENO);
}
close(p1[0]);
}
}

出于测试目的,使用附加参数递归调用该函数,而在不使用附加参数的情况下调用父程序(因此是 if(argc>1) )。在最终的程序中,通过其他方式避免了无限递归。

我是不是理解错了什么?我很困惑,唯一不起作用的是重定向我自己的输出程序...

提前非常感谢您,非常感谢任何帮助或想法。

最佳答案

主要问题正如评论中所概述的那样 - 您没有调用 execlp()正确(也不是替代方案中的 ls)。您必须将这些函数调用的最后一个参数设置为显式空指针,如以下代码所示,这是问题中内容的大部分经过轻微编辑的版本:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

static void usage(void)
{
fprintf(stderr, "RIP\n");
exit(EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
if (argc > 1)
{
dprintf(STDOUT_FILENO, "Please work\n");
printf("THIS IS A MESSAGE FROM THE CHILD\n");
fputs("Pretty Please!\n", stdout);
fflush(stdout);
exit(EXIT_SUCCESS);
}
int p1[2];
if (-1 == pipe(p1))
{
fprintf(stderr, "pipe: %s\n", strerror(errno));
usage();
}
int f = fork();
if (f == 0)
{
close(p1[0]);
if (dup2(p1[1], STDOUT_FILENO) < 0)
{
fprintf(stderr, "dup2: %s\n", strerror(errno));
usage();
}
close(p1[1]);
execlp(argv[0], argv[0], "-e", (char *)0);
fprintf(stderr, "failed to exec %s again\n", argv[0]);
exit(EXIT_FAILURE);
}
else if (f == -1)
{
usage();
}
else
{
close(p1[1]);
char b[13];
memset(b, 0, 13);
if (read(p1[0], &b, 12) < 0)
{
fprintf(stderr, "Failed to read from pipe (%s)\n", strerror(errno));
exit(EXIT_FAILURE);
}
int len = strcspn(b, "\n");
printf("M1 [%.*s]\n", len, b);

char reading_buf[1];

while (read(p1[0], reading_buf, 1) > 0)
{
write(1, reading_buf, STDOUT_FILENO);
}
close(p1[0]);
int w = -1;
if (-1 == wait(&w))
usage();
}
return 0;
}

应强调两个重要的变化:

  • 此代码回显第一行数据 - 由 dprintf() 写入的数据 - 而原始代码只是读取它并丢弃它。
  • wait() 调用是在输入之后,而不是之前。如果子进程要写入的数据多于一组固定消息,则它可能会阻塞等待父进程读取某些数据,而父进程则被阻塞等待子进程退出。这将陷入僵局。

usage() 函数的命名不恰当——它没有报告如何运行程序。如果子进程执行 execlp() 失败,我也会以失败状态退出,而不是成功。

在特殊情况下,wait() 调用可能会报告除 fork 子进程之外的某个子进程的退出状态。通常最好使用循环来收获此类子项。然而,所需的情况非常特殊 - 使用 exec*() 函数启动父进程的进程必须先前创建了一些它没有等待的子进程,以便它们被父进程继承。父进程(因为 PID 在 exec*() 调用中不会改变)。

关于c - C 中通过管道重定向 stdin 和 stdout 适用于外部程序,但不适用于递归调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53586021/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com