Link to home
Start Free TrialLog in
Avatar of demonzzz
demonzzz

asked on

Returning signals from parent to child

Say I have;

int routine1 (void)
{
   some command;   //ppp connection which stays alive
   return;
}
int routine2 (void)
{
   some command;
   return;
}
int main (void)
{
   int child1;

   child1 = fork();
   if (child1)
       {
      routine2();
       }
else
routine1();
return 0;
}

I've read the section on signals and returns in my book a million times over, i cant make sense of it!
How would I get routine1 to terminate when all children finish?
To explain a little better: my program connects to gprs via ppp then run the children. routine1 is the gprs connection which i would like terminated once the children processes have completed.

As I said, i can't get my head around it and would like a little enlightenment please.
This solution is pretty urgent!
Thnx
Avatar of idt
idt

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>

int childcounter;
void sigchld();

void Childroutine(void) {
    sleep(6);
    printf("\nCHILD %d: sending SIGCHLD\n",getpid());
    kill(getppid(),SIGCHLD);
    exit(0);
}

void Mainroutine(void) {
    signal(SIGCHLD,sigchld);
    while (1) {
      sleep(1);
      printf("\nMAIN: I have %d children\n",childcounter);
      if(childcounter==0) exit(0);
    }
}
int main(){
  int pid;
  int i;
  childcounter=0;

  for (i=0;i<4;i++) {
    sleep(1);
    childcounter++;
    printf("\nMAIN: giving birth to a child\n");
    pid = fork();
    if (pid == 0)
      Childroutine();
  }
  if(pid!=0) Mainroutine();
}

void sigchld() {
  childcounter--;
  return;
}


Note that if 2 signals are sent at the same time, the main can miss 1.

Daniel
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int childcounter;

void Childroutine(void) {
    //do some child work...
    printf("\nCHILD %d: exiting\n",getpid());
    exit(0);
}

void Mainroutine(void) {
    int childstatus;
    int deadchild;
    while (1) {
      //do some main work...
      sleep(1);
      if((deadchild=waitpid(0,&childstatus,WNOHANG))!=0) {
        childcounter--;
        printf("\nMAIN: child %d died\n",deadchild);
      }
      printf("\nMAIN: I have %d children\n",childcounter);
      if(childcounter==0) exit(0);
    }
}
int main(){
  int pid;
  int i;
  childcounter=0;
  for (i=0;i<10;i++) {
    childcounter++;
    printf("\nMAIN: giving birth to a child\n");
    pid = fork();
    if (pid == 0)
      Childroutine();
  }
  if(pid!=0) Mainroutine();
}

You may want to think about not using signals at all and use waitpid with the WNOHANG argument as above, this seems to elinimate the problem of missing signals.

Daniel
Avatar of sunnycoder
Hi demonzzz,

The basic thing you need to realize about signals is that signal handling is totally asynchronous. A signal may be received at anytime and control will be transferred to the signal handler irrespective of where the control previously was.

Now I am not very clear about what you are trying to ask? Do you wish to know how to incorporate signal handling in the above code skeleton so that parent exits when all children have completed? If yes, then SIGCHLD signal is sent to the parent when a child exits. You can simply write a signal handler for SIGCHLD. This will be called whenever a child exits. You can then do the cleanup in the signal handler.

A word of caution, signal() interface has been replaced by sigaction() interface. New interface is safer and better. I would recommend that you use sigaction in place of signal

man sigaction

A link which I found very educative when I learnt sigaction interface
http://www.linux-mag.com/2000-01/compile_01.html
idt has given a program for you to look into.
I haven't tried it to see if it works.
But, I will try to explain the logic behind his code.
When a child exits, parent gets a signal SIGCHLD.
If you use waitpid() function inside the parent, then you will also know the pid and status of the exiting child.
When waitpid() returns from wait, that means all the child processes have exited and you can exit the routine1() by calling exit(0).
So, only thing you have to learn is waitpid.
Here is the manual page for waitpid():

WAIT(2)                    Linux Programmer's Manual                   WAIT(2)

NAME
       wait, waitpid - wait for process termination

SYNOPSIS
       #include <sys/types.h>
       #include <sys/wait.h>

       pid_t wait(int *status);
       pid_t waitpid(pid_t pid, int *status, int options);

