Attempting to learn C forking. It correctly prints the number of times the main loop is suppose to run and the right number of threads but the execution time is off and the program never terminates. Am I making an infinite amount of processes?
After some suggestions here is a cleaner version of the code. The old version is located below. The updated part is still creating to many child processes and never exiting. I'm just not seeing what's going wrong.
Update: Suggestion by John Hascall fixed a formatting and threads running out of order. An infinite number of threads are still generated but now in the correct order. I.e prints thread execution time 1, 2, 3, 4... etc. Don't think the problem is the wait syscall but going to study it and see if I can't find anything.
Update**: I found the solution. The first problem I believe was that I didn't have a wait command and the second is that when putting in the wait I accidentally removed the check for count < argv[1]. I put it back in and it seems to be running correctly! Thanks for the help and style pointers everyone! Working version is below.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "./processes.h"
int main(int argc, char** argv) {
if (argc != 4) {
printf("Wrong number of arguments entered. Usage: #processes sleepTime inputFile.\n");
return 1;
}
if(atoi(argv[1]) <= 0){
printf("Incorrect number of children, must be greater than 0");
return -1;
}
int count = 0;
int index;
Child *child = malloc(sizeof(Child) * atoi(argv[1]));
int childIndex;
int pid;
do{
switch (pid = fork()){
case -1:
printf("Fork failed\n");
exit(1);
case 0:
sleep(atoi(argv[2]) * childIndex);
gettimeofday(&child[childIndex].endTime, NULL);
double elapsed = child[childIndex].endTime.tv_usec - child[childIndex].startTime.tv_usec;
printf("Time for process %d = %f microseconds\n", childIndex, elapsed);
break;
default:
childIndex = count + 1;
gettimeofday(&child[count].startTime, NULL);
child[count].index = count + 1;
child[count].pid = pid;
count++;
}
} while((wait(NULL) != -1) && (count < atoi(argv[1])));
return 1;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "./processes.h"
int main(int argc, char** argv) {
if (argc != 4) {
printf("Wrong number of arguments entered. Try again.");
return 1;
}
if(atoi(argv[1]) <= 0){
printf("Incorrect number of children, must be greater than 0");
return -1;
}
int count;
int index;
Child *child = malloc(sizeof(Child) * atoi(argv[1]));
int pid = 1;
int childIndex;
for (count = 0; count < atoi(argv[1]); count++) {
if (pid != 0) {
childIndex = count + 1;
gettimeofday(&child[count].startTime, NULL);
child[count].index = count + 1;
pid = fork();
if (pid != 0){
child[count].pid = pid;
printf("Main thread loop: %d\n", count);
printf("Child process: %d\n", getpid());
}
}
}
if (pid == 0) {
//this is the child process
sleep(atoi(argv[2]) * childIndex);
gettimeofday(&child[childIndex].endTime, NULL);
double elapsed = child[childIndex].endTime.tv_usec - child[childIndex].startTime.tv_usec;
printf("Time for process %d = %f microseconds\n", childIndex, elapsed);
//printf("This is thread %d reporting in.\n", childIndex);
}
// printf("Testing\n");
return 1;
}
The bigest issue is your child's code:
if (pid == 0) {
....
}
belongs in the same loop (say right after) the parent's code:
if (pid != 0) {
....
}
Also, you never check for pid == -1 (the fork() failed).
A more standard way to write something like this is:
switch (pid = fork()) {
case -1:
/* handle fork error */
exit(1);
case 0:
/* child code goes here */
_exit(0);
default:
/* parent code goes here */
}
/* Also you probably want to look into the `wait()` syscall. */
do {} while (wait(NULL) != -1); /* <--- the very minimum */
Related
I've been working on the following code for quite some time, but can't really figure it out.
The task is to read a terminal command and to run it every x seconds; if the command hasn't finished within the waiting time, we want to kill the process and afterwards run the command again.
Any help would be really appreciated.
I'm pretty sure I'm not using waitpid() correctly; how would I go about using waitpid to achieve the goal?
Additionally, how would I go about detecting an error within a child process? The plan is to kill the parent process if an error occurred in a child process.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
/*
*
*/
int main(int argc, char** argv) {
int waitingTime;
if (argc < 3) {
printf("Invalid number of arguments provided. Please specify a command and exactly one parameter.");
return (EXIT_FAILURE);
}
// -n parameter specified? If so, set the waiting time.
if (argc == 5 && strcmp(argv[3], "-n") == 0) {
waitingTime = atoi(argv[4]);
} else {
waitingTime = 5; // Default waiting time.
}
char* cmd = (char*)malloc(sizeof(argv[1]));
cmd = argv[1];
char* param = (char*)malloc(sizeof(argv[2]));
param = argv[2];
// Print the read command and its param
printf("Command: %s, Parameter: %s, Interval: %d\n\n", cmd, param, waitingTime);
pid_t pid;
for (;;) {
// Declared here for scope
int secsWaited;
secsWaited = 0;
pid = fork();
if (pid == 0) {
pid = getpid();
printf("==============\n");
execlp(cmd, cmd, param, "/", (char *)NULL);
printf("Excec failed; killing the proccess.");
kill(pid, SIGKILL);
} else if (pid > 0) {
int status, code;
for (;;) {
code = waitpid(pid, &status, WNOHANG);
if (code == 0 && secsWaited >= waitingTime) {
kill(pid, SIGKILL);
printf("Child stopped");
break;
} else if (code == 0 && secsWaited < waitingTime) {
secsWaited++;
sleep(1);
} else {
break;
}
}
/*if (!WIFEXITED(status)) {
printf("Time exceeding, stopping child.");
// Get parent process id and kill it.
kill(getpid(), SIGKILL);
}*/
// Sleep for the specified time
sleep(waitingTime - secsWaited);
} else {
return (EXIT_FAILURE);
}
}
free(cmd);
free(param);
return (EXIT_SUCCESS);
}
Your logic was a bit too complicated (e.g. too many different sleep calls and if/else ladder logic).
Also, no need to malloc the argv strings--they can be used directly.
I've simplified it and restructured a bit to get it to work [please pardon the gratuitous style cleanup]:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
/*
*
*/
int
main(int argc, char **argv)
{
int waitingTime;
if (argc < 3) {
printf("Invalid number of arguments provided. Please specify a command and exactly one parameter.");
return (EXIT_FAILURE);
}
// -n parameter specified? If so, set the waiting time.
if (argc == 5 && strcmp(argv[3], "-n") == 0) {
waitingTime = atoi(argv[4]);
}
else {
waitingTime = 5; // Default waiting time.
}
char *cmd = argv[1];
char *param = argv[2];
// Print the read command and its param
printf("Command: %s, Parameter: %s, Interval: %d\n\n",
cmd, param, waitingTime);
pid_t pid;
int code = -1;
int status;
int killflg = 1;
for (;;) {
// Declared here for scope
int secsWaited;
secsWaited = 0;
pid = fork();
// stop on fork failure
if (pid < 0) {
killflg = 1;
break;
}
// child process
if (pid == 0) {
pid = getpid();
printf("==============\n");
#if 0
execlp(cmd, cmd, param, "/", (char *) NULL);
#else
execlp(cmd, cmd, param, (char *) NULL);
#endif
printf("Excec failed; killing the proccess.");
// NOTE/BUG: this is the child so pid is zero, so killing it is wrong
#if 0
kill(pid, SIGKILL);
#else
exit(1);
#endif
}
killflg = 0;
for (;;) {
code = waitpid(pid, &status, WNOHANG);
if (code > 0)
break;
if (killflg)
continue;
secsWaited++;
sleep(1);
if (secsWaited >= waitingTime) {
printf("timeout\n");
kill(pid, SIGKILL);
killflg = 1;
}
}
if (! killflg)
break;
}
#if 0
free(cmd);
free(param);
#endif
if (killflg)
code = EXIT_FAILURE;
else
code = EXIT_SUCCESS;
return code;
}
UPDATE:
Right now, the program will stop after one iteration; if I remove the breakpoint at if (! killflg), it will work as expected. Am I missing something or is this just a misunderstanding?
You are correct--my bad. I had missed the following in your question:
The task is to read a terminal command and to run it every x seconds;
Change the break into sleep(waitingTime - secsWaited).
But, a more robust way to keep track of elapsed time may be via two calls to time(2):
After the killflg = 0, do: time_t todbeg = time(NULL); time_t todelap;. Then, you can get elapsed time [anywhere] with: todelap = time(NULL) - todbeg; [here, todelap is similar to secsWaited]. This may be better than incrementing secsWaited.
time only has seconds resolution. For more precision control, consider using clock_gettime [has nanosecond resolution].
Here's a function that I use a lot for elapsed time [in fractional seconds]:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <time.h>
double
tvgetf(void)
{
struct timespec ts;
double sec;
clock_gettime(CLOCK_REALTIME,&ts);
sec = ts.tv_nsec;
sec /= 1e9;
sec += ts.tv_sec;
return sec;
}
/*
*
*/
int
main(int argc, char **argv)
{
int waitingTime;
if (argc < 3) {
printf("Invalid number of arguments provided. Please specify a command and exactly one parameter.");
return (EXIT_FAILURE);
}
// -n parameter specified? If so, set the waiting time.
if (argc == 5 && strcmp(argv[3], "-n") == 0) {
waitingTime = atoi(argv[4]);
}
else {
waitingTime = 5; // Default waiting time.
}
char *cmd = argv[1];
char *param = argv[2];
// Print the read command and its param
printf("Command: %s, Parameter: %s, Interval: %d\n\n", cmd, param, waitingTime);
pid_t pid;
int code = -1;
int status;
int killflg = 1;
double todzero = tvgetf();
for (;;) {
// Declared here for scope
double todbeg = tvgetf();
double todelap;
pid = fork();
// stop on fork failure
if (pid < 0) {
killflg = 1;
break;
}
// child process
if (pid == 0) {
pid = getpid();
printf("============== (%.9f)\n",tvgetf() - todzero);
execlp(cmd, cmd, param, (char *) NULL);
printf("Excec failed; killing the proccess.");
exit(1);
}
killflg = 0;
for (;;) {
code = waitpid(pid, &status, WNOHANG);
if (code > 0)
break;
if (killflg)
continue;
usleep(1000);
todelap = tvgetf() - todbeg;
if (todelap >= waitingTime) {
printf("timeout\n");
kill(pid, SIGKILL);
killflg = 1;
}
}
// do _not_ wait -- we already timed out
if (killflg)
continue;
// get final elapsed time for this round and the amount of time
// remaining until the next interval
todelap = tvgetf() - todbeg;
useconds_t time_to_wait = ((double) waitingTime - todelap) * 1e6;
// wait until the next time period
if (time_to_wait > 0)
usleep(time_to_wait);
}
if (killflg)
code = EXIT_FAILURE;
else
code = EXIT_SUCCESS;
return code;
}
Side note: I used usleep here, but, although slightly more complex, it's considered better to use nanosleep
I'm trying to create the process tree shown in the picture. Basically if the level is even I want to create one child process and terminate the parent process. If the level is odd I wanna create two child processes and then terminate the parent process. I have written a program right now but I think it's so hard to visualize what process tree my program is actually creating. I've written some comments to the code to explain how I've been thinking. I also want to output the PID of the bottom children of the tree which my code doesn't do correctly.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[]){
pid_t pid, ppid;
int n, i;
int childstate;
int count = 0;
if(argc != 2){
printf("Wrong number of arguments");
exit(-1);
}
n = atoi(argv[1]);
fork(); //start process 0
for(i = 1; i < n + 1; i++){
if(i % 2 != 0){
fork(); //if odd level start 1 child process
if(getpid() == 0){
kill (getppid(), 9); //terminate parent process
}
} else {
if(fork() > 0){ //start new process
fork(); //if new process is not a child start another process
if(getpid() == 0){
kill (getppid(), 9); //terminate parent process
}
}
}
if(i == n){ //print pid of leaves (not working correctly)
printf("Process: %d \n", getpid());
}
}
return 0;
}
I also want to output the PID of the bottom children of the tree which my code doesn't do correctly.
Have your processes output the tree in Dot language, and use Graphviz to output the tree.
For example, if you save the following as say tree.c:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int process(const unsigned int level, const unsigned int maxlevel, FILE *dot)
{
int status = EXIT_SUCCESS, childstatus;
unsigned int children, i;
pid_t p, child[2];
if (dot) {
/* Output a node for this child, */
fprintf(dot, " \"%ld\" [ label=\"Process %ld\" ];\n", (long)getpid(), (long)getpid());
/* and if not at the top level (0), an edge from our parent. */
if (level)
fprintf(dot, " \"%ld\" -> \"%ld\";\n", (long)getppid(), (long)getpid());
fflush(dot);
}
/* No more forking? */
if (level >= maxlevel) {
if (level)
exit(status);
else
return status;
}
/* Odd levels create two child processes, even one. */
if (level & 1)
children = 2;
else
children = 1;
/* Fork the child processes, */
for (i = 0; i < children; i++) {
child[i] = fork();
if (child[i] == -1) {
fprintf(stderr, "Cannot fork: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
} else
if (!child[i]) {
/* have each child run process() and nothing else, */
exit(process(level + 1, maxlevel, dot));
}
/* This line is run in parent only. */
}
/* and wait for them. */
for (i = 0; i < children; i++) {
if (child[i] != -1) {
do {
p = waitpid(child[i], &childstatus, 0);
} while (p == -1 && errno == EINTR);
if (p != child[i])
status = EXIT_FAILURE;
} else
status = EXIT_FAILURE;
}
if (level)
exit(status);
else
return status;
}
int dot_process_tree(const int levels, FILE *out)
{
int retval = EXIT_SUCCESS;
if (out) {
fprintf(out, "digraph {\n");
fflush(out);
}
if (levels > 0)
retval = process(0, levels - 1, out);
if (out) {
fprintf(out, "}\n");
fflush(out);
}
return retval;
}
int main(void)
{
return dot_process_tree(5, stdout);
}
and compile and run it using
reset ; gcc -Wall -Wextra -O2 tree.c -o tree && ./tree | dot -Tx11
you'll get a nice graphic process tree. (Use dot -Tsvg > out.svg or dot -Tpng > out.png to save it as an SVG or PNG image.) On my system:
Do note that there is no reason why the process IDs should be in the tree order. Although e.g. Linux hands them off in a rather ordered fashion, they can be in any order, even totally random. So do not make any assumptions on the PIDs.
The Dot language itself is simple. The output of the above program is something like
digraph {
"12375" [ label="Process 12375" ];
"12377" [ label="Process 12377" ];
"12375" -> "12377";
"12378" [ label="Process 12378" ];
"12377" -> "12378";
"12379" [ label="Process 12379" ];
"12377" -> "12379";
"12380" [ label="Process 12380" ];
"12378" -> "12380";
"12381" [ label="Process 12381" ];
"12379" -> "12381";
"12382" [ label="Process 12382" ];
"12380" -> "12382";
"12384" [ label="Process 12384" ];
"12381" -> "12384";
"12383" [ label="Process 12383" ];
"12380" -> "12383";
"12385" [ label="Process 12385" ];
"12381" -> "12385";
}
which should be obvious; nodes are named by the process ID, and [ label="Title" ] sets the text in the node. It is not from the same run as the diagram above, so the process IDs differ.
In Dot, numbers do need to be quoted if used as a name, but if a name starts with a letter, you don't need to quote it. See Graphviz documentation for further details. (The Node, Edge and Graph Attributes page is the one you usually need.)
If you want the level display in each node, use
fprintf(dot, " \"%ld\" [ label=\"Process %ld, level %u\" ];\n", (long)getpid(), (long)getpid(), level + 1);
in process(). (It uses level 0 forwards, with all nonzero levels being child processes, and level 0 being the original process. That's why level 0 returns, and all other levels exit().)
From you description, your basic logic should be:
void fork_loop(int level, int stop) {
if (level > stop) return;
if (is_even(level)) {
fork_child(level, stop);
exit(0);
} else {
fork_child(level, stop);
fork_child(level, stop);
exit(0);
}
}
Where fork_child() calls fork(). The child process would call fork_loop(level+1, stop), while the parent would return.
fork(); //if odd level start 1 child process
if (getpid() == 0){
kill (getppid(), 9); //terminate parent process
}
This logic is wrong: getpid() does not return 0 / fork doesn't return a pid in the child process - it just returns 0 to signify that it is the child process - it can know parent's pid by calling getpid before.
The logic should be:
pid_t child = fork();
if (child > 0) {
// use exit instead of kill! exit terminates this process
exit(0);
}
if (child < 0) {
... an error occurred in fork ...
}
The getpid can never be zero. As I mentioned in my top comments, you want the parent to wait on children, not the other way round and too many forks.
Here's a cleaned up version that I think works:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
pid_t pid;
pid_t ppid;
int i;
int n;
int pcur;
int pcnt;
if (argc != 2) {
printf("Wrong number of arguments");
exit(-1);
}
n = atoi(argv[1]);
pid = fork(); // start process 0
if (pid != 0) {
wait(NULL);
n = -5;
}
for (i = 1; i < n + 1; i++) {
// odd/even level -- get number of children to start
// NOTE: you may need to reverse this if
if (i % 2 != 0)
pcnt = 1;
else
pcnt = 2;
// get parent pid
ppid = getpid();
// do the forks
for (pcur = 0; pcur < pcnt; ++pcur)
fork();
// get current pid
pid = getpid();
// parent should wait on children
if (pid == ppid) {
while (wait(NULL) >= 0);
break;
}
// print pid of leaves (not working correctly)
if (i == n) {
printf("Process: %d\n", pid);
}
}
return 0;
}
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 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;
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<errno.h>
int main(int argc, char **argv){
int n = atoi(argv[1]);
int superdaddy = getpid();
int p[n+1][2];
int i=0;
int cpid,output;
int result = 0;
if(pipe(p[0])<0){
perror("1");
return 1;
}
if(pipe(p[n])<0){
perror("2");
return 1;
}
output = p[0][1];
if(getpid()==superdaddy){
if(write(p[0][1],&result,sizeof(result))<0){
perror("3");
return 1;
}
if(close(p[0][1])<0){
perror("4");
return 1;
}
}
while(1){
if(i==n){
if(read(p[n-1][0],&result,sizeof(result)<0)){
perror("5");
return 1;
}
result++;
output = p[n][1];
if(write(output,&result,sizeof(result))<0){
perror("6");
return 1;
}
if(close(p[n-1][0])<0){
perror("7");
return 1;
}
if(close(p[n][1])<0){
perror("8");
return 1;
}
break;
}
i++;
cpid = fork();
if(cpid==0){
if(i==n)
continue;
if(pipe(p[i])<0){
perror("9");
return 1;
}
if(read(p[i-1][0],&result,sizeof(result))<0){
perror("10");
return 1;
}
result++;
output = p[i][1];
if(write(output,&result,sizeof(result))<0){
perror("11");
return 1;
}
if(close(p[i-1][0])<0){
perror("12");
return 1;
}
if(close(p[i][1]<0)){
perror("13");
return 1;
}
continue;
}
else if(cpid<0){
perror("14");
return 1;
}
break;
}
if(getpid()==superdaddy){
wait(NULL);
if(read(p[n][0],&result,sizeof(result))<0){
perror("15");
return 1;
}
printf("Result: %d\n",result);
if(close(p[n][0])<0){
perror("16");
return 1;
}
}
return 0;
}
The Program aims to read a number n from command line and then forks n child process and create n pipes. process p0 will be parent of process p1, p1 will be parent of p2, so and so on. One variable (named result here) will be passed through pipes, every time it is passed it will be added by 1. So the output should be n as well. Pipe Fi connects Pi and P(i+1). Attached is my code.
When n=1 or n=2, the program can output correctly, which is 1 and 2 correspondingly. However, when n=3, it gives me a bad file error at error 5. I have hand-tracked the code for the whole afternoon but got no idea what is wrong with it. Anyone could help? Appreciate it first!
when n=3, it gives me a bad file error at error 5.
This could be fixed by removing that if(close(p[i][1]<0)){ in your code, because you need to read from p[i][0] in your last iteration, i.e.
if (i == n) {
if(read(p[n-1][0],&result,sizeof(result)<0)){
...
}
}
This is an implementation of your idea, I hope it may be helpful:
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s N\n", argv[0]);
exit(EXIT_FAILURE);
}
int n = atoi(argv[1]);
int pipes[n][2];
int i, val;
pid_t pid;
val = 0;
for (i = 0; i < n; i++) {
if (pipe(pipes[i]) < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}
if ((pid = fork()) < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
else if (pid == 0) {
close(pipes[i][1]);
if (read(pipes[i][0], &val, sizeof(val)) != sizeof(val)) {
perror("read");
exit(EXIT_FAILURE);
}
printf("C %d read %d\n", getpid(), val);
val++;
}
else {
close(pipes[i][0]);
printf("P %d writes %d\n", getpid(), val);
if (write(pipes[i][1], &val, sizeof(val)) != sizeof(val)) {
perror("write");
exit(EXIT_FAILURE);
}
if (waitpid(pid, NULL, 0) != pid) {
perror("waitpid");
exit(EXIT_FAILURE);
}
printf("%d is going to leave.\n", getpid());
exit(EXIT_SUCCESS);
}
}
printf("%d is going to leave.\n", getpid());
exit(EXIT_SUCCESS);
}
Testing run:
$ ./a.out 3
P 2005 writes 0
C 2006 read 0
P 2006 writes 1
C 2007 read 1
P 2007 writes 2
C 2008 read 2
2008 is going to leave.
2007 is going to leave.
2006 is going to leave.
2005 is going to leave.
Explanation:
The frame of that code is for (i = 0; i < n; i++) { pipe(); fork(); }, which means it will create n pipes, and n new processes. In each iteration, the parent will write to pipes[i][1] and child will read from pipes[i][0]. Eventually, it will create a process chain connected by a series of pipes, and a value is passed down from the first process to the last through that series of pipes.