C fork and pipe program with non-deterministic output - c

Lets consider the following code (please do not write, that there are naming problems, structuring problems, etc, I know this, too). It was written to write out the random generated x,y,z and r (and pid) numbers for its 3 children, but it often happens that it only prints two/one "Got this..." lines, and I dont know, why. Could you please explain me, what the problem is, or correct my code?
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h> //fork
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h> //lock
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include "sys/ipc.h"
#include "sys/sem.h"
int child;
int cs[3];
int fd[2];
int t;
int parent;
int child;
void okay(int sign)
{
t = 0;
}
void rr(int sign)
{
char b[50];
while(read(fd[0], &b, 50)<0) sleep(0.5);
printf("Got this: %s \n", b);
}
void ch(int argc, char** argv)
{
printf("Mypid: %i\n", getpid());
close(fd[0]);
while(t==1) sleep(1);
srand((unsigned)time(NULL)); // init
int x,y,z,r,pid;
x = rand() % 101; y = rand() % 101; z = rand() % 101; r = rand() % 101;
pid = getpid();
char b[50];
sprintf(b, "%i %i %i %i %i", pid, x, y, z, r);
while(write(fd[1], b, 50)<0) sleep(0.2);
kill(parent, SIGUSR2);
close(fd[1]);
}
int main(int argc, char** argv)
{
if(argc < 4)
{
printf("Too few args!\n");
return 0;
}
pipe(fd);
t = 1;
parent = getpid();
signal(SIGUSR1, okay);
child = fork();
if(child < 0) perror("FORK");
if(child > 0)
{
cs[0] = child;
child = fork();
if(child < 0) perror("FORK");
if(child > 0)
{
cs[1] = child;
child = fork();
if(child < 0) perror("FORK");
if(child > 0)
{
cs[2] = child; // MAIN
printf("%i %i %i\n", cs[0], cs[1], cs[2]);
close(fd[1]);
signal(SIGUSR2, rr);
kill(cs[0], SIGUSR1); kill(cs[1], SIGUSR1); kill(cs[2], SIGUSR1);
int status;
waitpid(cs[0], &status, 0);
waitpid(cs[1], &status, 0);
waitpid(cs[2], &status, 0);
close(fd[0]);
}else
{ // ch 3
ch(argc, argv);
}
}else
{ // ch 2
ch(argc, argv);
}
}else
{ // ch 1
ch(argc, argv);
}
return 0;
}