DESCRIPTION
       The  wait  function  suspends  execution of the current process until a
       child has exited, or until a signal is delivered  whose  action  is  to
       terminate  the  current  process or to call a signal handling function.
       If a child has already exited by the time  of  the  call  (a  so-called
       "zombie"  process),  the  function  returns  immediately.   Any  system
       resources used by the child are freed.

       The waitpid function suspends execution of the current process until  a
       child as specified by the pid argument has exited, or until a signal is
       delivered whose action is to terminate the current process or to call a
       signal  handling  function.  If a child as requested by pid has already
       exited by the time of the call  (a  so-called  "zombie"  process),  the
       function  returns  immediately.  Any system resources used by the child
       are freed.

       The value of pid can be one of:

       < -1   which means to wait for any child process whose process group ID
              is equal to the absolute value of pid.

       -1     which  means  to  wait  for  any child process; this is the same
              behaviour which wait exhibits.

       0      which means to wait for any child process whose process group ID
              is equal to that of the calling process.

       > 0    which  means  to wait for the child whose process ID is equal to
              the value of pid.

       The value of options is an OR of zero or more  of  the  following  con-
       stants:

       WNOHANG
              which means to return immediately if no child has exited.

       WUNTRACED
              which  means  to also return for children which are stopped, and
              whose status has not been reported.

       (For Linux-only options, see below.)

       If status is not NULL, wait or waitpid store status information in  the
       location pointed to by status.

       This  status  can  be evaluated with the following macros (these macros
       take the stat buffer (an int) as an argument â not  a  pointer  to  the
       buffer!):

       WIFEXITED(status)
              is non-zero if the child exited normally.

       WEXITSTATUS(status)
              evaluates to the least significant eight bits of the return code
              of the child which terminated, which may have been  set  as  the
              argument  to  a  call  to exit() or as the argument for a return
              statement in the main program.  This macro can only be evaluated
              if WIFEXITED returned non-zero.

       WIFSIGNALED(status)
              returns  true  if  the  child process exited because of a signal
              which was not caught.

       WTERMSIG(status)
              returns the number of the signal that caused the  child  process
              to  terminate.  This  macro can only be evaluated if WIFSIGNALED
              returned non-zero.

       WIFSTOPPED(status)
              returns true if the child process which  caused  the  return  is
              currently  stopped;  this  is only possible if the call was done
              using WUNTRACED.

       WSTOPSIG(status)
              returns the number of the signal which caused the child to stop.
              This   macro  can  only  be  evaluated  if  WIFSTOPPED  returned
              non-zero.

       Some versions of Unix (e.g. Linux, Solaris, but not  AIX,  SunOS)  also
       define  a  macro  WCOREDUMP(status)  to  test whether the child process
       dumped core. Only use this enclosed in #ifdef WCOREDUMP ... #endif.

RETURN VALUE
       The process ID of the child which exited, or zero if WNOHANG  was  used
       and  no child was available, or -1 on error (in which case errno is set
       to an appropriate value).

ERRORS
       ECHILD if the process specified in pid does not exist or is not a child
              of the calling process.  (This can happen for oneâs own child if
              the action for SIGCHLD is set to SIG_IGN.  See  also  the  LINUX
              NOTES section about threads.)

       EINVAL if the options argument was invalid.

       EINTR  if  WNOHANG was not set and an unblocked signal or a SIGCHLD was
              caught.

Hope this helps........

-ssnkumar
Hi idt,
>     kill(getppid(),SIGCHLD);
>     exit(0);

This will happen automatically, there is no need for an explicit SIGCHLD in this case.

It's more appropriate to wait for a child to finish in the parent:

waitpid(-1,NULL,0);

(-1 waits for any child to finish. Use WNOHANG as third argument for non-blocking calls. AFAIK, errno should be EAGAIN when no child was finished with WNOHANG.)

Cheers,
Stefan
Avatar of demonzzz

ASKER

idt's post was extremely helpful, however, I still have a problem.
In the example i've posted here, 'mainroutine' runs, then spwns processes for 'startacap' and 'startpcap'. I want the mainroutine (which is the pppd gprs connection) to terminate once the children have finished, otherwise the program execution never gets to run 'poweroff' therefore the program and system continues to run.
It should also be noted here that i'm coding for Linux (k2.6.1).
I hope i've explained the problem well enough.
Thnx idt for getting me this close!
Nick

