Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

27.6.5 Stopped and Terminated Jobs

When a foreground process is launched, the shell must block until all of the processes in that job have either terminated or stopped. It can do this by calling the waitpid function; see Process Completion. Use the WUNTRACED option so that status is reported for processes that stop as well as processes that terminate.

The shell must also check on the status of background jobs so that it can report terminated and stopped jobs to the user; this can be done by calling waitpid with the WNOHANG option. A good place to put a such a check for terminated and stopped jobs is just before prompting for a new command.

The shell can also receive asynchronous notification that there is status information available for a child process by establishing a handler for SIGCHLD signals. See Signal Handling.

In the sample shell program, the SIGCHLD signal is normally ignored. This is to avoid reentrancy problems involving the global data structures the shell manipulates. But at specific times when the shell is not using these data structures—such as when it is waiting for input on the terminal—it makes sense to enable a handler for SIGCHLD. The same function that is used to do the synchronous status checks (do_job_notification, in this case) can also be called from within this handler.

Here are the parts of the sample shell program that deal with checking the status of jobs and reporting the information to the user.

     /* Store the status of the process pid that was returned by waitpid.
        Return 0 if all went well, nonzero otherwise.  */
     
     int
     mark_process_status (pid_t pid, int status)
     {
       job *j;
       process *p;
     
       if (pid > 0)
         {
           /* Update the record for the process.  */
           for (j = first_job; j; j = j->next)
             for (p = j->first_process; p; p = p->next)
               if (p->pid == pid)
                 {
                   p->status = status;
                   if (WIFSTOPPED (status))
                     p->stopped = 1;
                   else
                     {
                       p->completed = 1;
                       if (WIFSIGNALED (status))
                         fprintf (stderr, "%d: Terminated by signal %d.\n",
                                  (int) pid, WTERMSIG (p->status));
                     }
                   return 0;
                  }
           fprintf (stderr, "No child process %d.\n", pid);
           return -1;
         }
       else if (pid == 0 || errno == ECHILD)
         /* No processes ready to report.  */
         return -1;
       else {
         /* Other weird errors.  */
         perror ("waitpid");
         return -1;
       }
     }
     
     /* Check for processes that have status information available,
        without blocking.  */
     
     void
     update_status (void)
     {
       int status;
       pid_t pid;
     
       do
         pid = waitpid (WAIT_ANY, &status, WUNTRACED|WNOHANG);
       while (!mark_process_status (pid, status));
     }
     
     /* Check for processes that have status information available,
        blocking until all processes in the given job have reported.  */
     
     void
     wait_for_job (job *j)
     {
       int status;
       pid_t pid;
     
       do
         pid = waitpid (WAIT_ANY, &status, WUNTRACED);
       while (!mark_process_status (pid, status)
              && !job_is_stopped (j)
              && !job_is_completed (j));
     }
     
     /* Format information about job status for the user to look at.  */
     
     void
     format_job_info (job *j, const char *status)
     {
       fprintf (stderr, "%ld (%s): %s\n", (long)j->pgid, status, j->command);
     }
     
     /* Notify the user about stopped or terminated jobs.
        Delete terminated jobs from the active job list.  */
     
     void
     do_job_notification (void)
     {
       job *j, *jlast, *jnext;
       process *p;
     
       /* Update status information for child processes.  */
       update_status ();
     
       jlast = NULL;
       for (j = first_job; j; j = jnext)
         {
           jnext = j->next;
     
           /* If all processes have completed, tell the user the job has
              completed and delete it from the list of active jobs.  */
           if (job_is_completed (j)) {
             format_job_info (j, "completed");
             if (jlast)
               jlast->next = jnext;
             else
               first_job = jnext;
             free_job (j);
           }
     
           /* Notify the user about stopped jobs,
              marking them so that we won't do this more than once.  */
           else if (job_is_stopped (j) && !j->notified) {
             format_job_info (j, "stopped");
             j->notified = 1;
             jlast = j;
           }
     
           /* Don't say anything about jobs that are still running.  */
           else
             jlast = j;
         }
     }

 
 
  Published under the terms of the GNU General Public License Design by Interspire