Understanding parent and child process execution order - c

According to my knowledge, the child process executes first. Why the parent process was executed before the child and the parent was executed again? How did the execution process went from parent to child to parent again? And why should the pipe be closed? I tried the code without the close pipe statement and I got the same output.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void main(){
int P1P2[2];
int P2P1[2];
pipe(P1P2);
pipe(P2P1);
int x,y;
if(fork()){ // father
close(P1P2[0]);
close(P2P1[1]);
printf("Enter one integer:");
scanf("%d",&x);
write(P1P2[1], &x, sizeof(x));
read(P2P1[0], &y, sizeof(y));
printf("Multiplication is %d\n", y);
}
else{ // Child
close(P1P2[1]);
close(P2P1[0]);
read(P1P2[0], &x, sizeof(x));
y = x*x;
write(P2P1[1], &y, sizeof(y));
}
}
output:
abbas#abbas-VirtualBox:~/Desktop$ ./simplle
Enter one integer:4
Multiplication is 16

After calling fork, there are two processes that run independently from each other. The processor switches between them like it does with other separate processes, so you won't always get the same behavior.
If you did want the parent to wait for the child to finish, you could use waitpid.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void){
int P1P2[2];
int P2P1[2];
pipe(P1P2);
pipe(P2P1);
int x,y;
if( fork() > 0 ){ // parent
close(P1P2[0]);
close(P2P1[1]);
printf("Enter one integer:");
scanf("%d",&x);
write(P1P2[1], &x, sizeof(x)); /* (2) */
read(P2P1[0], &y, sizeof(y)); /* (3) */
printf("Multiplication is %d\n", y);
} else { // Child (or pipe error)
close(P1P2[1]);
close(P2P1[0]);
read(P1P2[0], &x, sizeof(x)); /* (1) */
y = x*x;
write(P2P1[1], &y, sizeof(y)); /* (4) */
}
}
Suppose the child is running immediately after the fork, and the parent is not. The child will block on the read at (1), waiting for data. Since there is no data, it will yield the cpu. At some point in the future, the parent will be scheduled and will execute printf, scanf and write. After the parent has written into the pipe at (2), either the child or the parent may execute in any order. Eventually, the parent will block on the read at (3) and will not be able to proceed until the child writes at (4). So the pipe works to synchronize the processes.

