Ring of processes using fork() and pipes - c

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.

Related

file descriptor of a pipe in parent and child can also cause confusion

I wrote a program creating pipe passing message from parent to a child, I saw something different about the file descriptor:
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
int
main(int argc, char *argv[]){
int fd[2];
int read_number[1];
int write_number[1];
pipe(fd);
int pid;
if ((pid = fork()) >0){
close(fd[0]);
for (int i = 2; i < 5; i++){
write_number[0] = i;
write(fd[1], &write_number, sizeof(write_number));
}
close(fd[1]);
//wait(0);
printf("write finished from parent process:%d\n", pid);
}else{// child process
//close(fd[1]);
int flag;
while((flag = read(fd[0], &read_number, sizeof(read_number))) > 0)
printf("The number is %d from child %d\n", read_number[0], getpid());
printf("finished %d\n", flag);
close(fd[0]);
printf("child process finished!\n");
}
exit(0);
}
the comments are what I intend to do, for the read system call, from API, it will return success value or error value, success value could be n or less than n, or 0, error value is -1, but here something is quite bizarre:
root#Thomas-sun:~# ./a.out
write finished from parent process:29173
The number is 2 from child 29173
The number is 3 from child 29173
root#Thomas-sun:~# The number is 4 from child 29173
it means the child went out of the loop, but the child does not print the finish information;
whereas if I close(fd[1]) in child, then the result is pretty what I need:
root#Thomas-sun:~# ./a.out
write finished from parent process:31057
root#Thomas-sun:~# The number is 2 from child 31057
The number is 3 from child 31057
The number is 4 from child 31057
finished 0
child process finished!
if I did not close fd[1] in parent, I also get the following result:
root#Thomas-sun:~# ./a.out
write finished from parent process:31842
The number is 2 from child 31842
The number is 3 from child 31842
root#Thomas-sun:~# The number is 4 from child 31842
finished 0
child process finished!
So from the above result, it seems the fd[1] in parent will not necessarily work, what matters is the close(fd[1]) in child!, but why it goes out of the loop and do NOT print finished information?? thx for any shared idea!
man 7 pipe says:
If all file descriptors referring to the write end of a pipe have
been closed, then an attempt to read(2) from the pipe will see
end-of-file (read(2) will return 0).
In your example code (after fork()), both the parent and the child process have an open file descriptor fd[1] for the write end.
If you close only the fd[1] of the parent process, there is still open fd[1] in the child process. So the write end exists, and read() won't return zero.
in the 1st log:
root#Thomas-sun:~# ./a.out
write finished from parent process:29173
The number is 2 from child 29173
The number is 3 from child 29173
root#Thomas-sun:~# The number is 4 from child 29173
The child is still running in the loop (read() is blocking) at background. And it won't print the "finished" log, if you don't send signal to it.

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?

Fork() Parent and Child

