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

#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

Related

How to have 3 calls of fork() and create 6 processes without using a loop

I was given the task of creating exactly 6 processes using the c language and the command fork() and then have all 6 sleep using sleep() for a minute or two. The tougher part with this is that we are not allowed to use any loops at all, and only 3 calls to fork() total.
I have managed to create 8 successfully but that is too many. So I was wondering if it is possible to fork or kill specific processes.I tried something I saw here but it is giving me 8 process not 6
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
/* fork a child process */
pid_t child = fork();
pid_t parent = getpid();
fork(); //2
if (fork() > 0) {//now we have 2 processes
//only the parent calls this fork because of the if, so we have 3 processes
fork(); //all 3 processes calls this fork, so we have 6 processes
wait(NULL);
printf("I am the child process");
printf("my PID = %d ",child);
printf("I am the parent process. My PID is %d\n", parent);
wait(NULL);
}
else if (child < 0) { /* error occurred */
fprintf(stderr, "Fork Failed");
return 1;
}
}
out I am getting is:
I am the child processmy PID = 22150 I am the parent process. My PID is 22149
I am the child processmy PID = 22150 I am the parent process. My PID is 22149
I am the child processmy PID = 0 I am the parent process. My PID is 22150
I am the child processmy PID = 22150 I am the parent process. My PID is 22149
I am the child processmy PID = 0 I am the parent process. My PID is 22150
I am the child processmy PID = 0 I am the parent process. My PID is 22150
I am the child processmy PID = 0 I am the parent process. My PID is 22150
I am the child processmy PID = 22150 I am the parent process. My PID is 22149
You can get a tree with 6 new processes (+1 the orginal) by making the third fork only in 3 out of the fourth processes
that you'll have after the 1st 2 forks.
#include <unistd.h>
#include <stdio.h>
/*
Process tree:
/
/\
/\/
\
\ /
/\
\
*/
int main()
{
_Bool descended_from_parent, descended_from_child;
pid_t pid;
if(0>(pid=fork())) return perror("fork"),1;
descended_from_parent = pid == 0;
if(0>(pid=fork())) return perror("fork"),1;
descended_from_child = pid == 0;
if(!descended_from_parent || descended_from_child) if(0>(pid=fork())) return perror("fork"),1;
sleep(60);
}
// run as `./a.out &` and then do `ps --forest` to see the process tree
#include <stdio.h>
#include <unistd.h>
int main(void) {
int f1 = -1, f2 = -1, f3 = -1;
printf("1 process running -- f1: %d; f2: %d; f3: %d\n", f1, f2, f3);
sleep(1); // try to prevent print lines mangling
f1 = fork(); // assume it worked
printf("2 processes running -- f1: %d; f2: %d; f3: %d\n", f1, f2, f3);
sleep(1); // try to prevent print lines mangling
f2 = fork(); // assume it worked for the 2 running processes
printf("4 processes running -- f1: %d; f2: %d; f3: %d\n", f1, f2, f3);
sleep(1); // try to prevent print lines mangling
if ((f1 > 0) || (f2 > 0)) f3 = fork(); // assume it worked for the 3 selected processes
printf("7 processes running -- f1: %d; f2: %d; f3: %d\n", f1, f2, f3);
return 0;
}
Your logic is just fine. I suspect the confusion is because of the initialization of the variable child, in which you invoke fork for the first time. Also, your output is odd, and you are not reporting output for each process. Simply refactoring your code to something simpler shows that your logic is correct:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int
main(void)
{
fork();
if( fork() == 0 ) {
fork();
}
printf("PID = %d\n", getpid());
}
The above is basically your code, and it gives exactly 6 lines of output, as desired. The only real difference is that I removed the spurious initial call to fork, and moved the printf to the right place. In your code, you have 4 processes when your comment claims there are only 2 (because of the spurious initialization fork), and all 4 of them fork again to make 8. Of those 8, 4 of them refork to give you a total of 12, of which only 8 print any output. (The 4 that forked and their new children.) The output that they give is most unusual, though, and does not reflect the actual pid of the process writing the output. They merely show the pids as the were after the initial fork in the initialization of child.
Simplest way
#include <stdio.h>
int main()
{
fork() && fork();
fork();
printf("%d %d\n",getpid(),getppid());
sleep(1);
}

Create a chain of n sub processes

