11.8 Job Control for ush

Team-FLY

11.8 Job Control for ush

This section describes an implementation of job control for ush . Start by testing ush7 in the following cases to make sure that it correctly handles the SIGINT and SIGQUIT .

  1. Simple commands.

  2. Incorrect commands.

  3. Commands with standard input and output redirected.

  4. Pipelines.

  5. Background processes.

  6. All of the above interrupted by Ctrl-C.

11.8.1 A job list object

To do job control, ush must keep track of its children. Use a list object similar to the one used in Program 2.9 to keep a program history. The nodes in the list should have the following structure.

 typedef enum jstatus        {FOREGROUND, BACKGROUND, STOPPED, DONE, TERMINATED}    job_status_t; typedef struct job_struct {     char *cmdstring;     pid_t pgid;     int job;     job_status_t jobstat;     struct job_struct *next; } joblist_t; static joblist_t *jobhead = NULL; static joblist_t *jobtail = NULL; 

Place the list structure in a separate file along with the following functions to manipulate the job list.

 int add(pid_t pgid, char *cmd, job_status_t status); 

Add the specified job to the list. The pgid is the process group ID, and cmd is the command string for the job. The status value can be either FOREGROUND or BACKGROUND . If successful, add returns the job number. If unsuccessful , add returns 1 and sets errno . It uses getlargest to determine the largest job number and uses a job number that is one greater than this.

 int delete(int job); 

Remove the node corresponding to the specified job from the list. If successful, delete returns the job number. If unsuccessful, delete returns 1 and sets errno . Be sure to free all space associated with the deleted node.

 showjobs(void); 

Output a list of jobs and each one's status. Use the following format.

 [job]  status  pgid  cmd 
 int setstatus(int job, job_status_t status); 

Set the status value of the node of the corresponding job. If successful, setstatus returns 0. If unsuccessful, setstatus returns 1 and sets errno .

 int getstatus(int job, job_status_t *pstatus); 

Return the status value associated with the specified job in *pstatus . If successful, getstatus returns 0. If unsuccessful, getstatus returns 1 and sets errno .

 pid_t getprocess(int job); 

Return the process group ID of the specified job. If job doesn't exist, getprocess returns 0.

 int getlargest(void); 

Scan the job list for the largest job number currently in the list. The getlargest function returns the largest job number if any nodes are on the list or 0 if the list is empty.

Write a driver program to thoroughly test the list functions independently of ush .

11.8.2 The job list in ush

After the job list functions are working, add the job list object to ush as follows .

  1. Each time ush forks a child to run a background process, it adds a node to the job list. It sets the pgid member of the joblist_t node to the value returned from fork . The process status is BACKGROUND .

  2. If the command is executed in the background, ush outputs a message of the following form.

     [job]  pid1  pid2 .... 

    job is the job number and pid1 , pid2 and so on are the process IDs of the children in the process group for the command. The parent ush knows only the process ID of the initial child, so the child that calls executecmd must produce this message.

  3. The ush calls showjobs when a user enters the jobs command.

  4. Replace the waitpid call in ush with a more sophisticated strategy by using waitpid in a loop with the WUNTRACED option. The WUNTRACED option specifies that waitpid should report the status of any stopped child whose status has not yet been reported . This report is necessary for implementing job control in the next stage.

Test ush with the job list. Do not add job control in this step. Execute the jobs command frequently to see the status of the background processes. Carefully experiment with an existing shell that has job control. Make sure that ush handles background and foreground processes similarly.

11.8.3 Job control in ush

Incorporate job control into ush by adding the following commands to ush in addition to the jobs command of the previous section.

stop

stop the current job

bg

start the current job running in the background

bg %n

start job n running in the background

fg %n

start job n running in the foreground

mykill -NUM %n

send the signal SIGNUM to job n

Some of these commands refer to the current job. When there are several jobs, one is the current job . The current job starts out as the first background job to be started. A user can make another job the current job by bringing it to the foreground with fg .

The ush shell now must handle SIGCONT , SIGTSTP , SIGTTIN and SIGTTOU in addition to SIGINT and SIGQUIT . When ush detects that a child has stopped because of a SIGTTIN or a SIGTTOU , it writes an informative message to standard error to notify the user that the child is waiting for input or output, respectively. The user can move that job to the foreground to read from or write to the controlling terminal.

Test the program thoroughly. Pay particular attention to how your regular shell does job control and adjust ush to look as similar as possible.

11.8.4 Process behavior in waiting for a pipeline

What happens when a shell starts a pipeline in the foreground and one of the processes in the pipeline terminates? The result depends on which process in the pipeline is the child of the shell.

Exercise 11.36

Make a new version of showid from Exercise 11.28 on page 395 that sleeps for one minute after displaying the IDs. Call the new program showidsleep . Run ush7 with each of the following command lines. What happens?

 showidsleep first  showid second showid first  showidsleep second 

Answer:

For the first command line, the shell displays the prompt after one minute since the first command in the pipeline is the child of the shell. For the second command line, the shell displays the prompt immediately since it waits only for the first command in the pipeline. This is probably not the desired behavior. Typically, a pipeline consists of a sequence of filters, and the last one in the sequence is the last to finish.

Exercise 11.37

How would you solve the problem described in Exercise 11.36?

Answer:

One solution would be to rewrite executecmdpipe so that the last command of the pipeline was executed by the first process created. A better solution would be to have all of the processes in the pipeline be children of the shell and have the shell wait for all of them.

Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net