what I need is to have 3 children and 1 parent (the main program) because the children and parent are going to have diferent functionality.
I don't know why I can't do it properly, as far I have read, when you do a fork(), if it returns 0, you execute the child's code and the parent's code otherwise.
So, this is my program:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int
main(int argc, char ** argv) {
printf ("Init Parent. PID =%d\n", getpid());
fork();
fork();
if (fork() == 0) {
printf ("Init Child. PID =%d\n", getpid());
sleep(1);
} else {
printf ("I am the parent. PID = %d\n",getpid());
sleep(1);
}
exit(0);
}
This is the output:
Init Parent. PID =9007
I am the parent. PID = 9007
I am the parent. PID = 9009
Init Child. PID =9010
Init Child. PID =9011
I am the parent. PID = 9008
I am the parent. PID = 9012
Init Child. PID =9013
Init Child. PID =9014
And this is what I want:
Init Parent. PID =9007
I am the parent. PID = 9007
Init Child. PID =9010
Init Child. PID =9011
Init Child. PID =9013
Init Child. PID =9014
I don't know why it says I am the parent 3 times and how to do it properly, because 1
if (fork() == 0)
seems bad.
I would really appreciate any help, thank you in advance.
You have to check for parent-child starting from after your first fork. Every time you call fork() you are spawning a child. What you are doing now is this:
Starting executing 1 program (+1 proc, 1 proc total)
First fork(): Main program spawns child (+1 proc, 2 procs total)
Second fork(): Child and main program spawn children. (+2 procs, 4 procs total)
Third fork(): All processes previously mentioned spawn children(+4 procs, 8 procs total).
The resulting process tree from your original code would look like this:
pi
/ | \
pi pi pi
/ | |
pi pi pi
|
pi
Possible solution(works for n-children):
int noProcs = 3;
int i;
for (i = 0; i < noProcs; ++i) {
if (fork() == 0) {
printf("in child\n");
//Do stuff...
exit(0);
}
}
//DONT FORGET: Reap zombies using wait()/waitpid()/SIGCHLD etc...
Note that after every fork() call, the children spawned are executing the same subsequent code. For each step in the loop's iteration, here's what happens after I spawn a child with fork():
Child process: executes child code, and calls exit(). This prevents us from looping and creating more children than intended.
Parent process: Bypasses the if(fork() == 0) statement's body and steps through the loop, spawning additional children as specified.
fork duplicates the process that calls it, creating a new (child) process that is in the exact same state and that continues running at the same point. The only difference between the parent and child is what is returned by the fork call.
So consider what happens with your code. You start with one process:
printf ("Init Parent. PID =%d\n", getpid());
so that one process prints a message
fork();
you fork, so now have two processes (parent and child)
fork();
both processes fork, so you now have 4 processes (parent, 2 children and grandchild)
if (fork() == 0) {
fork again, so you now have 8 processes -- parent, 3 children, 3 grandchildren, and a great-grandchild. Half of them (one child, two grandchildren, and the great-grandchild) have a 0 return from this fork, as they were the children created by this third fork. The other half are parents.
So after these 3 forks, you have 8 processes, 4 of which will print "Init Child" and 4 of which will print "I am the parent"
If you want just 3 direct children (and no grandchildren), you need to arrange your code such that the children do no call fork again. You could use something like:
if (fork() && fork() && fork())
printf ("I am the parent. PID = %d\n",getpid());
else
printf ("Init Child. PID =%d\n", getpid());
Change the code as below.
You need to check on every fork() its return code and not only on the last call of fork. I didn't run and try but this is the error I feel.
You said - you need 1 Parent and 3 child - so during first fork() - 1 child will be created and then for second fork() - 1 grandchild of child and 1 more child of a parent will be created.
EDIT:
parent
/ \
fork-1 fork-2
| |
child1 child-2
|
fork-2
|
child3
for(int i=0;i<2;i++)
{
if (fork()==0)
printf ("Init Child. PID =%d\n", getpid());
sleep(1);
} else {
printf ("I am the parent. PID = %d\n",getpid());
sleep(1);
}
}

What exactly does fork return?

