Reputation: 343
The program terminates after taking 4 or 5 values from scanf
.
But I want it to accept 8 values (totaling 8 processes) and then terminate.
void main() {
fork();
fork();
fork();
scanf("%d",&j);
printf("The values are %d\n",j);
wait(0);
}
Upvotes: 1
Views: 1054
Reputation: 12668
Well, the first question you have to answer to yourself is how many processes you think you have there? As you don't use the returned value from the fork(2)
system call, you don't know even if you are the parent or you are the child after execution of each fork.
You'll have one process at the beginning of the tale, which does a fork(2)
system call and converts in two processes, that then execute (both, parent and child) a second fork(2)
call, converting (each) in two processes (two more, so four in total) when the second call returns. Then comes the third, which duplicates again the number of processes, so you'll get at the end of the story eight running processes at all in a binary tree execution history hierarchy fully populated up to height three.
The processes can each wait(2)
for each of their own children, but there are processes that, since their inceptions have made three, two, one, or no fork()
at all. The root node is the only that makes three fork()
s, so it can do up to three wait(2)
s without error (for each of its children), the first of its children, makes only two, the second makes only one... like this:
proc[0]---fork()-----fork()---fork()----exit();
| | |
| | proc[3]---exit();
| |
| proc[2]--fork()----exit();
| |
| proc[4]---exit();
|
proc[1]----fork()---fork()----exit();
| |
| proc[5]---exit();
|
proc[6]--fork()----exit();
|
proc[7]---exit();
so
proc[0]
can wait(2)
to proc[1]
, proc[2]
and proc[3]
;proc[1]
can wait(2)
to proc[5]
and proc[6]
;proc[2]
can wait(2)
to proc[4]
onlyproc[3]
cannot wait(2)
(it will result in error if wait(2)
is called);proc[4]
cannot wait(2)
;proc[5]
cannot wait(2)
;proc[6]
can wait(2)
only to proc[7]
andproc[7]
cannot wait(2)
.As wait(2)
can only wait for one of such children (children must be created with fork, or the call will result in error) you have to issue as many wait(2)
calls as fork(2)
s you have issued, to wait for them all, so you must control the number of children you have (as you see, this number is different for each process). You can, for example, increment a counter in the parent (the process that receives a 0
result from fork(2)
so you know the number of fork(2)
s you have issued up to now.
int main()
{
int forks_made = 0;
if (fork() > 0) forks_made++;
if (fork() > 0) forks_made++;
if (fork() > 0) forks_made++;
for (i = 0; i < forks_made; i++) wait(NULL);
exit(0);
}
or simply, you can wait(2)
until the system call results in an error (you don't have more children)
int main()
{
fork();
fork();
fork();
while(wait(NULL) == 0)
continue;
exit(0);
}
Be careful as the process hierarchy is a different tree from the binary history tree. The process hierarchy is like this:
proc[0]
|
+--proc[1]
| |
| +--proc[5]
| |
| `--proc[6]
| |
| `--proc[7]
|
+--proc[2]
| |
| `--proc[4]
|
`--proc[3]
Suppose I write the following code:
int main()
{
fork(); fork(); fork();
wait(0); wait(0); wait(0);
exit(0);
}
The result is:
ok ok ok
p[0]-f()-f()-f()----------------w()------------w()--------w()-exit();
| | | ^ ^ ^
| | | err err err | | |
| | +-p[3]-w()-w()-w()-exit(); | |
| | | |
| | ok err err | |
| +-p[2]-f()----------------w()-w()-w()-exit(); |
| | ^ |
| | | |
| | err err err | |
| +-p[4]-w()-w()-w()-exit(); |
| ok ok err |
+-p[1]-f()-f()----------------w()------------w()-w()-exit();
| | ^ ^
| | err err err | |
| +-p[5]-w()-w()-w()-exit(); |
| ok err err |
+-p[6]-f()----------------w()-w()-w()-exit();
| ^
| err err err |
+-p[7]-w()-w()-w()-exit();
Even if the children die before the parent does a wait(2)
the kernel maintains them in the process table (but without any resource allocated) as zombie processes just to wait for the parent to do the proper wait(2)
system call. This is why the kernel knows that you can do a wait(2)
or not (you can only wait if you have made a fork(2)
).
Why the process only read(2)
s part of the results and finishes? Well, I have been reading some documentation and making some tests, and the behaviours are different on the three operating systems I have tested on:
exit(2)
s, all of its children are awaken from the read(2)
call and given a ETIMEDOUT
error (surprisingly)EIO
).As normal job control makes the shell to reacquire the control terminal and the process group ceases to be the control group of the terminal device, the terminal should awake all the processes trying to read(2)
from it with an error code, so perhaps FreeBSD is the most coherent result we should have.
The code used to test this case follows:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
void hndlr(int sig)
{
printf("%d(prnt=%d,pgrp=%d): signal %d received\n",
getpid(), getppid(), getpgrp(), sig);
}
int main()
{
int i;
char line[1024];
for (i = 1; i < 64; i++)
signal(i, hndlr);
printf("pid=%d, pgrp=%d\n", getpid(), getpgrp());
fork();
fork();
fork();
i = read(0, line, sizeof line);
switch(i) {
case -1:
printf("pid=%d, prnt=%d, pgrp=%d: read: %s(errno=%d)\n",
getpid(), getppid(), getpgrp(), strerror(errno), errno);
break;
case 0:
printf("pid=%d, prnt=%d, pgrp=%d: read: EOF\n",
getpid(), getppid(), getpgrp());
break;
default:
printf("pid=%d, prnt=%d, pgrp=%d: read: [%*.*s]\n",
getpid(), getppid(), getpgrp(), i, i, line);
break;
}
#if 0
wait(NULL);
wait(NULL);
wait(NULL);
#endif
} /* main */
Upvotes: 2
Reputation: 123460
The problem here is that wait(0)
only waits for the first child. The main process will exit once it has successfully raced to get its own input, and a direct child has done the same.
The easiest kludge is to just wait until there are no more children to wait for:
void main() {
int j;
fork();
fork();
fork();
scanf("%d", &j);
printf("The values are %d\n",j);
while (wait(0) != -1);
}
This reads 8 values from the interactive terminal as expected.
You should never do this in a real program though. The main thread should instead read all 8 values and create the children as needed to process them. Failing to do so causes race conditions and buffering issues.
Here's an example of this, using one less fork()
for brevity:
$ for i in {1..4}; do echo "$i"; done | ./foo
The values are 1
The values are 0
The values are 0
The values are 0
$ for i in {1..4}; do echo "$i"; sleep 0.1; done | ./foo
The values are 1
The values are 2
The values are 3
The values are 4
In the former case, multiple writes to the pipe become a single read, which is picked up by an arbitrary process. The others get nothing.
In the second case, a short sleep is added to allow more time between each read, and now they get the values as expected.
Upvotes: 1
Reputation: 824
You need to wait for all your children and your children need to wait for their children, etc. Currently your wait(0)
call only waits for one child to change state.
// wait(0);
while (1) {
int status;
pid_t done = wait( &status );
if ( done == -1 )
break; // no more children
}
Upvotes: 0