Copyright (c) 1999 Massachusetts Institute of Technology This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ------------------------------ JOBS IN ITS @. ABSTRACT. In ITS, a "job" is an entity that consists mainly of memory to hold a program, registers to run it with, and I/O channels for it to do I/O on through the system. Whenever a program is running, it must be running "in" some job. Jobs are structured into trees; the root of a tree is a "top-level" job, and for any job, the job one step closer to the root is the first job's superior; the first job is one of its inferiors. Normally, each user has one tree of jobs, but he may also have a few other secondary trees. Jobs may read anything associated with any job, but usually may write only their direct inferiors. TABLE OF CONTENTS A. OVERVIEW OF JOBS. B. CREATING AND DELETING INFERIORS, AND OTHER METHODS OF CHANGING THE 0. OPENING THE USR: DEVICE - A BASIC TOOL. 1. CREATION 2. DELETION 3. DISOWNING 4. REOWNING 5. DETACHING 6. ATTACHING 7. LOGGING IN 8. LOGGING OUT AND GUNNING C. CONTROL OF INFERIORS. 1. READING AND WRITING 2. SHARING MEMORY BETWEEN JOBS 3. LOADING a. Resetting 4. DUMPING a. Pure dumping b. Impure dumping 5. STARTING AND STOPPING 6. HANDLING INTERRUPTS 7. DEBUGGING CONTROLS. 8. TTY PASSING D. SYSTEM JOBS AND WHAT THEY DO 1. THE SYSTEM JOB. 2. THE CORE JOB. 3. DEMON JOBS. A. OVERVIEW OF JOBS. Each ITS job has an address space, the slots of which may be filled with various types of memory, perhaps shared with other jobs; its own set of accumulators (copied temporarily into the real ones when the job is running); a program counter (PC); sixteen I/O channels, each of which may be used to handle a device or file; system "user variables" with which the program in the job may communicate with the system; and other variables used by the system in scheduling or swapping the job, or as temporaries for system call routines. The word "user" has two meanings on ITS: one is the human or other being using a terminal; the other is an individual "user job". In specific contexts either "job" or "user" sounds better for their common meaning. A "program", however, is a more abstract entity; the same program may be running in several jobs (but using different files, etc. in each). Each job is identified by two names, the UNAME and the JNAME, each of which is up to six SIXBIT characters, left justified in a PDP-10 word. They are available in the .UNAME and .JNAME user variables of the job. These two names uniquely identify the job. All the jobs in a tree must have the same uname. Two trees may have the same uname; that usually means that they really correspond to the same user. Each job is also uniquely identified by its "job number" (or "user number"), which is a small positive number. This number is the usual way to specify a job in a system call. A job may find out its own number by reading its .UIND user variable; its superior's is in its own .SUPPRO user variable. The term "user index" may mean, loosely, the job number, but more strictly it means the job number times LUBLK, the length of the system's per-job data block (the "user variable block"). Supported communications paths between the user and the system use the job number, not the user index itself, even if the term "user index" is used. A job has several "user variables" which are really variables in the system that pertain to it, and are readable by that job or others using the .USET and .SUSET UUO's (see ITS UUOS). Each user variable has a name, which starts with ".". User variables can be opened in DDT just like memory locations, although what DDT does to access them is a .USET. Some user variables, such as .UIND, .SUPPRO, .UNAME and .JNAME mentioned above, exist solely to be read by programs. Others also allow the program to tell the system how to treat it; examples are .OPTION, which contains bits that enable various system features, and .MASK, which is used to enable user interrupts (see ITS INTRUP). A complete list of user variables and what they contain is in the file ITS USETS. When a user connects to ITS by typing ^Z or CALL on a terminal, he is supplied with a single top-level job using that terminal, running the program "DDT" (on DM, "MONIT" is used instead). The jname of that job is always "HACTRN" (pronounced "hack-tran"), and the job is called "a HACTRN". The uname will be three backarrows followed by three digits, identifying the tree as non-logged-in. "Logging in" is nothing other than altering the uname of the hactrn to a more meaningful name. It makes little direct difference. DDT and MONIT both provide facilities for creating other jobs for the user, loading programs into them, and running them. There are no user commands per se for controlling jobs built into ITS; instead, the user gives commands to DDT or MONIT telling them to execute the system calls to control his other jobs. The two main types of job trees are the console-controlled trees and the non-console-controlled (or "disconsolate") trees. A console controlled tree has one terminal which is "in use as a console" by the whole tree. All the jobs in the tree can refer to that terminal as device TTY:. The number of that terminal can be found in the .CNSL variable of any job of the tree. At any time, only one of the jobs in the tree has permission to do I/O to the console; that job is said to "own" the console. Ownership is transferred by means of .ATTY and .DTTY UUO's executed by jobs in the tree (see ITS TTY). If ^Z (or CALL on TV's) is typed on a console, the job that owns it gets a fatal interrupt. The jobs may also have other terminals open "as devices", but those can be in use only by a single job, not the whole tree. The console-controlled trees are usually the ones with hactrn's at the top. Disconsolate trees do not have any console, but in all other respects bahave just like console-controlled trees. They may or may not be "disowned". If they are, they get worse treatment from the scheduler and swapper, and also may be more conveniently "reowned" (made into a subtree of another tree - see below). Usually only system demons and job-device handlers are disconsolate non-disowned trees. Job-devices will have a .CNSL equal to that of the tree of the job using the job-device. Other disconsolate jobs will have a .CNSL of -1 if they are scheduled as part of the system, or -2 if scheduled as disowned jobs. Jobs often refer to each other using a device, the "USR" device. A job may be opened on the USR device by specifying its uname and jname as the opened filenames. It is then possible to examine or deposit the job's memory or user variables, etc. This is why DDT, when it starts up, mentions its uname and jname by saying "USR: ". The USR device should not be confused with the JOB device, which is a mechanism for creating software devices which enables the system calls that manipulate the device to be simulated and handled by a user program, the job-device handler program (See ITS JOBDEV). The remainder of this file is devoted to desribing the ITS system calls with which jobs may create, delete, sense and control other jobs. B. CREATING AND DELETING INFERIORS, AND OTHER METHODS OF CHANGING THE TREE STRUCTURE OF JOBS. 0. OPENING THE USR: DEVICE - A BASIC TOOL. Unfortunately, many different things are done by opening the USR device in different ways in different situations. They include creation of inferiors, reowning disowned jobs, selection of existing jobs (inferiors or not), and testing for the existence of a job. Opening USR is the ONLY way to do those things. What's more, a job's memory cannot be read or written without having a USR channel open in the appropriate direction, and a job's user variables cannot be read or written without at least one USR channel open in either direction. Thus, opening USR: is very important. It is intended that this mechanism will be made more rational in the future. This section will describe what opening the USR device will do, organised by how it is done. Later sections will say what to do to achieve a specific goal. By convention, "USRI" and "USRO" are the canonical names in MIDAS programs for the I/O channels used for opening the USR device for input and output, respectively. Their use in an inferior-handling program is a sort of comment. A USR: open always specifies two filenames. Normally they are the uname and jname of the job to be opened. However, 0 may be used as the first filename to specify your own uname. If the second filename is 0, then the first is not really a name but a job spec; this crock makes it possible to open a job known only by its number. A USR: open must also ask for either block mode or unit mode. Right now SIOT on unit mode USR channels is not as efficient as IOT on block mode channels, so it is usually better to open in block mode. In any case, this choice is orthogonal to everything else. All USR: opens should specify image mode. Ascii mode doesn't now exist for the USR device, and if you ask for it you will get image mode instead, so for compatibility with the future you should ask for image mode. There are four things specified in each USR open that affect the actions taken in important ways. They are the two filenames, the direction, and the device-dependent bit 1.4 of the open-mode, known here (and inside ITS) as UBPFJ. If UBPFJ is set, the open may be for reading only, and will fail with code 4 if the job specified by the uname and jname does not exist. If the job does exist, it will be opened as a "foreign job", which means that the opening job may not modify it at all using the channel opened in this way. Also, the process of opening the job with UBPFJ is guaranteed not to alter it. This style of open is good for asking whether a job exists with particular names. Without UBPFJ, if the open is for writing, then if a job with the specified uname and jname exists and is the inferior of the job doing the open, it will be opened for writing, with no special side effects. If a job with the specified names exists and can be reowned (it is a top-level disowned job), it will be reowned, and opened for writing. Reowning may change its uname or jname, so whenever an open might reown a job the program should read back the uname and jname of the opened job just in case. If no job with the specified names exists, then an attempt is made to create an inferior by that name. If the specified uname isn't the same as yours, that is impossible, and the open fails with code 20. If you already have the maximum number of inferiors, it fails with code 5. If there are no job slots available, it fails with code 6. If the attempt succeeds, the new inferior is opened for writing. Otherwise (a job with the specified names exists, but isn't an inferior of the running job already and can't be made one by reowning it), the open fails with code 12. Note that if an open for writing succeeds, you know the job that is open is your inferior (thought it might not have been so before). An open for reading, without UBPFJ, is like an open for writing except that when the job exists but isn't an inferior and can't be reowned, it will be opened as a foreign job (as if UBPFJ were 1). To understand these possibilities better, think of the FOO$J command in DDT, which means, essentially, "open the job named FOO". That command will reselect a job named FOO if there is one; if not, it will create one. If there is a job named FOO that isn't a direct inferior of DDT, it will be selected, but will be unwriteable (DDT will type a "#"). If there is a disowned job named FOO, it will be reowned (made into an inferior of DDT), and DDT will type ":$REOWNED$"). There is no easy way to tell after the fact what has happened when an open without UBPFJ has succeeded. You know you have an inferior, if an open for writing succeeds, but whether it is new, reowned or old is forgotten. If you care, you should find out in advance what is going to happen by doing an open with UBPFJ set. If it fails, you will get a newly created inferior. If it succeeds, look at the .APRC and .SUPPRO user variables. If both are negative, you have a top-level disowned job which would be reowned. If .SUPPRO equals your .UIND variable, the job is your inferior already. Otherwise, an open for writing would fail, and an open for reading would open it as a foreign job. Some information is still available after an open. The job's .UPC variable will be nonzero iff the job has ever run (because the user-mode bit, %PCUSR, will be 1). If the job has been run, then it certainly wasn't just created. Also, if the job's uname or jname was changed by the open, then it must have been reowned. 1. CREATION In order to create an inferior, one must choose a jname for it. Its uname will have to be the same as its creator's, and together the uname and jname must be unique among all jobs in existence. An attempt to create an inferior looks just like an attempt to open an existing job, so if the would-be creator is mistaken about the uniqueness of the jname it may get confused (a way out of this problem is described later). Given a jname which isn't in use, an inferior of that name may be created by simply opening the USR device with the uname and jname as filenames, as in section 0. You should open a channel in each direction (call the input channel USRI and the output channel USRO). If you are unwilling to try to live with a foreign job, do the output open first. One way to make sure of getting a newly created job is to check in advance that the names are not in use, by opening them with UBPFJ (see section 0) before actually trying to create the job. If the test open succeeds, close the channel again and pick a new jname. The new inferior will have 1K of zeroed unshared core, and will not be running (its .USTP will be nonzero). Then, if you wish to handle interrupts from the inferior, read the inferior's interrupt bit, which may be found in the inferior's .INTB variable, and remember it somewhere. That is the bit .IFPIR on which YOU will be interrupted, if the inferior gets a fatal interrupt. 2. DELETION Inferiors do not need to remain open to continue to exist. Simply closing the channels one is open on does not delete it; it is merely hard to refer to until it is opened again. This is so that it is possible to have many inferiors without tying up many I/O channels. When it IS desired to delete an inferior, the .UCLOSE uuo should be used: .UCLOSE , This uuo kills the inferior open on . All the channels it is open on are closed. The uuo does not skip. If a foreign job instead of an inferior is open, all the channels in this job that it is open on are closed, but it is not killed. If there isn't a job open on the specified channel, an ILOPR interrupt happens. 3. DISOWNING Disowning an inferior causes it to continue to exist, but no longer as your inferior. This can be useful for two reasons: 1) you wish to log out but want the inferior to remain, or 2) you want the job to become someone else's inferior. Disowning is done with the DISOWN system call, as follows: .CALL [ SETZ ? 'DISOWN 401000,,] .VALUE It fails if the specified job isn't an inferior of the executing one. If it succeeds, the specified job and its inferiors become a separate disowned tree, of which the specified job is the top. All channels on which the running job had that job open are closed. See the DISOWN symbolic system call for more details and features. 4. REOWNING A top-level disowned job may be "reowned" (made one's inferior) by opening it with bit 1.4 of the mode set to 0. The uname of every job in the reowned tree will be changed to equal that of the reowning job, if necessary. If as a result the uname/jname pair of one of the reowned jobs is no longer unique, the jname will be altered until the uname/jname pair is unique. Another way to reown a job is to use the REOWN symbolic system call. The job to be reowned should already be open on an I/O channel as a foreign user; the REOWN will cause it to be open as an inferior instead on the same channel. An example is: .CALL [ SETZ ? SIXBIT/REOWN/ ? 401000,,USRI] .VALUE In DDT, selecting a job that may be reowned will always reown it. DDT will type ":$REOWNED$" when that happens. If the uname or jname changes, DDT will type the new names. 5. DETACHING An entire tree may be made disowned, have its console taken away from it, or both, with the DETACH system call. The goal might be to free the console for other use, or to make the tree into a subtree of another tree by reowning it. 6. ATTACHING Attaching is the operation of connecting a terminal to a tree as the tree's console. The simplest kind of attachment takes a free console and a disowned tree and connects them. A top-level console-controlled job may also attach one of its inferiors to its console; this has the same effect as if it were to disown the inferior, detach itself, and then attach the erstwhile inferior to the now free console, except that it happens more smoothly. DDT's :ATTACH command uses the second flavor of ATTACH. See the ATTACH system call for more details. 7. LOGGING IN When the system creates a top-level job (such as a HACTRN), its uname will be three "_"'s followed by three unique digits. The job may change its uname to one that gives more information to humans by "logging in", which simply sets the uname. The job must not have any inferiors at the time; the new uname must not start with "___" and must not be in use. A logged-in job may not log in again, but it may change its uname with a .SUSET of .UNAME. Logging in is done with the LOGIN symbolic system call, as follows: .CALL [ SETZ ? SIXBIT/LOGIN/ SETZ []] .VALUE The system call takes an optional third argument which now should be used only by network server telnet jobs. For them, its hould be the sixbit string "HSTnnn", where nnn is the octal number of the foreign host. 8. LOGGING OUT AND GUNNING A top-level job may delete itself, and all its inferiors, by "loggin out". This is done with the .LOGOUT UUO, as follows: .LOGOUT The UUO returns only if it fails (the job isn't top level); it does not skip. Programs that do not normally run in top-level jobs might nevertheless be run in disowned jobs sometimes, so they should always try to log out before requesting another command string or trying to ask DDT to kill them. A job may force any top-level job to log out by "gunning" it, using the .GUN UUO. The user index of the job to be gunned must be known. The way to do it is: MOVEI AC, .GUN AC, JRST LOSE ;FAILED - JOB DIDN'T EXIST OR WASN'T TOP LEVEL. ... ;SUCCEEDED. Gunning should go on only between consenting adults. C. CONTROL OF INFERIORS. Jobs can control their inferiors by means of several special-purpose system calls. The more specific of them are documented below under their uses. One more general one is .USET, which allows the inferior's "user variables" to be read or set. For example, when section C.5. speaks of "setting the job's .USTP variable", a .USET is meant. The way to set the .FOO variable with a .USET is this: .USET ,[.SFOO,,[]] Thus, .USET ,[.SUSTP,,[0]] will stop the job open on channel . .USET ,[.RFOO,,TEMP] will read the value of the .FOO variable into TEMP. For a complete description of the available .USET variables, and more features of .USET, see the file ITS .USETS. 1. READING AND WRITING Reading or writing the contents of another job requires having the job open on a USR device channel. It is done by .IOT'ing on that channel; thus, reading requires an input channel and output requires an output channel. Selection of the region to read or write is done by setting the channel's access pointer with .ACCESS. At the moment, SIOT on unit mode USR channels is not very efficient, so it is best to open a job in block mode. That requires one extra instruction when transferring a single word, which is a slight cost when compared with the ease of transferring several words. Note that ascii mode does not exist for the USR device - all channels should be opened in image mode. For example, to read the word at 500 into A, do .ACCESS USRI,[500] HRROI B,A .IOT USRI,B assuming that USRI is the USR input channel. This clobbers B. To write A into the word at 500, do .ACCESS USRO,[500] HRROI B,A .IOT USRO,B assuming that USRO is the USR output channel. This clobbers B. If a non-existent location in another job is read or written with an IOT, an MPV interrupt results. The .MPVA variable of the job doing the reading will point at the non-existent page of the other job. (Actually, in ITS before version 1017, when a non-existent page is written, the system may give an MPV, or may do a .CORE on the inferior so that it has enough core to make the IOT succeed. ITS version 1017 and up will always MPV). 2. SHARING MEMORY BETWEEN JOBS Another way to refer to the core of other jobs is to share memory with them. The slots of a job's page map should be understood to be pointers to pages; one page may be pointed to independently by several slots in several jobs' page maps. The CORBLK call can be used to copy other jobs' pointers to pages into one's own job, resulting in shared memory. Of course, the memory will not have the same address in both slots unless that is specificaly arranged for. Unfortunately, other jobs' accumulators cannot be referred to via memory sharing, since they are not stored anywhere in the job's memory even when the job isn't running. 3. LOADING Loading a binary file into a job is not actually a fundamental operation, since it could be done by a program that understood the format of the binary file and deposited in appropriate locations in the job being loaded. However, for convenience' sake, the LOAD system call exists for loading a binary file into an inferior, or into the running job itself. Both types of ITS binary files may be loaded with LOAD; it is not necessary to know which type of file is being loaded. Unfortunately, this call works only for disk files. For other devices, the program must do it the hard way. Code may be copied from DDT for that purpose. The LOAD system call takes two arguments: the job to be loaded, and the file to load. The first is expressed by a job spec; the second, by a channel number. For example, .CALL [ SETZ ? SIXBIT /LOAD/ 1000,,-1 ;into myself 401000,,DISKC] ;the file open on DISKC .VALUE The LOAD will leave the disk channel set up to read the file's start instruction, which is a jump to the starting address, or zero if there is no starting address. After that word in the file should come the symbol table and another copy of the start instruction; see DDT ORDER for their format. Loading a PDUMP file (pure file) inserts pages into the job as specified by the file. It does not alter the pages of the job that the file has no data for. It also does not alter the accumulators, even if page 0 is loaded. Loading an SBLK file (impure file) alters only the words of the job that the file specifies data for. The other words are NOT cleared. If data are specified in a nonexistent page, fresh memory will be put there, and also in all nonexistent pages below it in the address space! Thus, if an impure dump is made of a job containing nonzero data only at address 100000, the resulting file when loaded will create core up through address 101777, even though only the last page of that is actually being loaded. This unfortunately cannot be changed, but often causes trouble. For that reason (and many others), it is hoped that there will be a new, more versatile binary format some day. a. Resetting Loading per se modifies only the locations that the binary file specifies data for. It does NOT clear the rest of core. If you want to do that, chances are that you really want to RESET the inferior. To do that, use the .RESET UUO or the RESET symbolic system call on an I/O channel with the inferior open. RESETting a job deletes all of its core, provides it with a single page of unshared writeable core, closes all the job's channels, kills all of its inferiors, and re-initializes most of the job's system variables. Most notably not cleared out are the job's translation lists for filenames. 4. DUMPING a. Pure dumping b. Impure dumping 5. STARTING AND STOPPING An inferior may be started (caused to run) by setting its .USTP variable to zero. It will continue with whatever PC it has. To set the PC, store the desired new PC in the .UPC variable. An inferior may be stopped by setting the .USTP variable to nonzero. The exact value deposited in .USTP will be forgotten since it corresponds only to a single bit of real storage. This is how the system stops a job which gets a fatal interrupt. DDT has another way of stopping a job, which it uses in executing the ^X command: it turns on bit %PIC.Z of the job's .PIRQC, causing a fatal ^Z-typed interrupt in that job. This not only makes the system set the job's .USTP, but causes DDT's interrupt handler to find out about the stoppage of the job and be properly faked out. 6. HANDLING INTERRUPTS Every job has the ability to enable interrupts on several conditions (see ITS INTRUP). Among those conditions is the occurrence of a fatal error interrupt in an inferior. This is the mechanism that enables a job to "return to DDT" deliberately - it simply causes a fatal interrupt in itself. This is also how DDT knows when an illegal instruction is executed by an inferior. Whenever a job acquires an inferior (by creation or reowning), one of the job's interrupt bits is assigned to that inferior. Each inferior has its own interrupt bit. There are exactly eight interrupt bits which can be used for that purpose, which is why a job can have at most eight inferiors at a time. Those eight are all located in the left half of the .IFPIR variable. The superior should find out which interrupt bit was assigned by reading the inferior's .INTB variable, in which that bit alone will be 1. See ITS INTRUP for more details. The two system calls .VALUE and .BREAK exist solely so that a job can cause a fatal interrupt to signal its superior. DDT has standard interpretations for those instructions; see .INFO.;DDT ORDER. 7. DEBUGGING CONTROLS. These features exist to facilitate debugging. They work currently on KA-10's only, though they will be extended to KL-10's eventually. They are generally used and controlled only by the superior of the job on which they are acting, to which they are generally transparent. a: One-proceed. If appropriate bits in a job's PC are set, that job will be able to execute one more instruction and then will get a fatal %PI1PR interrupt. This feature is used by DDT's "single step" command (^N). Which bits to use depends on the processor type: for KA's, %PC1PR is the right bit (it is an 18 bit quantity, to be OR'ed into the flags); for KL's, %PS1PR is right. On any machine, the symbol OIPBIT will be defined in the ITS symbol table as either %PC1PR,, or %PS1PR,, , whichever is appropriate. b: Address-stop (MAR). The MAR feature makes it possible to trap all references to a particular address. When the specified location is referred to ("the MAR is hit"), the inferior will get a fatal %PIMAR interrupt, which may or may not allow the instruction that tripped the MAR to finish. The MAR is turned off when it is hit and must be turned on again explicitly or it will stay off forever. The MAR is controlled by the .MARA user variable. Its RH contains the address to be traced. Its LH contains the control code, which is 0 to turn the MAR off, 1 to trap instruction fetches only, 2 to trap writes only, and 3 to trap all references. There is also a variable .MARPC which, after the MAR is hit, contains the address of the instruction that hit it. In addition, the indirect bit will be set on KA's if the instruction that hit the MAR was completed (on KL's, the instruction is always aborted). On KL's, after a MAR, the job's PC (.UPC) will have %PSINH set, which will inhibit MAR hitting for one instruction when the job is continued. The job's PC in .UPC should NOT be used to locate the instruction that hit the MAR, since if the instruction was not aborted and was a jump, the PC will be the address jumped to. Even if it wasn't a jump, it might have skipped. c: .JPC - Last Jump Address. The user variable .JPC always contains the address of the last jump instruction executed in the job. It can be used, for example, to trace a job backward from the point of failure by putting in a breakpoint, examining the .JPC when the breakpoint is hit, and running the program again with the breakpoint now located at the previous jump, etc. Unfortunately, subroutine calls (including user-trapping UUOs) alter the JPC, even though the information is redundant for them, but returnable UUO's and interrupts do not. For that reason, after a returnable UUO or interrupt a program should always read .JPC before jumping anywhere, for debugging purposes. The canonical names UUOJPC and INTJPC exist for the locations .JPC is saved in. Also, error-handling uuos used by the program should be returnable uuos rather than direct user-trapping uuos, so that the JPC will be meaningful. The vectored interrupt mechanism has the ability to store .JPC automatically on each interrupt (see ITS INTRUP). 8. TTY PASSING For now, see under .ATTY and .DTTY in ITS TTY. D. SYSTEM JOBS AND WHAT THEY DO There are two jobs built into the system, that run in exec mode and perform housekeeping tasks; they are the system job and the core job. There are also various "demon" jobs that exist at times which, though receiving no special treatment from the system, may be regarded as part of it in that they provide a system-wide service. 1. THE SYSTEM JOB. This job, whose full name is SYS SYS, provides a great many independent services; it is really a timesharing system for many trusting processes, each of which is controlled by a single bit in the system variable SUPCOR. Setting the bit for a process causes the process to run, and clear the bit. The process should never run for very long, or it may tie up the system by making the services of the other processes unobtainable. If a process wishes to run continually, it does so by setting its bit in SUPCOP. SUPCOP is or'ed into SUPCOR every second, so a process whose bit in SUPCOP is set will be run again that often. The process is always started the same way, but it can of course have state variables. The reasons why a given activity may involve the system job are 1) it may wish to print a message on the system log terminal, or 2) it may need to run in a job (eg, do system calls), but not be associated with any one job, or 3) it may need to be able to hang up for short periods of time, but the need to do it may be noticed at a time when that is impossible, or 4) it needs to be done once every two minutes or every two hours. The routines for those "very slow" and "super slow" clocks are in the system job. Re-initialising the connection to the IMP is an example of reasons 1 and 2. Typing "console free" messages is an example of reason 3 - it involves doing I/O to the terminal, but it isn't done until the last stage of killing a tree, when waiting of any sort would allow PCLSR'ing, which would leave things in a screwed-up state. An example of reason 4 is the detection of core-link files which have not been used for more than 2 minutes. A complete list of the system job's functions follows: a: Allocating or Deallocating Memory for Job Slots. Most of the memory occupied by the system is marked as belonging to the system job. For that reason, the way more is allocated for user variable blocks is by having the system job do a .CORE UUO. This is somewhat of a historical crock. Creating a job does not require such allocation if there are free slots in the memory already allocated. However, it may need to. For that reason, if the system job is hung up, often jobs cannot be created. b: Printing Various Messages on All Terminals. Sometimes, messages such as "ITS GOING DOWN IN ..." or "ITS BEING DEBUGGED" are printed on all the terminals in the world. It is the system job that prints them on terminals which are free. Consoles have them printed by DDT, however. c: Checksumming the Constant Areas of the System. The system job is always checksumming parts of the system and comparing the checksums with those previously calculated. This mechanism causes the clobberage of a word in the code of the system to be detected. If only a single word in an area is clobbered, which word it is and the original contents can be detarmined. In any case, a message is printed on the system log terminal when clobberage is found. d: Determining the Date and Time. e: Detaching Trees. When a top-level job is halted by a fatal interrupt, it is the system job which executes the DETACH system call to detach it. Similarly, it is the system job which detaches trees controlled by TV terminals when the TV system crashes, and trees controlled by dialup lines whose connections are broken (only when the hardware can detect that). f: Depositing in the System. When a job deposits in the system using .SETLOC or .IFSET, the system job is informed so that it can update its checksums and avoid a system clobbered" message. It also makes the change. This used to be a great screw, in that if the system job were hung, it could not be fixed by depositing in the system. Now, .SETLOC will do the work itself without calling the system job, if the system job appears to be hung. g: Printing "Console-free" Messages. When a terminal ceases to be in use, the system job is signalled to look at all terminals and print console-free messages on all those which have recently stopped being in use. h: Logging Out. The final stages of killing a job must be run by some other job. When a top-level job is being killed, it does some of the work itself (kills its inferiors and closes its channels). That is to make sure the system job never hangs up trying to do it. The rest of the work is then done by the system job, which can afford to wait for short peiods of time when the job is almost killed. i: Reinitialising the NCP. When the connection to the ARPA network goes down, the system job gives interrupts to all the jobs using the network, and also checks every so often whether the network is useable again. j: Printing of Various Messages on the System Log Terminal. Events which cause messages on the log terminal include logging in, logging out, writing on a directory whose name starts with "SYS", garbage-collecting a disk directory, creating a disk directory, gunning, parity errors, NXM errors. l: Spooling the LPT. On AIKA and ML, the system job checks periodically for spooled files and prints them. m: Starting Other Demons. On DM, the system job checks for any domon programs which have been signaled. If there are any, it loads them into newly created jobs and strats them. n: Reinitialising the TV System, or Noticeing that it is Down. 2. THE CORE JOB. This job, whose full name is CORE JOB, allocates core to jobs that request it, and also frees up memory for use as user variable blocks, which have to be allocated consecutively, by relocating the old contents. Before there was pageing, the core job used to do shuffling. 3. DEMON JOBS. a: The Comsat. This demon, always named after some spacecraft or rocket, is responsible for handling mail. Requests to send mail are placed in the .MAIL. directory, and are eventually processed by the demon. The demon is necessary because it may not be possible to send mail to other machines right away, and in any case the user should not have to wait the time it takes. It also provides other services such as forwarding. b: NAMDRG. This demon maintains the list of logged in users which appears on all free TV consoles. It also updates a file saying when every user logged out last. It exists on AIKA only. c: PFTHMG DRAGON. This demon updates the logout times file and keeps track of each user's total CPU time. This demon originated on ML, but has run on all ITS machines at one time or another.