On success, the PID of the child
process is returned in the parent’s
thread of execution, and a 0 is
returned in the child’s thread of execution.
p = fork();
I'm confused at its manual page,is p equal to 0 or PID?
I'm not sure how the manual can be any clearer! fork() creates a new process, so you now have two identical processes. To distinguish between them, the return value of fork() differs. In the original process, you get the PID of the child process. In the child process, you get 0.
So a canonical use is as follows:
p = fork();
if (0 == p)
{
// We're the child process
}
else if (p > 0)
{
// We're the parent process
}
else
{
// We're the parent process, but child couldn't be created
}
p = fork();
/* assume no errors */
/* you now have two */
/* programs running */
--------------------
if (p > 0) { | if (p == 0) {
printf("parent\n"); | printf("child\n");
... | ...
Processes are structured in a directed tree where you only know your single-parent (getppid()). In short, fork() returns -1 on error like many other system functions, non-zero value is useful for initiator of the fork call (the parent) to know its new-child pid.
Nothing is as good as example:
/* fork/getpid test */
#include <sys/types.h>
#include <unistd.h> /* fork(), getpid() */
#include <stdio.h>
int main(int argc, char* argv[])
{
int pid;
printf("Entry point: my pid is %d, parent pid is %d\n",
getpid(), getppid());
pid = fork();
if (pid == 0) {
printf("Child: my pid is %d, parent pid is %d\n",
getpid(), getppid());
}
else if (pid > 0) {
printf("Parent: my pid is %d, parent pid is %d, my child pid is %d\n",
getpid(), getppid(), pid);
}
else {
printf("Parent: oops! can not create a child (my pid is %d)\n",
getpid());
}
return 0;
}
And the result (bash is pid 2249, in this case):
Entry point: my pid is 16051, parent pid is 2249
Parent: my pid is 16051, parent pid is 2249, my child pid is 16052
Child: my pid is 16052, parent pid is 16051
If you need to share some resources (files, parent pid, etc.) between parent and child, look at clone() (for GNU C library, and maybe others)
Once fork is executed, you have two processes. The call returns different values to each process.
If you do something like this
int f;
f = fork();
if (f == 0) {
printf("I am the child\n");
} else {
printf("I am the parent and the childs pid is %d\n",f);
}
You will see both the messages printed. They're being printed by two separate processes. This is they way you can differentiate between the two processes created.
This is the cool part. It's equal to BOTH.
Well, not really. But once fork returns, you now have two copies of your program running! Two processes. You can sort of think of them as alternate universes. In one, the return value is 0. In the other, it's the ID of the new process!
Usually you will have something like this:
p = fork();
if (p == 0){
printf("I am a child process!\n");
//Do child things
}
else {
printf("I am the parent process! Child is number %d\n", p);
//Do parenty things
}
In this case, both strings will get printed, but by different processes!
fork() is invoked in the parent process. Then a child process is spawned. By the time the child process spawns, fork() has finished its execution.
At this point, fork() is ready to return, but it returns a different value depending on whether it's in the parent or child. In the child process, it returns 0, and in the parent process/thread, it returns the child's process ID.
Fork creates a duplicate process and a new process context. When it returns a 0 value it means that a child process is running, but when it returns another value that means a parent process is running. We usually use wait statement so that a child process completes and parent process starts executing.
I think that it works like this:
when pid = fork(), the code should be executed two times, one is in current process, one is in child process.
So it explains why if/else both execute.
And the order is, first current process, and then execute the child.

Forking and Process Management

System information: I am running 64bit Ubuntu 10.10 on a 2 month old laptop.
Hi everyone, I've got a question about the fork() function in C. From the resources I'm using (Stevens/Rago, YoLinux, and Opengroup) it is my understanding that when you fork a process, both the parent and child continue execution from the next command. Since fork() returns 0 to the child, and the process id of the child to the parent, you can diverge their behavior with two if statements, one if(pid = 0) for the child and if(pid > 0), assuming you forked with pid = fork().
Now, I am having the weirdest thing occur. At the beginning of my main function, I am printing to stdout a couple of command line arguments that have been assigned to variables. This is this first non assignment statement in the entire program, yet, it would seem that sometimes when I call fork later in the program, this print statement is executed.
The goal of my program is to create a "process tree" with each process having two children, down to a depth of 3, thus creating 14 total children of the initial executable. Each process prints its parent's process ID and its process ID before and after the fork.
My code is as follows and is properly commented, command line arguments should be "ofile 3 2 -p" (i haven't gotten to implementing -p/-c flags yet":
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
if(argc != 5)//checks for correct amount of arguments
{
return 0;
}
FILE * ofile;//file to write to
pid_t pid = 1;//holds child process id
int depth = atoi(argv[2]);//depth of the process tree
int arity = atoi(argv[3]);//number of children each process should have
printf("%d%d", depth, arity);
ofile = fopen(argv[1], "w+");//opens specified file for writing
int a = 0;//counter for arity
int d = 0;//counter for depth
while(a < arity && d < depth)//makes sure depth and arity are within limits, if the children reach too high(low?) of a depth, loop fails to execute
//and if the process has forked arity times, then the loop fails to execute
{
fprintf(ofile, "before fork: parent's pid: %d, current pid: %d\n", getppid(), getpid());//prints parent and self id to buffer
pid = fork(); //forks program
if(pid == 0)//executes for child
{
fprintf(ofile, "after fork (child):parent's pid: %d, current pid: %d\n", getppid(), getpid());//prints parent's id and self id to buffer
a=-1;//resets arity to 0 (after current iteration of loop is finished), so new process makes correct number of children
d++;//increases depth counter for child and all of its children
}
if(pid > 0)//executes for parent process
{
waitpid(pid, NULL, 0);//waits on child to execute to print status
fprintf(ofile, "after fork (parent):parent's pid: %d, current pid: %d\n", getppid(), getpid());//prints parent's id and self id to buffer
}
a++;//increments arity counter
}
fclose(ofile);
}
When I run "gcc main.c -o ptree" then "ptree ofile 3 2 -p", the console is spammed with "32" a few times, and the file "ofile" is of seemingly proper format, but a bit too large for what I think my program should be doing, showing 34 child processes, when there should be 2^3+2^2+2^1=14. I think this is somehow related to the statement that is printing "32", as that would seem to possibly spawn more forks than intended.
Any help would be greatly appreciated.
When you call printf, the data is stored in a buffer internally. When you fork, that buffer is inherited by the child. At some point, (when you call printf again, or when you close the file), the buffer is flushed and data is written to the underlying file descriptor. To prevent the data in the buffer from being inherited by the child, you can flush the FILE * before you call fork, via fflush.

Resources