Two simple case studies of fork() - c

I wrote two simple programs to understand the fork() API.
The first program:
int multiple_fork(){
fork(); //prints 2 times
fork(); //prints 4 times
printf("hello\n");
exit(0);
}
int main(){
multiple_fork();
return 0;
}
This program will print "hello" four times because the first fork() produces a child thread (C0), the second fork produces another two child threads C1 and C2 from parent thread (P) and C0, respectively. So far so good.
The second program:
#define N 2
int main(){
int status, i;
pid_t pid;
/* parent creates N children */
for(i = 0; i < N; i++)
if((pid = fork()) == 0){ /* Child */
printf("Child\n");
exit(100+i);
}
return 0;
}
This will print "Child" twice which I do not understand because I expect the "Child" get printed three times. Reason:
in the first loop, one child process (C0) is generated so this child will print "Child" once. Since C0 contains the same address space as parent process P. Then both P and C0 will execute the second loop. So C1 and C2 are generated from P and C0, respectively. So for C1 and C2, each child process will print "Child" once. Therefore, it should print "Child" three times. But apparently, I am wrong. Could someone explain what has happened?

When fork(2) returns, only the child will have pid == 0, so only the two children, forked on each iteration, will enter the if block and print.
These two children won't continue forking, because you call the exit(2) system call.

Related

Interpretation of pipes

I have two codes related on pipes and I´d like to share what I understand the code would do. I am starting with this pipe thing, so I want to get it well.
The first code is:
main (){
int fd[2];
char c;
fork();
pipe(fd);
read(fd[0], &c, 1);
wait(NULL);
}
I think in this what´s happening is the main process creates a child process and then a pipe. The parent awaits the death of his child. The child tries to read a character from the pipe, but because the pipe is empty, the child gets stuck. Therefore, both processes are blocked.
Is this right?
The second code is:
main (){
int n = getpid() ;
char s[80] ;
fork() ; /∗ 1 ∗/
if (getpid() == n) fork() ; /∗ 2 ∗/
fork(); /* 3 */
if (getppid() != n) fork() ; /∗ 4 ∗/
if (getppid() == n) {
sprintf( s , "%d\n" , getpid() ) ;
write( 1 , s , strlen(s) ) ;
}
}
In this second, I think the main process creates two child processes and two other possible child processes. First a child is created and then see if the process identification number corresponds to n, in which case another child is created. Another new child is then created and finally checked to see if the parent process ID number matches the process ID number. If it is false, the last child is created, but if it is true, the process identifier number is printed on the screen and finally the parent writes in the s parameter.
Are my interpretations good?

Can't understand the output of fork () when there is an exit(0) in the parent

When I run below program:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
//multiple children of the same parent
void main(){
int pid,i;
for(i=0;i<3;i++){
pid = fork();
if(pid<0)
printf("fork failed.");
else if(pid>0){
wait(NULL);
printf("i =%d, Child has finished. I am the parent[PID=%d].\n",i,getpid());
_exit(0);
}
else{
printf("i = %d, pid =%d, I am the child[PID=%d], parent[PID=%d] \n",i,pid, getpid(),getppid());
}
}
}
I got the following output:
i = 0, pid =0, I am the child[PID=7720], parent[PID=7719]
i = 1, pid =0, I am the child[PID=7721], parent[PID=7720]
i = 2, pid =0, I am the child[PID=7722], parent[PID=7721]
i =2, Child has finished. I am the parent[PID=7721].
i =1, Child has finished. I am the parent[PID=7720].
i =0, Child has finished. I am the parent[PID=7719].
However, I can’t understand the output.
If we trace the tree. Assuming that the parent represents the bash running this program.
So after fork we will have a child (call it C1) for that parent. The parent has to wait for it’s child because of the Wait(NULL) system call which is going to receive the SIGCHLD signal (PID) number returned by the child to the parent to terminate. Then another fork() has been done producing another child from (C2). However, there is no other child produced by the parent (C3) as in the normal case scenario without the exit(0) in the parent.
In this case n forks = n children (3 forks are going to produce only 3 children not 8).
Can someone help me to understand why the there is no child (C3) for the parent (bash) since it was waiting for C1, so it didn’t exit from the program.
Here's the walkthrough:
Process P creates C1
Process P then blocks waiting for C1 [i=0]
C1 prints the "I am the child" and loops back [i=1]
C1 forks C2
C1 blocks waiting for C2
C2 prints "I am the child" and loops back [i=2]
C2 forks C3
C2 blocks waiting for C3
C3 prints "I am the child" and the loop terminates
C3 exits
C2 unblocks, prints that C3 has exited and then itself exits
C1 unblocks, prints that C2 has exited, and then exits
P unblocks, prints that C1 has exited, then exits
There are not 8 children because the parent never finishes the full loop iteration except in the very last case. Look at it from just process P's perspective: you fork a child, wait for it to finish, and then exit.

