-1

Can someone explain fork() and give examples of its uses?

What I understood by some online sources is that:

  1. fork() creates a child process that would run the same as the parent process.
  2. 2^n process will be created, n = number of fork().
  3. ID of the Child Process will always be 0, while the ID of the Parent Process will be something else - A Positive Integer != 0.

And I have another question, for that first please take a look at my code below:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main() {
    int id = fork();
    int i = 45;
    fork();
    fork();
    if(id == 0) {
        printf("\nChild Process id = %d",id);
        printf("\ni = %d ; i = %d",i++,i);  
    }
    else {
        printf("\nParent Process id = %d",id);
        printf("\ni = %d ; i = %d",i,i+2);
    }
    return 0;
}   
/*
O/P:
Parent Process id = 9048
i = 45 ; i = 47
Parent Process id = 9048                                                                                                                                                        
i = 45 ; i = 47
Parent Process id = 9048
i = 45 ; i = 47
Parent Process id = 9048
i = 45 ; i = 47
Child Process id = 0
i = 45 ; i = 46
Child Process id = 0
i = 45 ; i = 46
Child Process id = 0
i = 45 ; i = 46
Child Process id = 0
*/

Why doesn't the last child process display the second printf() i.e.,

printf("\ni = %d ; i = %d",i++,i);

Can you please explain why does the second printf() doesn't get executed while the first printf() gets executed for the 8th time - Child Process.