int childcounter;
void sigchld();
void mainroutine(void)
{
      char command[MAXSTRING];
      sprintf(command, "pppd /dev/ttyS0 115200 file /etc/ppp/options.gprs");
      system(command);
}
void startacap(void)
{
      char command[MAXSTRING];
      sprintf(command, "arecord -t wav -c 1 -d 1 -r 5500 /root/peye/caps/stream-%g.wav", g);
      system(command);
      sleep(1);
      printf("\nCHILD %d (acap): sending SIGCHLD\n",getpid());
      kill(getpid(),SIGCHLD);
      exit(0);
}
void startpcap(void)
{
      char command[MAXSTRING];
      sprintf(command, "./pcap-dev6 | cjpeg > /root/peye/caps/capture-%s.jpg", s, i);
      system(command);
      printf("\nCHILD %d (pcap): sending SIGCHLD\n",getpid());
      kill(getpid(),SIGCHLD);
      exit(0);
}
void sigcounter(void)
{
      signal(SIGCHLD,sigchld);
      while (1)
      {
            sleep(20);
            printf("\nSIGCOUNTER: I have %d children\n", childcounter);
            if (childcounter == 1)
            {
                  sleep(10);
                  return;
            }
      }
}      
int main ()
{
      int i;
      childcounter=0;
      int child1;
      int child2;
      for (i=0;i<4;i++)
      {
            sleep(1);
            childcounter++;
            printf("\nMAIN: giving birth to a child\n");
            child1 = fork();
            if (child1)
            {
                  sleep(1);
                  childcounter++;
                  printf("\nMAIN: giving birth to a child 2\n");
                  child2 = fork();
                  if (child2)
                  {
                              startpcap();
                  }
                  else
                  {
                              sleep(18);
                              startacap();
                  }
            }
            else
            startsmsgprs();
            sleep(30);
            powerdown();      //shutdown -h now
            return 0;
      }
}
void sigchld()
{
      childcounter--;
      return;
}
Hi Nick,

Your children need to kill with getppid() not getpid() since you want the processid of the parent, not the child.

I don't see where you are calling  mainroutine(void) or  sigcounter(void)
Nor see the prototype for startsmsgprs()

Does the system() in mainroutine finish, or is that the routine that you want to stop once all children proceses are complete?

Daniel

Daniel,
Sorry, my bad, mainroutine is "startsmsgprs", i had renamed to make it easier reading but forgot that one!

When using kill(getppid(),SIGCHLD); on BOTH childprocesses, does this mean that the parent is killed when BOTH children are finished, or just the one?
I will attach the code again with all my typo's fixed, sorry 'bout this.
The system(command) in the mainroutine does not finish, this is what i would like killed after ALL child processes have finished.
After looking over this again, i'm not sure where to call sigcounter, would it be called at the start of main()?
Thnx

int childcounter;
void sigchld();
void mainroutine(void)
{
     char command[MAXSTRING];
     sprintf(command, "pppd /dev/ttyS0 115200 file /etc/ppp/options.gprs");
     system(command);
}
void startacap(void)
{
     char command[MAXSTRING];
     sprintf(command, "arecord -t wav -c 1 -d 1 -r 5500 /root/peye/caps/stream-%g.wav", g);
     system(command);
     sleep(1);
     printf("\nCHILD %d (acap): sending SIGCHLD\n",getpid());
     kill(getppid(),SIGCHLD);
     exit(0);
}
void startpcap(void)
{
     char command[MAXSTRING];
     sprintf(command, "./pcap-dev6 | cjpeg > /root/peye/caps/capture-%s.jpg", s, i);
     system(command);
     printf("\nCHILD %d (pcap): sending SIGCHLD\n",getpid());
     kill(getppid(),SIGCHLD);
     exit(0);
}
void sigcounter(void)
{
     signal(SIGCHLD,sigchld);
     while (1)
     {
          sleep(20);
          printf("\nSIGCOUNTER: I have %d children\n", childcounter);
          if (childcounter == 1)
          {
               sleep(10);
               return;
          }
     }
}    
int main ()
{
     int i;
     childcounter=0;
     int child1;
     int child2;
     for (i=0;i<4;i++)
     {
          sleep(1);
          childcounter++;
          printf("\nMAIN: giving birth to a child\n");
          child1 = fork();
          if (child1)
          {
               sleep(1);
               childcounter++;
               printf("\nMAIN: giving birth to a child 2\n");
               child2 = fork();
               if (child2)
               {
                         startpcap();
               }
               else
               {
                         sleep(18);
                         startacap();
               }
          }
          else
          mainroutine();
          sleep(30);
          powerdown();     //shutdown -h now
          return 0;
     }
}
void sigchld()
{
     childcounter--;
     return;
}
Nick,

Ahh, makes things a bit clearer

When using kill(getppid(),SIGCHLD); on BOTH childprocesses, does this mean that the parent is killed when BOTH children are finished, or just the one?
kill is how to send a signal, it does not actually mean kill, well it could if the recieving process doesn't handle it, but in this case the parent has a handler.

Okay, since the main thread is off doing a system instruction, you will have a lot of problems in this model.
Instead, create a new thread to do the system(), and you can kill it by sending it a signal that it does not have a handler for, like SIGHUP after all the other children die.
That thread would then exit, and your main thread would do any cleanup.

If you want to wait a few hours, I can get a chance to revamp the code to this new paradigm.
It's pretty easy now.

