0

有谁知道为什么printf("Type q to quit")当我运行此代码时该行在终端中打印两次:

#include <stdio.h>
#include <unistd.h>

int main (int argc, char *argv[])
{
    char run[2];
    run[0]='a';
    int pid=0;
    while (run[0]!= 'q')    
    {
        printf("Type q to quit \n");
        fgets (run, 2, stdin);
  
        pid=fork();
        //wait();
        if(pid==0) { break;}
    }
}

我希望孩子从循环中中断,而父母继续循环(以创建新的孩子)。如果我wait()在第一次迭代后调用执行结束,无论我是否进入'q'。否则它会按预期工作,但"Type q to quit"每次都会打印两次。为什么会这样?

4

3 回答 3

2

你有三个错误。首先,用户在任何字母之后键入的回车都算作输入。假设用户每行只输入一个字符,您的fgets调用将在返回您关心的字符和返回之间交替'\n'。在每次迭代中,您需要读取并丢弃字符,直到到达每行输入的末尾。这就是导致双重打印输出的原因。

其次,您需要'q' 读取stdin和调用之间进行测试fork;现在,您在阅读完'q'. 现在这是不可见的,但是一旦子进程开始做一些有用的事情,它就不会了。

第三,如果用户键入该程序,该程序将进入无限循环^D,因为您没有检查 EOF。

把它们放在一起,更正的代码看起来像这样。我还修复了一些风格细节,并安排父进程立即退出而不是退出for循环;这意味着当控制到达标有注释的点时,您知道您处于子进程中,而不是在父进程中。

#include <stdio.h>
#include <unistd.h>

int
main(void)
{
    pid_t pid;
    int c;
    for (;;)
    {
        puts("Type q to quit");
        c = getchar();

        if (c == 'q')
            return 0;
        if (c == '\n')
            continue;
        while (c != EOF && c != '\n')
            c = getchar();
        if (c == EOF)
            return 0;

        pid = fork();
        if (pid == 0)
            break;
        else if (pid == -1)
        {
            perror("fork");
            return 1;
        }
    }

    /* control reaches this point only in child processes */
    return 0;
}
于 2013-10-23T18:10:35.890 回答
1

当您x<enter>在终端上键入时,您将收到(假设全部是普通编码)两个字节发送到您的进程:一个用于x,一个用于换行符。

您的fgets呼叫最多读取一个字节(因此,x),“立即”死亡的孩子的分叉,打印消息和呼叫fgetsfgets拿起它离开的地方:它读取换行符而不阻塞,你的代码无缘无故地再次分叉,然后循环回来。
此时输入流中没有任何内容,因此fgets等待 I/O。

例如,请参阅:我无法刷新标准输入以“清除”您可以在此处使用的输入流的方法。

于 2013-10-23T18:11:25.743 回答
0

您需要等待这些分叉的进程:

   int main (int argc, char *argv[])
   {
    char run[2];
    run[0]='a';
    int pid=0;
    int pids[256]; // should be enough.
    int j,i = 0;

    while (run[0]!= 'q')    
    {
        printf("Type q to quit \n");
        fgets (run, 2, stdin);

        pid=fork();
        //wait();
        if(pid==0) { break;}
        else { pids[i] = pid; i++; }
    }
    for (j = 0 ; j < i && pid != 0 ; j++)
       wait(pids[j]);
      if(pid == 0){
         // do something
      }

   }

您可以改为让孩子调用另一个函数(例如程序包装​​器,甚至是不可分叉版本中的自身)而不是中断。等待将保持不变,即仅在所有分叉都分叉后才等待。

于 2013-10-23T18:18:15.463 回答