Museum

Home

Lab Overview

Retrotechnology Articles

Online Manuals

⇒ jobs(2) — DG/UX R4.11MU05

Media Vault

Software Library

Restoration Projects

Artifacts Sought

Related Articles

csh(1)

ioctl(2)

killpg(2)

setpgrp(2)

signal(2)

sigset(2)

wait3(2)

termio(7)



jobs(2)                        DG/UX R4.11MU05                       jobs(2)


NAME
       jobs - summary of DG/UX job control facilities

SYNOPSIS
       #include <sys/sgtty.h>
       #include <signal.h>
       #include <sys/resource.h>
       #include <sys/time.h>
       #include <wait.h>

       int fildes, signo;
       int pid, pgrp;
       union wait status;
       int options;
       struct rusage ru;

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

       setpgrp2(pid, pgrp)
       getpgrp2(pid)
       killpg(pgrp, signo)

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

       wait3(&status, options, &ru)

DESCRIPTION
       The facilities described here support the job control implemented in
       csh(1) and may be used in other programs to provide similar
       facilities.

       For descriptions of the individual routines, see SEE ALSO below.
       This section describes the facilities in general.

   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 pipeline)
       are given the same process group.  The system assigns a single
       process group number to each terminal.  Processes running on a
       terminal are given read access to that terminal only if they are in
       the same process group as that terminal.

       Thus, a command interpreter may start several jobs running in
       different process groups and arbitrate access to the terminal by
       controlling which, if any, of these processes is in the same process
       group as the terminal.  When a process outside 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.  This usually
       stops them  until they are continued with a SIGCONT signal.  (See
       signal(2) for a description of these signals; see tty(4) for a
       description of process groups.)

       If a process is not in the process group of the terminal, and it
       tries to change the terminal's 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 try to write to the
       terminal while not in its process group; this is controlled by the
       LTOSTOP bit in the tty mode word. LTOSTOP is  enabled by

   stty tostop
       and disabled (the default) by stty-tostop.  (The LTOSTOP bit is
       described in tty(4)).

   How the shell manipulates process groups
       An interactive shell first establishes its own process group and a
       process group for the terminal; this keeps other processes from being
       stopped while the terminal is under its control.  The shell then
       assigns a distinct process group to each job it creates.  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 completes, the shell reclaims the
       terminal by resetting the terminal's process group to that of the
       shell, using TIOCSPGRP again.

       Shells 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 whose parents have exited, and descendants 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 that ignore or hold the SIGTTIN or
       SIGTTOU signal are not sent these signals when accessing their
       control 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.

       Before a shell suspends itself, it places itself back in the process
       group in which it was created. It then sends this original group a
       stopping signal, stopping the shell, and any other intermediate
       processes, back up to an interactive parent.  The shell also restores
       the process group of the terminal when it finishes; the process that
       resumes might not have control of the terminal otherwise.

   Naive processes
       A naive process does not alter the state of the terminal, and does no
       job control. It can usually invoke subprocesses safely, even if it
       has shell escapes or invokes other processes.  If such a process
       issues a system(3C) call and this command is then stopped, both of
       the processes will stop together.  Thus simple processes need not
       worry about job control.

   Processes that 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.

            int  tpgrp;
            ...

       retry:
            sigset(SIGTSTP, SIGHOLD);
            sigset(SIGTTIN, SIGHOLD);
            sigset(SIGTTOU, SIGHOLD);
            if (ioctl(2, TIOCGPGRP, &tpgrp) != 0)
                 goto nottty;
            if (tpgrp != getpgrp(0)) { /* not in foreground */
                 sigset(SIGTTOU, SIGDFL);
                 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);

       SIGTSTP is ignored in this code to prevent our process from being
       moved from the foreground to the background while checking if it is
       in the foreground.  The process holds all the stopping signals in
       this critical section so that no other process in our process group
       can block us on one of these signals in the middle of our check.
       (This code assumes that the command interpreter will not move a
       process from foreground to background without stopping it; if it did,
       we could not make the check correctly.)

       The signal-handling routine 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 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, SIGDFL);
            kill(0, signo);
            /* stop here until continued */
            sigset(signo, onstop);
            ... restore our special terminal state ...
       }

       This routine can also 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 that 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 that would normally have hung will return end-of-file; writes
       that would normally have caused SIGTTOU signals are instead permitted
       while SIGTTOU is ignored.

   Interrupt-level process handling
       sigset(2) lets you handle process state changes as they occur. It
       provides an interrupt-handling routine for the SIGCHLD signal, a
       signal that occurs whenever the status of a child process changes.
       You establish a signal handler as follows:

              sigset(SIGCHLD, onchild);

       The shell or other process then waits for a change in child status
       with code like this:

       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, sighold temporarily blocks the SIGCHLD signal while the data
       structures are checked for a child to process.  If we didn't block
       the signal, we would have a race condition; 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, we call sigpause, which automatically 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.

       Important: a long-standing bug in the signal mechanism has been
       fixed.  The bug lost a SIGCHLD signal if it occurred while the signal
       was blocked.  This is because sighold uses the SIG_HOLD signal set of
       signal(2) to prevent the signal action from being taken without
       losing the signal.  Similarly, a signal action set with sigset has
       the signal held while the action routine is running, much as the
       interrupt priority of a processor is raised when a device interrupt
       is taken.

       In this interrupt-driven style of termination processing, wait calls
       must not block when they retrieve status in the SIGCHLD signal
       handler.  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 only one interrupt is taken.

       It is even possible that no children will 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 no
       more information is available.  Further status changes are guaranteed
       to be reflected in another SIGCHLD handler call.

   Restarting system calls
       In older versions of UNIX, "slow" system calls were interrupted when
       signals occurred, returning EINTR.  The new signal mechanism
       sigset(2) 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 that case, they return indicating how much data
       was read or written.  In programs that use the older signal(2)
       mechanisms, all of these calls return EINTR if a signal occurs during
       the call.

SEE ALSO
       csh(1), ioctl(2), killpg(2), setpgrp(2), signal(2), sigset(2),
       wait3(2), termio(7).


Licensed material--property of copyright holder(s)

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