Why Does the runFile Child Process Receive the Termination Signal Later?

  • Thread starter freshcoast
  • Start date
  • Tags
    Process
In summary, the child processes are running correctly, but the runFile process is lingering for a longer time after it receives the signal.
  • #1
freshcoast
185
1
Hello,

1. Homework Statement

I am to implement a program where the parent process creates three child processes each running their own specific code, child 1 will be running a wallclock, child 2 will be a file manager and child 3 will be the countdown timer and is also the signaler. Child 1 is a basic wallClock which just outputs the current local time in military format. Child 2 is a file manager, therefore it creates it's own child process to run the exec command on the file "/usr/bin/uptime" and handles it's return to execute every 5 seconds or until it has been signaled. Child 3 is a basic countdown timer which upon reaching 00:00 it will signal the sibling processes to terminate.

The Attempt at a Solution



So far my implementation of the features have been correct, my only issue is with the runFile child. The output(given below the code) shows that when the countdown child process is done, it successfully signals the wallClock process to exit which it does successfully. However, after both child processes has closed, the runFile child is lingering for a couple more runs until it eventually receives the signal later. My question is why is that?, I don't get how it receives the signal MUCH later, because obviously the pipes are working because it eventually gets the signal and successfully terminates it self, so I don't understand why it receives the signal at a later time?

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

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

int fd[pipeSize];
int fd2[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 in wallClock\n");
      _exit(EXIT_FAILURE);
   }
  
   if(close(fd2[0]) == -1){                        /*close unused pipes*/
      printf("Error closing reading end of pipe2 in wallClock\n");
      _exit(EXIT_FAILURE);
   }
  
   if(close(fd2[1]) == -1){
      printf("Error closing writing end of pipe2 in wallClock\n");
      _exit(EXIT_FAILURE);
   }
  
   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(EXIT_FAILURE);
   }

   printf("Message has been received in wallClock()--Terminating now\n");
   _exit(EXIT_SUCCESS);
}

void runFile(){
   int status = RUN;
   int newStatus;
   pid_t pid;

   if(close(fd[1]) == -1){                            /*close all unused pipes*/
      printf("Error closing writing end of pipe runFile\n");
      _exit(EXIT_FAILURE);
   }
  
   if(close(fd[0]) == -1){
      printf("Error closing reading end of pipe runFile\n");
      _exit(EXIT_FAILURE);
   }
  
   if(close(fd2[1]) == -1){
      printf("Error closing writing end of pipe2 in runFile\n");
      _exit(EXIT_FAILURE);
   }
   while(status != STOP){                        /* wait until signal occurs */
      if((pid = fork()) == -1){                        /*create fork within child process to run exec */
         printf("Fork failed\n");
     _exit(EXIT_FAILURE);
      }
      else if(pid == 0){
     execl("/usr/bin/uptime", "uptime", NULL);
         printf("exec failed!\n");
     _exit(EXIT_FAILURE);
      }
      else{                            /*parent handler*/
         int ret;    
     pid_t justLeft;
     justLeft = wait(&ret);
     read(fd2[0], &newStatus, sizeof(newStatus));
         status = newStatus;
      }
      sleep(5);  
   }
  
   printf("Signal has been received, closing pipes and exiting uptime mananger\n");
  
   if(close(fd2[0]) == -1){
      printf("Error closing reading end of runFile pipe\n");
      _exit(EXIT_FAILURE);
   }
  
   _exit(EXIT_SUCCESS);
  
}

void countDown(int start){
   int stopNow = STOP;         /*signals*/
   int cont = RUN;
  
   if(close(fd[0]) == -1){                         /*close all unused pipes*/
      printf("Error closing reading end of pipe in countDown\n");
      _exit(EXIT_FAILURE);
   }
  
   if(close(fd2[0]) == -1){
      printf("Error closing reading end of pipe2 in countDown\n");
      _exit(EXIT_FAILURE);
   }
  
   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*/
      write(fd2[1], &cont, sizeof(cont));
      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*/
   write(fd2[1], &stopNow, sizeof(stopNow));
  
   if(close(fd[1]) == -1){
      printf("Error closing writing end of pipe in countDown\n");
      _exit(EXIT_FAILURE);
   }
  
   if(close(fd2[1]) == -1){
     printf("Error closing writing end of pipe2 in countDown\n");
     _exit(EXIT_FAILURE);
   }
  
   _exit(EXIT_SUCCESS);
}

