Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Help with Child processes

  1. Jun 27, 2015 #1
    Hey everybody, I am having trouble with working with child processes. I am to implement a parent process creating 2 child processes to each run different code. e.g child 1 is a wallclock and child 2 is a countdown program. I also have to implement a pipe for when child 2, the countdown program, upon reaching 0 it will use the pipe to tell the other child process to terminate.

    So far I seem to have written everything correct but for some reason, my child processes are not exiting like they are supposed to and it is causing my parent process to be in a forever loop waiting until the kids end. I swear before a recent modification the code was working fine, until I added another child process then I started having this trouble. I tried back tracking as best as I can to where I remember it was working correctly, but for some reason I am still getting this error. So now I am asking if someone can take a look at my code and see where the problem lies because I for one have been looking at my source code for hours but can not notice anything that would make it behave this way.

    Don't mind the runFile() function as I will be implementing that in the future.

    Code (C):
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <sys/types.h>
    #include <time.h>

    #define DEFAULT 3
    #define pipeSize 2
    #define NUMBEROFCHILD 2
    #define STOP 1
    #define RUN 0

    int fd[pipeSize];

    void wallClock(){
       int status = RUN;        /*signals*/
       int newStatus = RUN;
       time_t now;
       struct tm *lcltime;
     
       if(close(fd[1]) == -1){
          printf("Error closing writing end of pipe\n");
          _exit(1);
       }
     
       while(status != STOP){                                /*loop until signal occurs*/
          now = time(NULL);
          lcltime = localtime(&now);
          printf("the time is %d:%d:%d\n", lcltime->tm_hour,lcltime->tm_min,lcltime->tm_sec);
          sleep(1);
          read(fd[0], &newStatus, sizeof(newStatus));                    /*check for status*/
          status = newStatus;
       }
     
       if(close(fd[0]) == -1){
          printf("Error closing reading end of pipe\n");
          _exit(1);
       }
       printf("Message has been received--Terminating now\n");
       exit(1);
    }

    void runFile(){
       int status = RUN;
       int newStatus = RUN;
     
       if(close(fd[1]) == -1){
          printf("Error closing writing end of pipe\n");
          _exit(1);
       }
     
       while(status != STOP){
                       /*run file in here*/
       }
    }

    void countDown(int start){
       int stopNow = STOP;         /*signals*/
       int cont = RUN;
     
       if(close(fd[0]) == -1){
          printf("Error closing reading end of pipe\n");
          _exit(1);
       }
     
       while(start > 0){                /*while loop until start reaches 0*/
          if(start >= 10){
          printf("Count Down: 00:%2d\n", start);
          }
          else{
          printf("Count Down: 00:0%d\n", start);
          }
          write(fd[1], &cont, sizeof(cont));    /*write in pipe to continue still*/
          start--;
          sleep(1);
       }
     
       printf("Count Down: 00:00 -- sending message to terminate other processess\n");
       write(fd[1], &stopNow, sizeof(stopNow));     /*Tell others to terminate*/
     
       if(close(fd[1]) == -1){
          printf("Error closing writing end of pipe\n");
          _exit(1);
       }
     
       exit(1);
    }

    int main(int argc, char *argv[]){
     
       int time , i, pid = 0;
       pid_t child[NUMBEROFCHILD];
     
       if(argc <= 2){            /*check arguments*/
          if(argv[1] != '\0'){
             time = atoi(argv[1]);
          }
          else
             time = DEFAULT;
       }
       else{
          printf("Too many parameters -- exiting program\n");
          return -1;
       }
     
       if(pipe(fd) == -1){            /*open pipes*/
          printf("Error opening pipe\n");
          exit(1);
       }
     
       for(i = 0; i < NUMBEROFCHILD; i++){       /* create children */  
          if((child[i] = fork())  == -1){
             printf("Error in creating child\n");
         exit(1);
          }
          if(child[i] == 0){
             switch(i){
            case 0:   wallClock();
            case 1:   countDown(time);
         }
          }
       }
     
       if(close(fd[0]) == -1){          
          printf("Error closing parent reading pipe\n");
          exit(1);
       }
     
       if(close(fd[1]) == -1){          
          printf("Error closing parent writing pipe\n");
          exit(1);
       }
     
       while(pid = wait(NULL)){            /*Wait for all children to finish*/
          if(pid == -1){
             printf("Error exiting child\n");
         exit(EXIT_FAILURE);
          }
       }
     
       printf("All processes has ended, exiting program --\n");
       return 0;
     
    }
     

    I have been racking my brain and am frustrated because I feel that my logic is correct and it literally worked hours ago but I must've changed something and now I can't find what is causing it to behave this way.
    So thanks for any/all input or advice
     
  2. jcsd
  3. Jun 27, 2015 #2

    Mark44

    Staff: Mentor

    Above is something I noticed that may or may not be the problem. If i == 0, you call wallClock(), and then it "crashes" through and also calls countDownTime(time).

    If you're new to the switch statement in C you might not be aware of this behavior of switch.

    If that's not what you intended, here's a fix for it.
    Code (C):

    if(child[i] == 0)
    {
       switch(i)
       {
          case 0:  wallClock();
          break;
          case 1:  countDown(time);
          break;
       }
    }
     
     
  4. Jun 27, 2015 #3
    Thanks for your reply, but it did not solve the solution. However, I did find a solution on google but I am not understanding why it is working all of a sudden, maybe you can explain. So I think the problem was with how I was waiting for the children, I guess it seemed like the child processes were exiting before the parent can wait on it? anyways I added this snippet of code and now everything is working fine.

    Code (C):
    int status;
    pid_t pid;
    n = NUMBEROFCHILD;
       while(n > 0){            /*Wait for all children to finish*/
       pid = wait(&status);
          --n;
       }
     
  5. Jun 27, 2015 #4

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    Your logic was incorrect. Your loop is written as if wait returns zero when there are no unwaited-for child processes. That's not what wait does. If there are no unwaited-for child processes, wait sets errno to ECHILD and returns -1.

    Your program waited for child processes three times. On the first two calls to wait resulted in your parent process being suspended until one of the child processes exited, at which point wait cleaned things up and returned the process ID of the child processes that had been reaped. On the third call to wait, your parent process no longer had any unwaited-for child processes, so wait returned -1.
     
  6. Jun 27, 2015 #5

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    The functions wallClock() and countDown() never return. They instead exit.

    @freshcoat: In most cases, don't repeat yourself (DRY) is a good rule to follow. But when your code does something unexpected, it's best to repeat oneself a bit. To avoid confusing others reading your code (e.g., Mark44), it's best to say that the function doesn't return in a comment at the point where it is called and in the commentary that describes the function.

    I noticed that your functions wallClock and countDown call _exit(1) on error conditions, exit(1) on success. You should be calling _exit(0) instead of exit(1) to indicate success in those functions. The reason for calling _exit rather than exit is because you are in a forked process where you haven't called exec. A call to exit will flush standard output; you might get a double flush (and confused output). The reason for using _exit(0) instead of _exit(1) at the end is to indicate success.

    Finally, you're calling exit(1) in a few places in main, exit(EXIT_FAILURE) in another. On most Unix systems, EXIT_FAILURE is #defined to be 1.
     
  7. Jun 27, 2015 #6
    I have another question, and since it is related to this program I'll just ask it here.

    My problem now is with implementing the 3rd child to run an execl command continuously until it reads the signal to terminate from the pipe. From what I understand is when it runs an execl command, it runs the file and then quits the process immediately, so I am assuming in order to keep it running I need some type of handler in the parent process to catch that process and somehow re-run it again. I was thinking of using fork() from the same child id but I don't think that works since it creates a new process with a new pid and such. Is there another command I don't know about to re-run the same child process or is there another way?
     
  8. Jun 28, 2015 #7

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    This is starting to sound a lot like homework. We have a homework help section, https://www.physicsforums.com/forums/engineering-and-computer-science-homework.158, where you should post homework-like computer science questions.


    Regarding your comment "From what I understand is when it runs an execl command, it runs the file and then quits the process immediately ...": This is incorrect. Consider the following:

    Code (C):
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/wait.h>

    int main ()
    {
       printf ("In parent: PID=%d\n", getpid());
       int pid = fork();
       switch (pid)
       {
       case -1: // Oh oh.
          fprintf (stderr, "Fork failure\n");
          exit(1);

       case 0: // Child
          sleep (2);
          printf ("In child:  PID=%d\n", getpid());
          sleep(2);
          // Execute the shell command
          // sh -c 'echo "In shell:  PID="$$; sleep 2; echo "In shell:  Exiting"'
          execl ("/bin/sh",
                 "/bin/sh",
                 "-c",
                 "echo 'In shell:  PID='$$;"
                 "sleep 2;"
                 "echo 'In shell:  Exiting shell'",
                 0);
          fprintf (stderr, "Exec failure\n");  // This should never happen
          _exit(1);

       default: // Parent
          printf ("In parent: Forked PID %d\n", pid);
          break;
       }

       // Also parent
       int waited_pid = wait(0);
       printf ("In parent: Reaped PID %d\n", waited_pid);
       sleep(1);
       printf ("In parent: Exiting parent\n");

       return 0;
    }
    If you compile and run the above, you'll see something like
    Code (Text):
    In parent: PID=7987
    In parent: Forked PID 7988
    In child:  PID=7988
    In shell:  PID=7988
    In shell:  Exiting shell
    In parent: Reaped PID 7988
    In parent: Exiting parent
    Note how the "In child: PID=xxxx" and "In shell: PID=xxxx" show that the child (before the execl call) and the shell (after the exec call) have the exact same process ID.

    In unix, when a process calls a member of the execl family, the process doesn't start the execution of another executable and exit. It instead becomes that other executable. Key resources (e.g., file descriptors) owned by the original process become resources of the execed process.

    In particular, this means that when you execl a program that invokes your wallClock, the file descriptors created in the parent process remain usable after a fork and exec.

    There's a problem, however: How is the forked/execed process to know which file descriptors to use? If you know with a certainty that the read end of the pipeline is file descriptor 3, you can read from file descriptor 3 because that fd is still open and readable in the forked/execed process. But maybe it's fd 42. Who knows?

    There is one way to know, which is to redirect standard input to the read end of the pipeline before calling execl. This is what lets you do things such as find . - type f | xargs grep -l fork. You need to use dup or dup2. This is where your manly ability to read the man pages comes to a test.
     
    Last edited by a moderator: Jun 29, 2015
  9. Jun 28, 2015 #8

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    For crying out loud. Sometimes I am not thrilled with our migration to XenForo. The above post is exemplary. I can't fix the silly font problems.
     
  10. Jun 29, 2015 #9

    Mark44

    Staff: Mentor

    I fixed it. You closed off one of your font tags with a /function tag instead of a /font tag.
     
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook




Similar Discussions: Help with Child processes
  1. Image Processing (Replies: 4)

  2. Signal Processing (Replies: 5)

  3. Parallel processing (Replies: 1)

  4. Data Processing (Replies: 5)

Loading...