In c++ create chain of n processes with n as input and the output of processes should be as parent1->child1(parent2)-->child2(parent3),by using recursive function im able to generate the output but unable to exit the loop i also need help in sending an input of n for which the loop should break.
below is my code:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int foo(const char *whoami) {
printf("I am a %s. My pid is:%d my ppid is %d\n", whoami, getpid(), getppid() );
return 1;
}
int func() {
pid_t pid=fork();
if (pid==0) { /* only execute this if child */
foo("child");
pid_t pid=fork();
if (pid==0) { /* only execute this if child */
foo("child");
func();
exit(0);
}
}
exit(0);
}
wait(0); /* only the parent waits */
return 0;
}
int main(void){
foo("parent");
func();
return 0;
}
You can't exit the loop for a simple reason, and that is, you spawn child processes endless. Whenever you fork() a new process starts, then it forks again.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int n=5;
int foo(const char *whoami) {
printf("I am a %s. My pid is:%d my ppid is %d\n", whoami, getpid(), getppid() );
return 1;
}
int func(int n)
{
if (n == 0)
{
return 0;
}
int pid = fork();
if (pid == -1) {
exit(0);
}
if (pid==0) {
foo("child");
n = n-1;
func(n);
exit(0);
}
else {
wait(NULL);
}
return 0;
}
int main()
{
func(n);
return 0;
}
gcc -std=c99 prog.c -o prog
./prog
OUTPUT:
I am a child. My pid is: 1159 my ppid is 1158
I am a child. My pid is: 1160 my ppid is 1159
I am a child. My pid is: 1161 my ppid is 1160
I am a child. My pid is: 1162 my ppid is 1161
I am a child. My pid is: 1163 my ppid is 1162
From what you are saying i understand you are having the following problems:
1st. You are trying to send 'data' from one process to another
2nd. You are trying to find a way to stop your program from running.
Now for the first. If you want to do that and i understood it correctly, there are 2 ways to achieve that. One is the use of shared memory and the other is the use of pipelines. Shared memory is pretty obvious on what is doing. Pipes are taking the stdout of a process and redirecting it as a stdin in the next process.
Now you need a closure to your program. A child process is executed when it executes a command(exec) or when it is told so(with an IF statement for example and a return). You can create a statement of your liking, and when a child process meets your requirments then you can make it die(There is also a way to kill the parent process from the child process with the kill(pid, SIGKILL); command.
I didn't provide you with any code because it is unclear to me the exact nature of your problem.
Hope my assuming led you to something!

Parallel processing and synchronization using semaphores in C [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
Questions:
Are my processes running in parallel?
I want six processes running in parallel.
How can I sync these processes (parent with five child processes) using semaphores, in an infinite loop? So the output would be: 1 2 3 4 5 reset 1 2 3 4 5 reset etc...
Any simple and understanding semaphore documentation?
Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
parentWithFiveChildren()
{
printf("1 "); //Parent
fflush(stdout);
int i, status;
for (i = 2; i < 7; i++)
{
sleep(1);
if (fork() == 0) //Child Processes
{
if (i == 6)
{
printf("reset ");
fflush(stdout);
exit(0);
}
printf("%d ", i);
fflush(stdout);
sleep(7);
exit(i); //Exiting child processes
}
}
while ((wait(&status)) > 0)
printf("\a");
return 0;
}
int main(void)
{
parentWithFiveChildren();
}
Output:
1 2 3 4 5 reset
1. Parallelism
No, the processes are not running in parallel (or, at least, they're only running in parallel transiently, and only two processes at a time), but that's only because:
The sleep(1) gives the parent process a long time (at least a second) doing nothing.
The child finishes and exits during that second.
Your printing code in the child is odd; there is effectively no difference between the i == 6 and the other operations. In main(), return 0; and exit(0); are practically the same — there can be differences, but they're obscure and not germane to your code.
You should #include <sys/wait.h> and you should collect the dead children's PID (and status); it would make things clearer to you.
You could also have the children report sleep for a while (say 7 seconds each). That would give you all the child processes 'running' (actually, sleeping) in parallel, and the parent then waits for the children to exit:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
printf("[PARENT] with pid %d\n", getpid());
fflush(stdout);
for (int i = 2; i < 7; i++) // Odd loop conditions, but not wrong
{
sleep(1);
if (fork() == 0)
{
printf("[CHILD] with pid %d from parent with pid %d\n", getpid(), getppid());
fflush(stdout);
sleep(7);
printf("[CHILD] with pid %d exiting with status %d\n", getpid(), i);
exit(i);
}
}
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("%d: child %d exited with status 0x%.4X\n", getpid(), corpse, status);
return 0;
}
Sample output:
$ ./test-forking
[PARENT] with pid 13904
[CHILD] with pid 13905 from parent with pid 13904
[CHILD] with pid 13906 from parent with pid 13904
[CHILD] with pid 13907 from parent with pid 13904
[CHILD] with pid 13908 from parent with pid 13904
[CHILD] with pid 13909 from parent with pid 13904
[CHILD] with pid 13905 exiting with status 2
13904: child 13905 exited with status 0x0200
[CHILD] with pid 13906 exiting with status 3
13904: child 13906 exited with status 0x0300
[CHILD] with pid 13907 exiting with status 4
13904: child 13907 exited with status 0x0400
[CHILD] with pid 13908 exiting with status 5
13904: child 13908 exited with status 0x0500
[CHILD] with pid 13909 exiting with status 6
13904: child 13909 exited with status 0x0600
$
An upgrade to the code would print the time with each line of output too.
2. Killing
Any of the processes (in the set created by the parent) can kill any other process (in the set) that it knows about, using the kill() system call. It really isn't clear whether you want the first child or the last child to kill the parent, or something else. If the first child kills the parent, the first child will be the only child (because of the delays). It also isn't clear why you want to send signals between the processes.
3. Looping
Yes, you could do something — the question is, what are you really after. Simply printing parent and child multiple times doesn't require multiple processes. If you want the parent to say "I'm here", and each child to say "I'm here" periodically, you need to have the children looping and sleeping, and the parent looping and sleeping after all the children have been created. Not hard to do.
question 3). ...Can I put main function in an infinite loop...?
Sure you can:
int main(void)
{
int c=0;
while((c != 'q') && (c != EOF))//loops until c == q (and c!=EOF)
{
c = getchar();//waits until stdin sees a "q", (i.e. from keyboard)
//An EOF (-1) or `q` will exit the loop
//any other input will allow execution flow to continue, 1 loop at a time.
//Add additional forking code here.
//for each loop, spawn a new thread.
//All secondary threads spawned will run parallel to other threads.
}
//exiting here will kill all threads (secondary and primary.)
return 0;
}
1) Yes, your parent process and child are running in parallel after fork,
You can see this by infinite looping child process and printing it's name while parent and other processes are doing the same.
2) Yes, here's how:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/types.h>
#include <signal.h>
int main()
{
pid_t pid;
int i = 0;
if ((pid = fork()) == 0)
{
if ((pid = getppid()) == -1)
{
fprintf(stderr, "child error: getppid()\n");
exit(1);
}
if (kill(pid, 9) == -1)
{
fprintf(stderr, "child error: kill()\n");
exit(1);
}
while (true)
{
printf ("child %d\n", ++i);
}
}
else if (pid == -1)
{
fprintf(stderr, "error: fork()\n");
return 1;
}
while (true)
{
printf("parent %d\n", ++i);
}
return 0;
}
3) If you need that specific pattern, you need interprocess communication and synchronization. Suggest this

