【问题标题】:fork() system call and while loop ( part 2)fork() 系统调用和 while 循环(第 2 部分)
【发布时间】:2023-03-07 11:16:01
【问题描述】:

有谁知道我运行这段代码时为什么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" 行。为什么会这样?

【问题讨论】:

    标签: c linux fork system-calls


    【解决方案1】:

    你有三个错误。首先,用户在任何字母之后键入的回车都算作输入。假设用户每行只输入一个字符,您的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;
    }
    

    【讨论】:

    • 您将c 声明为int 而不是char 的任何特殊原因?
    • 另外,while 循环的目的是什么?该程序应该在c 不等于EOF\n 的任何时候暂停并等待用户输入?
    • c 必须声明为int,因为EOF 是(故意)超出char 范围的值。而while循环的目的是丢弃初始getchar之后的多余用户输入,直到并包括第一个\n或EOF。试试看:它不会导致程序停止。
    • 明确一点,getchar() 只会在缓冲区为空时暂停并等待用户输入,否则每次调用时它都会从缓冲区中一次读取一个字符,对吧?
    • 对。而且,无需特殊准备(参见tcsetattr),当该程序在终端中运行时,stdin 缓冲区将始终包含至少一个完整的用户输入行,包括终止的'\n',除非用户强制EOF,也被处理。所以你不能陷入 while 循环。
    【解决方案2】:

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

       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
          }
    
       }
    

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

    【讨论】:

      【解决方案3】:

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

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

      请参阅例如:I am not able to flush stdin,了解“清除”您可以在此处使用的输入流的方法。

      【讨论】:

        猜你喜欢
        • 2013-10-24
        • 2015-07-16
        • 1970-01-01
        • 2015-11-08
        • 1970-01-01
        • 1970-01-01
        • 2013-10-09
        • 2011-10-03
        • 1970-01-01
        相关资源
        最近更新 更多