Rewritten answer
I was able to get the behaviour described even with various amended versions of the code. For example, one trace I got from a diagnostic-laden version of the code was:
14607 at work
Children: 14608 14609 14610
Children signalled
Child 14609: signal 30 - setting t to 0
Child 14608: signal 30 - setting t to 0
Child 14610: signal 30 - setting t to 0
Child 14609: at work
Child 14610: at work
Child 14608: at work
Child 14609: sending 14609 65 24 97 0
Child 14609: exiting
Child 14610: sending 14610 87 17 23 57
Adult 14607: signal 31 - reading input
Child 14610: exiting
Child 14608: sending 14608 5 89 95 8
Child 14608: exiting
Adult 14607: got <<14609 65 24 97 0>>
Adult 14607: signal 31 - reading input
Adult 14607: got <<14610 87 17 23 57>>
Child 1 ended
Child 2 ended
Child 3 ended
14607 exiting
You can see that the parent got the data from 14609 and 14610, but not from 14608. I'm going to attribute this to the use of signals. They're a very poor mechanism for IPC. And, in this case, they seem to be unreliable on the timing. This was code using sigaction() and with the sa.sa_mask value set to block all signals (sigfillset(&sa.sa_mask)).
However, there really isn't any need to use signals from the child back to the parent. I've left the signal handler in place for the parent to notify the children to get weaving, but simplified it to simply change the value of a volatile sig_atomic_t variable (t by name, still) from 1 to 0. The expression is to 'use' the signal number parameter (called sign in the code); it avoids a warning when I compile using GCC 4.7.1 on Mac OS X 10.7.5:
gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
pipes-13905948.c -o pipes-13905948
The seeds to srand() mix the time with the PID of the process to give different values from each child (using the PID alone would also do that). I weeded out the 16 headers in the original (including two repeats) to 7. I've removed rr() since the parent is no longer responding to signals from the children. I've restructured the code in main() so it doesn't dive off the RHS of the page. The code includes copious diagnostics about what's going on. It is helpful when dealing with multiple processes like this if the majority of the messages have a PID printed as part of the message. I used 'Adult' instead of 'Parent' so the output is neatly aligned with the lines tagged 'Child'. Note that the signal handler is set before the children are forked. On a multi-CPU machine, there is no guarantee about the sequence in which the processes will execute, so leaving signal setup until after forking is unwise at best and liable to lead to unexpected death at worst.
The reading in the signal handler is replaced by reading in the parent code in main(); this is a much more satisfactory way of dealing with input. You should aim to do as little as possible in a signal handler. The C standard doesn't reliably support much more:
ISO/IEC 9899:2011 §7.14.1 The signal function
¶5 If the signal occurs other than as the result of calling the abort or raise function, the
behavior is undefined if the signal handler refers to any object with static or thread
storage duration that is not a lock-free atomic object other than by assigning a value to an
object declared as volatile sig_atomic_t, or the signal handler calls any function
in the standard library other than the abort function, the _Exit function, the
quick_exit function, or the signal function with the first argument equal to the
signal number corresponding to the signal that caused the invocation of the handler.
POSIX is more lenient, but you still need to be very careful about what you do in a signal handler, and you should do as little as possible in a signal handler.
These changes lead to this code:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
static int fd[2];
static volatile sig_atomic_t t = 1;
static int parent;
static void okay(int sign)
{
t = (sign == 0);
}
static void ch(void)
{
int pid = getpid();
printf("Child %i: at work\n", pid);
close(fd[0]);
while (t == 1)
{
printf("Child %d: pausing on t\n", pid);
pause();
}
srand((unsigned)time(NULL) ^ pid);
int x = rand() % 101;
int y = rand() % 101;
int z = rand() % 101;
int r = rand() % 101;
char b[50];
sprintf(b, "%i %i %i %i %i", pid, x, y, z, r);
printf("Child %d: sending %s\n", pid, b);
while (write(fd[1], b, strlen(b)) < 0)
printf("Child %d: write failed\n", pid);
close(fd[1]);
printf("Child %d: exiting\n", pid);
exit(0);
}
int main(void)
{
int cs[3];
pipe(fd);
parent = getpid();
printf("%d at work\n", parent);
struct sigaction sa;
sa.sa_flags = 0;
sigfillset(&sa.sa_mask);
sa.sa_handler = okay;
sigaction(SIGUSR1, &sa, 0);
if ((cs[0] = fork()) < 0)
perror("fork 1");
else if (cs[0] == 0)
ch();
else if ((cs[1] = fork()) < 0)
perror("fork 2");
else if (cs[1] == 0)
ch();
else if ((cs[2] = fork()) < 0)
perror("fork 3");
else if (cs[2] == 0)
ch();
else
{
printf("Children: %i %i %i\n", cs[0], cs[1], cs[2]);
close(fd[1]);
kill(cs[0], SIGUSR1);
kill(cs[1], SIGUSR1);
kill(cs[2], SIGUSR1);
printf("Children signalled\n");
char buffer[64];
int nbytes;
while ((nbytes = read(fd[0], buffer, sizeof(buffer)-1)) > 0)
{
buffer[nbytes] = '\0';
printf("Adult %d: read <<%s>>\n", parent, buffer);
}
int status;
waitpid(cs[0], &status, 0);
printf("Child 1 ended\n");
waitpid(cs[1], &status, 0);
printf("Child 2 ended\n");
waitpid(cs[2], &status, 0);
printf("Child 3 ended\n");
close(fd[0]);
}
printf("%d exiting\n", (int)getpid());
return 0;
}
The code is still flabby on the error handling; there are a lot of unchecked system calls, and unreported results (like child statuses). I'm not convinced about retrying writes on failures, but the code was never exercised.
This is a trace from the revised version of the code.
15745 at work
Children: 15746 15747 15748
Children signalled
Child 15746: at work
Child 15746: sending 15746 63 4 70 89
Child 15748: at work
Child 15746: exiting
Child 15747: at work
Adult 15745: read <<15746 63 4 70 89>>
Child 15748: sending 15748 44 0 99 37
Child 15748: exiting
Child 15747: sending 15747 3 69 68 97
Adult 15745: read <<15748 44 0 99 37>>
Child 15747: exiting
Adult 15745: read <<15747 3 69 68 97>>
Child 1 ended
Child 2 ended
Child 3 ended
15745 exiting
A few times, I got inputs such as:
Adult 15734: read <<15736 83 95 64 2915737 42 63 66 89>>
That combines the output of processes 15736 and 15737 into a single result from read. I'm not happy about that; AFAIK, the reads should be getting the atomic writes of the separate children as separate messages. I'm going to put that down to a quirk of Mac OS X without having researched it further.
Original answer
Since you're using signal() rather than sigaction(), it is possible that your signal handler is reset to SIGDFL before the signal handler is called. You could fix that in okay() by adding:
void okay(int sign)
{
signal(sign, okay);
t = 0;
}
You could monitor whether that's a problem by checking the return value from signal() in the handler.
The rest of your code isn't currently using t (though it is set to 1 in main()). (Inaccurate observation!)
You could make your debugging easier by having more print operations. You could use a loop to kill and collect your children (though it is possible to write the loop out as you have done; don't put three function calls on a single line, though).

Related

Blocking signals in C

I am currently trying to write a program that calls fork() to spawn a child process which sends a random number of signals to the parent process. Both the child and the parent process should show the same number, but I have an issue with blocking the signals when incrementing the counter.
I tried multiple methods of blocking the signals but I have failed. Anybody with a suggestion? Thanks a lot.
int nreceived = 0;
void handler(int sig)
{
nreceived++;
signal(SIGUSR1,handler);
}
int main()
{
int nsignals;
pid_t pid;
srand(time(NULL));
nsignals = rand() % 256;
signal(SIGUSR1,handler);
if((pid = fork()) > 0)
{
wait(NULL);
printf("Received %d signals from process %d\n",nreceived,pid);
}
else if (pid == 0)
{
for(int i = 0; i < nsignals; i++)
kill(getppid(),SIGUSR1);
printf("Sent %d signals to process %d\n", nsignals, getppid());
}
return 0;
}
As discussed extensively in the comments, it is important to use POSIX function sigaction() rather than the standard C function signal() because there are many implementation-defined aspects to signal() (primarily because there were many divergent implementations before the C standard was created, and the standard tried to accommodate existing implementations without breaking any of them).
However, the system is not obligated to queue signals that are not real-time signals (signal numbers in the range SIGRTMIN..SIGRTMAX). SIGUSR1 is not a real-time signal. Frankly, even with signal queueing, I'm not sure whether implementations would handle up to 255 pending signals of a specific type for a process — it isn't an area I've experimented with.
This is the best code I was able to come up with:
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifndef SEND_SIGNAL
#define SEND_SIGNAL SIGUSR1
#endif
static const char *arg0;
static volatile sig_atomic_t nreceived = 0;
static _Noreturn void err_syserr(const char *syscall);
static void handler(int sig)
{
assert(sig == SEND_SIGNAL);
nreceived++;
}
int main(int argc, char **argv)
{
if (argc != 1)
{
fprintf(stderr, "Usage: %s\n", argv[0]);
exit(EXIT_FAILURE);
}
arg0 = argv[0];
struct sigaction sa = { .sa_handler = handler, .sa_flags = SA_RESTART };
/* Block all blockable signals */
if (sigfillset(&sa.sa_mask) != 0)
err_syserr("sigfillset");
if (sigaction(SEND_SIGNAL, &sa, 0) != 0)
err_syserr("sigaction");
pid_t pid = fork();
if (pid > 0)
{
int status;
int corpse = wait(&status);
if (corpse != -1)
printf("Child process %d exited with status 0x%.4X\n", corpse, status);
else
fprintf(stderr, "%s: wait() failed: (%d) %s\n", argv[0], errno, strerror(errno));
printf("Caught %d signals from process %d\n", nreceived, pid);
}
else if (pid == 0)
{
srand(time(NULL));
int nsignals = rand() % 256;
for (int i = 0; i < nsignals; i++)
kill(getppid(), SEND_SIGNAL);
printf("Sent %d signals to process %d\n", nsignals, getppid());
}
else
err_syserr("fork");
return 0;
}
static _Noreturn void err_syserr(const char *syscall)
{
fprintf(stderr, "%s: %s() failed: (%d) %s\n", arg0, syscall, errno, strerror(errno));
exit(EXIT_FAILURE);
}
When run as program sig53 (source code sig53.c) on a Mac running macOS Monterey 12.3.1, I got variable numbers of signals received:
$ sig53
Sent 50 signals to process 37973
Child process 37974 exited with status 0x0000
Caught 14 signals from process 37974
$: sig53
Sent 39 signals to process 38442
Child process 38443 exited with status 0x0000
Caught 16 signals from process 38443
$: sig53
Sent 28 signals to process 38478
Child process 38479 exited with status 0x0000
Caught 6 signals from process 38479
$
Sometimes, the number received reached near 100, but never very near to all the signals sent.
YMMV on Linux. There may be alternative mechanisms for handling signals on Linux. But for portable code, sending a myriad signals to a single process at full tilt is not a reliable way of communicating between processes. Some of the signals will be delivered, but it may not be all of them.

Trying to communicate between two processes with pipe() breaks the program

I'm trying to communicate between two processes in C using a pipe. Everything works fine until it is supposed to print "hi\n". The output is
(8841) Child here stopping self
(8841) SAYS: 19
DATA WRITED
C: 8
(8841) CONTINUING
This is a simplified version of the program. I know for a fact the reading part works, but it seems that the writing call does not, because it never prints "hi\n". Any clues on why is that?
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
volatile sig_atomic_t sigchld = 0;
void sigchldHandler(){
sigchld = 1;
return;
}
int main(){
sigset_t mask,prev;
signal(SIGCHLD, sigchldHandler);
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
int pid = fork();
int fd[2];
pipe(fd);
sigprocmask(SIG_BLOCK, &mask, &prev);
if (pid == 0){
dup2(STDIN_FILENO,fd[0]);
printf("(%d) Child here stopping self\n",getpid());
raise(SIGSTOP);
printf("(%d) CONTINUING\n",getpid());
char* hello = malloc(sizeof("hi\n"));
read(STDIN_FILENO,hello,sizeof("hi\n"));
printf("%s",hello);
exit(0);
}
sleep(0.1);
sigprocmask(SIG_SETMASK, &prev,NULL);
while(1){
if (sigchld){
int status;
int p = waitpid(-1,&status,WNOHANG|WUNTRACED);
if (WIFSTOPPED(status)){
if (WSTOPSIG(status) == SIGSTOP){
printf("(%d) SAYS: %d\n",p, WSTOPSIG(status));
kill(pid,SIGCONT);
printf("DATA WRITED\n");
char* h = "hi\n";
int c=write(fd[1],h,sizeof(h));
printf("C: %i\n",c);
break;
}
}
sigchld = 0;
}
}
}
Primary problem
Your key problem is that you call pipe() after you've called fork(). That means the two processes have completely separate pipes; they are not talking to each other.
Secondary issues
There are other issues too, of course.
You have (in the parent): int c=write(fd[1],h,sizeof(h));. You're writing 8 bytes (your output includes C: 8 because the variable h is a pointer of size 8 (you're on a 64-bit system). However, the string only points to 4 bytes — you should be using strlen() or thereabouts to limit the amount of data written.
You aren't closing enough file descriptors for comfort.
You have the arguments to dup2() reversed. This too is crucial.
It seems weird to be using dynamic allocation for just 4 bytes of data, but it should work.
You should print the PID along with the value in hello in the child (for consistency, if nothing else). It's good you do that with the other printing.
The parent should probably wait for the child after the loop (after closing the pipe).
The sleep() function takes an integer; calling sleep(0.1) sleeps for zero seconds. For sub-second sleeping, you need nanosleep() or maybe. usleep() (older, no longer part of POSIX, but widely available and easier to use).
Here's working code:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
volatile sig_atomic_t sigchld = 0;
static void sigchldHandler(int signum)
{
sigchld = signum;
}
int main(void)
{
sigset_t mask, prev;
signal(SIGCHLD, sigchldHandler);
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
int fd[2];
pipe(fd);
int pid = fork();
sigprocmask(SIG_BLOCK, &mask, &prev);
if (pid == 0)
{
/* Child */
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
printf("(%d) Child here stopping self\n", getpid());
raise(SIGSTOP);
printf("(%d) CONTINUING\n", getpid());
char *hello = malloc(sizeof("hi\n"));
int nbytes = read(STDIN_FILENO, hello, sizeof("hi\n"));
printf("(%d) received %d bytes: %.*s\n", getpid(), nbytes, nbytes, hello);
exit(0);
}
/* Parent */
close(fd[0]);
nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 100000000}, NULL);
sigprocmask(SIG_SETMASK, &prev, NULL);
while (1)
{
if (sigchld)
{
int status;
int p = waitpid(-1, &status, WNOHANG | WUNTRACED);
if (WIFSTOPPED(status))
{
if (WSTOPSIG(status) == SIGSTOP)
{
printf("(%d) SAYS: %d\n", p, WSTOPSIG(status));
kill(pid, SIGCONT);
char *h = "hi\n";
int c = write(fd[1], h, strlen(h));
printf("DATA WRITTEN: %i\n", c);
close(fd[1]);
break;
}
}
sigchld = 0;
}
}
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("PID %d exited with status 0x%.4X\n", corpse, status);
return 0;
}
Sample output:
(66589) Child here stopping self
(66589) SAYS: 17
DATA WRITTEN: 3
(66589) CONTINUING
(66589) received 3 bytes: hi
PID 66589 exited with status 0x0000
The difference between 17 (on a Mac running macOS Mojave 10.14.6) and 19 (on a Linux box) is normal; the actual values for signal numbers is not standardized by POSIX (though signals 1 SIGHUP through 15 SIGTERM are the same across systems because they were standard in 7th Edition Unix).