victcdva
  • 35
  • 2
  • 4
  • 16
  • 2
    Your printing issues are almost certainly caused by buffering issues, e.g. [printf anomaly after “fork()”](https://stackoverflow.com/q/2530663/364696). Everything else is just layering complications on top of that. – ShadowRanger Dec 29 '20 at 20:32
  • Did you read [fork(2)](https://man7.org/linux/man-pages/man2/fork.2.html) several times? With [errno(3)](https://man7.org/linux/man-pages/man3/errno.3.html) and [stdio(3)](https://man7.org/linux/man-pages/man3/stdio.3.html) and [Advanced Linux Programming](https://mentorembedded.github.io/advancedlinuxprogramming/)? Did you study the source code of [GNU libc](https://www.gnu.org/software/libc/) ? See also https://kernelnewbies.org/ – Basile Starynkevitch Dec 29 '20 at 20:36
  • 1
    Note that the behavior of a program that executes `printf("\ni = %d ; i = %d",i++,i);` is not defined by the C standard, and the C standard places no requirements on how the program must behave. As specified by the C standard, this program does not have to `fork` at all; it could trap or fail in other ways as soon as the `main` routine is started. – Eric Postpischil Dec 29 '20 at 20:43
  • @BasileStarynkevitch we might as well close stackoverflow and have a single html page listing sources to study. – Tony Tannous Dec 29 '20 at 20:46
  • @TonyTannous The owners of stackoverflow could certainly close it. – Basile Starynkevitch Dec 29 '20 at 20:47
  • @ShadowRanger: That question does not speak to the question asked here. In that one, output printed before a `fork` is buffered and appears twice because the buffer is duplicated in the `fork`, and the solution is to flush (explicitly or with `\n`) before `fork`. The program here does not print before executing `fork` and has no duplicated output. It leaves buffers unflushed as it returns from `main`, but normal program termination flushes buffers. – Eric Postpischil Dec 29 '20 at 23:04
  • @HowardAnderson: Please copy and paste the complete output of the program, including the command line that started it and the shell prompt that appears after it. Please also execute the program with output redirected to a file and paste the contents of that file. – Eric Postpischil Dec 29 '20 at 23:16
  • @EricPostpischil: I have posted the exact same copy of my program and the exact same output that I got while executing the program. – Howard Anderson Dec 30 '20 at 04:17
  • @HowardAnderson: The output you have posted is not what I requested. Please read the details in my request. – Eric Postpischil Dec 30 '20 at 04:27
  • @EricPostpischil: I have posted the exact same copy of my program and the exact same output that I got while executing the program in the terminal. Program compilation: `gcc testfork.c -o tfork` Execution command: `./tfork` – Howard Anderson Dec 30 '20 at 04:30
  • @HowardAnderson: Posting the output and posting the command line separately is not what I requested, which is to post one stretch of text that is an exact copy of the text in the terminal window that contains the command that runs the program, the program output, and the shell prompt that follows it. The reason for asking for that is that it may contain clues about the issue. One answer speculates that output was concealed or overwritten by the shell prompt, and seeing the exact output **as requested** could have clues about that. Notably, the exact position of the prompt may be relevant. – Eric Postpischil Dec 30 '20 at 11:24
  • @HowardAnderson: Additionally, I asked for the output as redirected to a file rather than written to the terminal. That may also reveal information using for diagnosing the problem. It might not, but it is a simple test that could be helpful. As long as you fail to cooperate in providing information, this question should be closed for lacking information. – Eric Postpischil Dec 30 '20 at 11:26
  • Also, after the shell prompt appears, wait a second to see if any further output appears after it. It is possible the parent could exit before the child process prints its output, in which case the sequence of events could be: parent exits, shell prompt appears, child output appears. Then the final output from the child would be after the shell prompt. – Eric Postpischil Dec 30 '20 at 11:34
  • This was an elaborate disguise for the old `i++,i` question – M.M Dec 31 '20 at 00:36
  • @M.M: Not unless you can explain how the compiler generated code so that three of the forked children did the `printf` and one did not. Their code is identical, and their states after the `fork` are essentially identical, so, even if the C standard permits them to behave differently, they have no way to do so. – Eric Postpischil Dec 31 '20 at 02:03
  • @EricPostpischil I'm not interested in guessing why undefined behaviour manifested itself in some particular way in some particular instance – M.M Dec 31 '20 at 02:52
  • @M.M: It did not manifest in any way relevant to this question: The observation reported by the OP cannot be caused by “undefined behavior” from the `printf` arguments, due to the way `fork` works. – Eric Postpischil Dec 31 '20 at 03:10
  • @EricPostpischil this discussion is a waste of time as OP could remove the undefined behaviour and re-run the program and then proceed with analysis of the output – M.M Dec 31 '20 at 03:12
  • @M.M: According to your view of undefined behavior, how could that help? Not only does the `i++, i` have undefined behavior by the C standard, so does `fork`. It has parts that are not written in C and cannot be. The C standard omits any definition of it, making it *undefined behavior* per C 2018 4 2: “Undefined behavior is otherwise indicated in this document by the words "undefined behavior" or by the omission of any explicit definition of behavior.” – Eric Postpischil Dec 31 '20 at 03:27
  • @EricPostpischil The behaviour of `fork` is defined by other standards , which we take the discussion to be in the context of , since `fork` is in use – M.M Dec 31 '20 at 03:58
  • @M.M: So “undefined behavior” is not a barrier to reasoning about a program’s behavior if you have additional specifications that apply. Then, if you can use the specification of `fork` to deduce the program execution will be duplicated, you can can use the specification of `fork` to deduce that the behavior of the children will be duplicated (that is, identical). The logical implication of the specification of `fork` is that the children of this program will produce the same output. – Eric Postpischil Dec 31 '20 at 11:22
  • @EricPostpischil: I apologize for the lack of information provided. Thanks for your advice, I will try to improve it. It seems that the output has been obscured or overwritten by the shell prompt as you have suspected to be 1 of the possible reasons for this anomaly. Anyways, Thanks and now I have figured it out by **that other guy 's post**. I admire your interest and desperation. – Howard Anderson Dec 31 '20 at 20:43

3 Answers3

0

Correct usage of fork implies testing immediately if the current process is the child (fork returned 0) or the parent (fork returned the pid of the child), and act accordingly. In your case, both parent and child launch a couple or spurious processes, which will not be able to know what they are, since the value returned is ignored. A couple of them will think they are parent processes.

Normally the child process will exec a new executable, if something serious has to be done.

Gerard H. Pille
  • 2,528
  • 1
  • 13
  • 17
  • This does not address the question “Can you please explain why does the second printf() doesn't get executed while the first printf() gets executed for the 8th time”. – Eric Postpischil Dec 29 '20 at 20:36
  • @EricPostpischil It doesn't. But if you can explain something that doesn't happen, feel free to add your own answer. The second printf clearly does get executed - so how could one explain it didn't? – Gerard H. Pille Dec 29 '20 at 21:12
0

fork() creates child process that would run same as the parent process.

True. fork() creates a child process which is a duplicate of it's parent process.


2^n process will be created, n = number of fork().

Depends on the code!

int i = 0;
while (i < n)
{
    fork();
    i++;
}

Here, assuming no fork() failed, then yes, there will be 2^n processes.


ID of the Child Process will always be 0, while ID of the Parent Process will be something else - A Positive Integer != 0.

The fork() return value in child process is 0! not id, but return value! the return value of fork() in parent process is the actual pid of the child process.


To your code now

1. int main() {
2.    int id = fork(); 
3.    int i = 45;
4.    fork();
5.    fork();
6.    if(id == 0) {
7.        printf("\nChild Process id = %d",id);
8.        printf("\ni = %d ; i = %d",i++,i);  
9.    }
10.   else {
11.       printf("\nParent Process id = %d",id);
12.       printf("\ni = %d ; i = %d",i,i+2);
13.   }
14.   return 0;
15. } 

At line 4, there are 2 processes

parent         |     child_1
    id > 0     |     id == 0
    i == 45    |     i == 45     

Now a fork is invoked

    parent     |     child_1    |    child_OF_1   |   child_2
    id > 0     |     id == 0    |    id == 0      |   id > 0
    i == 45    |     i == 45    |    i == 45      |   i == 45

child_1 and child_OF_1 will have id == 0 and enter the if statement, while child_2 and parent will go to the else statement and print 47

I ignored the 3rd fork() which by then you have 8 processes, of which, 4 will go inside the if statement while the others will enter the else block. This is why you see

i = 45 ; i = 47

4 times!

Tony Tannous
  • 14,154
  • 10
  • 50
  • 86
  • This does not address the question “Can you please explain why does the second printf() doesn't get executed while the first printf() gets executed for the 8th time”. – Eric Postpischil Dec 29 '20 at 20:36
  • @EricPostpischil I am working on that part. The question has multiple questions in it, feel free to add your own answer if you think mine is not good enough. – Tony Tannous Dec 29 '20 at 20:37
  • I vote based on the text in the answer, not the hypothetical text in some future draft of the answer. Hypothetically, if this answer is updated to be good, it will have a hypothetical future vote up. – Eric Postpischil Dec 29 '20 at 20:40
  • @EricPostpischil I never asked for an upvote anyway. – Tony Tannous Dec 29 '20 at 20:42
  • @TonyTannous Thank You for your explanation on fork() now I understand it better. Can you say some uses of fork() ? And I am going to ask a rather silly question: Can we use fork() instead of using loops to print the same statement several number of times? For Example, `fork(); fork(); fork(); printf("\nText printed");` instead of `for(i=0;i<7;i++) printf("\Text printed"); ` – Howard Anderson Dec 30 '20 at 05:14
  • @HowardAnderson You can do that, but be ware, the output might interleave. AND more importantly, that's not a good use of fork(). – Tony Tannous Dec 30 '20 at 12:22
0

All the output you expect is there, it's just that you write it out in the background without a terminating linefeed. This means that some of the output may be obscured or overwritten by the shell prompt appearing too soon and in the middle of the program's output.

To see exactly how this is possible, here's strace output for some of the processes in your program:

3215092 write(1, "\n", 1)               = 1
3215092 write(1, "Child Process id = 0\n", 21) = 21
3215092 write(1, "i = 45 ; i = 46", 15) = 15
3215094 write(1, "\n", 1)               = 1
3215094 write(1, "Child Process id = 0\n", 21) = 21
3215094 write(1, "i = 45 ; i = 46", 15) = 15

And here's how Zsh draws its prompt. First a missing linefeed indicator and then the prompt itself. Note in particular that the prompt starts with \r, a carriage return:

3215036 write(10, "\33[1m\33[3m%\33[23m\33[1m\33[0m          "..., 306) = 306
3215036 write(10, "\r\33[0m\33[23m\33[24m\33[Jmyhostname.i"..., 34) = 34

Since you fork processes in the background that write to the terminal, all your forked processes are in a race with Zsh to write their output.

Here is one possible, valid ordering that this may result in:

1. 3215036 write(10, "\33[1m\33[3m%\33[23m\33[1m\33[0m          "..., 306) = 306
2. 3215092 write(1, "\n", 1)               = 1
3. 3215092 write(1, "Child Process id = 0\n", 21) = 21
4. 3215092 write(1, "i = 45 ; i = 46", 15) = 15
5. 3215094 write(1, "\n", 1)               = 1
6. 3215094 write(1, "Child Process id = 0\n", 21) = 21
7. 3215094 write(1, "i = 45 ; i = 46", 15) = 15
8. 3215036 write(10, "\r\33[0m\33[23m\33[24m\33[Jmyhostname.i"..., 34) = 34

Steps 1-7 happen fairly uneventfully, resulting in a screen like this (cursor marked with |):

[...]
i = 45 ; i = 46
Child Process id = 0
i = 45 ; i = 46|

Since you have no terminating linefeeds, the cursor ends up after instead of under the last string.

Now, as you can see in write 8, Zsh starts its default prompt with a leading carriage return. This means that will go to the start of the line:

[...]
i = 45 ; i = 46
Child Process id = 0
|i = 45 ; i = 46

and then proceed to overwrite the line with the prompt:

[...]
i = 45 ; i = 46
Child Process id = 0
myhostname.internal% 

In effect, even though the last line of output was written to screen, the Zsh prompt effectively erased it by overwriting it using the carriage return.

You can use redirection to output to a file to more clearly see what output your program produces:

./foo > myfile

Opening the file in an editor shows your expected result:

Parent Process id = 3135806
i = 45 ; i = 47
Parent Process id = 3135806
i = 45 ; i = 47
Parent Process id = 3135806
i = 45 ; i = 47
Child Process id = 0
i = 45 ; i = 46
Parent Process id = 3135806
i = 45 ; i = 47
Child Process id = 0
i = 45 ; i = 46
Child Process id = 0
i = 45 ; i = 46
Child Process id = 0
i = 45 ; i = 46

You can similarly add your own terminating linefeed and delay to show the output in your terminal:

./foo; sleep 5; echo

This gives the same result as above.

A correct program would output linefeeds after and not before lines, and would wait() for each child process. (Please note that it won't work if you fix only one of these, while leaving the other)

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • Re “All the output you expect is there”: There are not eight “i =…” lines in the output shown. A good answer should either explain why there are not eight or diagnose that the OP failed to show all of the output (and explain how that happened). – Eric Postpischil Dec 29 '20 at 20:34
  • @EricPostpischil I'm not sure I understand. Are you saying that " the output is likely to be cosmetically jumbled on your terminal" is too mysterious and it should include the exact mechanic of how a prompt can overwrite part of background output on a tty? – that other guy Dec 29 '20 at 20:37
  • “Cosmetically jumbled” does not create understanding in a reader’s mind about what happened. Saying “The output is sent to the terminal, but the command-line prompt overwrites it” could. However, I find that dubious, as the user’s program does not print a carriage-return, and it is not common for shell prompts (they usually continuing printing from the current position on a line, so the prompt would appear after the program’s output and would not overwrite it). To answer this question, the cause that not all eight lines appear should be explained and confirmed. – Eric Postpischil Dec 29 '20 at 23:01
  • @thatotherguy: I tried `./tfork; sleep 5; echo;` . It is true that only now it prints the **last second printf() statement of the child process.** But, while writing the output to a new text file i.e., `/tfork > myfile ` it again returns the same output as the output as given by post - last second printf() statement doesn't get printed/executed. – Howard Anderson Dec 30 '20 at 04:41
  • @thatotherguy: Sorry, both shell prompts: `./tfork; sleep 5; echo` and `./tfork > myfile` prints the output as expected - last second printf() is printed. But, why just `./tfork` doesn't last second printf() statement ? and why your shell prompts does it correctly as expected ? – Howard Anderson Dec 30 '20 at 05:05
  • @HowardAnderson I tried to answer this in the first paragraph. Can you please reread it and let me know what's unclear? – that other guy Dec 30 '20 at 05:11
  • @thatotherguy: I want to know why `./tfork` doesn't print the last second printf() statement and why `./tfork; sleep 5; echo` prints the last second printf() statement ? Can you please explain it briefly ? – Howard Anderson Dec 30 '20 at 05:22
  • Since it has no terminating linefeed, the shell prompt is shown to the right (not under) and potentially entirely overwrites it. To explain in detail exactly how this happens can you provide your shell and shell prompt settings? What do you see if you run `printf "hello"`? – that other guy Dec 30 '20 at 05:27
  • @thatotherguy: I use zsh shell - (gnome-terminal). I hadn't changed any settings and left it as default. `printf "Hello"` prints `Hello%` in my terminal. – Howard Anderson Dec 30 '20 at 05:50
  • @HowardAnderson Ok, have a look – that other guy Dec 31 '20 at 00:32