I have a program where two child processes are created belonging to the same father. Now the program starts by hitting control C and then works by pressing control Z each time.
Aim is for child 1 to write two numbers to child 2 and child two divides the numbers and writes the result back to child 1 that displays it. As a result two pipes are needed (fd and sd).
I got the first pipe working fine, so the numbers are sending over...in child 2(for debugging) it displays the correct number so if it had 8 and 2....the right answer of 4 is displayed in child 2. Now I can't seem to get this "4" or whatever the result is back to child 1.
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
void handleSignal(int sig)
{
if (sig == SIGTSTP)
{
return;
}
}
int main()
{
int fd[2];
int sd[2];
int pipe1 = 0;
int pipe2 = 0;
pid_t fork1 = 0;
pid_t fork2 = 0;
int num1, num2, result;
int myJump = 0;
int returnResult = 99;
signal(SIGINT, handleSignal);
printf("Waiting for interrupt\n");
pause();
signal(SIGINT, SIG_IGN);
pipe1 = pipe(fd);
pipe2 = pipe(sd);
//pipe checks been omitted...for simplicity
fork1 = fork();
//fork check been omited..for simplicity
signal(SIGTSTP, handleSignal); //wait till control Z is pressed
if (fork1 == 0)
{
dup2(fd[1], 1);
dup2(sd[0], 0);
close(fd[0]);
close(sd[1]);
while(1)
{
pause();
int randNum1 = rand() % 9 + 1;
fprintf(stderr, "Child 1: %d\n", randNum1);
printf("%d\n", randNum1);
fflush(stdout);
scanf("%d", &returnResult);
fprintf(stderr, "result in A :%d \n", returnResult);
}
}
else if (fork1 > 0)
{
fork2 = fork();
if (fork2 == 0)
{
signal(SIGTSTP, handleSignal);
dup2(fd[0], 0);
dup2(sd[1], 1);
close(fd[1]);
close(sd[0]);
if (myJump == 0)
{
pause();
scanf("%d", &num1);
printf("CHild 2: %d\n", num1);
myJump = 1;
}
if (myJump == 1)
{
while (1)
{
pause();
scanf("%d", &num2);
result = num2 / num1;
fprintf(stderr, "result from B: %d \n", result);
num1 = num2;
printf("%d \n", result);
}
}
}
else
{
wait();
}
}
else
{
printf("errror \n");
}
return 0;
}
If anyone could see whats wrong, if you was to run it...it works by hitting control C first then you have to keep hitting control Z. You can then see that the result from child A doesn't match that of B as shown below
Waiting for interrupt
^C^ZChild 1: 2
result in A :99
^ZChild 1: 8
result in A :99
result from B: 4
^ZChild 1: 1
result in A :99
result from B: 0
The pipes are actually working fine... it's scanf() that's failing. When your second child process starts, it calls:
if (myJump == 0)
{
pause();
scanf("%d", &num1);
printf("CHild 2: %d\n", num1); /* <== problem here */
myJump = 1;
}
...and the printf() there leaves "CHild 2: " as the next data in the sd[] pipe. Because of that, when your first child process calls scanf() to read the result from the second, scanf() fails and leaves returnResult unchanged. Since scanf() failed, the data is left on the stream, and future attempts to scanf() the result fail the same way.
How to fix it depends on what you want. If you want your second child process to just return the number it read as the result for the first pass, then just modify the offending printf() to write just the number without the text:
if (myJump == 0)
{
pause();
scanf("%d", &num1);
printf("%d\n", num1); /* <== Changed: number only, no text */
myJump = 1;
}
I should add that although the change mentioned above will fix the problem, you should generally check the return from scanf() to see whether it succeeds and take appropriate action if it doesn't.
Related
I have a program that creates a child process from a fork() call. The child will continuously receive 1-byte integer input from the user. Once an integer has been sent to the child, the child will send the value to the parent using a pipe. When the parent receives the value, it will add it to an array. -1 is sent to end the program. Once the child sends the parent -1, the parent will sum the previous values in the array and, using another pipe, send this sum value to the child in which the child will print it and terminate the program.
As of right now this is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
//Gets input from the user
int getInput() {
int val; char temp;
scanf("%hhd", &temp);
val = temp;
return val;
}
//Sums the values of the entered numbers and returns it
int finish(int arr[], int i) {
int sum = 0;
for (int j = 0; j < i; j++) {sum+= arr[j];}
return(sum);
}
int main() {
int fd[2], fd2[2], val = 0, i = 0, sum, final = -9999999;
int arr[1000];
pid_t pidVal;
//Pipe for sending numbers from child to parent
pipe(fd);
//Pipe for sending the final sum from parent to child
pipe(fd2);
//Create parent and child processes
pidVal = fork();
//Used to make it run continously until -1 is pressed
while(1) {
//Child Process
if (pidVal == 0) {
printf("Child Process (should be 0): %d\n", pidVal);
val = getInput();
printf("You typed: %d\n", val);
//Write to parent
close(fd[0]);
write(fd[1], &val, sizeof(val));
//Read if parent sends sum yet
close(fd2[1]);
read(fd2[0], &final, sizeof(final));
//If sum sent from parent, print and terminate
if (final != -9999999) {
printf("%d\n", final);
exit(0);
}
}
//Parent Process
if (pidVal > 0) {
printf("I'm the parent (should be > 0): %d\n", pidVal);
//Read what child sent to the pipe
close(fd[1]);
read(fd[0], &val, sizeof(val));
//If exit value recieved
if (val == -1) {
//Sum the numbers sent
sum = finish(arr, i);
//Close read directory
close(fd2[0]);
//Write the sum to the pipe
write(fd2[1], &sum, sizeof(sum));
}
//Not -1 as input
else {
//Collect input
arr[i] = val;
i++;
}
}
}
}
However the problem is when I try and send more than one number, the program is stuck, as you can see from this sample output:
I'm the parent (should be > 0): 5673
Child Process (should be 0): 0
3 //My Input
You typed: 3
I'm the parent (should be > 0): 5673
1 //My Input
2 //My Input
I noticed how the child process does not seem to execute at the second iteration, yet the parent does, which leads me to believe the child is terminating after the first run. How can I keep this child alive until the user enters -1? More importantly I want to achieve this functionality with only one parent and one child process derived from only a single fork call for the entire program. Is this possible?
In your child section:
while(1) {
if (pidVal == 0) {
printf("Child Process (should be 0): %d\n", pidVal);
val = getInput();
printf("You typed: %d\n", val);
//Write to parent
close(fd[0]);
write(fd[1], &val, sizeof(val));
//Read if parent sends sum yet
close(fd2[1]);
read(fd2[0], &final, sizeof(final));
//If sum sent from parent, print and terminate
if (final != -9999999) {
printf("%d\n", final);
exit(0);
}
}
...
You're reading a single value from the user, sending it to the parent, then waiting for the result from the parent. The parent meanwhile has read the first value from the child and is waiting for another value, so the parent and child are deadlocked waiting for the other to send them something.
You want the child to loop reading values until it gets -1, then wait for the parent.
if (pidVal == 0) {
printf("Child Process (should be 0): %d\n", pidVal);
do {
val = getInput();
printf("You typed: %d\n", val);
//Write to parent
close(fd[0]);
write(fd[1], &val, sizeof(val));
} while (val != -1);
close(fd2[1]);
read(fd2[0], &final, sizeof(final));
//If sum sent from parent, print and terminate
if (final != -9999999) {
printf("%d\n", final);
exit(0);
}
}
I would like to ask you guys some help with C programming. Basically Im having issues with fork() system call.
Here's my question:
We have a Manager Process which has to create POP_SIZE Student processes. Manager Process and Student Processes itself cannot do anything else until all Student Processes have been created.
Every Student Process is identified by:
1) its identification number (6-digit integer)
2) grade obtained in specific exam (integer)
Here's the code I managed to write:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#define POP_SIZE 10
int main(int argc, char *argv[]){
pid_t firstFork;
int *status;
int numStudents = 0;
pid_t managerChild, managerParent;
pid_t students[POP_SIZE];
int studentStatus[POP_SIZE];
switch(firstFork = fork()){
case -1:
perror("Something wrong with fork()\n");
break;
case 0:
managerChild = getpid();
printf("Manager Child Process %d started\n", managerChild);
printf("I have to create %d Student Processes\n", POP_SIZE);
for(int i = 0; i < POP_SIZE; i++){
switch(students[i] = fork()){
case -1:
perror("Something wrong with FORK in Manager Child Process\n");
break;
case 0:
printf("Created first Student Process PID: %d\n", getpid());
numStudents++;
break;
default:
printf("Haven't created all Student Processes\n");
waitpid(managerChild, status, WUNTRACED | WNOHANG);
printf("%d Student Processes succesfully created\n", numStudents);
break;
}
}
break;
default:
for(int i = 0; i < POP_SIZE; i++)
wait(NULL);
}
}
I'd need some help in understanding where to put wait(*status) or waitpid(pid, *status, __options) functions in my code in order to achieve my requirements specified above?
Moreover, how can I assign and keep storing of variables for every single process?
Thank you very much
Since you will be creating many child processes, it is best to start by creating a function that creates the child process, and has it execute a function specified by the caller. Let's assume both the ID number and grade are ints. Then,
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
/* Run func(id, grade) in a child process.
Returns the child process PID if success,
or -1 with errno set in case an error occurs.
*/
pid_t run_child(int id, int grade,
int (*func)(int id, int grade))
{
pid_t p;
p = fork();
if (p == -1) {
/* fork() failed; it set errno to indicate the error. */
return -1;
} else
if (!p) {
/* Run child process function. When it returns,
have the child exit with that exit status. */
exit(func(id, grade));
} else {
/* Parent process. p is positive. */
return p;
}
}
Note that the third parameter is a function pointer. We specify it using the function name. That function must take two int parameters (the ID and the grade, respectively), and return an int. For example:
/* Each child process runs this function.
*/
int child_process(int id, int grade)
{
printf("Child: id = %d, grade = %d, PID = %d.\n", id, grade, (int)getpid());
return EXIT_SUCCESS;
}
We can create a child process that runs that function using child_pid = run_child(123456, 5, child_process);. Note how the name of the function can be used to specify a function pointer. The standard C qsort() function uses the exact same mechanism to allow one to quicksort anything; the caller just needs to specify a function that can compare two elements in the array to be sorted.
We will be creating several children, and reaping them at once. That means it makes sense to write a function that reaps all child processes, essentially blocking until they all exit. We are likely interested in the exit statuses of at least some of them, so let's pass the interesting child processes PIDs, ints to save the status to, and the number of processes in those arrays, as parameters:
/* Reap all child processes.
If child_count > 0, child processes with PID in child_pid[]
will have child_pid[] negated when reaped, with exit status saved
in child_status.
The function returns the number of child processes reaped.
*/
size_t reap_children(pid_t *child_pid, int *child_status, size_t child_count)
{
size_t reaped = 0;
size_t i;
int status;
pid_t p;
while (1) {
/* Reap a child process, if any. */
p = wait(&status);
if (p == -1) {
/* errno == EINTR is not an error; it occurs when a
signal is delivered to a hander installed without
SA_RESTART flag. This will not occur in this program,
but it is good practice to handle that case gracefully. */
if (errno == EINTR)
continue;
/* errno set by wait(). */
return reaped;
}
/* Another child process was reaped. */
reaped++;
/* If the reaped child was one of the interesting ones,
negate its pid and save the exit status. */
for (i = 0; i < child_count; i++) {
if (child_pid[i] == p) {
child_pid[i] = -p;
child_status[i] = status;
break;
}
}
}
}
Note that p = wait(&status) reaps a child process. This means that if one or more child processes have already exited, it picks one of them, and returns its PID, with exit status saved to &status. If all child processes left are still running, the call will wait until at least one of them exits. If there are no more child processes, it returns -1 with errno set to ECHILD.
If signal handlers were used, wait() can also return -1 with errno set to EINTR, if a signal was delivered to a signal handler that was installed without the SA_RESTART flag with sigaction(). Many programmers forgo this check (because "it'll never happen"), but I do like to include that check because it is easy, and makes sure adding signal handling to my code won't bite me in the butt later on. I very often do, too. (Add signal handling, I mean.)
The reason we negate the pids when the respective child process is reaped, is simple: it allows us to easily detect which child processes were reaped. (POSIX says all process IDs are positive, and pid_t is a signed type. Negating a PID is a commonly used technique, too; just see e.g. waitpid().)
If we wanted to reap a specific child process, we'd use waitpid(). For example,
pid_t child, p; /* wait for 'child'. */
int status;
do {
p = waitpid(child, &status, 0);
if (p == -1) {
if (errno == EINTR)
continue;
break;
}
} while (p != child);
if (p == child) {
/* Reaped 'child', status in 'status'. */
} else {
/* Error: failed to reap 'child'. See 'strerror(errno)'. */
}
Do note that in POSIX/Unix terminology 'child process' refers to processes created by this process only; not "grandchildren", processes created by child processes.
I prefer to write my processes to take in parameters from the command line. If no parameters are specified, or -h or --help is specified, a short help ("usage") is displayed; this is extremely common in POSIX and Unix command-line tools, and therefore very intuitive.
The following main() takes one or more ID:grade as command-line parameters. For each one, it creates a child process, and has it run the child_process() function with the specified ID and grade. The main program will then reap them all, and describe the exit status of each child process.
int main(int argc, char *argv[])
{
pid_t child_pid[argc];
int child_status[argc];
int count, i, n, arg, id, grade, status;
char dummy;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s ID:GRADE [ ID:GRADE ]*\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
status = EXIT_SUCCESS;
count = 0;
for (arg = 1; arg < argc; arg++) {
if (sscanf(argv[arg], "%d:%d %c", &id, &grade, &dummy) == 2) {
child_pid[count] = run_child(id, grade, child_process);
if (child_pid[count] == -1) {
fprintf(stderr, "Cannot fork a child process: %s.\n", strerror(errno));
status = EXIT_FAILURE;
} else
count++;
} else {
fprintf(stderr, "%s: Not a valid ID:GRADE specification.\n", argv[arg]);
status = EXIT_FAILURE;
}
}
if (count < 0) {
fprintf(stderr, "No running child processes.\n");
return EXIT_FAILURE;
}
n = reap_children(child_pid, child_status, count);
printf("Reaped %d child processes.\n", n);
for (i = 0; i < count; i++) {
if (child_pid[i] < 0) {
printf("Child process %d (%d of %d)", (int)(-child_pid[i]), i + 1, count);
if (WIFEXITED(child_status[i])) {
if (WEXITSTATUS(child_status[i]) == EXIT_SUCCESS)
printf(" exited with success (EXIT_SUCCESS), %d.\n", EXIT_SUCCESS);
else
if (WEXITSTATUS(child_status[i]) == EXIT_FAILURE)
printf(" exited with failure (EXIT_FAILURE), %d.\n", EXIT_FAILURE);
else
printf(" exited with status %d.\n", WEXITSTATUS(child_status[i]));
} else
if (WIFSIGNALED(child_status[i])) {
printf(" died from signal %d.\n", WTERMSIG(child_status[i]));
} else {
printf(" died from unknown causes.\n");
}
} else {
printf("Child process %d (%d of %d) was lost!\n", (int)child_pid[i], i + 1, count);
}
}
return status;
}
If you save the above as example.c, you can compile it to example using e.g.
gcc -Wall -O2 example.c -o example
If you then run say
./example 100001:1 100002:5 100003:3 21532:4
the output will be something like
Child: id = 100002, grade = 5, PID = 1260.
Child: id = 100001, grade = 1, PID = 1259.
Child: id = 100003, grade = 3, PID = 1261.
Child: id = 21532, grade = 4, PID = 1262.
Reaped 4 child processes.
Child process 1259 (1 of 4) exited with success (EXIT_SUCCESS), 0.
Child process 1260 (2 of 4) exited with success (EXIT_SUCCESS), 0.
Child process 1261 (3 of 4) exited with success (EXIT_SUCCESS), 0.
Child process 1262 (4 of 4) exited with success (EXIT_SUCCESS), 0.
Note that the initial Child: lines can be in any order, because the child processes run essentially in parallel. Each child process runs as soon as it is started, so this example is not a copy-and-paste answer to OP's requirements.
If you want to experiment with complex process hierarchies, I recommend using Graphviz to visualize them. For example, dot-kids.c:
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static void reap_all(void)
{
pid_t p;
int status;
while (1) {
p = wait(&status);
if (p == -1) {
if (errno == EINTR)
continue;
if (errno == ECHILD)
return;
fprintf(stderr, "Process %d: reap_all(): %s.\n", (int)getpid(), strerror(errno));
return;
}
printf(" \"%d\" -> \"%d\" [ color=\"#ff0000\" ];\n", (int)p, (int)getpid());
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == EXIT_SUCCESS)
printf(" \"%d\" [ label=\"%d\" ];\n", (int)p, (int)p);
else
printf(" \"%d\" [ label=\"%d (exit %d)\" ];\n", (int)p, (int)p, WEXITSTATUS(status));
} else
if (WIFSIGNALED(status))
printf(" \"%d\" [ label=\"%d (signal %d)\" ];\n", (int)p, (int)p, WTERMSIG(status));
else
printf(" \"%d\" [ label=\"%d (lost)\" ];\n", (int)p, (int)p);
fflush(stdout);
}
}
static pid_t run_child(int (*child)(int depth, int width), int depth, int width)
{
pid_t p;
fflush(stdout);
fflush(stderr);
p = fork();
if (p == -1) {
fprintf(stderr, "Process %d: Cannot fork: %s.\n", (int)getpid(), strerror(errno));
return -1;
} else
if (!p) {
exit(child(depth, width));
} else {
printf(" \"%d\" -> \"%d\" [ color=\"#0000ff\" ];\n", (int)getpid(), (int)p);
fflush(stdout);
return p;
}
}
int child(int depth, int width)
{
if (depth > 0) {
while (width > 0)
run_child(child, depth - 1, width--);
reap_all();
}
return EXIT_SUCCESS;
}
int main(int argc, char *argv[])
{
int depth, width, i;
char dummy;
if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[2], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s depth width | dot -Tx11\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
if (sscanf(argv[1], " %d %c", &depth, &dummy) != 1 || depth < 0) {
fprintf(stderr, "%s: Invalid depth.\n", argv[1]);
return EXIT_FAILURE;
}
if (sscanf(argv[2], " %d %c", &width, &dummy) != 1 || width < 1) {
fprintf(stderr, "%s: Invalid width.\n", argv[2]);
return EXIT_FAILURE;
}
printf("digraph {\n");
printf(" \"%d\" [ shape=\"box\", label=\"%d\" ];\n", (int)getpid(), (int)getpid());
fflush(stdout);
for (i = 0; i < width; i++)
run_child(child, depth, width - 1);
reap_all();
printf("}\n");
return EXIT_SUCCESS;
}
Compile it using e.g.
gcc -Wall -O2 dot-kids.c -o dot-kids
and run using e.g.
./dot-kids 1 3 | dot -Tx11
to see a process graph similar to
where the numbers are process IDs, blue arrows show which process created which, and red arrows show which process reaped which.
I think there are some mistakes in your code. The output I get is something like:
5 Student Processes succesfully created
Haven't created all Student Processes
Haven't created all Student Processes
3 Student Processes succesfully created
4 Student Processes succesfully created
Created first Student Process PID: 11436
Created first Student Process PID: 11438
Created first Student Process PID: 11437
Haven't created all Student Processes
4 Student Processes succesfully created
Haven't created all Student Processes
3 Student Processes succesfully created
Created first Student Process PID: 11439
Haven't created all Student Processes
3 Student Processes succesfully created
Created first Student Process PID: 11440
Haven't created all Student Processes
3 Student Processes succesfully created
Created first Student Process PID: 11441
Haven't created all Student Processes
2 Student Processes succesfully created
Created first Student Process PID: 11442
Created first Student Process PID: 11443
You see there are too much children executing, so this should make you suspicious (paricularly note that sometimes the number for the student processes seems decreasing from print to print). The parent will continue executing the for loop. However the child continues executing from the point where the fork is called and being it inside a loop, it will fork too creating another child and so on and so on. To avoid that you need a break from the for loop for the children processes.
You can try something like the following. I added a variable jj that if <0 means it is a child process executing. Before next loop iteration the variable is checked and if <0 it breaks from the for loop.
It is not the most elegant solution but seems ok.
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#define POP_SIZE 10
int main(int argc, char *argv[]){
pid_t firstFork;
int *status;
int numStudents = 0;
pid_t managerChild, managerParent;
pid_t students[POP_SIZE];
int studentStatus[POP_SIZE];
switch(firstFork = fork()){
case -1:
printf("Something wrong with fork()\n");
break;
case 0:
managerChild = getpid();
printf("Manager Child Process %d started\n", managerChild);
printf("I have to create %d Student Processes\n", POP_SIZE);
int jj = 0;
for(int i = 0; i < POP_SIZE; i++){
switch(students[i] = fork()){
case -1:
printf("Something wrong with FORK in Manager Child Process\n");
jj = -1;
break;
case 0:
printf("Created first Student Process PID: %d\n", getpid());
numStudents++;
jj = -1;
break;
default:
printf("Haven't created all Student Processes\n");
waitpid(managerChild, status, WUNTRACED | WNOHANG);
printf("%d Student Processes succesfully created\n", numStudents);
break;
}
if (jj<0) break;
}
break;
default:
for(int i = 0; i < POP_SIZE; i++)
wait(NULL);
}
}
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.
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 of exit(0) and do the computation where I calling wait(NULL) and call 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
I'm trying to do a program with fork() in C but when I create a char array inside my code the program produces a unexpected result.(Instead of create 5 five sons it creates 6 sons? And the father?)
The code is this
#include <stdlib.h>
#include <stdio.h>
#define NUMPROC 5
int main (int argc, char *args[]){
//UNCOMMENTTHIS LINE
//char a[] = {'a','b','c','d','\0'};
int i;
pid_t status[NUMPROC];
for(i = 0; i< NUMPROC; i++){
status[i] = fork();
//fork error
if(status[i] == -1){
perror("fork() error");
exit(1);
}
//quit because I'm a son
if(status[i] == 0)
break;
}
//son
if(status[i] == 0){
printf("I'm son number: %i\n", i);
}
//father
else{
//wait sons
for(i = 0; i < NUMPROC; i++){
wait(&status[0]);
}
printf("Father terminated\n");
}
}
If you try to uncomment the array's line the result changes but this array is never used!
Can you explain me why??
//son
if(status[i] == 0){
printf("I'm son number: %i\n", i);
}
Code above doesn't work correctly in the father, for I will be equal to NUMPROC, which should be NUMPROC - 1.
This is a simple array index out of bounds problem. Value of status[NUMPROC] is uninitialized, it's value could be affected by the char array, so you will get different result.
Add the lines indicated and the problem will be obvious:
//son
if (i >= NUMPROC) // add this line
printf("ACK\n"); // and this line
if(status[i] == 0){
printf("I'm son number: %i\n", i);
}
there seems to be a misunderstanding about when/where each possible return state from the call to fork() occurs and how to handle the child state.
all the child actions need to be within the for() loop
all the parent actions (except the call to wait() need to be within the for() loop
The child actions must terminate in a call to exit()
Suggest the following code:
cleanly compiles
performs the desired operation
incorporates the initial comments on OPs question
and now, the proposed code
#include <stdlib.h> // exit(), EXIT_SUCCESS
#include <stdio.h> // printf()
#include <unistd.h> // fork(), pid_t
#include <sys/types.h> // #defines for waitpid()
#include <sys/wait.h> // waitpid()
#define NUMPROC (5)
int main ( void )
{
int i;
pid_t pid[NUMPROC] = {0};
for(i = 0; i< NUMPROC; i++)
{
pid[i] = fork();
switch( pid[i] )
{
case -1: // error
perror("fork() error");
// keep going,
// so if any current child's exit,
// they do not become zombie processes
break;
case 0: // child
printf("I'm son number: %i\n", i);
exit( EXIT_SUCCESS );
break;
default: // parent
break;
} // end switch
} // end for
// only father gets here
//wait sons
for(i = 0; i < NUMPROC; i++)
{
if( -1 != pid[i] )
{ // then child process successfully created
waitpid( pid[i], NULL, 0 );
}
}
printf("Father terminated\n");
}
here is the output from a typical run of the program:
I'm son number: 0
I'm son number: 1
I'm son number: 4
I'm son number: 2
I'm son number: 3
Father terminated
I currently have to write a program which creates as much forked processes as the user wants, then wait for all of them to complete and let them return a random number between 1 and 6.
This is my code so far:
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
int main(){
int n,i;
int *returnvalue, value;
int pid;
int waitingID;
returnvalue = &value;
printf("How many processes to start?\n");
scanf("%d",&n);
for(i=0; i < n; i++){
pid = fork();
if(pid==0){
printf("I am %d, from iteration %d\n",getpid(), i);
}
else if(pid > 0){
waitingID = waitpid(pid, returnvalue, 0);
printf("Return-value of %d is: %d\n", waitingID, *returnvalue);
break;
}
else{
printf("A problem occured.");
}
}
srand(time(NULL));
exit((rand()%6)+1);
return 0;
}
It actually basically works so far, but I am never getting a number between 1 and 6, but rather some values like 768, 512, 256 and so on.
It feels like the random line is just being ignored.
How can I fix my code in order to return proper random values?
The value waitpid() returns via its second argument is not the exit code of the collected process. Rather, it is a bitmask containing the exit code (if in fact the process exited) along with several other details. There is a collection of macros declared in wait.h by which you can extract the various pieces.
In particular, given waitpid(pid, returnvalue, 0) > 0, you can determine whether, in fact, the process exited (as opposed, for example, to being stopped) by testing WIFEXITED(*returnValue). If indeed it did, then you can get the exit status WEXITSTATUS(*returnValue). Thus, you might write
else if (pid > 0){
waitingID = waitpid(pid, returnvalue, 0);
if (waitingID < 0) {
perror("While waiting on a child process");
} else if (waitingId == 0) {
printf("wait() unexpectedly returned 0\n");
} else if (WIFEXITED(*returnValue)) {
printf("Process %d exited with code: %u\n", waitingID,
WEXITSTATUS(*returnvalue));
} else {
printf("Process %d was stopped or continued\n", waitingID);
}
break;
}