Processes in C for Linux(Ubuntu)

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

Why can "X" be the last character of output?

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
if (fork() == fork()){
fprintf(stderr, "Z");
}
else {
if (waitpid((pid_t)-1,NULL,0)!=-1) {
fprintf(stderr, "Y");
}
else {
fprintf(stderr, "X");
}
}
}
I was studying this program and I found out "ZYYX" can be the output. I don't quite understand why. In my understanding, there are four process in total, parent->parent, parent->child, child->parent, child->parent. And there is no doubt child->child prints Z. And child->parent prints Y after child->child prints Z. And parent->parent should wait until parent->child prints X. So why it is possible that X is printed as the last character of the output?
I realized that waitpid waits for any child process, so if ZY has been printed out, then Y can be printed out immediately since it has waited for "Y". Thus, X can be printed as the last character.
I don't think I've seen if (fork() == fork()) used in code before — congratulations! I'm not wholly convinced by your tracking; I'd want to see the PID of each process included in any printing it did, and I'd want a newline at the end of each output. Also, there's at least one process not waited for in the original code.
Here's a rewrite which reports the PID of the process doing each print operation. Some of the processes exit with non-zero statuses, mainly to make it a little more interesting in the output. The code tracks the corpses detected and reports on their status. It also introduces a loop to clean up dead children. The original process has a child that is not cleaned up otherwise.
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
if (fork() == fork())
{
fprintf(stderr, "Z(%d)\n", (int)getpid());
return 23;
}
else
{
int corpse;
int status;
if ((corpse = waitpid((pid_t)-1, &status, 0)) != -1)
fprintf(stderr, "Y(%d) - PID %d 0x%.4X\n", (int)getpid(), corpse, status);
else
{
fprintf(stderr, "X(%d)\n", (int)getpid());
return 37;
}
while ((corpse = waitpid((pid_t)-1, &status, 0)) != -1)
fprintf(stderr, "Q(%d) - PID %d 0x%.4X\n", (int)getpid(), corpse, status);
}
return 0;
}
Sample output:
X(16551)
Y(16547) - PID 16551 0x2500
Z(16552)
Y(16550) - PID 16552 0x1700
Q(16547) - PID 16550 0x0000
As you note, there will be 4 process, based on the two forks, and each will print one letter:
1st fork 2nd fork prints PP
parent parent Y / \
parent child X CP PC
child parent Y \
child child Z CC
The graph on the left shows the parent/child relationships -- / is the first fork and \ is the second fork(s).
So CC prints Z because both forks return 0, while PC prints X. The other two both wait for a child to exit, then print Y. In the cas of CP, there's only one child, so that Y will always be after the Z, but PP has two childre, so that Y might be after the ZY or after the X. Either is possible. So you can get any of
XYZY PC,PP,CC,CP
XZYY PC,CC,PP,CP or PC,CC,CP,PP
ZXYY CC,PC,PP,CP or CC,PC,CP,PP
ZYXY CC,CP,PC,PP
ZYYX CC,CP,PP,PC