Defunct processes, fork()

If I run this program will I have defunct processes? I am trying to create a main program that runs 5 process in parallell and then not getting defunct processes. The trouble is mostly to be sure that this is not happening. Im not quite sure if Im doing it right this far. I have heard that it is good practice to make sure you dont have defunct processes by making your process "wait()" for as many children that has been "fork()"ed.
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
void forkChildren(int nrofChildren, int *nr_of_children) {
pid_t pid;
int i;
for(i=0; i<5; i++) {
/* fork a child process */
pid = fork();
(*nr_of_children)++;
/* error occurred */
if (pid < 0) {
fprintf(stderr, "Fork failed\n");
exit(-1);
}
/* successful child */
else if (pid == 0) {
int sleeptime=1; //rand()%10;
printf("I am child: %d \nwith parent: %d \nin loop: %d \nand will sleep for: %d sec\n\n", getpid(), getppid(), i, sleeptime);
sleep(sleeptime);
printf("Ending of child: %d \nwith parent :%d in loop: %d\n\n", getpid(), getppid(), i);
}
/* parent process
else {
wait(NULL); Do I need this to make sure I dont get defunct processes???
} */
}
}
int main(int argc, char *argv[]) {
srand((unsigned int)time(NULL));
int nr_of_children=0;
if (argc < 2) {
/* if no argument run 5 childprocesses */
forkChildren(5, &nr_of_children);
} else {
forkChildren(atoi (argv[1]), &nr_of_children);
}
wait(NULL);
printf("End of %d, with %d nr of child-processes\n\n", getpid(), nr_of_children);
return 0;
}
Yes, you need to wait on the child processes. The reason is that otherwise there will still be data associated with the now zombie process, for example space for the process return value.
Have a look at using the daemon() command to place your app in the background, then use pthreads to manage the parallelism.
NAME
daemon - run in the background
SYNOPSIS
#include
int daemon(int nochdir, int noclose);
Feature Test Macro Requirements for glibc (see
feature_test_macros(7)):
daemon(): _BSD_SOURCE || (_XOPEN_SOURCE && _XOPEN_SOURCE < 500)
DESCRIPTION
The daemon() function is for programs wishing to detach themselves from the controlling terminal and run in the background as
system daemons.
If nochdir is zero, daemon() changes the process’s current working directory to the root directory ("/"); otherwise,
If noclose is zero, daemon() redirects standard input, standard output and standard error to /dev/null; otherwise, no changes are made
to these file descriptors.

C fork and pipe program with non-deterministic output

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).

Resources