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).
Related
The assignment is
Write a program in C/Posix (Linux environment) that synchronizes a parent process and his child process (using signals) that reads and write one by one numbers from 1 to n (given by the user) in the first position of a txt file.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
int n = atoi(argv[1]);
int i;
int fd = open("exercise.txt", O_RDWR | O_CREAT, 0777);
pid_t pid = fork();
if (!pid) {
for (i = 1; i <= (n / 2); i++) {
pause();
char c[2];
sprintf(c, "%d", i + 1);
write(fd, c, sizeof(c));
kill(pid, SIGUSR1);
}
}
else {
for (i = 1; i <= (n / 2); i++) {
char c[2];
sprintf(c, "%d", i);
write(fd, c, sizeof(c));
kill(pid, SIGUSR1);
pause();
}
}
return 0;
}
For each number I pass to my program I always find a txt file with just "1" written in it instead of 1234...n.
What did I do wrong?
The child process will immediately pause upon entering the first iteration of the loop, waiting to receive a signal.
In the first iteration of its loop, the parent process will write two bytes of data to the file, send the SIGUSR1 signal to the child process, and then itself pause, waiting to receive a signal.
The default action when receiving SIGUSR1 is to terminate the process.
pause() returns only when a signal was caught and the signal-catching function returned.
Thus the child process never does anything but possibly wait for a signal and certainly terminate almost immediately.
The parent process remains paused, waiting for a signal.
Some notes:
signal(7) for an overview of signals.
The instructions were, as far as I can tell, to write in one process, and read in the other. Your program attempts to write in both processes.
char c[2];
sprintf(c, "%d", i); /* or i + 1 */
c is only large enough to store strings of, at most, length 1 (optionally one character, plus the null-terminating byte, always). This risks Undefined Behaviour, as there will not be enough room in the buffer if the third argument to sprintf is outside the range [0, 9].
In the child process, pid will be 0 in kill(pid, SIGUSR1);. From kill(2):
If pid equals 0, then sig is sent to every process in the process group of the calling process.
Opening the file before forking, means the two processes share the same file description. Changing the file offset (by reading, writing, or seeking) in one process will change it in the other.
In any case, the instructions are to always write and read at the start of the file (lseek(2)).
You need to catch the delivery of SIGUSR1, or the default action of process termination will occur.
Additionally, the pattern of kill and then pause is not sufficient to avoid a race condition wherein a signal is delivered before the process pauses to wait for signals. This answer covers the problem, and a solution. The method detailed is to use sigprocmask(2) to normally delay (block) the delivery of signals and then sigsupend(2) to temporarily handle those signals.
Here is a cursory example, lacking error handling, wherein the parent process writes and the child process reads.
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
void pdie(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
void handler(int sig)
{
signal(sig, handler);
}
int main(int argc, char const *argv[])
{
if (argc < 2)
return EXIT_FAILURE;
int fd = open("exercise.txt", O_RDWR | O_CREAT | O_TRUNC, 0777);
if (-1 == fd)
pdie("open");
int n = atoi(argv[1]);
if (n < 1)
n = 10;
sigset_t set, old;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigprocmask(SIG_BLOCK, &set, &old);
signal(SIGUSR1, handler);
pid_t pid = fork();
if (-1 == pid)
pdie("fork");
for (int i = 0; i < n; i++) {
char buf[32];
if (pid) {
/* parent writes */
int len = snprintf(buf, sizeof buf, "%d", i + 1);
lseek(fd, 0, SEEK_SET);
write(fd, buf, (size_t) len);
kill(pid, SIGUSR1);
sigsuspend(&old);
} else {
/* child reads */
sigsuspend(&old);
memset(buf, 0, sizeof buf);
lseek(fd, 0, SEEK_SET);
ssize_t bytes = read(fd, buf, sizeof buf - 1);
printf("Child read %zd bytes: %s\n", bytes, buf);
kill(getppid(), SIGUSR1);
}
}
close(fd);
if (pid)
waitpid(pid, NULL, WUNTRACED);
}
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.
I am currently working on a C project for university. Among other things I should signal the parent process using SIGUSR1.
The problem I'm facing at the moment is that I also need to wait for the child process to terminate so I can safely shut down everything in the end (removing shared Memory etc.).
At the moment I am using sigaction() to respond to the signal and waitpid() to wait for the child to terminate (that was the plan anyways ^^). But when I signal the parent using kill(), waitpid() stops waiting and runs the remainder of the parent even though the child is still running.
I feel like I'm missing something obvious but I can't figure it out.
Any help is greatly appreciated,
stay safe
Tim
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
void handle_sigusr1(int sig) {
printf("Recieved signal %i.\n", sig);
}
int main() {
pid_t pid;
pid = fork();
if (pid == -1) {
perror("fork:");
return EXIT_FAILURE;
}
else if (pid == 0) {
printf("Hello from the child.\n");
kill(getppid(), SIGUSR1);
sleep(3);
printf("Hello again from the child.\n");
return EXIT_SUCCESS;
}
else {
printf("Hello from the parent.\n");
struct sigaction sa;
sa.sa_handler = &handle_sigusr1;
sigaction(SIGUSR1, &sa, NULL);
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status))
printf("Exit status: %i\n", WEXITSTATUS(status));
printf("Finished waiting for child.\n");
return EXIT_SUCCESS;
}
}
Output:
Hello from the parent.
Hello from the child.
Recieved signal 10.
Exit status: 0
Finished waiting for child.
tim#schlepptop:signalTest$ Hello again from the child.
PS: WEXITSTATUS(status) is usually 0 but sometimes it's also something like 16 or 128.
Per POSIX waitpid() documentation:
RETURN VALUE
... If wait() or waitpid() returns due to the delivery of a signal to the calling process, -1 shall be returned and errno set to [EINTR]. ...
You need to check the return value:
int status
do
{
errno = 0;
int rc = waitpid(pid, &status, 0);
if ( rc != -1 )
{
break;
}
}
while ( errno == EINTR );
I am trying to send signals between two child in alternative way for 100 times.
Here is my snippet of code.
here is the link to the whole question:
sending signal between two child process
But i have synchronization issue in the loop.
where is the right position to put the sigsuspend()?
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <ctype.h>
pid_t pid2;
struct sigaction act;
sigset_t mask,oldmask,temp;
void sighandler(int signum, siginfo_t *info, void *ptr)
{
printf("Received signal %d\n", signum);
printf("Signal originates from process %lu\n",
(unsigned long)info->si_pid);
pid2 = info->si_pid;
}
int main(int argc,char **argv)
{
int i,j,counter = 0,counter2 = 0;
sigemptyset(&mask);
sigemptyset(&temp);
//sigemptyset(&oldmask);
sigaddset(&mask,SIGUSR1);
//sigset_t mask;
memset(&act, 0, sizeof(act));
act.sa_sigaction = sighandler;
act.sa_flags = SA_SIGINFO;
if(sigaction(SIGUSR1, &act, NULL) == -1)
fprintf(stderr, "sigaction failed: %s\n", strerror(errno));
pid_t current, pidOther;
current = getpid();
pidOther = atol(argv[1]);
int k;
for(k = 0;k < 100;k++){
if(pidOther != 0){ // second child
kill(pidOther,SIGUSR1);
sigprocmask(SIG_BLOCK,&mask,&oldmask);
counter++;
printf("2nd child = %d sent signal to 1st child = %d signal number = %d\n",getpid(),pidOther,counter);
//sigprocmask(SIG_BLOCK,&mask,&oldmask);
sigsuspend(&temp);
}
if(pidOther == 0) // fisrt child
{
//pause();
kill(pid2,SIGUSR1);
sigprocmask(SIG_BLOCK,&mask,&oldmask); // was blank
counter++;
printf("\nj=%d 1st child = %d sent signal to 2nd child = %d signal counter = %d\n",j,getpid(),pid2,counter);
printf("test1\n");
sigsuspend(&temp); // was pause()
}
}
return 0;
}
I don't see you calling fork() anywhere. Also taking the process ID of the second process is not the way your program should know about the child process. Here's a simple example of how to use fork.
pid_t pid = fork();
if (pid == 0)
{
// executes only in child process..
// do stuff related what you need to do in child process
}
else
{
// executes only in parent process
// pid variable contains the child process's PID.
// do stuff related what you need to do in parent process
}
// runs in both parent and child.
The problem is that the first time the first child loops, pid2 is 0, so it sends the signal to every process in the process group (including itself), which means it will start looping immediately, sending signals (just) back to itself...
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).