1

我正在为家庭作业编写自己的外壳,并且遇到了问题。

cat scores | grep 100我的 shell 程序从控制台获取输入并按预期打印输出,但grep命令不会终止,我可以看到它从ps命令无限运行。


编辑 - 关闭 fds 时出错。现在 grep 命令未执行,控制台输出为 -

grep:(标准输入):错误的文件描述符


我正在从控制台读取命令数量并创建必要的管道并将它们存储在二维中int array fd[][],然后再分叉第一个进程。

fd[0][0]将包含第一个管道的读取端,fd[0][1]并将包含第一个管道的写入端。fd[1][0]将包含第二个管道的读取端,fd[1][1]并将包含第二个管道的写入端,依此类推。

每个新进程将stdin其管道的读取端复制到前一个进程,并将stdout其管道的写入端复制到下一个进程。

以下是我的功能:

void run_cmds(char **args, int count,int pos)
{
    int pid,status;
    pid = fork();
    if ( pid == 0 )
    {
        if(pos != 0) dup2(fd[pos-1][0],0); // not changing stdin for 1st process
        if(pos != count) dup2(fd[pos][1],1); //not changing stdout for last process
        close_fds(pos);
        execvp(*args,args);
    }
    else
    {
        waitpid(pid,&status,0);
        count--;
        pos++;
        //getting next command and storing it in args
        if(count > 0)
            run_cmds(args,count,pos);
        }
    }
}
  • args将包含命令的参数。
  • count是我需要创建的命令数。
  • pos是命令在输入中的位置

我无法弄清楚问题所在。在此之前,我对硬编码值使用了相同的方法并且它正在工作。

我对 / 的理解/实现缺少什么dup2fork为什么命令无限等待?

任何输入都会有很大帮助。这两天被这个问题困扰!


编辑:close_fds() 函数如下 - 对于任何进程,我正在关闭链接进程的两个管道。

void close_fds(int pos)
{
 if ( pos != 0 )
        {
        close(fd[pos-1][0]);
        close(fd[pos-1][1]);
        }
 if ( pos != count) 
        {
        close(fd[pos][0]);  
        close(fd[pos][1]);
        }
}
4

2 回答 2

3

首次诊断

你说:

每个新进程都将其管道的读取端与前一个进程复制其标准输入,并将其管道的写入端与下一个进程复制其标准输出。

你没有提到魔法词close()

您需要确保在使用dup()dup2()将其连接到标准输入时关闭每个管道的读取和写入端。这意味着使用 2 个管道,您可以调用 4 次close().

如果您没有正确关闭管道,正在读取的进程将不会获得 EOF(因为有一个进程,可能是它自己,可以写入管道)。对close().


close_fds()在 dup2 通话后打电话。该函数将遍历fd[][2]数组并对数组close()中的每一个进行调用fd

好的。这很重要。这意味着我的主要诊断可能不是正确的。

二次诊断

其他几个项目:

  1. 如果返回(这意味着它失败),您应该有execvp()报告错误并退出的代码。execvp()

  2. 你不应该立即打电话waitpid()。应该允许管道中的所有进程同时运行。您需要启动所有进程,然后等待最后一个进程退出,在其他进程死亡时清理它们(但不必担心管道中的所有内容在继续之前退出)。

    如果您确实在启动第二个命令之前强制第一个命令完整执行,并且如果第一个命令生成的输出超出管道的容量,您将遇到死锁 - 第一个进程无法退出,因为它被阻止写入,并且第二个进程无法启动,因为第一个进程尚未退出。中断和重新启动以及宇宙的终结都会稍微粗略地解决问题。

  3. 在递归之前,您会减少count和增加pos。那可能很糟糕。我认为你应该增加pos.

第三次诊断

更新显示close_fds()功能后。

我回到“关闭管道存在问题”(尽管等待和错误报告问题仍然是问题)。如果管道中有 6 个进程,并且在任何进程运行之前创建了所有 5 个连接管道,则每个进程都必须关闭所有 10 个管道文件描述符。

另外,不要忘记,如果管道是在父 shell 中创建的,而不是在执行管道中命令之一的子 shell 中创建的,那么父 shell 必须在等待命令完成之前关闭所有管道描述符。

请制作一个 MCVE(如何创建一个最小、完整和可验证的示例?)或 SSCCE(简短、自包含、正确的示例)——两个名称和链接用于相同的基本思想。

您应该创建一个程序来制造您传递给调用的代码的数据结构run_cmds()cat score | grep 100也就是说,您应该创建解析代码创建的任何数据结构,并显示为“ ”命令创建管道的代码。

我不再清楚递归是如何工作的——或者它是否在您的示例中被调用。我认为它是未使用的,实际上在您的示例中,这可能也是因为您最终会多次执行相同的命令,AFAICS。

于 2014-10-11T00:39:36.503 回答
2

grep不终止的最可能原因:

  • 您没有waitpid使用正确的 PID 调用(即使您的代码中有这样的调用,它也可能由于某种原因无法执行),因此grep成为僵尸进程。也许您的父 shell 进程首先等待另一个进程(无限期,因为另一个进程永远不会终止),并且它不会waitpid使用grep. 你可以在if is a zombieZ的输出中找到。psgrep

  • grep在其标准输入(fd 0)上未收到 EOF,某些进程正在保持其管道的写入端打开。fd您是否在父 shell 进程中关闭了数组中的所有文件描述符?如果没有在所有地方关闭,grep则永远不会收到 EOF,并且永远不会终止,因为它将被阻塞(永远)等待其标准输入上的更多数据。

于 2014-10-10T23:51:19.427 回答