I don't know if I'm asking the right question, but after several hours of searching, I can't find my answer. So I explain what I need.
I would like to create a child process with fork and that child process will wait until it receives a signal or changes a variable value. I mean I don't want to be creating a process every time I need it, I want it to wait and run when I tell it to, as many times as it needs to.
I have tried several things, but they have not worked, I put the code, I really do not know what to look for to do what I need without thread, or is it only possible to do it with thread?
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
volatile sig_atomic_t runChild = 0;
static void handler(int signum, siginfo_t *sinfo, void *unused){
if (signum == SIGUSR1) {
printf("SIGUSR1 received\n");
runChild = 1;
}
}
int main(int argc, char *argv[]) {
char *newArgv[] = {"myecho", "hello", "world", NULL};
char *newEnviron[] = { "MY_NAME=jcarlosweb", NULL };
struct sigaction sa;
sa.sa_flags =0;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handler;
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
while (!runChild) {
usleep(200);
}
runChild = 0;
execve("./process-pipe-fork/execve/myecho", newArgv, newEnviron);
/* The execve function returns only if an error occurs. */
perror("An error occurred in execve");
abort();
}
// continue execution of parent process without waiting for child process to finish
waitpid(-1, NULL, WNOHANG);
printf("Run myecho:\n");
kill(pid,SIGUSR1);
sleep(3);
printf("Run myecho:\n");
kill(pid,SIGUSR1);
printf("Done\n");
exit(EXIT_SUCCESS);
}
The execve of my myecho program only runs once, and I would like to run it when I say so throughout the main program.
In this case I'm using the exec in the child process, but I could have just put a function that prints a message, for example. The idea I am looking for is the same
Block on a pipe. This might look something like...
#include <err.h>
#include <errno.h>
#include <unistd.h>
int main(void) {
char ch;
int fd[2];
pid_t pid;
ssize_t ret;
pipe(fd); /* to block on */
pid = fork();
if (pid < 0) {
err(1, "fork failed");
} else if (pid == 0) { /* child */
close(fd[1]);
warnx("child %d start", getpid());
/* block until parent goes away */
ret = read(fd[0], &ch, 1);
warnx("child %d is go", getpid());
close(fd[0]);
/* child now does things here ... */
} else { /* parent */
sleep(3);
close(fd[0]);
close(fd[1]);
/* parent does more things here ... */
}
return 0;
}
You cannot wait on a child process for a variable change because the parent process and the child are different processes, and so, they have each their own virtual address space (with no intersections)
There are several ways to do what you want. One has been suggested in another answer, so I will not extend on it, as it looks fine to me.
You can create a thread. A thread runs in a separate stack (separate from main thread) but the whole virtual address space is the same. Both running threads (main and the one you create) share the same addresses and so, one can modify a variable and the other can be looking at it for a change. This solution is feasible, but not very efficient, as while the waiting process is waiting for the variable to change (the variable needs to be defined as volatile, so the compiler doesn't cache the value anywhere and the true value is the one the other thread stores in it) it is consuming cpu making your resources wasted for no reason.
You can use (in a different process) a semaphore, (man semop) for the parent process to change it.
You can use shared memory between processes (see man shmop or man mmap) and so, only the memory corresopnding to the shared memory segment is shared between both processes.
You can use a socket. A socket is a bidirectional communication channel between parent and child, and so it allows you to use a complex protocol to do the most variable communication messages between both processes. You can use reliable unix sockets for this (they are fast, reliable, but require both processes to be in the same machine) or network sockets (the API is the same, which gives you a versatile approach that you can later change in case your needs change)
I recommend you to read:
pthread_create(3) and similar manual pages. A reference of threads.
pthread_mutex(3) and similar manual pages. A reference to synchronization between threads.
semop(2) and friends. Reference to sysv5 semaphores.
mmap(2) and friends. Reference to shared memory segments.
pipe(2) and fifo related documentation. This will show you how a pipe can be used, as it blocks readers and writers when the internal buffer it has is full/empty. This can be a good starting point.
The following code fork()s a child and then it feeds its child with all its own input, while the parent is writing to standard output its own input, it also feeds to the child its input, so the child will work on it and write to a file output.txt:
###pru.c
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int fd[2];
/* we need to get the pipe before fork() so we have it in both,
* parent and child. */
int res = pipe(fd);
if (res < 0) {
perror("pipe");
exit(1);
}
int child_pid = fork();
if (child_pid < 0) {
perror("fork");
exit(1);
}
/* both processes get here. */
if (child_pid == 0) { /* we are the child only */
close(fd[1]); /* we are not using the writing end */
FILE *out = fopen("output.txt", "a+");
FILE *f = fdopen(fd[0], "r");
int c;
while ((c = fgetc(f)) != EOF) {
/* print an hex representation of char */
fprintf(out, "<%02x>", c);
}
/* let's close both */
fclose(out); fclose(f);
/* remember the child gets 0 from fork, so it needs to
* call getpid() syscall to get its pid. */
printf("Child (pid=%d) exiting.\n", getpid());
exit(0);
/* NOTREACHED */
} else {
close(fd[0]); /* we don't use the reading end */
/* we are writing on the writing side of the pipe */
FILE *out = fdopen(fd[1], "w");
int c;
while ((c = fgetc(stdin)) != EOF) {
/* make a copy for the child */
fputc(c, out);
/* and write to stdout */
fputc(c, stdout);
}
fclose(out); /* signal the child that there's no more output */
fprintf(stderr,
"Waiting for the child (whose pid should be %d)"
" to finish\n",
child_pid);
int status;
pid_t exited_pid = wait(&status);
if (exited_pid < 0) {
perror("wait");
exit(1);
}
fprintf(stderr,
"%d exited with status %d\n",
exited_pid, status);
exit(0);
/* NOTREACHED */
} /* else */
} /* main */
whose output is:
$ echo "Hello, world" | ./a.out
Hello, world
Waiting for the child (whose pid should be 2833) to finish
Child (pid=2833) exiting.
2833 exited with status 0
$ _
and the contents of output.txt:
$ cat output.txt
<48><65><6c><6c><6f><2c><20><77><6f><72><6c><64><0a>$ _
I suppose your complaint is that myecho runs after the first kill(pid, SIGUSR1) but not the second time? But as you said yourself, execve doesn't return if it succeeds!
Upon successful execve the child process is no longer running your program at all, it's running the myecho program, which doesn't handle signals, and when myecho calls _exit() the process exits. So by the time the parent calls kill(pid, SIGUSR1) the second time, your child process is a zombie. The second kill won't fail, but it also won't have any effect; a zombie can't respond to signals.
If you want the child to stick around and keep responding to signals, then the child cannot call execve itself to run myecho. Instead, each time the signal is received, the child must fork again, creating a grandchild process to do the exec. The original child doesn't call execve but just returns to its sleep loop to wait for another signal.
As a side remark, I think you've misunderstood the role of waitpid. Your waitpid(-1, NULL, WNOHANG); effectively does nothing at all. It would be used to test whether the child had already exited and reap it if so, immediately returning 0 if yes and -1 if no, but you ignore the return value. You don't need any system call for the parent and child to run independently; that's what happens by default when you fork. You call waitpid or one of its relatives when you want the parent to wait instead of going on running. I think you may have misunderstood something in the documentation.
Which raises the question of who is going to clean up all those zombies. They will stay around and pollute the process table until their parent calls waitpid. You could have the child process, on every wakeup, call waitpid(WNOHANG) in a loop until it returns -1 to reap all grandchildren that have exited. Or you could use a SIGCHLD handler. Likewise, the parent should really be arranging somehow for the child to terminate before the parent does (kill it with another signal, for instance), and then the parent should waitpid for the child, without WNOHANG, before it itself exits.
Finally, instead of usleep in a loop, the preferred way to wait for a signal is with sigwait(). In that case you don't actually need the runChild flag. You still have to install a signal handler for SIGUSR1 but it can just do nothing.
I can try to come back later and post a fixed version of your program as an example.
Thanks to the answers of Nate Eldredge and Luis Colorado I have been able to create 3 examples. I'm going to post them in case it helps other people, I've really done this as an exercise to learn C, but these the examples are nothing without the help of Nate Eldredge and Luis Colorado. I also did another example with sigwait, but I've seen that with Semaphore it's simpler.
1 - Posix Semaphore:
struct sigaction sa; // signal
short int *runChildProcess; // mmap shared memory
sem_t *sem; // semaphore
static void handleChildTerm(int signum, siginfo_t *sinfo, void *unused) {
printf("Child %d exited\n", (int)sinfo->si_pid);
}
static void runChild(pid_t pid) {
if (sem_post(sem) == -1) {
perror("sem_post run child");
exit(EXIT_FAILURE);
}
// When SIGCHLD is caught, sleep(3) is interrupted
// https://stackoverflow.com/questions/14266485/understanding-sigchld-when-the-child-process-terminates
unsigned short int sleepTime = 3;
while ((sleepTime = sleep(sleepTime)) > 0)
;
}
static void termChild(pid_t pid) {
*runChildProcess = 0; // set to 0 to exit the loop
runChild(pid);
if(munmap(runChildProcess, sizeof *runChildProcess) == -1) {
perror("Munmap");
exit(EXIT_FAILURE);
}
if (sem_destroy(sem) == -1) {
perror("sem_destroy");
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[]) {
setbuf(stdout, NULL); // make stdout unbuffered
// mmap: memory mapping file, shared with child process with the flag MAP_ANONYMOUS | MAP_SHARED
runChildProcess = mmap(NULL, sizeof *runChildProcess, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (runChildProcess == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
*runChildProcess = 1;
// semaphore
sem = mmap(0, sizeof(*sem), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (sem_init(sem, 1 /*shared*/, 0 /*init val*/) == -1) {
perror("sem_init");
exit(EXIT_FAILURE);
}
// signal handler for SIGCHLD (exit child process)
sa.sa_flags = SA_SIGINFO | SA_RESTART | SA_NOCLDWAIT;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handleChildTerm;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction SIGCHLD");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) { // child process
while (*runChildProcess) {
sem_wait(sem);
if (*runChildProcess == 0) {
break;
}
printf("Child process started: %d\n", getpid());
}
_exit(EXIT_SUCCESS);
}
printf("Parent process: %d\n", getpid());
runChild(pid);
runChild(pid);
runChild(pid);
termChild(pid);
printf("Done\n");
exit(EXIT_SUCCESS);
}
2 - Pipes
#include <errno.h> // for errno
#include <signal.h> // sigaction
#include <stdbool.h> // bool
#include <stdio.h> // printf
#include <stdlib.h> // exit
#include <string.h> // strlen
#include <sys/mman.h> // mmap
#include <sys/types.h> // pid_t
#include <unistd.h> // fork
bool *runChildProcess; // mmap shared memory
int fd[2]; // pipe
static void runTermChildHandler(int signum, siginfo_t *sinfo, void *unused) {
*runChildProcess = 0;
}
static void termChildHandler(int signum, siginfo_t *sinfo, void *unused) {
printf("Child %d exited\n", (int)sinfo->si_pid);
}
static void runChild(int pipeFd, char *buffer) {
if (write(pipeFd, buffer, strlen(buffer) + 1) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
// When SIGCHLD is caught, sleep(3) is interrupted
// https://stackoverflow.com/questions/14266485/understanding-sigchld-when-the-child-process-terminates
unsigned short int sleepTime = 3;
while ((sleepTime = sleep(sleepTime)) > 0)
;
}
static void termChild(pid_t pid, int pipeFd) {
kill(pid, SIGUSR1);
close(pipeFd);
unsigned short int sleepTime = 1;
while ((sleepTime = sleep(sleepTime)) > 0);
munmap(runChildProcess, sizeof *runChildProcess);
}
int main(int argc, char *argv[]) {
setbuf(stdout, NULL); // make stdout unbuffered
// mmap: memory mapping file, shared with child process with the flag MAP_ANONYMOUS | MAP_SHARED
runChildProcess = mmap(NULL, sizeof *runChildProcess, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (runChildProcess == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
*runChildProcess = 1; // set to 1 to run the loop
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_sigaction = runTermChildHandler;
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
perror("sigaction SIGUSR1");
exit(EXIT_FAILURE);
}
sa.sa_flags = SA_RESTART | SA_SIGINFO | SA_NOCLDSTOP;
sa.sa_sigaction = termChildHandler;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction SIGCHLD");
exit(EXIT_FAILURE);
}
if (pipe(fd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
char buffer[100];
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
close(fd[1]); // close write end of pipe
printf("Child process: %d\n", getpid());
while (*runChildProcess) {
ssize_t nbytes = read(fd[0], buffer, sizeof(buffer)); // block until data is available from parent process
if (nbytes == -1) { // EINTR: interrupted by a signal, although SA_RESTART is set no is no need to check for EINTR
if (errno == EINTR) {
continue;
}else{
perror("read");
exit(EXIT_FAILURE);
}
}
if (nbytes == 0) {
continue;
}
printf("Buffer: %s\n", buffer);
}
close(fd[0]); // close read end of pipe
_exit(EXIT_SUCCESS);
}
printf("Parent process: %d\n", getpid());
close(fd[0]); // close read end of pipe
runChild(fd[1], "Thanks Nate Eldredge");
runChild(fd[1], "Thanks Luis Colorado");
runChild(fd[1], "Thanks www.stackoverflow.com");
termChild(pid, fd[1]);
printf("Done\n");
exit(EXIT_SUCCESS);
}
3 - Semaphore with execve
#include <errno.h>
#include <semaphore.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/stat.h>
struct sigaction sa; // signal
short int *runChildProcess; // mmap shared memory
sem_t *sem; // semaphore
static void handleChildTerm(int signum, siginfo_t *sinfo, void *unused) {
printf("Child %d exited\n", (int)sinfo->si_pid);
}
static void runChild(pid_t pid) {
if (sem_post(sem) == -1) {
perror("sem_post");
exit(EXIT_FAILURE);
}
// When SIGCHLD is caught, sleep(3) is interrupted
// https://stackoverflow.com/questions/14266485/understanding-sigchld-when-the-child-process-terminates
unsigned short int sleepTime = 3;
while ((sleepTime = sleep(sleepTime)) > 0)
;
}
static void termChild(pid_t pid) {
*runChildProcess = 0; // set to 0 to exit the loop
runChild(pid);
signal(SIGCHLD, SIG_DFL); // restore default signal handler
if(munmap(runChildProcess, sizeof *runChildProcess) == -1) {
perror("Munmap");
exit(EXIT_FAILURE);
}
if (sem_destroy(sem) == -1) {
perror("sem_destroy");
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[]) {
setbuf(stdout, NULL); // make stdout unbuffered
// mmap: memory mapping file, shared with child process with the flag MAP_ANONYMOUS | MAP_SHARED
runChildProcess = mmap(NULL, sizeof *runChildProcess, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (runChildProcess == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
*runChildProcess = 1;
// semaphore
sem = mmap(0, sizeof(*sem), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (sem_init(sem, 1 /*shared*/, 0 /*init val*/) == -1) {
perror("sem_init");
exit(EXIT_FAILURE);
}
// signal handler for SIGCHLD (exit child process)
sa.sa_flags = SA_SIGINFO | SA_RESTART | SA_NOCLDWAIT;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handleChildTerm;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction SIGCHLD");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) { // child process
char rootChildPidParam[10];
snprintf(rootChildPidParam, 10, "%d", getpid());
char *childArgv[] = {"process-pipe-fork/execve/myecho", "hello", "world", rootChildPidParam, NULL};
char *childEnviron[] = {"MY_NAME=jcarlosweb", NULL};
printf("Root Child started: %s\n", rootChildPidParam);
while (*runChildProcess) {
sem_wait(sem);
// check again
if (*runChildProcess == 0) {
break;
}
pid_t leafChildPid = fork();
if (leafChildPid == -1) {
perror("fork sibling");
_exit(EXIT_FAILURE);
}
if (leafChildPid == 0) { // child process
printf("Leaf process with execve started: %d\n", getpid());
// TODO: make child process in background
execve("process-pipe-fork/execve/myecho", childArgv, childEnviron);
perror("execve myecho");
_exit(EXIT_FAILURE);
}
}
_exit(EXIT_SUCCESS);
}
printf("Parent process: %d\n", getpid());
runChild(pid);
runChild(pid);
runChild(pid);
termChild(pid);
printf("Done\n");
exit(EXIT_SUCCESS);
}
I have picked the following example from APUE :
void daemonize(const char* cmd)
{
int i,fd0,fd1,fd2;
pid_t pid;
struct rlimit r1;
struct sigaction sa;
//clear all file masks
umask(0);
//get max number of file descriptors
if(getrlimit(RLIMIT_NOFILE,&r1) < 0)
{
perror("error getting file descriptor size");
return;
}
//become a session leader
if((pid = fork()) < 0)
{
perror("error forking");
return;
}
else if(pid == 0)
{
setsid();
}
else
{
exit(0); //parent exits
}
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if(sigaction(SIGHUP,&sa,NULL) < 0)
{
return;
}
if((pid = fork()) < 0)
{
return;
}
else if(pid != 0)
{
exit(0); //parent
}
//child continues
syslog(LOG_ERR,"chile continuing with pid : %d",getpid());
//change the working directory
if(chdir("/") < 0)
{
return;
}
if(r1.rlim_max == RLIM_INFINITY)
r1.rlim_max = 1024;
for(i=0;i<r1.rlim_max;i++)
close(i);
//attach the file descriptors to /dev/null
fd0 = open("/dev/null",O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
//initialize the log file
openlog(cmd, LOG_CONS,LOG_DAEMON);
if(fd0!=0 || fd1!=1 || fd2!=2)
{
syslog(LOG_ERR,"unexpected file descriptors %d %d %d\n",fd0,fd1,fd2);
exit(1);
}
}
int main()
{
daemonize("date");
pause(); //how is this working???
}
What I don't understand is how the pause() from the main function is working? What I was expecting is that since we have done exit(0) for the parent process in daemonize(), it should have exited and resulted in the normal termination of the main() process. It should have never returned to the main() and the call to pause() should not even happen. Why it did not terminate and why the pause() got called?
The code forks twice, producing a parent, a child, and a grandchild. The first two each exit(0); the last returns from daemonize.
I am trying to implement the behaviour of Ctrl+Z in a mini C Shell. My code currently receives the SIGTSTP signal and correctly suspends the process, however I can't seem to get it to move to the background and put the shell process back in the foreground.
I have tried my best to change the process group of the child that is suspended so that my waitpid() loop can continue (because waitpid(-1, ...) should only find children with the same process group as the parent, right?), however it always gets stuck waiting for the suspended process to end.
The while loop gets to the point where it prints Continuing and then the waitpid() never ends, unless I kill the child process externally.
Ctrl+C works fine.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <assert.h>
#include <string.h>
int main()
{
char inbuf[256];
pid_t pid;
struct sigaction act;
for ( ; ; )
{
tcsetpgrp(fileno(stdin), getpgrp());
act.sa_handler = SIG_IGN;
assert(sigaction(SIGINT, &act, NULL) == 0);
assert(sigaction(SIGTSTP, &act, NULL) == 0);
printf("> ");
gets(inbuf);
pid = fork();
switch (pid)
{
case -1:// error
perror("fork");
exit(EXIT_FAILURE);
break;
case 0: // child
setpgrp();
tcsetpgrp(fileno(stdin), getpgid(pid));
act.sa_handler = SIG_DFL;
assert(sigaction(SIGINT, &act, NULL) == 0);
assert(sigaction(SIGTSTP, &act, NULL) == 0);
execlp( inbuf, inbuf, (char *)0 );
printf("execlp failed\n");
exit(EXIT_FAILURE);
break;
default:// parent
setpgid(pid, pid);
int status = 0;
signal(SIGTTOU, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
tcsetpgrp(fileno(stdin), getpgid(pid));
int parent_group = getpgid(pid);
while (1) {
pid = waitpid(-1, &status, WUNTRACED);
printf("PID: %d\n", pid);
if (pid < 1) {
printf("Breaking\n");
break;
}
if (WIFSTOPPED(status)) {
printf("STOPPED: %d!\n", pid);
// I feel like I should be sending the process, pid, to the background here
//tcsetpgrp(fileno(stdin), parent_group);
//setpgid(pid, pid);
}
printf("Continuing\n");
}
}
memset(inbuf, '\0', sizeof(inbuf));
}
return 0;
}
I'm writing a Unix program where the parent process has to send signals to children and a grandson. How could I know if all processes have been already created before sending signals? Because sometimes they don't exist yet. Thanks a lot!
void t(int sig)
{
kill(SIGKILL, pidc1);
kill(SIGKILL, pidc2);
kill(SIGKILL, pidg2);
kill(SIGKILL, pidc3);
}
void handler()
{
write(1, "Signal SIGUSR1\n", 15);
}
pid_t pidc1, pidc2, pidc3, pidg2;
int main(int argc, char **argv)
{
struct sigaction action;
int status;
action.sa_flags = 0;
action.sa_handler = handler;
sigaction(SIGUSR1, &action, NULL);
pidc1 = fork();
if(pidc1 == 0)
{
printf("Child 1\n");
}
pidc2 = fork();
if(pidc2 == 0)
{
printf("Child 2\n");
pidg2 = fork();
if(pidg2 == 0)
{
printf("Grandson 2\n");
}
wait(&status);
}
pidc3 = fork();
if(pidc3 == 0)
{
printf("Child 3\n");
}
kill(pidg2, SIGUSR1);
kill(pidc3, SIGUSR1);
signal(SIGALRM, t);
alarm(10);
wait(&status);
}
Preliminary note: The child code parts in your example program fall through to their parent's code, which is certainly not intended; I'll assume something like return sleep(5); at the end of each block. Also note that the printf()s may malfunction with fork()s and buffered output.
Barmar wrote:
If you need to wait for the grandchild processes to be created, you need some kind of communication from the child to the parent, so it can send the grandchild's PID. Shared memory and a mutex would be a way to do this.
That's absolutely correct. (The direct children are no problem, since the parent knows their PIDs.) Another way to communicate the grandchild's PID is a pipe; your example main() could become:
int main(int argc, char **argv)
{
int status;
sigaction(SIGUSR1, &(struct sigaction){.sa_handler = handler}, NULL);
setbuf(stdout, NULL); // printf() may malfunction without this
pidc1 = fork();
if (pidc1 == 0)
{
printf("Child 1\n"); return sleep(5);
}
int pipefd[2];
pipe(pipefd); // for communicating the grandson's PID
pidc2 = fork();
if (pidc2 == 0)
{
printf("Child 2\n");
pidg2 = fork();
if (pidg2 == 0)
{
printf("Grandson 2\n"); return sleep(5);
}
write(pipefd[1], &pidg2, sizeof pidg2); // write pidg2 to write end
wait(&status); return sleep(5);
}
pidc3 = fork();
if(pidc3 == 0)
{
printf("Child 3\n"); return sleep(5);
}
read(pipefd[0], &pidg2, sizeof pidg2); // read pidg2 from pipe's read end
kill(pidg2, SIGUSR1);
kill(pidc3, SIGUSR1);
}