Thread: Understanding fork() example

  1. #1
    Registered User
    Join Date
    Sep 2011
    Posts
    16

    Understanding fork() example

    Hello all! I have been trying to figure out how this works for the past 5 hours and I cannot figure out exactly what is going on. I understand that it will begin by printing out 20 and then 30 for the first problem. At this point there will be a total of 4 processes after the loop has completed. I do not understand how it works after this. I don't understand how the second problem works either.

    I have been reading countless posts after searching in attempt to understand how fork() works, but I cannot grasp it for some reason. It is completely frustrating. I would greatly appreciate any help you guys could offer me.

    Code:
    #include<stdio.h>
    main()
    {
    int x, y, count;
    count =1;  x=10; y=10;
    
       while (count < 3)
       {
      if(x != 0) {
        x = fork(); y = y + 10;}
      else{
        x  = fork(); y = y+50;}
      printf(“\n y = %d”, y);
      count = count + 1;
       }
    }
    Code:
    #include<stdio.h>
    main()
    {
    
    int p1=1, p2=2, p3=3;
    p1 = fork();
    if(p2>0) p2 = fork();
    if(p1>0) p3 =fork();
    if(p1==0) printf(“type 1\n”);
    if(p3!=0) printf(“type 2\n”);
    if(p2!=0) printf(“type 3\n”);
    if((p1>0)||(p2>0) || p3>0)) printf(“type 4\n”);
    if((p2==0) && (p3==0)) printf(“type 5\n”);
    }
    Thanks a ton for any input!
    Last edited by Frankie15; 10-01-2012 at 09:44 PM.

  2. #2
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    So fork splits one process into two processes, both of which are in the exact same state, the same place in the code, etc.* In the parent process, fork() will return the PID of the new child process it forked. In the child, fork() will act as though it returned 0. I say "act as though" since, from the child's perspective, fork wasn't really called from that child process, that child process came into being immediately after the fork call from the parent.

    One thing that always really helps me in following like this with complex forking behavior is to print out the PID and parent's PID (PPID) to track who came from who and who is printing what. Do that with everything you print. For example:
    Code:
    print("The process that started it all is PID = %d\n", getpid());  // put this above the while loop
    printf("PID = %d, PPID = %d, y = %d\n", getpid(), getppid(), y);  // replace the line in the while loop with this
    It's not 100% portable/safe to assume you can print a PID as an integer, but it will probably work, if not print it as a long int or long long int (with necessary casting).

    For the first program, the code enters the while loop, x and y are 10, and count is 1.

    1. Assume the initial parent program is process A.
    2. x is its initial value, 10 (non-zero), so it takes the "if" branch and forks, and you have two processes, A (parent) and B (child). After the fork, both processes add 10 to y, giving 20 in each process. They both print it out, add 1 to counter (making it 2 in each process) and continue into the next iteration of the loop.
    3. Process A was the parent last time, so fork return the PID of the child process (B), so x is non-zero. That means it takes the "if" branch again. So process A forks again, and has a second child process, C (a "sibling" process to B). Both A and C add 10 to y (making it 30 in processes A and C). They print y and add 1 to counter, making it 3. The loop terminates and processes A and C exit.
    4. Process B was the child, so fork returned 0 to it. That means x was 0, and the second time through the loop, it takes the "else" path. It forks a child process, process D (A's "grandchild", C's "nephew"), and both of those processes add 50 to y, making it 70. They each print that out, add 1 to counter, making it 3 and the loop terminates. They both exit and everything is now finished.


    Now, I put those steps in a numbered list, but really, they can, and often do in practice, happen in a mixed up order. The reason is, the OS doesn't guarantee any particular order of execution for the processes that are running, even if one is a prent/child of another. Thus, it's possible that A adds 10 to y, but then B executes and forks, and D adds 50 to y then prints that out, before A gets a chance to print out y = 20.

    Add a bunch of debugging output to the second program, and include the PID and PPID in each line, and see if that helps you track it. Try to figure it out on your own now, and come back if you need any more help. I'll be back in the morning, if somebody else hasn't stepped in to help.

    Hope that all makes sense (I always find these things difficult to explain).

    * There are a few ways in which the parent and child process differ, like PID, memory locks, semaphores, pending signals, etc. Read about them in the fork man page: fork(2): create child process - Linux man page.

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,661
    While you're taking anduril462's advice, also print the value of count as well. Count how many processes exist for each value of count.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    Registered User
    Join Date
    Sep 2011
    Posts
    16
    Quote Originally Posted by anduril462 View Post
    So fork splits one process into two processes, both of which are in the exact same state, the same place in the code, etc.* In the parent process, fork() will return the PID of the new child process it forked. In the child, fork() will act as though it returned 0. I say "act as though" since, from the child's perspective, fork wasn't really called from that child process, that child process came into being immediately after the fork call from the parent.

    One thing that always really helps me in following like this with complex forking behavior is to print out the PID and parent's PID (PPID) to track who came from who and who is printing what. Do that with everything you print. For example:
    Code:
    print("The process that started it all is PID = %d\n", getpid());  // put this above the while loop
    printf("PID = %d, PPID = %d, y = %d\n", getpid(), getppid(), y);  // replace the line in the while loop with this
    It's not 100% portable/safe to assume you can print a PID as an integer, but it will probably work, if not print it as a long int or long long int (with necessary casting).

    For the first program, the code enters the while loop, x and y are 10, and count is 1.

    1. Assume the initial parent program is process A.
    2. x is its initial value, 10 (non-zero), so it takes the "if" branch and forks, and you have two processes, A (parent) and B (child). After the fork, both processes add 10 to y, giving 20 in each process. They both print it out, add 1 to counter (making it 2 in each process) and continue into the next iteration of the loop.
    3. Process A was the parent last time, so fork return the PID of the child process (B), so x is non-zero. That means it takes the "if" branch again. So process A forks again, and has a second child process, C (a "sibling" process to B). Both A and C add 10 to y (making it 30 in processes A and C). They print y and add 1 to counter, making it 3. The loop terminates and processes A and C exit.
    4. Process B was the child, so fork returned 0 to it. That means x was 0, and the second time through the loop, it takes the "else" path. It forks a child process, process D (A's "grandchild", C's "nephew"), and both of those processes add 50 to y, making it 70. They each print that out, add 1 to counter, making it 3 and the loop terminates. They both exit and everything is now finished.


    Now, I put those steps in a numbered list, but really, they can, and often do in practice, happen in a mixed up order. The reason is, the OS doesn't guarantee any particular order of execution for the processes that are running, even if one is a prent/child of another. Thus, it's possible that A adds 10 to y, but then B executes and forks, and D adds 50 to y then prints that out, before A gets a chance to print out y = 20.

    Add a bunch of debugging output to the second program, and include the PID and PPID in each line, and see if that helps you track it. Try to figure it out on your own now, and come back if you need any more help. I'll be back in the morning, if somebody else hasn't stepped in to help.

    Hope that all makes sense (I always find these things difficult to explain).

    * There are a few ways in which the parent and child process differ, like PID, memory locks, semaphores, pending signals, etc. Read about them in the fork man page: fork(2): create child process - Linux man page.
    I don't understand because when I run the program it prints the following:

    y=20
    y=30xxx@xxx-VirtualBox:~/program$ y =20
    y=30
    y=20
    y=70 y= 20
    y=70

    You said it prints out 20 twice during the first iteration of the loop. Why is it printing 20 followed by 30?

  5. #5
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by Frankie15 View Post
    I don't understand because when I run the program it prints the following:

    y=20
    y=30xxx@xxx-VirtualBox:~/program$ y =20
    y=30
    y=20
    y=70 y= 20
    y=70

    You said it prints out 20 twice during the first iteration of the loop. Why is it printing 20 followed by 30?
    Did you read my reply thoroughly? The answer is in there. Did you try adding the PID and PPID to the print statements like I told you? I don't see it in the output you provided there, but it should help clarify why things are printed in the order they are, and why the results might not be the same every time.

    EDIT: As a hint, recall, I said each process prints out 20. Then, I said some pretty important stuff about execution order after the numbered list.
    Last edited by anduril462; 10-02-2012 at 03:01 PM.

  6. #6
    Registered User
    Join Date
    Sep 2011
    Posts
    16
    Quote Originally Posted by anduril462 View Post
    Did you read my reply thoroughly? The answer is in there. Did you try adding the PID and PPID to the print statements like I told you? I don't see it in the output you provided there, but it should help clarify why things are printed in the order they are, and why the results might not be the same every time.

    EDIT: As a hint, recall, I said each process prints out 20. Then, I said some pretty important stuff about execution order after the numbered list.
    I'm sorry! I totally missed where you said how it could be random.

    What I don't understand is how it is fair to be tested on what the output is if they are always random.

    I think I understand how the first program works. I have a question here

    You say the program prints 20,20,30,30,70,70 (not necessarily in that particular order), but the program also sends out two more prints of 20. Where are these from? It prints out 20,20,20,20,30,30,70,70.


    Last edited by Frankie15; 10-02-2012 at 03:43 PM.

  7. #7
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by Frankie15 View Post
    What I don't understand is how it is fair to be tested on what the output is if they are always random.
    Yes, it will print the same values every time, but in a completely arbitrary order. Most likely your professor is simply going to sort the lines of output* from your program so that it will always appear as
    Code:
    y = 20
    y = 20
    y = 20
    y = 30
    y = 30
    y = 70
    y = 70
    Then that is compared* to the sorted output from his "master" program that he knows works. If the output of each one is different, there is something wrong.


    Sometimes it won't even print out 70 which confuses me.
    That's definitely strange, it should always print it out, but it may be stuffed on the same line as your prompt, so you might have missed it. Try adding a \n at the end of the printf statement too, to force the output to the screen immediately (remember, stdout is line buffered, meaning it needs a new line to cause the output to print). Alternatively, you could call fflush(stdout).

    I think I understand how the first program works thanks to your great explanation. I am trying to understand the second one now.
    Cool, glad I could help. Let us know if you have questions on the second one. Remember, throw in some debugging output with PID and PPID to help out.

    One more note, your programs there never call wait(), so it's possible the parent process terminates before the children, leaving a bunch of orphans. That means the init process will "reap" them, and the PPID may show as 1, which may be confusing. You can add a loop like the following to the end of your program to force the parents to wait, for testing purposes:
    Code:
    while (wait(NULL) != -1 || errno != ECHILD);  // note the semicolon, empty loop body
    You'll need to #include <errno.h> and <sys/types.h> and <sys/wait.h> to use that loop. Check the man page (section 2) for the wait() function for more details of what it does. Type "man 2 wait" from the command line or Google it.

    * There is a program available on every *nix system I'm aware of called "sort". It does precisely that, sort lines of output. The "diff" program is also available on every *nix I'm aware of, it compares two files/streams. They're both really simple, handy utilities you should become familiar with, along with a few others like wc (word count, which also counts lines, characters, etc) and grep, and maybe awk, cut and tr (translate). And of course, having decent bash skills is very handy.

  8. #8
    Registered User
    Join Date
    Sep 2011
    Posts
    16
    Thanks! Check my edit regarding printing the two extra 20's.

    It does print out the same values every time. I may have missed the 70 one time.

  9. #9
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Don't have time to dig into it right now, have a meeting for work.

    Try adding the two printf lines I suggested in post #2, with PID and PPID. Then also add the following 2 lines after the while loop:
    Code:
    printf("Process PID = %d, PPID = %d is ending\n", getpid(), getppid());
    while (wait(&status) != -1 || errno != ECHILD);  // empty loop body
    See if that clears up which process is printing the 20 and how it happened. You may also want to add, as Salem suggested, count to the printf statements, and possibly x as well, just for extra info.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Fork()
    By Drainy in forum C Programming
    Replies: 1
    Last Post: 11-13-2009, 10:08 AM
  2. Understanding fork()
    By NuNn in forum C Programming
    Replies: 8
    Last Post: 02-27-2009, 12:09 PM
  3. understanding fork() ???
    By ^^B.H.A^^ in forum C Programming
    Replies: 2
    Last Post: 12-05-2003, 11:12 AM
  4. Fork ()
    By bladex in forum Windows Programming
    Replies: 3
    Last Post: 02-10-2003, 03:30 PM