Reputation: 33
Here is what I am trying to do:
Write a C program that takes an integer command line argument n, spawns n processes that will each generate a random numbers between -100 and 100, and then computes and prints out the sum of these random numbers. Each process needs to print out the random number it generates.
This is what I have so far:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int command,processCheck; // processCheck: to check if fork was successful or not and to
char * strNumProcess = NULL;// check the status of child process
while((command = getopt(argc, argv, "n:"))!=-1){
if(command == 'n'){
strNumProcess = optarg;
break;
}
}
int numProcess = atoi(strNumProcess);
int pipes[numProcess][2];
int randomNum; // Variable to store the random number
int randomNumSum=0; // Initialized variable to store the sum of random number
/** A loop that creates specified number of processes**/
for(int i=0; i<numProcess; i++){
processCheck = fork(); // creates a child process. Usually fork() = 2^n processes
if(processCheck < 0){ // Checks for the error in fork()
printf("Error");
exit(1); // Terminates with error
}
else if(processCheck == 0){
close(pipes[i][0]);
/** Child process**/
srand(time(NULL)+getpid()); // sets the randomness of the number associted with process id
randomNum = rand()% 201 + (-100); // sets the range of random number from -100 to 100 and stores the random number in randomNum
printf("%d\n" , randomNum); // Prints out the random number
write(pipes[i][1], &randomNum, sizeof randomNum);
close(pipes[i][1]);
exit(0);// Terminates successfully
}
else{
if(wait(NULL)){ // Waits for the child process to end and directs to parent process
int v;
if(read(pipes[i][0], &v, sizeof v)==sizeof(v)){
randomNumSum+=v;
close(pipes[i][0]);
}
}
}
close(pipes[i][1]);
}
printf("%d\n", randomNumSum); // Prints the sum of the random number
return 0;
}
The program goes in infinite loop after second process.
Upvotes: 0
Views: 196
Reputation: 13570
edit
The OP has made significant changes to the question, it's not the same question as it was yesterday. This answer might henceforth make no sense any more.
end edit
The reason for this is that fork()
creates a new independent process with its
own virtual memory. It only inherits the values from the parent, the forked process do not share variables
with the parents. So randomNumSum
is for every child a unique variable and
changing it does not affect the randomNumSum
of the parent.
You need to use for example pipes for communication between parents and children, the children write the results in the pipe, the parent reads from the children.
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char **argv)
{
if(argc != 2)
{
fprintf(stderr, "usage: %s num_of_children\n", argv[0]);
return 0;
}
int noc = atoi(argv[1]);
if(noc <= 0)
{
fprintf(stderr, "Invalid number of children\n");
return 1;
}
int pipes[noc][2];
pid_t pids[noc];
for(size_t i = 0; i < noc; ++i)
{
if(pipe(pipes[i]) == -1)
{
perror("pipe");
pids[i] = -2; // used later for error checking
continue;
}
pids[i] = fork();
if(pids[i] == -1)
{
perror("fork");
continue;
}
if(pids[i] == 0)
{
// CHILD
// closing reading end
close(pipes[i][0]);
srand(time(NULL)+getpid());
int r = rand()% 201 + (-100);
printf("Child %zu: r = %d\n", i, r);
// sending value to parent
write(pipes[i][1], &r, sizeof r);
close(pipes[i][1]);
return 0;
}
// closing writing end
close(pipes[i][1]);
}
int sum = 0;
for(size_t i = 0; i < noc; ++i)
{
if(pids[i] == -2)
{
fprintf(stderr, "Pipe could not be created for child %zu\n", i);
continue;
}
if(pids[i] == -1)
{
fprintf(stderr, "Child %zu was not started\n", i);
close(pipes[i][0]);
continue;
}
int status;
if(waitpid(pids[i], &status, 0) == -1)
{
fprintf(stderr, "Could not wait for child %zu\n", i);
close(pipes[i][0]);
continue;
}
if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
{
int v;
if(read(pipes[i][0], &v, sizeof v) != sizeof(v))
{
fprintf(stderr, "Could not read from child %zu\n", i);
close(pipes[i][0]);
continue;
}
sum += v;
close(pipes[i][0]);
} else
printf("Child %zu did not exit normally\n", i);
}
printf("The sum is: %d\n", sum);
return 0;
}
Gives me the output:
Child 0: r = -6
Child 1: r = 63
Child 3: r = 78
Child 2: r = 77
Child 4: r = -47
The sum is: 165
So the technique here is the creation of the pipes with the pipe
. A pipe
is a unidirectional data channel that can be used for interprocess communicationcite.
With a pipe two processes can communicate with each other, but the pipe has only
one direction. In this example the child process will write into the pipe and
the parent will read from the pipe.
That's why before doing the fork
, the parent creates the pipe, does the fork
and then closes the it's writing end of the pipe. The child closes it's reading
end of the pipe. Then the child calculates the value and writes into the pipe
the value it calculated and exists with the status 0.
After creating the children the parent waits for the children to terminate. If the children terminate normally and with exit status 0, the parent reads from the pipe and gets the calculated value of the child.
Btw, as David C. Rankin points out in the comments, your method of getting
a random value in the range [-100, 100] is incorrect. rand()% 201 + (-100)
would give you values between -100 and 100, because rand()%201
gives you a
value between 0 and 200.
edit2
OP asked in the comments
based on my understanding can I just return
randonNum
instead ofexit(0)
and do the computation where I callingwait(NULL)
andcall wait(randomNum)?
Yes, you can use the exit status of a process to send information back to the parent without the need of creating a pipe. But I think this is not a particular good solution for these reasons:
the exit status in Unix/POSIX is a unsigned 8-bit value, meaning the exit codes are in the range [0, 255]. So if your random value is let's say -1, the parent process will see 255. In your case that wouldn't be such a problem, because you for values greater than 127, you can subtract 256 to get the negative value.
You can only return an (unsigned) 8-bit value. If your child process has to
send something more "complex" like a 16-bit value, a float
, double
, or a
struct
, you cannot use the exit status, so you
are limiting what you can return to the parent. When you want to return
something more "complex" than a 8-bit value, then a pipe is perfect tool for that.
I consider it as a hack to use the exit status to send other information that is not an error value. The purpose of the exit status is that a process can tell it's parent that it exited without an error by returning 0, or that it exited with an error and the exit status has the error code. That's why I consider it a hack, for me it's like using a screwdriver instead of a hammer for nailing nails.
Your wait
call would be invalid though, because wait
expects a pointer to
int
and you would need to use the macros WIFEXITED
and WEXITSTATUS
to get
the exit status. But the problem of using wait
in this case is that wait
returns -1 on error and you wouldn't be able to tell for which child it returned
-1 and how many waits you have to
call to wait for the rest of the children. The children don't end in the same order as you
forked them, so you would need to keep track which child has been wait()
ed.
It's much more simpler to use waitpid
. With waitpid
you can wait for a
particular child. I personally prefer waitpid
here.
So, changing the code to do the same without pipes and using the exit status:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char **argv)
{
if(argc != 2)
{
fprintf(stderr, "usage: %s num_of_children\n", argv[0]);
return 0;
}
int noc = atoi(argv[1]);
if(noc <= 0)
{
fprintf(stderr, "Invalid number of children\n");
return 1;
}
pid_t pids[noc];
for(size_t i = 0; i < noc; ++i)
{
pids[i] = fork();
if(pids[i] == -1)
{
perror("fork");
continue;
}
if(pids[i] == 0)
{
// CHILD
srand(time(NULL)+getpid());
int r = rand()% 201 + (-100);
printf("Child %zu: r = %d\n", i, r);
exit(r);
}
}
int sum = 0;
for(size_t i = 0; i < noc; ++i)
{
if(pids[i] == -1)
{
fprintf(stderr, "Child %zu was not started\n", i);
continue;
}
int status;
if(waitpid(pids[i], &status, 0) == -1)
{
fprintf(stderr, "Could not wait for child %zu\n", i);
continue;
}
if(WIFEXITED(status))
{
int v = WEXITSTATUS(status);
// checking if the child wrote a 8-bit negative value
// in 2-complement format
if(v > 127)
v -= 256;
printf("Parent: child %zu returned %d\n", i, v);
sum += v;
} else
fprintf(stderr, "Child %zu did exit abnormally, ignoring\n", i);
}
printf("The sum is: %d\n", sum);
return 0;
}
Gives me the output for 10 children:
Child 0: r = -59
Child 1: r = 73
Child 2: r = 61
Child 3: r = 98
Child 4: r = 18
Child 6: r = 31
Child 5: r = -88
Parent: child 0 returned -59
Parent: child 1 returned 73
Parent: child 2 returned 61
Child 8: r = 58
Parent: child 3 returned 98
Parent: child 4 returned 18
Parent: child 5 returned -88
Child 7: r = 53
Parent: child 6 returned 31
Child 9: r = -43
Parent: child 7 returned 53
Parent: child 8 returned 58
Parent: child 9 returned -43
The sum is: 202
Upvotes: 3