Search code examples
c++cprocessforkwaitpid

Fork off more processes as previous finish, until max is reached


To fork off X processes and have the parent wait for all, I have the following code:

int maxProcesses = 10;

for (int currentChild = 0; currentChild < maxProcesses; currentChild++) {
    pid_t pid = fork();

    if (pid < 0) {
        // Error
    } else if (pid == 0) {
        // Child
    } else {
        // Parent
        // Should I call waitpid on pid and wait here instead?
    }
}

// Wait for all children
for (int currentChild = 0; currentChild < maxProcesses; currentChild++) {
    wait(NULL);
}

Now I would like to modify the code so that of X total processes, Y are forked off first, and then as they finish, more forks are made till desired total number is reached. I have made some changes to the above code, with some questions.

int totalProcessesToBeForked = 10;
int maxAllowedAtOnce = 5;

for (int currentChild = 0; currentChild < maxAllowedAtOnce; currentChild++) {
    forkChild(currentChild);
}

// Wait for all children
// # How do I modify this to wait for new children forked as well
// # if I move it inside parent, it will make things easier, right?
for (int currentChild = 0; currentChild < maxAllowedAtOnce; currentChild++) {
    wait(NULL);
}

void forkChild(currentChild) {
    pid_t pid = fork();

    if (pid < 0) {
        // Error
    } else if (pid == 0) {
        // Child
    } else {
        // Parent
        // # I think waiting here using waitpid will be better b/c
        // # as new forks are made, parent begins to wait for them
    }
}

I will probably need to keep a count of how many children have been forked and compare it to totalProcessesToBeForked, and fork new ones accordingly.

Updated Code v1:

int maxProcesses = 10;
int maxAllowedAtOnce = 5;

int main(int argc, char* argv[]) {
    // Start timer
    alarm(10);                  // Terminate after 10s
    signal(SIGALRM, onTimeout);
    signal(SIGCHLD, catchChild);

    for (int currentChild = 0; currentChild < maxAllowedAtOnce; currentChild++) {
        forkChild(currentChild);
    }

    // # This sections runs immediately before death of any child is reported
    // # and starts cleanup processes, thus killing any/all running children

    // Completed before timeout
    endTimer = true;
    int timeRemaining = alarm(0);
    if (timeRemaining > 0) {
        printf("\nCompleted w/ %is remaining. Performing cleanup.\n", timeRemaining);

        // Kill children any running child processes, cleanup
        cleanup();
    }

    return 0;
}

void forkChild(currentChild) {
    pid_t pid = fork();

    if (pid < 0) {
        // Error
    } else if (pid == 0) {
        // Child
        execl("/bin/date", "date", 0, 0);
    } else {
        // Parent
        printf("#Log: Started %i.\n", currentChild + 1);
    }
}

void catchChild(int sig) {
    pid_t p;
    int state;
    p=wait(&state);
    printf("Got child %d\n",p);
}

void cleanup() {
    // Cleanup Code
}

Example Run:

enter image description here

Edit #2: http://ideone.com/noUs3m


Solution

  • Instead of using wait as you have done, you want to look into signal handling to handle the child processes dying.

    You add before you start forking, this line

    signal(SIGCHLD,catchchild);
    

    and this function to your code

    void catchchild(int sig)
      {
      pid_t p;
      int state;
      p=wait(&state);
      printf("Got child %d\n",p);
      }
    

    and then whenever a child process dies, your main process will call catchchild.

    As you've already worked out, if you have a count of how many children that have been forked, you could have catchchild update that so that your main code will know to fork a new child.

    To answer your comment something like the following, except with more error checking

    while(totalProcessesToBeForked)
      {
      if(currentChild < maxAllowedAtOnce)
        {
        forkChild(currentChild);
        totalProcessesToBeForked--;
        }
      sleep(1);
      }