pipe stdout of a child to stdin of another child in c

This is what I'm trying to do: a parent process creates two child processes, then pipes stdout of one to stdin of another. Then parent waits for 5 secs and kills the first child.
This is how I approached it: First I create a pipe. Then fork to create the first child (gen in code) and dup2 to duplicate pipe's write onto stdout. Another fork for second child (called cons in code), dup2 to duplicate read end onto stdin. Cons just prints the data. Parent sends SIGTERM to first child, second child reads until EOF, so it closes on it's own.
Nothing but my error output (here used for debugging) is printed. Gen generates two random numbers, but loop in cons doesn't get executed. So I suppose there's nothing on stdin of cons.
I consulted Google and followed this How do I chain stdout in one child process to stdin in another child in C?, but didn't manage to figure out what I messed up. Would appreciate any help. Thanks
Compilation: gcc -std=c99 -Wall -Werror main.c -o main on Bash in Windows 10
Code:
#define _POSIX_SOURCE
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <signal.h>
#define READ_END 0
#define WRITE_END 1
#define BUF_SIZE 10
int main(int argc, char* argv[])
{
pid_t gen, cons;
int fd[2];
if (pipe(fd) < 0) {
// pipe error
exit(1);
}
gen = fork();
if (gen < 0) {
// fork error
close(fd[READ_END]);
close(fd[WRITE_END]);
exit(2);
} else if (gen == 0) {
// gen child
close(fd[READ_END]);
time_t t;
srand((unsigned)time(&t));
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[WRITE_END]);
while(1) {
int a = rand() % 1000;
int b = rand() % 1000;
printf("gen %d %d\n", a, b);
fprintf(stderr, "err, gen %d %d\n", a, b);
sleep(1);
}
}
else {
cons = fork();
if (cons < 0) {
// fork error
close(fd[READ_END]);
close(fd[WRITE_END]);
kill(gen, SIGKILL);
exit(2);
} else if (cons == 0) {
// cons child
close(fd[WRITE_END]);
dup2(fd[READ_END], STDIN_FILENO);
close(fd[READ_END]);
char line[BUF_SIZE];
while (fgets(line, sizeof(line), stdin)) {
printf("cons received: %s\n", line);
fprintf(stderr, "cons lives!\n");
}
} else {
// parent
close(fd[READ_END]);
close(fd[WRITE_END]);
sleep(5);
kill(gen, SIGTERM);
}
}
return 0;
}
Standard output is by default buffered, so your gen child only queues output for later sending. So you must fflush it to force your messages to be immedialely delivered:
...
printf("gen %d %d\n", a, b);
fprintf(stderr, "err, gen %d %d\n", a, b);
fflush(stdout); // <= force immediate send
sleep(1);
...

Resources