int main(int argc, char *argv[]){
  
   int time , i, status, newStatus, ret, n, newChild, numExited;
   pid_t child[NUMBEROFCHILD];
   pid_t pid;

   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(EXIT_FAILURE);
   }
  
   if(pipe(fd2) == -1){
      printf("Error opening pipe\n");
      exit(EXIT_FAILURE);
   }
  
   for(i = 0; i < NUMBEROFCHILD; i++){       /* create children */   
      if((child[i] = fork())  == -1){
         printf("Error in creating child\n");
     exit(EXIT_FAILURE);
      }
      if(child[i] == 0){
         switch(i){
        case 0:   wallClock();
        break;
        case 1:   runFile();
        break;
        case 2:   countDown(time);
        break;
     }
      }
   }
  
     
   if(close(fd[1]) == -1){           
      printf("Error closing parent reading pipe\n");
      exit(EXIT_FAILURE);
   }
  
   if(close(fd[0]) == -1){           
      printf("Error closing parent writing pipe\n");
      exit(EXIT_FAILURE);
   }
  
   if(close(fd2[1]) == -1){           
      printf("Error closing parent reading pipe2\n");
      exit(EXIT_FAILURE);
   }
  
   if(close(fd2[0]) == -1){           
      printf("Error closing parent writing pipe2\n");
      exit(EXIT_FAILURE);
   }
  
   n = NUMBEROFCHILD;
   while(n > 0){            /*Wait for all children to finish*/
      pid = wait(&status);
         if(pid == -1){
        printf("Error exiting with childPID %d\n", pid);
        exit(EXIT_FAILURE);
     }
    --n;
    printf("Child %d has exited successfully\n", pid);
   }
  
   printf("All processes has ended, exiting program --\n");
   return 0;
  
}

Code:
> gcc -g assign3.c
> a.out 15
Count Down: 00:15
the time is 18:5:25
  6:05pm  up 279 day(s),  8:52,  27 users,  load average: 3.48, 3.78, 4.80
Count Down: 00:14
the time is 18:5:26
Count Down: 00:13
the time is 18:5:27
Count Down: 00:12
the time is 18:5:28
Count Down: 00:11
the time is 18:5:29
Count Down: 00:10
the time is 18:5:30
  6:05pm  up 279 day(s),  8:52,  27 users,  load average: 3.46, 3.77, 4.79
the time is 18:5:31
Count Down: 00:09
the time is 18:5:32
Count Down: 00:08
the time is 18:5:33
Count Down: 00:07
Count Down: 00:06
the time is 18:5:34
Count Down: 00:05
the time is 18:5:35
  6:05pm  up 279 day(s),  8:52,  27 users,  load average: 3.45, 3.76, 4.79
Count Down: 00:04
the time is 18:5:36
the time is 18:5:37
Count Down: 00:03
Count Down: 00:02
the time is 18:5:38
the time is 18:5:39
Count Down: 00:01
the time is 18:5:40
Count Down: 00:00 -- sending message to terminate other processess
Child 9779 has exited successfully
  6:05pm  up 279 day(s),  8:52,  27 users,  load average: 3.44, 3.75, 4.77
Message has been received in wallClock()--Terminating now
Child 9777 has exited successfully
  6:05pm  up 279 day(s),  8:52,  27 users,  load average: 3.42, 3.74, 4.77
  6:05pm  up 279 day(s),  8:52,  27 users,  load average: 3.40, 3.73, 4.76
  6:05pm  up 279 day(s),  8:52,  27 users,  load average: 3.39, 3.72, 4.75
  6:06pm  up 279 day(s),  8:52,  27 users,  load average: 3.38, 3.71, 4.74
  6:06pm  up 279 day(s),  8:53,  27 users,  load average: 3.36, 3.71, 4.73
  6:06pm  up 279 day(s),  8:53,  26 users,  load average: 3.37, 3.70, 4.73
  6:06pm  up 279 day(s),  8:53,  26 users,  load average: 3.46, 3.71, 4.72
  6:06pm  up 279 day(s),  8:53,  26 users,  load average: 3.87, 3.80, 4.75
  6:06pm  up 279 day(s),  8:53,  26 users,  load average: 4.59, 3.95, 4.79
  6:06pm  up 279 day(s),  8:53,  27 users,  load average: 5.29, 4.10, 4.84
  6:06pm  up 279 day(s),  8:53,  27 users,  load average: 5.84, 4.23, 4.88
  6:06pm  up 279 day(s),  8:53,  27 users,  load average: 6.36, 4.37, 4.92