Understanding fork() in for loop and usage of exit()

Hey I have a question regarding fork() and how it behaves in a for loop.
So following is asked from me:
Create a Parent Process that creates 10 child Processes each of them printing the actual i value in the for loop. Is this code correct?
int i;
for(i=0;i < 10;i++){
if(fork()==0){
print(i);
}
exit(0);
}
My understanding is that this snippet of code creates on every loop iteration a parent and a child, where the parent terminates directly and the child prints i;
So to have only one parent and 10 children each of them printing i, I should wrap the exit(0) like this:
int i;
int p;
for(i=0;i < 10;i++){
if((p=fork())==0){
print(i);
}
if(p > 0){
exit(0);
}
}
Can someone confirm or this if its right, or help me to get a better understanding if its wrong.
Thanks :)
The fork() call creates not a pair of new processes (parent, child), but leaves original process (parent) and creates one another process (child), so it "returns twice".
In your first snippet you really have only one parent. The only problem, it finishes on the very first iteration. :)
Look: i = 0, now we have only parent process (call it P).
P enters fork() and leaves it twice: in P (returning the PID of the child) and in the newly created child C0 (returning 0). Then, according to if statement, C0 prints 0, P does nothing. Then execution paths converges, and both P and C0 exit. Now we have no our processes at all.
Your second snippet's loop body can be rewritten as follows:
p = fork();
if (p == 0) {
print(i);
}
if (p > 0) {
exit(0);
}
Supposing fork() will not return negative number (error), these two if bodies are in fact just like then-else branches. They cause child process to print its number and old parent process to exit, so you got a waterfall of processes replacing one another in a sequence (most of them act exactly once as a child and then as parent).
You just need to rewrite it as such:
for(i = 0; i < 10; i++) {
p = fork();
if (p == 0) {
print(i);
exit(0);
}
// In fact, you should place waitpid(...) somewhere here,
// otherwise the child will become a so called zombie process
// after its termination.
// Only after parent termination they all will be
// finally recycled by init (PID 1) generally using up your system's
// resources for indefinite time
}
Now you have P that creates C0. C0 prints its number and immediately exits, while P just continues to the next loop iteration creating C1 that just like C0 prints its number and exits and so on. AFAIK it is what initially requested.
Please note that in real life you will have to somehow process the -1 return value that indicate some error in the fork() call (so in reality, in the second snippet rewritten by me there is a possibility that neither if statement will execute), I have omitted them for simplicity.

Ring of processes using fork() and pipes

