Computing History · Mainframe Era

How a Resident Monitor Chained Jobs Without an Operator

The loop that read a control card, loaded the named program, and took control back when the job ended, and why an interval timer was required to make that loop reliable.

A machine with no operating system starts a program by mechanical means. The operator loads a deck into the reader, presses a button, and the first cards are read directly into storage at a fixed address; the last of them is a branch instruction, so the loaded program begins executing with no caller behind it. When that program reaches its end it has nowhere to return. It halts, and the machine sits in the wait state until a person intervenes. The cost is not the few seconds of button pressing. It is that every job, however short, is bracketed by human attention, and the machine is idle for as long as the operator takes to notice.

A resident monitor removes the operator from the middle of that cycle. It is a program that never ends, occupies a fixed region of low storage, and does nothing but start the next job and take control back when the job finishes. The interesting part is the second half: a program that has finished does not normally hand control anywhere, so the monitor has to obtain control through hardware events rather than through cooperation.

The loop, and the fact that the reader is shared

The monitor’s main loop is short enough to state in full. It reads one card from the input unit. It interprets that card as a control statement. It loads the program the statement names, from a library on tape or disk, into storage above the monitor. It transfers control to the loaded program. It waits. When an interruption arrives that signifies the end of the job, or a program check, or an exhausted time limit, it regains control, writes an accounting record, and reads the next card.

Two details in that description decide most of the resulting behaviour. The first is that the control statements and the job’s data arrive on the same device. A card reader is a sequential device with one input path, so the deck is a single stream in which control and data are interleaved, and the monitor has to know where one ends and the other begins. Every batch system therefore invents a delimiter: a card whose content is not data, marking the end of the data set and the resumption of control statements. This is why the deck is a program in a small line-oriented language, and why that language looks the way it does. It is line-oriented because the input device is.

The second is that the monitor reads ahead by exactly as far as it must. It cannot buffer an arbitrary deck, because storage is the scarce resource and the monitor itself has to fit in it. The monitor’s own code, its tables, and the buffers for the reader and printer occupy the low end of the address space; the loaded job gets what remains. A monitor of the mid-1960s occupied tens of kilobytes, which on a machine with 256 KB of core is a visible fraction of the machine.

The accounting record is not an afterthought. Because the monitor is the only program that sees every job, it is the natural place to measure consumption, and the fields it writes at end-of-job (cards read, lines printed, CPU time charged, mounting of tape volumes) become the basis on which the machine’s time is billed to a department. Commercial computing and accounting for it arrive in the same program.

Regaining control: interruptions, not calls

A monitor cannot rely on the loaded program calling it, because a program that crashes, or one that was written for a machine with no supervisor, will not call anything. The mechanism that makes the monitor authoritative is the interruption: a hardware event that saves the current program status word, loads a new one from a fixed storage location, and continues execution at the address that new status word names. The locations holding those new status words belong to the monitor. The problem program has no legitimate way to change them, so the transition is not negotiable.

Three classes of event matter to the loop. A supervisor call is the cooperative case: the program wants a data set opened or a record written, and the instruction traps into the monitor. An input/output interruption reports that a channel has finished an operation, which is the only way a program learns that its read completed. A program check reports an invalid instruction, an addressing error, or a protection violation, and is the monitor’s route to terminating a broken job instead of letting it run.

The fourth class is the one that makes the whole arrangement sound, and it is the one that is easy to overlook. A program in a tight arithmetic loop issues no supervisor calls and no I/O. Nothing interrupts it. On a machine with only the first three classes of interruption, such a program owns the processor for as long as it wants, and the monitor is exactly as stuck as the operator was. The interval timer exists to close that hole. On the System/360 the timer is a storage location that decrements at a fixed rate derived from the power line, fifty or sixty times a second, and reaching zero raises an external interruption to a location the monitor owns.

The consequence is a limit on CPU time that the machine can enforce rather than merely observe. If the timer ticks sixty times a second, a job granted thirty seconds of processor time accumulates at most 1,800 ticks before the monitor’s handler can terminate it, and the resolution of any CPU-time measurement is one tick. The same facility gives a batch system its central guarantee: no single job can monopolise the machine, whatever it contains. A job that exceeds its limit is cancelled at a step boundary the monitor chooses, and the next job starts. The interval timer, the accounting record, and the scheduler are all the same mechanism viewed from different ends.

Job and step: two units, not one

The monitor schedules jobs but loads steps, and the distinction is what makes a deck useful. A job is the whole unit of work described by the control statements up to the delimiter; a step is one program execution within it. A deck that compiles a source file and then runs the resulting program has two steps under one job, and the monitor executes them in order without returning to the reader.

Between steps, the monitor keeps the data sets that the steps named. It does not close and reopen them unless the control statement says to; it keeps the volume mounted and the extent allocated, so the second step sees what the first wrote. The condition code left by a step becomes the test that decides whether the next step runs at all, which is why job control languages carry a conditional-execution clause attached to each step rather than a general branching construct. There is no branch: there is only “run this step if the previous one returned less than some value.”

That restriction is deliberate. The control language is interpreted by a monitor that must remain simple enough to fit in low storage and to be trusted, so it has no variables, no arithmetic, and no loops. Its whole expressive power is naming data sets, naming programs, and testing a return code.

What the loop cannot fix

Single-stream batch is one job at a time, and the mismatch between processor and peripheral speeds is severe. A card reader of the period moves on the order of a thousand cards a minute and a line printer on the order of a thousand lines a minute; both figures are slow compared with instruction execution measured in microseconds. While the reader is reading, the processor executes nothing; while the printer is printing, it executes nothing either. The monitor’s loop is correct and the machine is idle for most of the wall clock.

Two later answers to that are covered elsewhere in this cluster. The channel programs described in channel programs and CCWs let the monitor overlap a transfer with computation, and spooling, described in spooling and the output queue, moves unit-record traffic onto disk so that the processor is not waiting on a mechanical device. Multiprogramming, described in storage keys and region sizing, keeps several jobs in storage at once so that one job’s I/O overlaps another’s computation. Both require the monitor to have something the earliest monitors did not: an enforced boundary between itself and the program it loaded.

Without storage protection, the monitor’s authority rests on convention. A loaded program that stores through a bad address can overwrite the monitor’s tables, its interruption vectors, or the monitor itself, and the only recovery is to reload the monitor from the reader and start the batch again. The System/360 storage key, a four-bit value compared against the program status word on every reference, turns that convention into hardware. It is the reason a batch monitor can survive a badly written job at all, and the reason the next job on the deck does not depend on the previous one having been well behaved.

The shape of the loop outlives the hardware. Read a unit of work, decide whether it may run, dispatch it, reclaim the machine at a boundary the supervisor chooses, and record what it consumed. Every batch queue since has the same five steps, and the arguments about batch systems are arguments about the third and fourth: what may run now, and where the boundary is placed.

References

  1. IBM System/360 Operating System: Concepts and Facilities — IBM, GC28-6535
  2. IBM System/360 Principles of Operation — IBM, GA22-6821
  3. Architecture of the IBM System/360 — G. M. Amdahl, G. A. Blaauw, F. P. Brooks Jr., IBM Journal of Research and Development, Vol. 8 No. 2, 1964
  4. The Mythical Man-Month: Essays on Software Engineering — Frederick P. Brooks Jr., Addison-Wesley, 1975