Signal has been received, closing pipes and exiting uptime mananger
Child 9778 has exited successfully
All processes has ended, exiting program --
 
Physics news on Phys.org
  • #2
I think I see your problem.
A quick caveat though, whenever working on this sort of task I always used pThreads instead of pipes & forks.

Let me ask you a couple questions to maybe help point you in the right direction.

If you were to describe someone what data structure a pipe is most like what would you use? A variable, an array, a stack etc?
Does the number of extra runs stay the same? or does it vary? What if you change to countdown time? Does it stay the same or vary?

Hope this gets you heading the right direction.A couple other things. Good job on the coding style, you should however try and get away from ALL magic numbers.
For example which is easier to understand if you haven't looked at the code before
This:
Code:
 if(close(fd2[0]) == -1){
or
Code:
#define THREAD0  0
#define FAIL   -1

...

if(close(fd2[THREAD0]) == FAIL){
 
  • #3
If I were to describe what data structure I pipe is I would say it would be an array in which the contents of a certain index points to either where stdout and stdin is or used to be?

The number of extra runs seem to be consistent in the amount, and I also did notice it changes when I change the amount of time I put the program to sleep(), when you say if I change to countdown time, do you mean instead of it doing sleep(), I should just make the countdown timer signal that process to exec every time after 5 seconds has passed through?

I think that's one solution but is that where you were getting at?
 
  • #4
freshcoast said:
If I were to describe what data structure I pipe is I would say it would be an array in which the contents of a certain index points to either where stdout and stdin is or used to be?

Its been a while since I covered Pipes in OS class but I believe the memory is copied. But that isn't relevant. You're on the right track. An array is a good way to think about it. Ask how stuff is put into and taken out of the array.

I stumbled upon your issue when I was trying to understand the code. One the things I checked was to examine
Code:
     read(fd2[0], &newStatus, sizeof(newStatus));
         status = newStatus;

What newStatus' value was. Maybe take a look at that?
 
  • #5
newStatus value is 0 every run like it's suppose to, when the signal is sent the other 2 processes terminate but this process lingers for exactly 12 runs until eventually the newStatus becomes 1 which means it received the signal then it terminates itself.

As for how stuff being taken out of the array or the pipe, I feel that the way it works is when the countdown process does a write function on the pipe it means that the contents that is passed through the parameters will be located on the writing end of the pipe and when I do the read, I take the contents at pipe[0] out and put it at the address where I want it. Unless something is over-writing the pipe so the process doesn't get the signal until later?
 
  • #6
So ask how was the process able to pull out 12 extra 0's? Did they just magically appear? Or was your code putting them there?

Do you need to explicitly send a "Keep running" message? or can you use the return value of read (it returns the number of bytes read) to get around this??
 
  • #7
I'm more of an experimentalist, so I tend to add a lot of debugging code to fix problems like this one.
 

Related to Why Does the runFile Child Process Receive the Termination Signal Later?

1. What is a child process in the context of programming?

A child process is a separate process that is created by another process, also known as the parent process. In programming, a child process may be created to perform a specific task or function, while the parent process continues to execute other tasks.

2. How can I terminate a lingering child process?

To terminate a lingering child process, you can use the kill command in your programming language. This command allows you to send a signal to the child process, which will terminate it. You can also use the wait command to wait for the child process to finish before terminating it.

3. Why is a child process lingering in my code?

There are a few reasons why a child process may linger in your code. It could be due to an error in your code, where the child process is not terminated properly. Another reason could be that the parent process is still waiting for the child process to finish, and it has not received a signal to terminate it.

4. Can a lingering child process cause any issues?

Yes, a lingering child process can cause issues in your code. It can use up system resources, leading to slower performance. It can also cause unexpected behavior if the parent process is waiting for it to finish before continuing execution.

5. How can I prevent lingering child processes?

To prevent lingering child processes, it is important to properly manage and terminate them in your code. Make sure to use the appropriate commands to terminate the child process, and handle any errors that may occur. You can also use a monitoring tool to detect any lingering child processes and terminate them if necessary.

Similar threads

  • Programming and Computer Science
Replies
8
Views
2K
  • Engineering and Comp Sci Homework Help
Replies
1
Views
3K
Back
Top