管道上的持久化execvp?

3
我正在为我的操作系统课程(Posix&C)做一个任务,构建一个迷你shell,并且我不知道如何解决以下问题:
我的迷你shell必须接受两个命令,例如ls | grep a。为此,我创建了一个大小为2的管道和一个子进程。子进程关闭所有需要关闭的内容并打开所有需要打开的内容(标准/管道的输入和输出)。然后使用execvp执行“ls”,我确信这很好用。之后,父进程关闭和打开输入和输出(我确定我做得很好),然后执行grep a
现在,问题是进程grep a永远不会完成。例如,tail -1也是如此。但是对于head -1,它确实有效。我认为发生这种情况是因为由父进程执行的greptail等命令正在等待更多的输入,即使子进程已经完成其操作。ls | grep a会产生正确的输出,显示在控制台上(管道的输出被设置为默认输出),但是,正如我所说,grep a没有完成。
那么,我的问题是:如何通知父进程管道已经完成写入,以便例如完成执行grep a
谢谢。
以下是代码:
[fd是管道,它在代码中之前初始化。如果您看到任何不一致的地方,请告诉我;我已经清理了代码,这只是有问题的部分,如您所见。]
   int fd[2];
   pipe(fd);

   if ((pid = fork()) != -1){ 
     if(pid == 0){         /*Child executing */
        close(fd[0]);
        close(1);
        dup(fd[1]);
        close(fd[1]);
        execvp(argvv[0][0], argvv[0]); /* Here's stored the first instruction */

      } else{             /* Parent executing */
        wait(&status);
        close(fd[1]);
        close(0);
        dup(fd[0]); 
        close(fd[0]);
        execvp(argvv[1][0], argvv[1]); /* Here's stored the second instruction */
       }
   }  

grep的行为不像“tail -f”,您可以使用工作shell(如bash)进行检查。因此问题在其他方面。您能否发布运行“ls”和“grep”的代码? - fghj
请包含调用pipe(fd);的代码。 - caf
你粘贴的代码运行正常。 - caf
1个回答

9
如果在ls退出后,grep仍然在运行,则表明您尚未关闭所有需要关闭的管道。
特别是,与grep进程连接的读端的写端仍在另一个进程中保持打开状态。要了解更多信息,您需要展示您的代码。
您粘贴的代码(如下所示)可以正常工作。两个子进程都可以正常退出。
这意味着当您创建此简化版本时,已经排除了存在问题的代码 - 也许在pipe()调用和此fork()之间还有另一个fork()
#include <unistd.h>
#include <sys/wait.h>

int main()
{
   pid_t pid;
   char *argvv[2][3] = { { "ls", 0, 0}, { "grep", "a", 0 } };
   int status;

   int fd[2];
   pipe(fd);

   if ((pid = fork()) != -1) {
     if(pid == 0){         /*Child executing */
        close(fd[0]);
        close(1);
        dup(fd[1]);
        close(fd[1]);
        execvp(argvv[0][0], argvv[0]); /* Here's stored the first instruction */

      } else{             /* Parent executing */
        wait(&status);
        close(fd[1]);
        close(0);
        dup(fd[0]);
        close(fd[0]);
        execvp(argvv[1][0], argvv[1]); /* Here's stored the second instruction */
       }
   }

   return 0;
}

是的,我确实有另一个 fork()。实际上,在这里父进程是一个子进程。非常感谢。我想我必须关闭“真正”的父进程的管道。我会检查的 ;) - Sori
问题在于我在创建第一个子进程之前就初始化了 pipe(fd)。这会在父子进程之间创建一个管道,但这是无用的。我只想让它在子孙进程之间使用。 - Sori
@Sori:没错 - 每次调用 fork() 都会复制管道的两端,如果它们仍然打开,则需要在父进程和子进程中最终关闭它们。 - caf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接