Museum

Home

Lab Overview

Retrotechnology Articles

Online Manuals

⇒ jobs(3J) — GENIX 4.1A

Media Vault

Software Library

Restoration Projects

Artifacts Sought

Related Articles

csh(1)

ioctl(2)

killpg(2)

setpgrp(2)

sigsys(2)

wait3(2)

nal(3)

tty(4)



      JOBS(3J)                GENIX Programmer's Manual                JOBS(3J)



      NAME
           jobs - summary of job control facilities

      SYNOPSIS
           #include <sys/ioctl.h>
           #include <signal.h>
           #include <sys/vtimes.h>
           #include <wait.h>

           int fildes, signo;
           short pid, pgrp;
           union wait status;
           struct vtimes vt;

           ioctl(fildes, TIOCSPGRP, &pgrp)
           ioctl(fildes, TIOCGPGRP, &pgrp)

           setpgrp(pid, pgrp)
           getpgrp(pid)
           killpg(pgrp, signo)

           sigset(signo, action)
           sighold(signo)
           sigrelse(signo)
           sigpause(signo)
           sigsys(signo, action)

           wait3(&status, options, &vt)

           cc ... -ljobs

      DESCRIPTION
           The facilities described here are used to support  the  job  control
           implemented  in csh(1), and may be used in other programs to provide
           similar facilities.  Because these facilities are  not  standard  in
           UNIX  and because the signal mechanisms are also slightly different,
           the associated routines are not  in  the  standard  C  library,  but
           rather in the -ljobs library.

           For descriptions of the individual routines see the various sections
           listed  in SEE ALSO below.  This section attempt only to place these
           facilities in context, not to explain the semantics of  the  indivi-
           dual calls.

           Terminal arbitration mechanisms.

           The job control mechanism works by associating with each  process  a
           number  called  a process group ; related processes (e.g. in a pipe-
           line) are given the same process group.  The system assigns a single
           process  group number to each terminal.  Processes running on a ter-
           minal are given read access to that terminal only if they are in the
           same process group as that terminal.



      Printed 11/16/83                                                        1





      JOBS(3J)                GENIX Programmer's Manual                JOBS(3J)



           Thus a command interpreter may start several jobs  running  in  dif-
           ferent  process  groups and arbitrate access to the terminal by con-
           trolling which, if any, of these processes is in  the  same  process
           group  as  the terminal.  When a process which is not in the process
           group of the terminal tries to read from the terminal,  all  members
           of  the process group of the process receive a SIGTTIN signal, which
           normally then causes them to stop until they are  continued  with  a
           SIGCONT  signal.  (See sigsys(2) for a description of these signals;
           tty(4) for a description of process groups.)

           If a process which is not in  the  process  group  of  the  terminal
           attempts  to  change  the  terminals mode, the process group of that
           process is sent a SIGTTOU signal, causing the process group to stop.
           A  similar  mechanism  is (optionally) available for output, causing
           processes to block with SIGTTOU when they attempt to  write  to  the
           terminal  while  not in its process group; this is controlled by the
           LTOSTOP bit in the tty mode word, enabled by "stty tostop" and  dis-
           abled (the default) by "stty -tostop." (The LTOSTOP bit is described
           in tty(4)).

           How the shell manipulates process groups.

           A shell which is interactive first establishes its own process group
           and  a process group for the terminal; this prevents other processes
           from being inadvertantly stopped while the  terminal  is  under  its
           control.  The shell then assigns each job it creates a distinct pro-
           cess group.  When a job is to be run in the  foreground,  the  shell
           gives  the  terminal  to  the  process  group  of  the job using the
           TIOCSPGRP ioctl (See ioctl(2) and tty(4)).  When a job stops or com-
           pletes,  the  shell reclaims the terminal by resetting the terminals
           process group to that of the shell using TIOCSPGRP again.

           Shells which are running shell scripts or running  non-interactively
           do not manipulate process groups of jobs they create.  Instead, they
           leave the process group of sub-processes and the terminal unchanged.
           This assures that if any sub-process they create blocks for terminal
           i/o, the shell and all its sub-processes will be blocked (since they
           are  a  single  process group).  The first interactive parent of the
           non-interactive shell can then be used to deal with the stoppage.

           Processes which are orphans (whose parents have exited), and descen-
           dants  of these processes are protected by the system from stopping,
           since there can be no interactive  parent.   Rather  than  blocking,
           reads from the control terminal return end-of-file and writes to the
           control terminal are permitted (i.e. LTOSTOP has no effect for these
           processes.)  Similarly processes which ignore or hold the SIGTTIN or
           SIGTTOU signal are not sent these signals when accessing their  con-
           trol  terminal;  if they are not in the process group of the control
           terminal reads simply return end-of-file.  Output and  mode  setting
           are also allowed.





      Printed 11/16/83                                                        2





      JOBS(3J)                GENIX Programmer's Manual                JOBS(3J)



           Before a shell suspends itself, it places itself back in the process
           group  in which it was created, and then sends this original group a
           stopping signal, stopping  the  shell  and  any  other  intermediate
           processes  back  to  an interactive parent.  The shell also restores
           the process group of the terminal when it finishes, as  the  process
           which then resumes would not necessarily be in control of the termi-
           nal otherwise.

           Naive processes.

           A process which does not alter the state of the terminal, and  which
           does  no job control can invoke subprocesses normally without worry.
           If such a process issues a system(3) call and this command  is  then
           stopped,  both  of  the  processes  will stop together.  Thus simple
           processes need not worry about job control, even if they have "shell
           escapes" or invoke other processes.

           Processes which modify the terminal state.

           When first setting the terminal into an unusual  mode,  the  process
           should  check,  with  the  stopping  signals held, that it is in the
           foreground.  It should then change the state of  the  terminal,  and
           set  the catches for SIGTTIN, SIGTTOU and SIGTSTP.  The following is
           a sample of the code that will be needed, assuming that  unit  2  is
           known to be a terminal.

                short     tpgrp;
                ...

           retry:
                sigset(SIGTSTP, SIG_HOLD);
                sigset(SIGTTIN, SIG_HOLD);
                sigset(SIGTTOU, SIG_HOLD);
                if (ioctl(2, TIOCGPGRP, &tpgrp) != 0)
                     goto nottty;
                if (tpgrp != getpgrp(0)) { /* not in foreground */
                     sigset(SIGTTOU, SIG_DFL);
                     kill(0, SIGTTOU);
                     /* job stops here waiting for SIGCONT */
                     goto retry;
                }
                ...save old terminal modes and set new modes...
                sigset(SIGTTIN, onstop);
                sigset(SIGTTOU, onstop);
                sigset(SIGTSTP, onstop);

           It is necessary to ignore SIGTSTP in this code because otherwise our
           process  could be moved from the foreground to the background in the
           middle of checking if it is in the foreground.   The  process  holds
           all  the  stopping signals in this critical section so no other pro-
           cess in our process group can mess us up by blocking us  on  one  of
           these  signals  in the middle of our check.  (This code assumes that



      Printed 11/16/83                                                        3





      JOBS(3J)                GENIX Programmer's Manual                JOBS(3J)



           the command interpreter will not move a process from  foreground  to
           background  without  stopping  it; if it did we would have no way of
           making the check correctly.)

           The routine which handles the signal should clear the catch for  the
           stop  signal and kill(2) the processes in its process group with the
           same signal.  The statement after this kill will  be  executed  when
           the process is later continued with SIGCONT.

           Thus the code for the catch routine might look like:

                ...
                sigset(SIGTSTP, onstop);
                sigset(SIGTTIN, onstop);
                sigset(SIGTTOU, onstop);
                ...

           onstop(signo)
                int signo;
           {
                ... restore old terminal state ...
                sigset(signo, SIG_DFL);
                kill(0, signo);
                /* stop here until continued */
                sigset(signo, onstop);
                ... restore our special terminal state ...
           }

           This routine can also be used to simulate a stop signal.

           If a process does not need to save and  restore  state  when  it  is
           stopped, but wishes to be notified when it is continued after a stop
           it can catch the SIGCONT signal; the SIGCONT  handler  will  be  run
           when the process is continued.

           Processes which lock data bases such as  the  password  file  should
           ignore  SIGTTIN,  SIGTTOU,  and SIGTSTP signals while the data bases
           are being manipulated.  While a process is ignoring SIGTTIN signals,
           reads which would normally have hung will return end-of-file; writes
           which would normally have caused SIGTTOU signals are instead permit-
           ted while SIGTTOU is ignored.

           Interrupt-level process handling.

           Using the mechanisms of sigset(3) it is possible to  handle  process
           state  changes as they occur by providing an interrupt-handling rou-
           tine for the SIGCHLD signal which occurs whenever the  status  of  a
           child  process  changes.   A signal handler for this signal is esta-
           blished by:

                sigset(SIGCHLD, onchild);




      Printed 11/16/83                                                        4





      JOBS(3J)                GENIX Programmer's Manual                JOBS(3J)



           The shell or other process would then await a change in child status
           with code of the form:

           recheck:
                sighold(SIGCHLD);        /* start critical section */
                if (no children to process) {
                     sigpause(SIGCHLD);  /* release SIGCHLD and pause */
                     goto recheck;
                }
                sigrelse(SIGCHLD);       /* end critical region */
                /* now have a child to process */

           Here we are using sighold to temporarily block  the  SIGCHLD  signal
           during  the  checking  of  the data structures telling us whether we
           have a child to process.  If we didn't block  the  signal  we  would
           have a race condition since the signal might corrupt our decision by
           arriving shortly after we had finished checking  the  condition  but
           before we paused.

           If we need to wait for something to happen, we call  sigpause  which
           automically  releases the hold on the SIGCHLD signal and waits for a
           signal to occur by starting a pause(2).  Otherwise we simply release
           the  SIGCHLD  signal  and process the child.  Sigpause is similar to
           the PDP-11 wait instruction, which returns the priority of the  pro-
           cessor to the base level and idles waiting for an interrupt.

           It is important to note that the long-standing  bug  in  the  signal
           mechanism  which  would  have  lost  a SIGCHLD signal which occurred
           while the signal was blocked has been fixed.  This is  because  sig-
           hold uses the SIG_HOLD signal set of sigsys(2) to prevent the signal
           action from being taken without losing  the  signal  if  it  occurs.
           Similarly, a signal action set with sigset has the signal held while
           the action routine is running, much as a the interrupt  priority  of
           the processor is raised when a device interrupt is taken.

           In this interrupt driven  style  of  termination  processing  it  is
           necessary that the wait calls used to retrieve status in the SIGCHLD
           signal handler not block.  This is because a  single  invocation  of
           the  SIGCHLD  handler  may  indicate  an arbitrary number of process
           status changes: signals are not queued.  This is similar to the case
           in  a  disk  driver  where several drives on a single controller may
           report status at once, while there is only one interrupt taken.   It
           is  even  possible for no children to be ready to report status when
           the SIGCHLD handler is invoked, if the signal was posted  while  the
           SIGCHLD  handler  was  active,  and  the  child was noticed due to a
           SIGCHLD initially sent for another process.  This causes no problem,
           since  the  handler will be called whenever there is work to do; the
           handler just has to collect all information by calling  wait3  until
           it  says  no  more information is available.  Further status changes
           are guaranteed to be reflected in another SIGCHLD handler call.





      Printed 11/16/83                                                        5





      JOBS(3J)                GENIX Programmer's Manual                JOBS(3J)



           Restarting system calls.

           In older versions of UNIX "slow" system calls were interrupted  when
           signals  occurred,  returning  EINTR.  The new signal mechanism sig-
           set(3) normally restarts such calls rather than  interrupting  them.
           To  summarize:  pause and wait return error EINTR (as before), ioctl
           and wait3 restart, and read and write restart unless some  data  was
           read  or  written in which case they return indicating how much data
           was read or written.  In programs  which  use  the  older  signal(2)
           mechanisms,  all of these calls return EINTR if a signal occurs dur-
           ing the call.

      SEE ALSO
           csh(1), ioctl(2), killpg(2), setpgrp(2), sigsys(2),  wait3(2),  sig-
           nal(3), tty(4)

      BUGS
           The job control facilities are not available in standard  version  7
           UNIX.   These  facilities are still under development and may change
           in future releases of the system as better inter-process  communica-
           tion  facilities and support for virtual terminals become available.
           The options and specifications of these system calls  and  even  the
           calls themselves are thus subject to change.
































      Printed 11/16/83                                                        6



Typewritten Software • bear@typewritten.org • Edmonds, WA 98026