Lucian Enache
Lucian Enache

Reputation: 2530

fork() used as for cycle condition generates race condition?

alright so here is an excersise we had on today's exam of operating systems

given this program in C

#include <unistd.h>
#include <stdlib.h>

int  main(){
    int i;
    for(i=2;i>=1&&!fork();i--)
            printf("%d\n",i);

exit(EXIT_SUCCESS);
}

executing it give this output :

2
1

What I've found ambiguous is how the index "i" is managed by the program and what would be the correct order in wich the processes execute (if there is an order or it's a random order estabilished by the scheduler ?)

And why there are only 2 and 1 printed - My assumptions are :

(father executes the "for" only for i=2, prints 2 then exits ?)
(the first child starts from i=1 forks a child prints 1 exits ?)

At this point my question is :

Is there a second child forked that doesn't enter the for ? and is 2 printed by father and 1 by the first child ?

And one final thing :

How would you rewrite this fork-conditioned-for to get it more readable (eg. an if-statement)

Upvotes: 1

Views: 1827

Answers (2)

Shahbaz
Shahbaz

Reputation: 47583

When you fork, it's like all the program is copy-pasted and two identical process start from that point (although the father gets the PId of the child as return value of fork and the child gets zero)

So what happens to i is that it will have the exact same value in the child as it had in parent at the time of fork.

So, when i is 2:

for parent: i>=1 && !fork()  <-- fails
for child1: i>=1 && !fork()  <-- succeeds

So, 2 is printed by the child of the original program.

Now the parent has exited and child1 is running. It executes i-- and now i is 1:

for child1:   i>=1 && !fork()  <-- fails
for child1.1: i>=1 && !fork()  <-- succeeds

So again, child1 exits and its child, child1.1 goes in the for and prints 1. Then it executes i-- and i becomes 0 which fails i>=1 and because of short-circuit evaluation, fork is not executed. This child1.1 also exits.

To answer your last question, this is usually how fork is written:

pid_t pid = fork();
if (pid)
{
    // in parent
}
else
{
    // in child
}

Now all you need is a for loop:

#include <unistd.h>
#include <stdlib.h>

int  main(){
    int i;
    for(i=2;i>=1;i--)
        if (fork())
            break;              /* parent wants to break */
        else
            printf("%d\n",i);   /* child prints something */

    exit(EXIT_SUCCESS);
}

Upvotes: 2

caf
caf

Reputation: 239321

The for() loop checks the condition before every iteration of the loop, including the first one. This means that the original parent process starts off with i = 2 and evaluates i >= 1 && !fork(). The fork() returns a non-zero PID to the parent and zero to the child, so the !fork() is false in the parent and the loop is not executed: the parent exits.

In the first child, !fork() is true, so the loop executes. It prints 2, then decrements i. It now executes i >= 1 && !fork() - again, !fork() is false in the first child, so it exits the for loop and the program.

In the second child (child of the first child), !fork() returns true so the loop executes. It prints 1, then decrements i. It now evaluates i >= 1 && !fork(). Because i >= 1 evalutes to false, the fork() is never called; it exits the loop and the program. All processes have exited at this point.

You can rewrite the for() loop as a while() loop, and make the short-circuit evaluation of && explicit:

int  main()
{
    int i;

    i = 2;
    while (i >= 1) {
        if (fork() != 0)
            break;

        printf("%d\n", i);
        i--;
    }

    exit(EXIT_SUCCESS);
}

Upvotes: 2

Related Questions