Search code examples
cforkparent-childchild-process

C: why after fork() child block is not running?


I'm studying how fork() actually works so my code below has no purpose other than spawning new processes with fork() and see them die randomly. So:

  1. I put my fork() in a for loop (to run twice for now) to see more than one child be created, and to my surprised it seems that the second Child has a parent that was not the same parent as the first child. So, if my initial PID was 1000, the two child created would be 1002 (child of 1000) and 1003 (child of 1001???). I didn't understand what happened that a parent was created. This guy explained but I can't say I fully understood.
  2. To try and find out what was going on, I printed my parent (and child) processes with their PID, but if I declare a char for my parent, my child won't run the function. I explain what I mean in my code between <<< >>>.

So, can anyone help me understand my 1st point, and identify why 2 is happening?

Full code below:

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

const int PASS = 5;
const int RANDLIMIT = 5;
const int FORKNUMBER = 2;
int i = 0;

void doSomeWork(char *who);

int main(int argc, char *argv[])
{
  printf("Just started, I am: %d\n", (int) getpid());
  int j;
  pid_t pid;

  for (j=0; j < FORKNUMBER; j++)
    pid = fork();

  printf("fork returned: %d\n", (int) pid);
  srand((int) pid + rand());

  if (pid < 0) {
    perror("fork failed");
  } else if (pid == 0) {
    char * childPid;
    char * childName;
    sprintf(childPid, "%d", (int) getpid());
    childName = (char *) malloc(strlen("Child - ") + strlen(childPid) + 1 );
    strcpy(childName, "Child - ");
    strcat(childName, childPid);
    doSomeWork(childName);
    exit(0);
  }

  //<<< The malloc above for the child to send a parameter >>>
  //<<< to the function, works fine. But when I try to do  >>>
  //<<< the same for my parent, the simple declaration of a>>>
  //<<< char below, makes the child block (the if PID==0)  >>>
  //<<< not run. The 3 lines commented below were an       >>>
  //<<< attempt to understand what was preventing the child>>>
  //<<< block from running. Now, if the parent calls the   >>>
  //<<< function with a string not declared before, the    >>>
  //<<< child block runs fine.>>>

  //char parentName[strlen("Parent") + 1];
  //strcpy(parentName, "Parent");
  //doSomeWork(parentName);
  doSomeWork("Parent");
  //wait(NULL);
  return(0);
}

void doSomeWork(char *who)
{
  int control = 0;
  for(; i < PASS; i++){
    sleep(rand() % RANDLIMIT);
    printf("%s: Done pass #%d, my parent = %d\n", who, i, getppid());

    if (control == 0)
    {
      char childWord[6];
      strncpy(childWord, who, 5);

      if (strcmp(childWord, "Child") == 0 && (int) getppid() == 1 )
      {
        control = 1;
        printf("%s: became orphan at #%d\n", who, i);
      }
    }
  }
  printf("%s: exiting...\n", who);
}

EDIT:

For 1, I created a function like below:

int nbDigits(int number)
{
  int i=0;
  for(; number > 10; i++)
  {
    number /= 10;
  }
  return ++i;
}

Now instead of declaring a pointer to a char like this, char * childPid; I declared a char array like this char childPid[nbDigits(getpid()) + 1]; and everything worked like a charm.

Check out Joseph's suggestion below using asprintf(), seems neat.


Solution

    1. You can't call fork() in a loop but only check what it returns at the end of the loop if you want your program to work. When you do so, you're starting an exponential number of child processes, and each one thinks it's "the parent" as long as it was the parent of its final fork(). Move your test of pid to inside the loop.
    2. char * childPid;
      sprintf(childPid, /* ... */);
      
      That's going to clobber some random memory. You need to point childPid to something before you sprintf to it, or replace sprintf with something like asprintf that will allocate itself.