I've been wracking my brain about this problem during an hour. I have to create a ring of processes with n processes (quantity is passed as an argument via cmd). The parent process sends his PID to his first child, and this one sends his parent's PID plus his own PID to his next brother, and it happens until we have created n children. After that, the parent process gets the addition of the PID of all his children.
Let's suppose that the parent process' PID is 3400 and we create two children, so the ring is made of three processes
3400 + 3401(first child's PID) + 3402(second child's PID) = 10203
The parent process should get this 10203.
I have thought about one "for" loop in which child processes send the addition of their brothers PID from brother to brother, using only one pipe. Nonetheless, I haven't come across a solution yet.
Given that the task is to use fork() and pipe(), you probably need to use an algorithm like:
Parent creates pipe for it to write to 1st child.
Parent keeps open the write end of pipe to 1st child.
Parent keeps open the read end of the pipe from Nth child.
For each child n = 1..N, Parent creates output pipe for nth child to talk to n+1th.
Parent forks nth child.
nth child closes the write end of its input pipe and the read end of its output pipe.
nth child reads sum of PIDs from input pipe, adds its own pid to the sum, and writes that to the output pipe, then exits.
Meanwhile, Parent closes both ends of the input pipe to the nth (except for the descriptors it must keep open), and loops back to create the n+1th child's pipe and then the child.
When all the children have been launched, parent writes its PID to 1st child and closes the write end of that pipe.
It then reads a response from the Nth child, closes the read end of the pipe, and prints the result.
What is less obvious how you can validate the sum, unless each child also writes its PID to standard output, or the parent (which knows all the child PIDs) computes the answer to verify it.
With zero marks because of the complete lack of error checking:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int N = (argc > 1) ? atoi(argv[1]) : 10;
int c1_wr;
int cN_rd;
int p1[2];
int p2[2];
int pid_chk = getpid();
if (N <= 0 || N >= 100)
N = 10;
pipe(p1);
c1_wr = dup(p1[1]);
printf("%d children\n", N);
printf("Parent = %d\n", pid_chk);
for (int n = 0; n < N; n++)
{
int pid;
pipe(p2);
fflush(stdout);
if ((pid = fork()) == 0)
{
close(p1[1]);
close(p2[0]);
int pid_sum;
read(p1[0], &pid_sum, sizeof(pid_sum));
pid_sum += getpid();
write(p2[1], &pid_sum, sizeof(pid_sum));
close(p1[0]);
close(p2[1]);
exit(0);
}
printf("Child %2d = %d\n", n+1, pid);
pid_chk += pid;
close(p1[0]);
close(p1[1]);
p1[0] = p2[0];
p1[1] = p2[1];
}
cN_rd = p2[0];
close(p2[1]);
int pid_sum = getpid();
write(c1_wr, &pid_sum, sizeof(pid_sum));
close(c1_wr);
read(cN_rd, &pid_sum, sizeof(pid_sum));
close(cN_rd);
printf("PID sum = %d\n", pid_sum);
printf("PID chk = %d\n", pid_chk);
return 0;
}
Sample run:
10 children
Parent = 49686
Child 1 = 49688
Child 2 = 49689
Child 3 = 49690
Child 4 = 49691
Child 5 = 49692
Child 6 = 49693
Child 7 = 49694
Child 8 = 49695
Child 9 = 49696
Child 10 = 49697
PID sum = 546611
PID chk = 546611
The purpose of the fflush(stdout); becomes clear if (a) you omit it and (b) you run the output through a pipeline. It is necessary.
You need some kind of inter process communication (IPC). Commonly used methods for IPC are:
loopback socket
shared memory
You can find all the other methods in wikipedia IPC article.
If you're in control of the behavior of the child processes, and the parent process doesn't have important/hard-to-recreate state before launching the children, you can simplify things.
Instead of an IPC, you can have the parent recursively launch the children detached, terminate itself, and have the last child re-start the parent:
Invoke parent n, where n is number of children to spawn.
parent:
invokes child arg1-1 0 and terminates,
if invoked with "0" for arg1, it adds its own PID to arg2 and you're done, even though in terms of the process tree it's not really a "parent" of anything.
child:
invokes child arg-1 arg2+pid, where pid is child's own PID.
if invoked with "0" for arg1, it invokes parent 0 arg2+pid.

Fork call working

can anybody explain me working of fork in detail
#include<unistd.h>
#include<stdio.h>
int main ()
{
int i, b;
for (i = 0; i < 2; i++) {
fflush (stdout);
b = fork ();
if (b == -1) {
perror ("error forking");
}
else if (b > 0) //parent process
{
wait ();
printf ("\nparent %d", getpid ());
}
else
printf ("\nchild %d %d", getpid (), getppid ());
}
return 0;
}
its just i need to know that if fork have same code as parent then this for loop should never stop creating child processes (every child will have its own for loop)
Yes, each child will continue the loop, but the operative word here is "continue". The variable i will be inherited by the first child, and then increased, and this increased value is inherited by the second child, etc.
The same will happen in the children, as i is inherited and keeps it value from the parent process. This means that the loops will soon end in all children.
When you fork, the child process will continue with the same next instruction as the parent one and the values.
So it will stop one day ;)
Take a look at a similar question : fork in a for loop
Yes although the parent and child code are the same but in parent the fork returns the child process id hence in parents code, the variable b contains the child's pid whereas the in child, the fork returns 0, hence in the child code segment the variable b will have 0 and so does we can achieve different jobs even though forking will have same parent code in child.

Resources