2

I am trying fork for the very first time. I have a file with list of integers and I want every line to fork off a child, the child processes the line and writes something in the output file. Basically the for loop starts with number of lines in the input file and goes in a loop, every line should fork a child and after the child is done the fork should launch another child. I am having trouble doing this because before the first child finishes its work the second child starts and in between the parent is also doing some execution.

Edit: the code looks something like this:

void dosomething(flag)
{
}
void forkingfunction(int size, int array[],char *outputfile)
{
    int i,j,starting=1,temp=1,status;

    for(i=0;i<size;i++,temp++)
        {

             pid_t pID = fork();

               if (pID == 0)                // child
               {
                   printf("\nchild pid %d\n",getpid());

                       const int count = dosomething(flag);
                       if(flag==1)
                       {
                           _exit(EXIT_SUCCESS);
                            kill(pID,SIGKILL);
                       }

                       else
                       {
                       FILE *fp;

                          fp = fopen(outputfile, "a+");
                          if (fp == NULL)
                          {perror("Unable to open the output file:");}

                           for (i = 0; i < len; i++)
                           {
                               if (solution[i])
                               {
                                       fprintf(fp," %u ",array[i]);

                               }
                           }
                           fprintf(fp,"=%d\n",sum);
                       }

                      _exit(EXIT_SUCCESS);
                       kill(pID,SIGKILL);

               }
               else if (pID < 0)            // failed to fork
               {
                   perror("Failed to fork:");
               }
               else
               {
                  // wait(NULL);
                   if ((pID = wait(&status)) == -1)
                                       {
                                           perror("Error in wait:");
                                       }
               }
        }
}
void readingfile(char *inputfile,char *outputfile)
{

    FILE *myFile = fopen(input, "r");

    if (myFile == NULL)
    {
        perror("Error: Failed to open file.");

    }
    //code to get number of lines 
    while ((c=getc(myFile)) != EOF)
    {
        //puts values of file in an array for processing
     }

    forkingfunction(size,array,output);

    // close file
    fclose(myFile);
}

int main(int argc, char **argv)
{

        readingfile(inputfilename,outputfilename);

    return 0;
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Fairy
  • 101
  • 2
  • 12
  • 1
    hi, perhaps use [`waitpid`](https://linux.die.net/man/2/waitpid) to wait for the child to finish (or use [signals](https://stackoverflow.com/questions/34969849/sending-signal-from-parent-to-child-and-vice-versa) to take action once the child exits) – IronMan Sep 17 '19 at 17:37

1 Answers1

2

You use a single call to wait():

else
{
    // wait(NULL);
    if ((pID = wait(&status)) == -1)
    {
        perror("Error in wait:");
    }
}

That (or using waitpid() instead) is the core of the answer to your problem, but…

  • You need a different variable than pID to hold the return value to make sure you got the right dead child because the relevant PID was in pID before it got clobbered.

  • If you have other processes running in the background from this process, you might get one of those instead of the child you just launched, so you need to test whether the PID that's reported by wait() is correct, and try again if not. That means you need a loop like:

    int corpse;
    int status;
    
    while ((corpse = wait(&status)) != pID && corpse != -1)    // Or corpse > 0
    {
        /* maybe report on what died */
    }
    
  • Or you can use waitpid() to directly wait for the relevant child to die:

    int corpse;
    int status;
    while ((corpse = waitpid(pID, &status, 0)) != pID && corpse != -1)
    {
        /* maybe report on what died */
    }
    

The wait() and waitpid() calls terminate with an error if there are no children left to wait for, or the specified PID no longer exists. This could happen if code somewhere else in the program waits for a process to die. It's as well to handle that; you don't want your loop locking up when the system says "there are no children left" or "that process you asked about doesn't exist". It can also happen if you have a signal handler for SIGCHLD and it does some waiting.

In both cases, you might need to decide what to do if corpse == -1 after the loop. It shouldn't happen, but… You may legitimately decide to do nothing.

Note that a process might inherit children it didn't know about (didn't create) if a parent process (call it Program1) forks some child processes and then uses exec*() to replace itself with another program, Program2. Program2 has no idea that its process is the parent of other children it did not create. Is this plausible? Yes, it can be done; I have test code that does it. Is it probable? No; it requires an unusual setup, but it isn't hard to do. For example, if a shell script launched some background processes and then issued exec program-that-forks, the process for the program that forks has the background processes that the shell created as children, even though the program that forks did not create those processes.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thank you :) I was stuck on this for 2 weeks! – Fairy Sep 18 '19 at 17:32
  • The `perror()` function has a moderately pathetically limited interface — you can specify a constant string that will be printed in front of the error message implied by the value in `errno`. It should have been (re)designed to mimic `printf()`. I essentially never use `perror()` because it doesn't do what I think is necessary. I use the code that is available in my [SOQ](https://github.com/jleffler/soq) (Stack Overflow Questions) repository on GitHub as files `stderr.c` and `stderr.h` in the [src/libsoq](https://github.com/jleffler/soq/tree/master/src/libsoq) sub-directory. – Jonathan Leffler Sep 19 '19 at 05:07
  • In this context, you may be able to use `char pmsg[64]; snprintf(pmsg, sizeof(pmsg), "PID %d exited with status 0x%.4X", corpse, status); perror(pmsg); exit(1);` or thereabouts. Incidentally, you should note that `_exit()` never returns, so the `kill()` calls after it in your code are never executed. With code in `stderr.c`, you'd simply write: `err_remark("PID %d exited with status 0x%.4X\n", corpse, status);` — which would include the process name (`argv[0]` in `main()`) at the start of the message (assuming you used `err_setarg0(argv[0]);` at the top of `main()`). – Jonathan Leffler Sep 19 '19 at 05:09