According to my knowledge, the child process executes first. Why the parent process was executed before the child and the parent was executed again? How did the execution process went from parent to child to parent again? And why should the pipe be closed? I tried the code without the close pipe statement and I got the same output.
Your knowledge is not correct. You don't know who is going to be scheduled first, if the parent or the child. Both cases can happen. The parent is normally free to run after the system call has checked all possible errors, while the child still has to build a complete environment to run. So it is very possible for the parent to run first.
The execution normally get's like this. The parent calls fork() which in turn switches to kernel mode. Here, the parent process (there's still no child anywhere) checks all conditions necessary to create a child process, enough resources, not trespassing of limits, etc. and if everything gets ok, then it creates a new process entry in the processes table. Once this is done, the parent kernel code has all the elements to start running a second kernel thread and starts it, to execute the second part of the fork call, while the first (the parent process) already has the return code of the system call and is ready to run (this means that the child process will be executed, not failing to do that, but it doesn't need to be immediately). From this point on, the parent process can return from kernel mode with the child pid, and continue, while the child kernel has still to build enough context to run and be scheduled. This can happen very quickly, even before the scheduler has had the time to reschedule the parent again, so the conclussion is that you don't know which one is to continue executing user code first.
In respect to your second question, the order of execution from the fork() onwards depends on so many things that is actually unpredictable. Both are different processes and execute as soon as the system allows them to proceed.
In respect to the closing of the pipes, you can do it or you cannot.... as you prefer... but a pipe works passing all the data from the writer process to the reader, until the pipe gets its last close(2) When you fork()ed, the system passed of having two pipes, with four file descriptors to have 4 (two on the parent, two on the child) with a total of eight file descriptor (four readers and four writer descriptors, two for each process) (and this means that both writers, the parent process and the child process, need to close(2) the writing side of the pipe for the readers --again, both the father and the child-- to receive the corresponding EOF indicating that the other end has closed its side) This is of no concern to you (I mean, doesn't show any difference) because you have limited your protocol to just one message in each direction, and there's no EOF condition to be waited for.... but try extending your sample code to read from the pipe until it closes it side, and you will see the difference (in that case, without having closing your writer side, your process will block waiting for input, and no input is received because the other process has closed the descritptor and the only process waiting for input is the only process that can write on it).

Related

How can the multi-core cpu run the program interleaved?

The output of the program are not obviously contents from the printf()s in teh code. Instead it looks like characters in irregular sequence. I know the reason is because the parent process and child process are running
at the same time, but in this program I only see pid=fork(), which I think means pid is only the id of child process.
So why can the parent process print?
How do the two processes run together?
// fork.c: create a new process
#include "kernel/types.h"
#include "user/user.h"
int
main()
{
int pid;
pid = fork();
printf("fork() returned %d\n", pid);
if(pid == 0){
printf("child\n");
} else {
printf("parent\n");
}
exit(0);
}
output:
ffoorrkk(()) rreettuurrnende d 0
1c9h
ilpda
rent
I focus my answer on showing how the observed output can result from the shown program. I think that it will already clear things up for you.
This is your output.
I edited it to use a good guess of what is parent (p) and child (c):
ffoorrkk(()) rreettuurrnende d 0\n
cpcpcpcpcpcpcpcpcpcpcpcpccpcpcppccc
1 c9h\n
pccpcpp
ilpda\n
ccpcpcc
rent
pppp
If you only use the chars with a "c" beneath, you get
fork() returned 0
child
If you only use the chars with a "p" beneath, you get
fork() returned 19
parent
Split that way, it should match what you know about how fork() works.
Comments already provided the actual answer to the three "?"-adorned questions in title and body of your question post.
Lundin:
It creates two processes and they are executed just as any other process, decided by the OS scheduler.
Yourself:
each time fork() is called it will return twice, the parent process will return the id of child process, and child process will return 0
Maybe for putting a more obvious point on it:
The parent process receives the child ID and also continues executing the program after the fork().
That is why the output occurs twice, similarily, interleaved, with differences in PID value and the selected if branch.
Relevant is also that in the given situation there is no line buffering. Otherwise there would be no character-by-character interleaving and everthing would be much more readable.

C - fork() and processes behaviour

I have a small program written in C on linux. It's purpose is to examine the behaviour of the fork() call and the resulting child processes
Upon first inspection everything seems simple enough. However
Sometimes output is written in a funny order
Sometimes the child PPID is '1' not whatever the parent PID is.
I can't find any pattern or correlation between when it works as expected and when it does not.
I think that point 2. is probably caused by the parent process dying before the child process has executed fully. If so, is there a way to stop this from happening.
However I have no idea what is causing point 1.
Code below:
#include <stdio.h>
#include <unistd.h>
int main()
{
int x = fork();
if (x == 0)
{
printf("Child:");
printf ("\nChild PID : %d", getpid());
printf ("\nChild PPID: %d", getppid());
printf("\nHello Child World\n");
}
if (x != 0)
{
printf("Parent :");
printf ("\nParent PID : %d", getpid());
printf ("\nParent PPID: %d", getppid());
printf("\nHello Parent World\n");
}
return 0;
}
this behaviour is seen because of scheduling policy of operating system. if you are aware of process management concepts of os, then if your parent code is running and fork() is encountered, child is created, but if within that time, parent's time slice has not been completed, then parent continues running and if within its time slice, parent executes and terminates, then child becomes orphan precess and after parent process' time slice completes, child's execution starts, thats why getppid() function returns 1, because child is an orphan process and it it now adopted by init process which starts first when operating system boots and is having process id 1.
Explanation of Behaviour 1:
The order of output cannot be controlled by the program normally. That's the point of parallel process. The OS decides which process to execute at any point of time and both processes are executed simultaneously (to the human eye).
Thus the output would generally be inter-tweened.
Explanation of Behaviour 2:
You guessed that right.
The parent process has finished before the forked one.
If you want the parent pid, you can use waitpid(x, &status, 0) in the parent process if you need the parent to stay alive till child execution. This link may help you.

Is there any formula to know how fork() makes near-perfect copy of the current process?

#include <stdio.h>
main()
{
int i, n=1;
for(i=0;i<n;i++) {
fork();
printf("Hello!");
}
}
I am confused if I put n=1, it prints Hello 2 times.
If n=2, it prints Hello 8 times
If n=3, it prints Hello 24 times..
and so on..
There's is no single "formula" how it's done as different operating systems do it in different ways. But what fork() does is it makes a copy of the process. Rough steps that are usually involved in that:
Stop current process.
Make a new process, and initialize or copy related internal structures.
Copy process memory.
Copy resources, like open file descriptors, etc.
Copy CPU/FPU registers.
Resume both processes.
you dont only fork the 'main' prozess, you also fork the children!
first itteration:
m -> c1
//then
m -> c2 c1-> c1.1
m -> c3 c1-> c1.1 c2->c2.1 c1.1 -> c1.1.1
for i = ....
write it in down this way:
main
fork
child(1)
fork child(2)
fork child(1.1)
fork child(3)
fork child(1.2)
fork child(2.1)
and so on ...
fork() always makes a perfect copy of the current process -- the only difference is the process ID).
Indeed in most modern operating systems, immediately after fork() the two processes share exactly the same chunk of memory. The OS makes a new copy only when the new process writes to that memory (in a mode of operation called "copy-on-write") -- if neither process ever changes the memory, then they can carry on sharing that chunk of memory, saving overall system memory.
But all of that is hidden from you as a programmer. Conceptually you can think of it as a perfect copy.
That copy includes all the values of variables at the moment the fork() happened. So if the fork() happens inside a for() loop, with i==2, then the new process will also be mid-way through a for() loop, with i==2.
However, the processes do not share i. When one process changes the value of i after the fork(), the other process's i is not affected.
To understand your program's results, modify the program so that you know which process is printing each line, and which iteration it is on.
#include <stdio.h>
# include <sys/types.h>
main() {
int i , n=4;
for(i=0;i<n;i++); {
int pid = getpid();
if(fork() == 0) {
printf("New process forked from %d with i=%d and pid=%d", pid, i, getpid());
}
printf("Hello number %d from pid %d\n", i, getpid());
}
}
Since the timing will vary, you'll get output in different orders, but you should be able to make sense of where all the "Hellos" are coming from, and why there are as many as there are.
When you fork() you create a new child process that has different virtual memory but initially shows in the same physical memory with the father. At that point both processes can only read data from the memory. If they want to write or change anything in that common memory, then they use different physical memory and have write properties to that memory. Therefore when you fork() your child has initially the same i value as its father, but that value changes separately for every child.
Yes, there is a formula:
fork() makes exactly one near-identical copy of the process. It returns twice, however. Once in the original process (the parent, returning the child pid) and once in the child (returning 0).

something about process schedule by OS?

I write the following codes and run it in my linux.Everytime after fork the terminals print two PID, which shows both processes are scheduled by the OS, and then it is time for "scanf" to execute, both processes are blocked waiting for the input.However every time I put a number, and then I get the same PID printed on the terminal. Does it mean the same process is invoked by the OS when a terminal IO meets, or something else happens?
int main(int argc, char* argv[])
{
int num;
if(fork() >= 0)
{
printf("%x\n",getpid());
while(1)
{
if(scanf("%d",&num) != EOF)
{
printf("%x\n",getpid());
}
}
}
printf("\nit is over:%x\n", getpid());
}
As Hunter McMillen already noted in comments you are grouping the cases for the parent and child. Now both of them are scheduled as noted by different PIDs outputted and both of them are now waiting at scanf. As soon as you enter data, you are seeing only one PID, because the input you entered was part of one process. Other process ( can be parent or child ) is still waiting for you to enter something. Now, even though your terminal is flooded by a single PID, continuously outputted by one process, try entering some data again and press enter. Now you can see both PIDs being printed!
This is because, in the if() statement, fork() creates child process and runs in an infinite loop. There's a concept of parent process, the parent of child process created using fork() system call. The statement after if() ends, belongs to parent process and here, it will execute only after child process ends. That's why you get the same process ID, that is of child process.

Can the order of execution of fork() be determined?

I'm working on an exercise on the textbook "Operating System Concepts 7th Edition", and I'm a bit confused about how does fork() work. From my understanding, fork() creates a child process which runs concurrently with its parent. But then, how do we know exactly which process runs first? I meant the order of execution.
Problem
Write a C program using fork() system call that generates the Fibonacci sequence in the child process. The number of sequence will be provided in the command line.
This is my solution:
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
void display_fibonacci_sequence( int n ) {
int i = 0;
int a = 1;
int b = 1;
int value;
printf( "%d, %d, ", a, b );
for( ;i < n - 2; ++i ) {
value = a + b;
printf( "%d, ", value );
a = b;
b = value;
}
printf( "\n" );
}
int main( int argc, char** argv ) {
int n;
pid_t pid;
pid = fork();
if( argc != 2 ) {
fprintf( stderr, "Invalid arguments" );
exit( -1 );
}
n = atoi( argv[1] );
if( pid < 0 ) {
fprintf( stderr, "Fork failed" );
exit( -1 );
}
else if( pid == 0 ) {
display_fibonacci_sequence( n );
}
else { // parent process
// what do we need to do here?
}
}
To be honest, I don't see any difference between using fork and not using fork. Besides, if I want the parent process to handle the input from user, and let the child process handle the display, how could I do that?
You are asking many questions, I'll try to answer them in a convenient order.
First question
To be honest, I don't see any difference between using fork and not
using fork.
That's because the example is not a very good one. In your example the parent doesn't do anything so the fork is useless.
Second
else {
// what do we need to do here?
}
You need to wait(2) for the child to terminate. Make sure you read that page carefully.
Third
I want the parent process to handle the input from user, and let the
child process handle the display
Read the input before the fork and "handle" the display inside if (pid == 0)
Fourth
But then, how do we know exactly which process runs first?
Very few programs should concern themselves with this. You can't know the order of execution, it's entirely dependent on the environment. TLPI says this:
After a fork(), it is indeterminate which process—the parent or the
child—next has access to the CPU. On a multiprocessor system, they may both simultaneously get access to a CPU.
Applications that implicitly or explicitly rely on a particular
sequence of execution in order to achieve correct results are open to
failure due to race conditions
That said, an operating system can allow you to control this order. For instance, Linux has /proc/sys/kernel/sched_child_runs_first.
We don't know which runs first, the parent or the child. This is why the parent generally has to wait for the child process to complete if there is some dependency on order of execution between them.
In your specific problem, there isn't any particular reason to use fork(). Your professor probably gave you this just for a trivial example.
If you want the parent to handle input and the child to calculate, all you have to do is move the call to fork() below the point at which you handle the command-line args. Using the same basic logic as above, have the child call display_fibonacci_sequence, and have the parent simply wait
The process which is selected by your system scheduler is chosen to run, not unlike any other application running on your operating system. The process spawned is treated like any other process where the scheduler assigns a priority or spot in queue or whatever the implementation is.
But then, how do we know exactly which process runs first? I meant the
order of execution.
There is no guarantee to which one ran first. fork returns 0 if it is the child and the pid of the child if it is the parent. Theoretically they could run at exactly the same time on a multiprocessor system. If you actually wanted to determine which ran first you could have a shared lock between the two processes. The one that acquires the lock first could be said to have run first.
In terms of what to do in your else statement. You'll want to wait for the child process to exit using wait or waitpid.
To be honest, I don't see any difference between using fork and not using fork.
The difference is that you create a child process. Another process on the system doing computation. For this simple problem the end user experience is the same. But fork is very different when you are writing systems like servers that need to deal with things concurrently.
Besides, if I want the parent process to handle the input from user, and let the child process handle the display, how could I do that?
You appear to have that setup already. The parent process just needs to wait for the child process to finish. The child process will printf the results to the terminal. And the parent process currently gets user input from the command line.
While you cannot control which process (parent or child) gets scheduled first after the fork (in fact on SMP/multicore it might be both!) there are many ways to synchronize the two processes, having one wait until the other reaches a certain point before it performs any nontrivial operations. One classic, extremely portable method is the following:
Prior to fork, call pipe to create a pipe.
Immediately after fork, the process that wants to wait should close the writing end of the pipe and call read on the reading end of the pipe.
The other process should immediately close the reading end of the pipe, and wait to close the writing end of the pipe until it's ready to let the other process run. (read will then return 0 in the other process)

Resources