Daniel
That makes sense. So the mainroutine will be a child aswell, and when the child count =1 a SIGHUP is sent to the mainroutine child?
Ive had a go at changing it, but my heads gone a bit fuzzy. If you wouldnt mind could you revamp it for me?
It's 1am here, so I don't think i'll be able to give it a go before i go to bed, so there is no rush.
Nick
Nick,

I changed the code to only use a signal to kill the GRPS process once the the A and PCAP process complete.  main just creates the processes, and waits on the kiddies to terminate.  I hope I understood your requirements.

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>

void waitInfo(int pid, int status);
void gprs_routine(void);
void startacap(void);
void startpcap(void);
void ErrorStopProcesses(void);

void waitInfo(int pid, int status) {
  if(WIFEXITED(status))
    printf("\tProcess %d exited normally with exit code %d.\n",pid,WEXITSTATUS(status));
  else {
    if(WIFSIGNALED(status))
      printf("\tProcess %d exited because of signal %d was not caught.\n",pid,WTERMSIG(status));
    if(WIFSTOPPED(status))
      printf("\tProcess %d is stopped because signal %d.\n",pid,WSTOPSIG(status));
  }
  return;
}

void gprs_routine(void) {
  sleep(1);   //simulate startup code
  printf("\tGPRS Process started.\n");
  while (1) {sleep(10);} //simulate long running process
  printf("\tGRPS Process ending (should be unreachable, unless gprs completes).\n");
  exit(0);
}

void startacap(void) {
  sleep(1);   //simulate startup code
  printf("\tACAP Process started.\n");
  sleep(8);   //simulate short running process
  printf("\tACAP Process ending.\n");
  exit(0);
}

void startpcap(void) {
  sleep(1);   //simulate startup code
  printf("\tPCAP Process started.\n");
  sleep(10);  //simulate short running process
  printf("\tPCAP Process ending.\n");
  exit(0);
}

void ErrorStopProcesses(void) {
  printf("\nMAIN: Error %d occurred, kill all processes with process group:%d.\n",errno, (getpid()*-1));
  kill((getpid()*-1),SIGTERM);
  return;
}

int main () {
  int childcounter=0;
  int status;
  int pid;
  int threadgprs;

  threadgprs=fork();
  if(!threadgprs) {
    gprs_routine();
    exit(0);
  }
  printf("MAIN: started GPRS process thread with pid:%d.\n",threadgprs);

  pid=fork();
  if(!pid) {
    startpcap();
    exit(0);
  }
  if(pid==-1) ErrorStopProcesses();
  childcounter++;
  printf("MAIN: started PCAP process thread with pid:%d.\n",pid);

  pid=fork();
  if(!pid) {
    startacap();
    exit(0);
  }
  if(pid==-1) ErrorStopProcesses();
  childcounter++;
  printf("MAIN: started ACAP process thread with pid:%d.\n",pid);


  printf("\nMAIN: entering main wait loop.\n");
  while (childcounter) {
    pid=waitpid(0,&status,0);
    if(pid!=threadgprs)
      childcounter--;
    else {
      threadgprs=0;
      // if gprs process terminates early, you could
      // kill all others by calling ErrorStopProcesses();
      // here, or just wait for them to complete.
      }
    printf("\tMAIN: %s process terminated, %d child processes still exist.\n",(threadgprs==0? "GRPS" : "child"), childcounter);
    waitInfo(pid, status);
  }
  printf("MAIN: exited main wait loop.\n\n");

  if(threadgprs) {
    printf("MAIN: signalling GPRS process to terminate (it will not catch the signal).\n");
    kill(threadgprs,SIGHUP);
    pid=waitpid(0,&status,0);
    waitInfo(pid,status);
  }
  else {
    printf("MAIN: GPRS already terminated.\n");
  }

  printf("\nGround control, shut down main engines.\n");
  sleep(1);  //simulate shutdown code
  return 0;
}

Should complile clean, t'is rather ammusing to watch run.

Daniel
ASKER CERTIFIED SOLUTION
Avatar of idt
idt

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Whoops.. bad prototype
change
...
void mainSigHandler();
...
to
...
void SigHandler();
...

Yeah, i did think about that, pppd continues to execute. However, the first revamp did work as you expected, and as my system poweroff's (init0) after the gprs_routine is killed, it kills the pppd anyway!
I know that its quite a sloppy way about it, but it works! Also, my dissertation is due in a few weeks and i still have lots of ends to tie up and make a project box.
If I have problems with the current setup then ill go over it again with what you've just given me.
Youve given me what i needed, and more, to tie up the loose ends of my uni dissertation project, thanks Daniel!!!!
Thanks also to those who contributed earlier.