Search code examples
clinuxforksystem-calls

fork() system call and while loop ( part 2)


Does anyone know why the printf("Type q to quit") line prints twice in the terminal when I run this code:

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

I would like the child to break from the loop and the parent to continue looping (to create new children). If I call wait() the execution ends after the first iteration regardless of whether I enter 'q' or not. Otherwise it works as expected but prints the "Type q to quit" line twice every time. Why does this happen?


Solution

  • You have three bugs. First, the carriage return that the user types, after whatever letter, counts as input. Assuming the user types only one character per line, your fgets calls will alternate between returning the character you care about, and returning '\n'. You need to read and discard characters until you reach the end of each line of input, on each iteration. This is what is causing the double printouts.

    Second, you need to test for 'q' in between reading from stdin and calling fork; right now, you fork once more after reading the 'q'. Right now this is invisible, but once the child process starts doing something useful it won't be.

    Third, this program will go into an infinite loop if the user types ^D at it, because you're not checking for EOF.

    Putting that together, corrected code looks like this. I've also fixed some style nits, and arranged so that the parent process exits immediately rather than dropping out of the for loop; this means that when control reaches the point marked with a comment, you know you're in a child process, rather than in the parent on its way out.

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