1. 1969年开始的TENEX操作系统中的load average相关源代码
https://github.com/PDP-10/tenex/blob/e788ef5bcdadfc0580e5df5d9af20d6f4e736902/134-tenex/SCHED.MAC
SCHED.MAC
这是使用LISP语言编写的,LISP语言起源于1958年,是现今第二悠久且仍广泛使用的高级编程语言,只有FORTRAN编程语言比它更早一年,所以看不懂说明你是一个正常人。
438行:NRJAVS==3 ;NUMBER OF LOAD AVERAGES WE MAINTAIN
;<135-TENEX>SCHED.MAC;343 17-DEC-75 12:12:00 EDIT BY ALLEN
; PROTECT AGAINST RE-ENTERING SCHEDULER BY ACCIDENT
;<ALLEN>SCHED.MAC;5 4-NOV-75 11:45:51 EDIT BY ALLEN
; FIX ERROR IN ASSFK LEADING TO NCP BUFFER SCREWUPS
;<134-TENEX>SCHED.MAC;339 30-SEP-75 10:29:30 EDIT BY ALLEN
;<134-TENEX>SCHED.MAC;338 26-SEP-75 16:27:31 EDIT BY ALLEN
; DO SIMPLE INSERT ON PLACING FORK ON COMPQ, RATHER THAN RESORTING
;<134-TENEX>SCHED.MAC;337 14-AUG-75 16:34:29 EDIT BY ALLEN
; AVOID LOCKUP IF BSYS OR SOME SUCH USER CODE UNLOCKS A DIRECTORY
; OR SUB-INDEX LOCK ON WHICH A CONFLICT HAS OCCURRED
;<134-TENEX>SCHED.MAC;336 5-AUG-75 11:38:26 EDIT BY CLEMENTS
; CHANGE TAGS P1 AND P2 TO BE PATCH1, PATCH2, TO AVOID CONFLICT WITH
; NEW ACCUMULATOR NAMES
;<134-TENEX>SCHED.MAC;335 12-JUN-75 15:18:53 EDIT BY ALLEN
; MINOR FIXES
;<134-TENEX>NSCHED.MAC;21 11-JUN-75 10:13:27 EDIT BY ALLEN
; FIX TO RESORT
;<134-TENEX>NSCHED.MAC;20 10-JUN-75 13:11:13 EDIT BY ALLEN
; LIST ROUTINES ARE NOW SUBROUTINES AGAIN INSTEAD OF MACROS
; FIX NECESSITY TO RESKED ON RELHIQ
;<134-TENEX>SCHED.MAC;334 4-JUN-75 13:09:12 EDIT BY ALLEN
; MORE FIXES TO NEW REGULATOR
;<134-TENEX>SCHED.MAC;332 3-JUN-75 15:59:55 EDIT BY ALLEN
; VARIOUS FIXES TO NEW REGULATOR
;<134-TENEX>NSCHED.MAC;13 3-JUN-75 11:27:03 EDIT BY ALLEN
; MINOR FIXES TO SPECIAL SCHEDULING FOR LOCKERS TO AVOID RESPONSE
; PROBLEMS AFTER CLEARING LOCK
;<134-TENEX>NSCHED.MAC;10 20-MAY-75 21:36:47 EDIT BY ALLEN
;<134-TENEX>NSCHED.MAC;9 20-MAY-75 16:49:14 EDIT BY ALLEN
; PARAMETERIZE UTILIZATION AVERAGE TIME CONSTANT
;<134-TENEX>NSCHED.MAC;8 20-MAY-75 16:23:57 EDIT BY ALLEN
; SHORTEN UTILIZATION AVERAGE TIME CONSTANT TO ABOUT
; 4 SECONDS
;<134-TENEX>NSCHED.MAC;7 20-MAY-75 08:26:17 EDIT BY ALLEN
;<134-TENEX>NSCHED.MAC;6 19-MAY-75 17:24:28 EDIT BY ALLEN
; REVISED PIE-SLICE REGULATOR
;<134-TENEX>SCHED.MAC;331 15-MAY-75 08:24:44 EDIT BY TOMLINSON
;<134-TENEX>SCHED.MAC;330 15-MAY-75 08:17:05 EDIT BY TOMLINSON
;<134-TENEX>SCHED.MAC;329 15-MAY-75 08:06:02 EDIT BY TOMLINSON
; ADDED CALL TO TNTCHK IN SCHEDULER
;<134-TENEX>SCHED.MAC;328 2-MAY-75 18:14:13 EDIT BY CLEMENTS
; INTERNED TTPSR1. XLISTED JTDVC1.
;<134-TENEX>SCHED.MAC;327 28-APR-75 12:10:32 EDIT BY CLEMENTS
;<134-TENEX>SCHED.MAC;326 14-APR-75 13:40:38 EDIT BY ALLEN
;<134-TENEX>SCHED.MAC;325 14-APR-75 13:28:24 EDIT BY ALLEN
;<134-TENEX>SCHED.MAC;324 11-APR-75 18:28:29 EDIT BY ALLEN
; FIXES TO NEW SPECIAL SCHEDULING FOR LOCKERS
;<134-TENEX>SCHED.MAC;323 10-APR-75 22:03:08 EDIT BY ALLEN
; REVISED HANDLING OF HIGH PRIORITY FOR LOCKERS
;<134-TENEX>SCHED.MAC;320 8-APR-75 23:44:35 EDIT BY ALLEN
; ADD SPECIAL SCHEDULING QUEUE. REDUCE BEHIND SCHED QUANTUM
;<134-TENEX>SCHED.MAC;318 12-MAR-75 13:08:18 EDIT BY PLUMMER
; CALLS TO ROUTINES IN SIGNAL.MAC
;<134-TENEX>SCHED.MAC;317 25-FEB-75 14:26:47 EDIT BY CLEMENTS
; MOVE DEFINITION OF MAXQ UP BEFORE USE OF IT TO AVOID PASS1 "V" ERRS
;<134-TENEX>SCHED.MAC;316 14-FEB-75 17:51:40 EDIT BY OPERATOR
; FIX BUG IN PSSKD2 LEADING TO LONG QUANTA ON Q0
;<134-TENEX>SCHED.MAC;315 5-FEB-75 15:32:43 EDIT BY ALLEN
;<134-TENEX>SCHED.MAC;314 4-FEB-75 20:07:02 EDIT BY ALLEN
; VARIOUS FIXES TO NEW LIST STUFF
;<134-TENEX>SCHED.MAC;313 4-FEB-75 12:56:11 EDIT BY ALLEN
;<134-TENEX>SCHED.MAC;312 4-FEB-75 12:30:10 EDIT BY ALLEN
; REORGANIZE LIST MANIPULATION STUFF
;<134-TENEX>SCHED.MAC;310 30-JAN-75 15:00:04 EDIT BY ALLEN
;DETAIL CHANGES TO NEW WAITLIST STUFF
;<134-TENEX>SCHED.MAC;309 30-JAN-75 00:12:03 EDIT BY ALLEN
; PLACE FORK DIRECTLY ON RUNLIST IN ASSFK
;<134-TENEX>SCHED.MAC;308 29-JAN-75 23:24:05 EDIT BY ALLEN
; WAITLST NOW DOUBLE LINKED LIST. FORK ON WAITLIST NOW INDICATED
;BY PRESENCE OF WTLS BIT IN FKFLGS, NOT LH(FKPT)=WTLST.
;NEW ROUTINE, 'PRWAKE', TRANSPARENT TO ACS, TAKES FORKX IN AC7
;AND AWAKENS SAID FORK.
;<134-TENEX>SCHED.MAC;301 17-JAN-75 15:12:30 EDIT BY ALLEN
; CORRECT NON-TRANSPARENCY OF HIQ
;<134-TENEX>SCHED.MAC;300 16-JAN-75 12:17:57 EDIT BY ALLEN
; NEWST HIQ'S UNCONDITIONALLY IF WAKEUP FROM TCI OR TCOTST
;FIX ERROR IN INCREMENTING NAPROC IN WTSCAN
;<134-TENEX>SCHED.MAC;299 12-JAN-75 21:17:14 EDIT BY ALLEN
; CORRECT RADIX ERROR IN CHGQ
;<134-TENEX>SCHED.MAC;298 10-JAN-75 11:54:28 EDIT BY ALLEN
; ADD HIQ ROUTINE FOR PIE-SLICE SCHEDULER
; PIE-SLICE PSSKD2 NOW HI-QUEUES
;<134-TENEX>SCHED.MAC;297 10-JAN-75 11:08:34 EDIT BY ALLEN
; HI-Q THE NCP ON WAKEUP
;<133-TENEX>SCHED.MAC;295 21-DEC-74 11:47:52 EDIT BY ALLEN
; MOVE CHKWT ROUTINE TO SCHED. UNDO PREVIOUS DUMB CHANGE I.E.
; DISMT IS NOW A LOCAL AGAIN.
;<133-TENEX>SCHED.MAC;294 19-DEC-74 17:53:04 EDIT BY ALLEN
; MAKE DISMT A GLOBAL
;<133-TENEX>SCHED.MAC;293 19-DEC-74 13:29:48 EDIT BY ALLEN
; REMOVED INIT OF PIEGRP TABLE TO -1
;<133-TENEX>SCHED.MAC;292 16-DEC-74 14:06:41 EDIT BY ALLEN
; MAKE NEWFKF A GLOBAL
;<133-TENEX>SCHED.MAC;291 11-DEC-74 16:58:17 EDIT BY ALLEN
; TREAT DDMP SAME AS NCP WITH RESPECT TO SCHEDULING
;<133-TENEX>SCHED.MAC;290 10-DEC-74 10:52:04 EDIT BY ALLEN
; INITIALIZE PIEGRP TO -1
;<133-TENEX>SCHED.MAC;289 9-DEC-74 19:32:16 EDIT BY ALLEN
; FIX BUG IN RMPROC UNCOVERED BY CHANGE TO MAXBSH LOGIC
;<133-TENEX>SCHED.MAC;288 9-DEC-74 13:21:11 EDIT BY ALLEN
; HOLD FORK IN BALSET FOR MAXBSH IF PREVIOUS DISMISS WAS OF DURATION
; NOT GREATER THAN MAXBSH
;<133-TENEX>SCHED.MAC;287 12-NOV-74 16:35:41 EDIT BY CLEMENTS
;<133-TENEX>SCHED.MAC;286 5-NOV-74 17:09:36 EDIT BY CLEMENTS
; PART OF CRJOB JSYS
;<133-TENEX>SCHED.MAC;285 1-NOV-74 13:37:28 EDIT BY ALLEN
; REMOVE UNNECESSARY GCCORS IN BKGND
;<133-TENEX>SCHED.MAC;284 29-OCT-74 16:56:28 EDIT BY PLUMMER
; RTISW CONDITIONS CALL TO GCCOR
;<133-TENEX>SCHED.MAC;283 18-OCT-74 09:11:20 EDIT BY BTHOMAS
; FIX JTENQ AND FRZPSI TO CALL ENSKED IN CORRECT PLACE
;<133-TENEX>SCHED.MAC;282 11-OCT-74 16:13:33 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;281 8-OCT-74 16:16:47 EDIT BY ALLEN
; TEST FOR VALID UNDERFLOW AT AVERG2+1
;<133-TENEX>SCHED.MAC;280 8-OCT-74 10:18:53 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;279 7-OCT-74 17:40:25 EDIT BY ALLEN
; PIE-SLICE SCHEDULER QUANTA SCALED BY TARGET
; UTILIZATION. PERIODIC SCAN OF AHEAD SCHEDULE QUEUE
;DONE TO DISCOVER NOW-BEHIND-SCHEDULE PROCESSES.
;<133-TENEX>SCHED.MAC;274 25-SEP-74 20:17:05 EDIT BY CLEMENTS
; MISSING CONDITIONAL ON PIESLC CODE FIXED
;<133-TENEX>SCHED.MAC;273 25-SEP-74 14:03:24 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;272 25-SEP-74 13:36:22 EDIT BY ALLEN
; UNLOCK GRPLOK AT HLTFK1 FOR PIE-SLICE SYSTEM
;<133-TENEX>SCHED.MAC;271 24-SEP-74 17:06:07 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;270 24-SEP-74 16:45:20 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;268 20-SEP-74 20:40:58 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;267 20-SEP-74 20:24:43 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;266 19-SEP-74 17:21:14 EDIT BY ALLEN
; VARIOUS PIE-SLICE REPAIRS AND ADDITION TO LOCK COLLISION LOGIC
; TO AVOID POSSIBLE DEADLOCK
;<133-TENEX>SCHED.MAC;264 16-SEP-74 20:04:35 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;263 16-SEP-74 19:34:37 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;262 5-SEP-74 15:27:24 EDIT BY ALLEN
; ADD ROUTINE FOR UNLOCKING LOCKS ON WHICH CONFLICT HAS OCCURRED
;<133-TENEX>SCHED.MAC;261 1-AUG-74 11:43:49 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;260 1-AUG-74 10:46:42 EDIT BY ALLEN
; MORE EFFICIENT HANDLING OF COLLISIONS ON NON-RESIDENT LOCKS
;<133-TENEX>SCHED.MAC;258 22-JUL-74 14:42:37 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;257 22-JUL-74 10:29:09 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;256 19-JUL-74 17:04:40 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;255 18-JUL-74 16:23:02 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;254 18-JUL-74 16:17:32 EDIT BY ALLEN
; ADD CODE FOR PIE-SLICE SCHEDULER REGULATOR
;<133-TENEX>SCHED.MAC;242 16-JUL-74 17:00:19 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;241 16-JUL-74 16:39:43 EDIT BY ALLEN
;<133-TENEX>SCHED.MAC;232 12-JUL-74 11:06:58 EDIT BY ALLEN
; CORRECT BLUNDER IN SCDRUN CAUSING OVERLOADING OF BALSET
;<133-TENEX>SCHED.MAC;231 10-JUL-74 11:26:12 EDIT BY ALLEN
; ELIMINATE RACE AT SCHED4, ELIMINATE RESCHEDULE ON CHAR ARRIVAL
; AND TEST FOR PENDING BALSET REMOVAL IN RMPROC
;<TENEX-132>SCHED.MAC;230 17-JUN-74 20:19:37 EDIT BY ALLEN
; CORRECT BUG IN MAINTENANCE OF OLDSUM
SEARCH PROLOG
TITLE SCHED
;TENEX SCHEDULER - D. MURPHY
;LINKAGE TO OTHER PARTS OF MON -- PAGEM AND PISRV
EXTERN ASSPT,BUGCHK,BUGNTE,BUGHLT,DCHKSW,DDTPRS,DESPT,DRMFRE,DRMIN0
EXTERN DSKRT,GCALC,GCCOR,XGC,ICAPT,IOIP,MENTR,MONCOR,MRETN
EXTERN NXTDMP,PIAPRX,POSTPG,PRELD,PRELDF,POSPGF,SETPPG
EXTERN SETPT,SPTC,SWPIN0,SWPINT,SWPRT
EXTERN SETMPG,JDSPTP
PGR==24 ;I/O DEVICE NUMBER FOR PAGER
EXTERN AUTONX
EXTERN TTCH7,TTBIGC,TTPSI,TADSEC,LSTERR,FACTSW,FLOGO
EXTERN TCITST,TCOTST,TTEMES,FRZWT,CAPMSK,CAPENB
EXTERN KSELF,LOGTOT,LOGDES
EXTERN TTFORK,TTFRK1,EXEC0
EXTERN BHC,BITS,SVN,CH6TAB
EXTERN GFKH,SETLF1
INTERN PSISV2,RSKEDN
INTERN BLOCK0,BLOCK1
INTERN ITRAP,DISGE,DISGET,DISL,DISLT,RSKP,R,JRET,JSKP,NJOBS
INTERN PJMPG,PJMA,PPMPG,PPMA,PSB,JSB
INTERN FREJPA,FREJP,JFNPC0,RJFN,MJFN,SJFN,SWPMA0
INTERN NNAMES,SCDIN,ILIST,SCHEDP,.DISMS,SCHED0
INTERN SCDRQ7,JOBSRT,TTPSRQ,PSIT1A,GETCHA,.DEBRK
INTERN DISE,DISET,DISN,DISNT
INTERN ASSFK,WTFPGS,WTSPT,SUSFKR,SUSWT,ITRAP1
INTERN NTASKT,NLOADT,NEVENT
INTERN STIME,ETIME,SETOVF,MAXQ,DISG,DISGT,BLOCKW
INTERN SUPERP,PARALP,INFERP,PSILOB,TMZONE,DISLE,DISLET
INTERN FFF,PATCH1,PATCH2,INDFLG
INTERN .HALTF,EDISMS,HALTF1,HALTT
INTERN HLTJB,HLTFK1,CLRM0,FRZB1,FRZB2,FRZBB,PSIJTR
INTERN PSIRQ0,PSIRQF,PSIRQB,CHNSON,PSIR4,FORCTM,PSIRQ
INTERN P7POV,P7FOV,P7OV,PI7P,WTSPTT,SCHEDR,APCLK1,MINNR,MPEINT
INTERN SCDVE,PISC7
INTERN JTULCK,PSIWTF,JTMCN,JTFRZB,JTDVC1,FRZBAL,TRPSI5
INTERN NEWFKF
INTERN TTFRKP
IFDEF RTISW,<INTERN .SOLO,.TUTTI>
NSKDP==40 ;LOCAL PUSH LIST
NSCDRQ==20 ;SIZE OF SCHEDULER REQUEST QUEUE
MINNR==3 ;MIN SIZE OF RPLQ FOR LOADING FORK
;MACROS FOR TIMING SUBROUTINES
DEFINE STMR
< SKIPL BKGFLG
JSP 4,STIME
>
DEFINE ETMR(CLK)
< SKIPGE BKGFLG ;BACKGROUND MODE?
JRST .+4 ;YES
JSP 4,ETIME
ADDM 1,CLK
AOS CLK+1
>
;STORAGE
LS SKDPDL,NSKDP ;SCHEDULER LOCAL PDL
LS MSCNT,1 ;INDEX TO MSEC/TICK TABLE
LS APCLKC,1 ;COMMUNICATION TO CH7 FROM CH1 CLOCK INTERRUPT
LS CLKAC2,1 ;CLOCK ROUTINE TEMP
LS CLKAC1,1 ; "
LS SYNCC,1 ; "
LS OLDTCK,1 ; "
LS PISC7R,1
LS FKPT6M,1 ;-FKPT(6)
GS FORKX,1 ;INDEX OF CURRENTLY RUNNING FORK
GS FKPGS,NFKS ;UPT,,PSB SPT NUMBERS
GS FKSTAT,NFKS ;FORK WAIT TEST
GS FKWSP,NFKS ;NO. PGS NEEDED TO PRELOAD,,NO. PGS IN CORE
GS FKPGST,NFKS ;TEST WORD WHILE IN PAGE WAIT
;IF ON WTLST, TIME WAIT BEGAN
GS FKQ,NFKS ;QUEUE NUMBER,,TIME REMAINING IN QUANTUM
GS FKPT,NFKS ;IF ON WTLST, =WTLST,,ADDRESS OF NEXT FKPT OR 0
;IF ON A RUNLST, ADDRESS OF PREV FKPT,, ADDRESS
;OF NEXT FKPT
;IF DELETED FORK, B0=1
GS FKINT,NFKS ;FORK INTERRUPT COMMUNICATION REG
;BITS IN FKINT --
; B0 = REQUEST FOR PSI PROCESSING
; B1 = PSI PROCESSING IN PROGRESS - DEFER FURTHER REQUESTS
; REST DEFINED AND COMMENTED AT TAG "PIRQ"
GS FKINTB,NFKS ;INTERRUPT CHANNELS REQUEST
GS FKJOB,NFKS ;JOB NUMBER ,, JSB
GS FKNR,NFKS ;AGE,,BALANCE SET SIZE (RESERVE)
GS FKFLGS,NFKS ;PER PROCESS FLAGS,,CORE NUMBER
;FLAG BIT DEFS FOR ABOVE TABLE
WTFK==:1B18 ;PROCESS IN BALANCE SET AND WAITING
NOSK==:1B19 ;PROCESS IS NOSKED
FORCEM==:1B20 ;PROCESS IS TO BE REMOVED FROM BS
BLST==:1B21 ;PROCESS IS IN BALANCE SET
RNLS==:1B22 ;PROCESS IS ON A RUNLIST
ZIFA==:1B23 ;ZERO IFAV AT NEXT PAGE-FAULT (PLEASE)
NOFLT==:1B24 ;INDICATES ADJUSTMENT OF FKNR DUE TO LACK OF PAGE-FAULTING HAS BEEN DONE
WTCLCT==:1B25 ;FORK HAS LEFT BAL SET BUT GCCOR NOT YET RUN
IFN PIESLC,<
NOCNT==:1B26 ;FORK BEING BORN OR DYING. DON'T INCLUDE IN NAPROC
;AS FORK IS NOT RECORDED IN SYSFK
> ;END PIE-SLICE SCHEDULER CONDITIONAL
HOLD==:1B27 ;HOLD FORK IN BALANCE SET FOR MAXBSH AFTER DISMISS
WTLS==:1B28 ;FORK IS ON THE WAIT LIST
IFN PIESLC,<
HIQFK==:1B29 ;FORK MUST REMAIN ON A HIGH QUEUE
SPQFK==:1B30 ;FORK MUST REMAIN ON THE SPECIAL QUEUE
PHIQFK==:1B31 ;USED TO REMEMBER THAT FORK WAS ALREADY ON INTERQ AT
;TIME OF CALL TO SETHIQ
>
FKCNO=:FKFLGS ;CORE NUMBER IN RIGHT HALF OF FKFLGS
GS FKJTQ,NFKS ;JSYS TRAP QUEUE - BACK PTR,,FWRD PTR
IFN PIESLC,<
GS FKSOLD,NFKS ;SYSTEM SOLD TIME WHEN FKUTIL LAST UPDATED
GS FKUTIL,NFKS ;PROCESSOR UTILIZATION PER FORK (FLOATING POINT)
GS FKPRT,NFKS ;PROCESS TIME SINCE FKUTIL LAST UPDATED
GS FKUDT,NFKS ;REAL TIME SINCE FKUTIL UPDATE
> ;END PIE-SLICE SCHEDULER CONDITIONAL
LS FREFK,1 ;LIST OF FREE FORKS
GS SYSIFG,1 ;SYSTEM HAS BEEN INITIALIZED IF NOT 0
GS ACCIFG,1 ;ACCOUNTING INITIALIZED WHEN NON-0
GS ENTFLG,1 ;PERMIT NEW JOB ON ^C IF NON-0
GS PWRDWN,1 ;POWER FAILURE DETECTED IF .G. 0, DONE IF .L. 0
GS JOBDIR,NJOBS ;ATTACHED DIRECTORY,,LOGIN DIRECTORY
GS JOBNAM,NJOBS ;JOB SUBSYSTEM NAME INDEX FOR SETNM
GS JOBRT,NJOBS ;JOB RUN TIME
GS JOBPT,NJOBS ;CONTROL TTY,,TOP FORK
LS FREJOB,1 ;LIST OF FREE JOBS
IFE PIESLC,<
MAXQ==:4 ;HIGHEST NUMBERED QUEUE
>
IFN PIESLC,<
MAXQ==:2 ;HIGHEST NUMBERED QUEUE
>
;RH(RUNLST(I)) CONTAINS A POINTER TO THE FKPT ENTRY OF THE HIGHEST
;PRIORITY PROCESS ON Q(I) REQUIRING SERVICE. SUBSEQUENT ENTRIES IN
;THE LIST MAY BE FOUND BY FOLLOWING THE POINTER IN RH(FKPT).
;LH(RUNLSB(I)) CONTAINS A POINTER TO THE FKPT ENTRY OF THE LOWEST
;PRIORITY PROCESS ON Q(I) REQUIRING SEERVICE. THE LIST MAY BE
;FOLLOWED VIA THE POINTERS IN LH(FKPT).
LS RUNLST,MAXQ+1 ;RUNNING FORK LISTS (DESCENDING PRIORITY)
LS RUNLSB,MAXQ+1 ;RUNNING FORK LISTS (ASCENDING PRIORITY)
LS WTLST,1 ;WAITING FORK LIST
LS WTLSTB,1 ;WAIT LIST TAIL POINTER
LS WAITFS,1 ;WAIT LIST FULL SCAN FLAG
LS WAITLS,1 ;TIME OF LAST FULL WAIT LIST SCAN
LS JTLST,1 ;JSYS TRAP QUEUE
LS JTLSTL,1 ; ... LAST
LS JB0FLG,1 ;RUN JOB 0 REQUEST
LS FRECB,1 ;FREE CORE NUMBER BITS
;BALANCE SET VARIABLES
LS NBPROC,1 ;NUMBER OF PROCESSES IN BAL SET
LS NBRUN,1 ;NUMBER RUNNABLE FORKS IN BALSET
GS TOTRC,1 ;TOTAL NUMBER REAL CORE PAGES
GS SUMNRX,1 ;SUM(FKNR FOR BALSET PROCS)+SUM(FKWSP FOR ALL OTHERS)
;+PAGES UNASSIGNED AND AWAITING COLLECTION
GS NRPMIN,1 ;MINIMUM VALUE OF NRPLQ
GS MAXNRX,1 ;MAX VALUE OF SUMNRX
GS CGFLG,1 ;DEASSIGNED PAGES MAY EXIST IF >0
LS PANIC,1 ;COUNT OF PANIC MODE GCCORS
LS RLBITS,1 ;FOR GCCOR -- PROC USE BITS OF RUNLIST PROCS
LS BSBITS,1 ;PROC USE BITS OF BAL SET PROCS
LS RUNT1,1 ;RUNTIME SINCE FORK BEGAN EXECUTION
GS NRPLQ,1 ;NUMBER OF PAGES ON REPLACABLE QUEUE
GS RPLQ,1 ;REPLACABLE QUEUE END,,BGN
GS PNDING,1 ;NO. UNASSIGNED PAGES AWAITING COLLECTION
GS NPMAX,1 ;MAX NUMBER OF PAGES IN CORE FOR ONE PROCESS
GS SNPMAX,1 ;SMALL NPMAX FOR LOADED CONDITIONS
GS SJSIZ,1 ;'SMALL' JOB SIZE
GS IRJAV,1 ;NEAREST INTEGER TO RJAV
;SCHEDULER VARIABLES
LS SKEDF1,1 ;START PROCESS VIA CH7 BREAK IN 1
LS SKEDF3,1 ;PROCESS CLOCK COUNTED TO 0
LS INSKED,1 ;IN SCHEDULER IF NON-ZERO
LS SSKED,1 ;LAST JOB RUNNING WAS NOSKED
GS PSKED,1 ;PAGE TRANSFER COMPLETED OR PSI NEEDS ATTENTION
GS ISKED,1 ;SCHEDULE REQUEST FLAG
GS FSHBAL,1 ; FLUSH BALSET REQUEST FLAG
GS BKGFLG,1 ;WHEN = 0 INDICATES BACKGROUND
LS NGPROC,1 ;NUMBER OF FORKS WAITING TO ENTER BAL SET
LS OLDSUM,1 ;SUMNRX AT TIME OF LAST ATTEMPT TO ADD TO
;BALANCE SET. -1 IF THIS VALUE HAS BEEN
;INVALIDATED BY SOME CHANGE SUCH AS A FORK
;ENTERING THE RUNLISTS.
LS POTEN,1 ;UPPER BOUND ON NUMBER OF PAGES TO BE GAINED
;BY DOING GCCOR
LS RJAVS1,1 ;RJTSUM AT LAST RJAV UPDATE
;TABLES FOR SETNM
NNAMES==^D50 ;NUMBER OF NAMES ALLOWED
GS SNAMES,NNAMES ;SIXBIT NAME OF SUBSYSTEM
GS STIMES,NNAMES ;ACCUMULATED RUNTIME OF SUBSYSTEM
GS SPFLTS,NNAMES ;ACCUMULATED PAGE FAULTS OF SUBSYSTEM
GS SWAKES,NNAMES ;WAKEUPS 0-14, SIZE INTEGRAL 15-35
GS SBLKTM,NNAMES ; BLOCKED FOR TTY TIME
;"QTIMES" GETAB TABLE
GS QSUM,5 ;ACCUMULATED TIME OF JOBS OF RESPECTIVE Q'S
;PROCESSOR TASK GETAB TABLE ("TASKTB")
;ACCOUNTS FOR WHERE PROCESSOR TIME IS BEING SPENT
;EACH CATEGORY CONSISTS OF TWO WORDS; FIRST WORD IS TOTAL TIME
;SPENT PERFORMING THE TASK OR ROUTINE, SECOND IS A COUNT OF INVOKATIONS
;OF THAT TASK OR ROUTINE.
GS SOLD,2 ;PROCESS-LEVEL TIME AND NUMBER OF PROCESS
;DISPATCHINGS
GS IDLE,2 ;IDLE TIME (NO PROCESSES REQUESTING SERVICE)
;AND NUMBER OF TIMES WE GO IDLE
GS SWAPWT,2 ;SWAP-WAIT TIME (NO RUNNABLE PROCESSES) AND
;NUMBER OF TIMES WE HAD TO WAIT
GS NSKWT,2 ;NOSKED WAIT TIME (NOSKED PROCESS PAGE FAULTS
;WHILE THERE ARE OTHER RUNNABLE PROCESSES) AND
;NUMBER OF TIMES THIS HAPPENS
GS SKMAIN,2 ;TOTAL TIME SPENT IN AND ENTRIES TO SCHEDULER
;MAIN ROUTINE
GS SKBAL,2 ;TIME SPENT IN AND ENTRIES TO BALANCE SET SCHED.
GS SKWAIT,2 ;TIME SPENT IN AND ENTRIES TO WAIT-LIST SCAN
GS SKLDPR,2 ;TIME SPENT IN AND ENTRIES TO LDPROC ROUTINE
;(ROUTINE THAT PROMOTES ACTIVE PROCESSES TO
;THE BALANCE SET)
GS GCCR,2 ;TIME SPENT IN AND ENTRIES TO GLOBAL GARBAGE
;COLLECTOR (GCCOR)
GS PPG,2 ;TIME SPENT IN AND ENTRIES TO POST-PURGE ROUTINE
GS PTRAP,2 ;TIME SPENT IN AND COUNT OF PAGER TRAPS
;PLEASE NOTE THAT PAGER TRAP TIME IS ALSO
;INCLUDED IN SOLD TIME
IFN PIESLC,<GS SORTIM,2 ;TIME SPENT IN AND ENTRIES TO RESORT>
IFE PIESLC,<NTASKT==^D22>
IFN PIESLC,<NTASKT==^D24>
;SYSTEM LOAD GETAB TABLE ("LOADTB")
GS RJTSUM,1 ;INTEGRAL OF NBPROC+NGPROC DT
GS BSTSUM,1 ;INTEGRAL OF NBPROC DT
GS NBRSUM,1 ;INTEGRAL OF NBRUN DT
NRJAVS==3 ;NUMBER OF LOAD AVERAGES WE MAINTAIN
GS RJAV,NRJAVS ;EXPONENTIAL AVERAGES OF NUMBER OF ACTIVE PROCESSES
NLOADT==NRJAVS+3
;SYSTEM MISCELLANEOUS EVENT COUNTERS ("EVENTS")
GS DRMRD,1 ;NUMBER OF DRUM READS
GS DRMWR,1 ;NUMBER OF DRUM WRITES
GS DSKRD,1 ;NUMBER OF DISK READS
GS DSKWR,1 ;NUMBER OF DISK WRITES
GS TOPTRP,1 ;COUNT OF TOP-LEVEL PAGER TRAPS
GS WAKEUP,1 ;NUMBER OF PROCESS WAKE-UPS
GS TTINTS,1 ;NUMBER OF TERMINAL INTERRUPTS
GS NTTYIN,1 ;TOTAL NUMBER TERMINAL INPUT CHARS
GS NTTYOT,1 ;TOTAL NUMBER TERMINAL OUTPUT CHARS
GS ENTDMS,1 ;ENTRIES TO SCHED DUE TO FORKX DISMS
GS ENTPGF,1 ;ENTRIES TO SCHED DUE TO FORKX PAGE-FAULT
GS NGCLCT,1 ;NUMBER OF PAGES COLLECTED BY GCCOR
GS PPCLCT,1 ;NUMBER OF PAGES COLLECTED BY POSTPG
GS NREMJ,1 ;COUNT OF FORCED BALSET REMOVALS
GS HSYST1,1 ;TIME OF SYSTEM SHUTDOWN PENDING (GTAD FORMAT)
GS HSYST4,1 ;AND GTAD TIME SYSTEM SCHEDULED BACK UP (OR 0)
NEVENT==^D15
;CLOCKS COUNTED DOWN AND TESTED, PARALLEL TO PCLKT, DON'T REORDER
LS RJQNT,1 ;RUNNING JOB REMAINING QUANTUM
GS TIM2,1 ;SECOND CLOCK, 100 MS
LS JOBRTT,1 ;JOB RUNTIME SINCE LAST UPDATE
LS SCDRQI,1 ;SCHEDULER REQUEST QUEUE
LS SCDRQO,1
LS SCDRQB,NSCDRQ
GS TODCLK,1 ;MILLISECOND CLOCK, MONOTONICALLY INCREASING
GS CHKTIM,1 ;FOUR MINUTES PAST LAST JOB 0 CHECK
GS CHKTM1,1 ;TWO MINUTES PAST LAST JOB 0 CHECK
GS DDMPFK,1 ;INDEX OF FORK RUNNING DDMP. -1 IF NON-EXISTANT
LS SCDRN1,1 ;RUN ONLY FORK N IF N > -1
LS GLOCK,1 ;USED TO AVOID UNNECESSARY WAKEUPS
;AFTER COLLISION ON NON-RESIDENT LOCK
LS RESDNT,1 ;ANYTHING BELOW THIS ADDRESS IS RESIDENT
;RESIDENT STORAGE FOR CRJOB JSYS
GS CRJLCK,1 ;PROCESS LOCK FOR USE OF CRJOB JSYS
GS CRJANS,1 ;ANSWER FROM JOBSRT. 0=WAITING, -1=SUCCESS,
; +N IS AN ERROR NUMBER.
GS CRJJNO,1 ;JOB NUMBER ASSIGNED BY JOBSRT
IFN PIESLC,<
;RESIDENT STORAGE FOR PIE-SLICE SCHEDULER
GS NAPROC,NGRPS ;NUMBER OF ACTIVE PROCESSES PER GROUP
GS DSHARE,NGRPS ;FRACTION (INCLUDING WINDFALL) PER GROUP
GS PIEGRP,NJOBS ;GROUP INDEX PER JOB
GS DEFGP,1 ;DEFAULT PIE-SLICE GROUP
GS PASSES,1 ;LOCAL COUNT OF PASSES PER ENTRY TO RESORT
GS EXCHGS,1 ;LOCAL COUNT OF EXCHANGES IN RESORT
GS SRTBEG,1 ;PLACE TO BEGIN NEW SORT PASS
GS SRTEND,1 ;PLACE TO END IT
GS SRTTMP,1 ;TEMPORARY FOR SORT
>; END PIE-SLICE SCHEDULER CONDITIONAL
;PATCH SPACE FOR RES MON
SCDV1==1 ;IF DEFINED MEANS ASSEMBLING MON
SCDVE==.-1 ;UPPER LIMIT FOR CORE CLEAR ON STARTUP
FFF:
PATCH1:
PATCH2: XLIST ;REPEAT 300,<0>
REPEAT 300,< 0>
LIST
INDFLG: 0 ; .NE. 0 MEANS LOCK JSB
; .L. 0 MEANS USE NO INDIRECT PTRS
;SCHEDULER INITIALIZATION
SCDIN: SETZM SYSIFG
SETZM AUTONX
SETZM PWRDWN
SETZM ENTFLG
SETZM WAITLS
MOVE 1,[JRST SCDRQ0]
MOVEM 1,SCDRQ+1 ;DISPATCH FOR JSR-CALLED ROUTINE
MOVEI 1,JTLST
MOVEM 1,JTLSTL ;SET UP JSYS TRAP WAIT LIST
MOVNI 1,FKPT ;BECAUSE MACRO DOESN'T HAVE NEG RELOC'N
HRLI 1,6
MOVEM 1,FKPT6M ;-FKPT(6)
MOVEI 1,SCDRQB
MOVEM 1,SCDRQI
MOVEM 1,SCDRQO
MOVEI 1,FKPT
MOVEI 2,NFKS
CALL ILIST ;INIT FREE FORK LIST
MOVEM 1,FREFK
IFDEF SIGIPC,< CALL SIGINI## ;INIT SIGNL/WTFOR TABLES>
MOVEI 1,JOBPT
MOVEI 2,NJOBS
CALL ILIST
MOVEM 1,FREJOB ;INIT FREE JOB LIST
SETOM JOBRT
MOVE 1,[XWD JOBRT,JOBRT+1]
BLT 1,JOBRT+NJOBS-1
SETZM 20
SETOM SCDRN1
SETOM GLOCK
SETOM FORKX
SETOM TADSEC
SETOM OLDSUM
SETOM SSKED
SETOM DDMPFK
SETOM CRJLCK ;FREE LOCK ON CRJOB CELLS
HRLOI 1,377777
MOVEM 1,CHKTM1 ;AVOID JOB0 BUGHLT ON STARTUP
MOVSI 1,(777B8!CORMB) ;INIT RLBITS NOT TO CLEAR AGE AND CORMB
MOVEM 1,RLBITS
HRLOI 1,377 ;BITS IN PROC. USE REG.
MOVEM 1,FRECB
MOVEM 1,INSKED
MOVE 1,[FACTON]
MOVEM 1,FACTSW ;FACT FILE ON AND INIT THE REST=0
MOVEI 1,MAXQ+1 ;CLEAR RUNLST AND RUNLSB
SCDIN2: MOVEI 2,RUNLST(1)
MOVEI 3,RUNLSB(1)
HRLZM 2,0(3)
HRRZM 3,0(2)
SOJGE 1,SCDIN2
MOVEI 1,WTLST
HRLZM 1,WTLSTB
MOVEI 1,WTLSTB
HRRZM 1,WTLST
MOVE 1,MONCOR
LSH 1,^D9
MOVEM 1,RESDNT
RET
ILIST: ADDI 1,-1(2)
SETZM 0(1) ;INIT FREE LIST, BLOCK ADR IN AC1,
SOJLE 2,ILIST1 ; SIZE OF BLOCK IN 2
MOVEM 1,-1(1)
SUBI 1,1
SOJG 2,.-2
ILIST1: RET
;CHANNEL 7 INTERRUPT
;CLOCK, POSSIBLE RESCHEDULING, OR START PROCESS FROM SCHEDULER
PISC7: XWD PISC7R,.+1
IFN KIFLG,< ;PROTECT SOME AREAS ON KI-10.
CLSB SCDCHN ;THE ISB MUST BE EXPLICITLY CLEARED
MOVEM 1,KIP7A ;STASH AN AC TO DO THE RANGE CHECK
SKIPE KIP7Q ;REQUEST FROM KI RETURN?
JRST [ MOVE 1,KIP7P ;YES. GET PC AND FLAGS
MOVEM 1,PISC7R ;SET TO GO THERE
SETZM KIP7Q ;CLEAR REQUEST
SETZM KIP7F
JRST .+1]
MOVE 1,PISC7R ;SEE IF WE WERE IN USER MODE
TLNE 1,UMODF ; ..
JRST KIP71 ;YES. OK TO RESCHEDULE
MOVEI 1,0(1) ;EXEC MODE. JUST THE ADDRESS. CLR FLAGS
CAIL 1,KIBGN ;CRITICAL SECTION RANGE CHECK
CAIL 1,KIEND ; ..
KIP71: SKIPA 1,KIP7A ;NO. OK TO BREAK HERE. GET THE AC BACK.
JRST [ AOS KIP7F ;CRITICAL. FLAG INTERRUPT NEEDED, BUT
MOVE 1,KIP7A ; FOR NOW JUST RESTORE AC AND GO AWAY
JEN @PISC7R] ;DISMISS FROM CHANNEL 7
>; ;END OF KI-10 SAFETY CHECK.
SKIPG PSKED ;RESCHEDULE ON PAGE ARRIVAL .. DCA
SKIPE ISKED
AOSA SKEDF3 ;RESKED REQUEST
SKIPE APCLKC ;CLOCK TICK?
JRST APCLK ;SERVICE IT
APCLKX: SKIPE SKEDF1 ;INITIATED BY SCHEDULER?
JRST SCDR ;YES, GO START PROCESS
SKIPG INSKED ;IN SCHEDULER NOW, OR
SKIPG SKEDF3 ;NO SCHEDULING REQUESTS?
JEN @PISC7R ;IGNORE INTERRUPT
SKIPE ENSKR ; ABOUT TO ENTER SCHEDULER AT PROC LVL?
JEN @PISC7R ; YES. NO NEED TO DO IT HERE
SKIPN TRAPPC ;PAGER TRAP STARTING?
SKIPE NSKED ;OK TO RESCHEDULE?
JRST SCDW ;NO, GO SET TRAPS
ENTSKD ;ENTER SCHEDULER ENVIRONMENT
MOVE 1,PISC7R
MOVEM 1,PPC
JEN @[SCHED0]
SCDW: MOVEM 1,RSKED ;SAVE AC1
MOVE 1,RSKEDT ;GET TRAP INSTRUCTION
EXCH 1,RSKED ;LEAVE IT TO GET EXECUTED
JEN @PISC7R
RSKEDN: JFCL 0 ;NO-TRAP CONTENTS OF RSKED
RSKEDT: JSYS RSKD0 ;TRAP CONTENTS OF RSKED
;SETUP AND RESUME PROCESS
SCDR: SETZM SKEDF1 ;CLEAR LOCAL FLAG
SETZM INSKED ;NO LONGER IN SCHEDULER
IFN KIFLG,<
JSP 7,KISLOD> ;RELOAD STUFF PECULIAR TO KI-10
IFE JTRPSW-1,< ;IF MAPPING RES MON FOR TRAPS
HRRZ 1,JTMNW ;1=FORKS MONITOR
CAIN 1,7777 ;NULL FORK?
JRST .+5 ;YES, DON'T MAP RES MON
SKIPN 1,PSB+JDVPG ;NO, IS FORK INIT'D?
JRST .+3 ;NO
MOVEM 1,MMAP+1 ;YES, SET MON MAP AND
CONO PGR,7 ;MAP RES MON
>
MOVE 1,PSB40
MOVEM 1,40
MOVSI 17,PAC ;RESTORE PROCESS AC'S
BLT 17,17
JEN @PPC ;RUN PROCESS
;VARIOUS WAYS OF ENTERING SCHEDULER
;JSYS HALTF - DISMISS FORK UNTIL INTERRUPT OR EXTERNALLY RESTARTED
.HALTF: JSYS MENTR
HALTF1: CALL FKTMI ;FORK TERM INTERRUPT
HALTX: MOVEI 1,HALTT
JSYS EDISMS
JRST MRETN ;IF CONTINUED
HALTT: JRST 0(4) ;IDENTIFIABLE TEST FOR HALTED FORK
;EXEC DISMISS - AC1 CONTAINS XWD DATA,TEST ROUTINE ADR
EDISMS: XWD FPC,.+1
ENTSKD ;ENTER SCHEDULER
DISMS1: MOVE 2,FPC ;USE JSYS RETURN AS PPC
MOVEM 2,PPC
DISMSE: SKIPE NSKED ;CHECK FOR BUGGY DISMISS
BUG(HLT,<DISMISS WHILE NOSKED>)
AOS ENTDMS ;COUNT DISMISSES
MOVEM 1,FKSTAT(7) ;STORE ACTIVATION TEST WORD
LSH 1,-^D9
ANDI 1,777
CAML 1,MONCOR
BUG(HLT,<DISMISS WITH NON-RES TEST ADDRESS>)
CALL SAVRT
SETZM JOBCK0 ;INIT MEASURING INTERVAL
SETZ 2,
MOVSI 1,HOLD ;THIS FORK TO BE KEPT IN BALSET?
TDNE 1,FKFLGS(7)
MOVE 2,MAXBSH ;YES
HRRZ 1,FKSTAT(7)
CAIE 1,BLOCKM
CAIN 1,BLOCKT ;DISMISSED FOR WAIT .G. 500 MS?
SETZ 2, ;YES, DON'T RETAIN IN BALSET
JUMPE 2,[ CALL REMBSJ ;IF NO HOLD TIME REMOVE FROM
SETOB 7,FORKX ;BAL SET IMMED.
JRST SCHED0 ]
MOVE 1,TODCLK
ANDI 1,377777
ADDI 1,0(2)
MOVSI 1,0(1)
HRRI 1,DISMT
MOVEM 1,FKPGST(7)
JRST SCHP3
MAXBSH: ^D100 ;MAX BALSET 'HYSTERESIS'
;RESCHEDULE ON PAGE WAIT
SCHEDP: XWD SKDPC,.+1
ENTSKD
SCHP1: AOS ENTPGF ;COUNT ENTRIES DUE TO PAGE-FAULT
MOVEM 1,FKPGST(7)
CALL SAVRT
MOVE 1,SKDPC
MOVEM 1,PPC
SCHP3: MOVSI 1,WTFK
SOS NBRUN ;FORK NO LONGER RUNNABLE .. COUNT
CALL SCHP2
JRST SCHED0
SCHP2: SKIPE NSKED
JRST [MOVEM 7,SSKED ;SAVE NOSKED FORK INDEX
TLO 1,NOSK
JRST .+1]
IORM 1,FKFLGS(7)
SETOB 7,FORKX
RET
;DO OKSKED AND RESCHEDULE
SCHEDR: XWD SKDPC,.+1
ENTSKD
SOSGE NSKED
BUG(HLT,<OKSKED WHEN NOT NOSKED>)
JRST SCHP1
;DEFERRED SCHEDULING REQUEST TRAP
RSKD0: XWD SKDPC,.+1
ENTSKD ;ENTER SCHEDULER
MOVE 1,SKDPC
RSKD2: MOVEM 1,PPC
RSKD3: MOVE 1,RSKEDN
MOVEM 1,RSKED
JRST SCHED0
;COMMON SCHEDULER ENTER ROUTINE, SAVE AC'S AND SET INSKED FLAG
;UPDATES PROCESS CLOCKS. SMASHES ACS 15 AND 16 IN DOING SO!!
ENSKED: XWD ENSKR,.+1
SKIPE INSKED
BUG(HLT,<CALL TO SCHEDULER WHEN ALREADY IN SCHEDULER>)
AOS INSKED ;PREVENT ACTION BY CH7 BREAK
MOVEM 17,PAC+17 ;SAVE PROCESS AC'S
MOVEI 17,PAC
BLT 17,PAC+16
MOVE 7,40
MOVEM 7,PSB40
IFN KIFLG,<
JSP 7,KISSAV> ;SCHEDULER SAVE ROUTINE FOR KI-10 HWARE
MOVE 7,FORKX ;GET INDEX OF CURRENT FORK
MOVE P,PI7P ;GET PDL POINTER
PUSH P,ENSKR ; SAVE ENSKR
SETZM ENSKR
;UPDATE PROCESS CLOCKS
UCLOCK: MOVE 16,JOBNO
SETZ 15,
EXCH 15,JOBRTT ;RUN TIME SINCE LAST UPDATE
JUMPE 15,R ;RETURN NOW IF NO CHANGE
ADDM 15,SOLD ;ACCUMULATE ALL SOLD TIME
ADDM 15,JOBRT(16) ;ACCOUNT FOR JOB
ADDM 15,FKRT ;ACCOUNT FOR FORK
IFN PIESLC,<
ADDM 15,FKPRT(7) ;RUNTIME SINCE LAST FKUTIL UPDATE
> ;END PIE-SLICE SCHEDULER CONDITIONAL
ADDM 15,RUNT1 ;LOCAL RUNTIME
ADDM 15,IFTIM ;INTERFAULT TIME
HRRZ 16,JOBNAM(16) ;GET SUBSYSTEM INDEX
ADDM 15,STIMES(16) ;ACCUMULATE SUBSYSTEM TIME
ADDB 15,PGTIM ;CORE MGT CLOCK
CAIGE 15,AGTICK ;TIME TO TICK AGE CLOCK?
RET ;NO
IDIVI 15,AGTICK ;YES, COMPUTE NUMBER TICKS IN INTERVAL
MOVEM 16,PGTIM ;LEAVE REMAINDER
CAILE 15,AGSEC ;STRANGELY LONG INTERVAL?
MOVEI 15,AGSEC ;YES, NORMALIZE TO A SECOND
LDB 16,[POINT 9,FKNR(7),17] ;CURRENT AGE
ADDI 15,0(16) ;ADD TICKS
TRNE 15,777000 ;OVERFLOW 9 BIT FIELD?
SUBI 15,1000-100 ;YES, WRAPAROUND
DPB 15,[POINT 9,FKNR(7),17] ;SET NEW AGE
DPB 15,[POINT 9,PGR72,8] ;FOR PAGER
PGRLAG ;LOAD NEW AGE INTO PAGER
RET
;INSTRUCTION TRAP - TRAP PC IN FPC, ASSUMED TO BE I +1
ITRAP1: MOVEM 1,LSTERR ;SAVE ERROR CODE GIVEN IN 1
ITRAP: SKIPE INSKED
BUG(HLT,<INSTRUCTION TRAP WHILE IN SCHEDULER>)
SKIPL FORKX ;NO FORK RUNNING, OR
CONSZ PI,177B27 ;PI IN PROGRESS?
BUG(HLT,<INSTRUCTION TRAP WHILE PI IN PROGRESS OR IN SCHEDULER>)
SKIPGE SLOWF ;NOW IN SLOW CODE?
JSYS MENTR ;NO, ENTER
ITR3: MOVE 1,MPP ;STACK PTR ON ENTERING THIS CONTEXT
MOVE 2,0(1) ;RETURN PC
TLNN 2,UMODF ;PREVIOUS CONTEXT INTERRUPTABLE?
SKIPGE -2(1) ;I.E. USER MODE, OR INTDF .L. 0
JRST ITR2 ;YES, OK
BUG(CHK,<INSTRUCTION TRAP AND PREVIOUS CONTEXT WAS NOINT>)
ITR2: SETZM NSKED
SETOM TRAPC ;CLEAR FLAGS AND COUNTERS
SETZM INTDF ;SET TO 1 LEVEL NOINTERRUPT
MOVE 1,CHNSON
ANDCAM 1,PSIBW ;FLUSH PREVIOUS PANIC BREAKS
MOVEI 1,^D15 ;INITIATE CHANNEL 15 INTERRUPT
CALL PSIRQ0
RESKD1 ;GET THE INTERRUPT "SEEN"
OKINT ;INTERRUPT SHOULD TAKE HERE
MOVE P,UPP ;RETURN TO USER IF CONTINUED
ADD P,BHC+2
JRST MRETN
;BLOCK UNTIL CONDITION SATISFIED
;BLOCK0 - STAYS IN BALSET, BLOCK1 - LEAVES BALSET
BLOCK0: XWD SKDPC,.+1
ENTSKD
CALL BLOCKS
JRST SCHP1
BLOCK1: XWD SKDPC,.+1
ENTSKD
CALL BLOCKS
MOVE 2,SKDPC
MOVEM 2,PPC
JRST DISMSE
BLOCKS: MOVNI 1,^D100
ADDM 1,RJQNT ;CHARGE Q TO PREVENT HOGGING
MOVNI 1,2
ADDM 1,SKDPC ;MAKE RETURN TO INSTRUCTION BEFORE CALL
MOVE 1,TODCLK
ANDI 1,377777
ADDI 1,^D100 ;ADD 100 MILLISECS
MOVSI 1,0(1)
HRRI 1,BLOCKW
RET
BLOCKM: JFCL ;SCHED TEST FOR .5 TO 64 SEC.
BLOCKW: MOVE 2,TODCLK ;SCHEDULER TEST, GET TIME
ANDI 2,377777
SUB 1,2 ;DESIRED - NOW = WAIT LEFT
BLK2: JUMPLE 1,1(4) ;NO WAIT TIME LEFT
CAIGE 1,200000 ;BIG DIFFERENCE?
JRST 0(4) ;NO, KEEP WAITING
SUBI 1,400000 ;YES, COMPENSATE FOR WRAPAROUND
JRST BLK2
;DEFAULT LOCK FAILURE ROUTINE
CNTLCK::
LCKTST::
PUSH P,1 ;SAVE AC1
PUSH P,1 ;DO IT AGAIN
MOVE 1,-2(P)
MOVE 1,-2(1) ;GET THE FAILING AOSE
EXCH 1,0(P) ;PUT IT ON THE STACK AND RESTORE AC1
HRRZI 1,@0(P) ;AC1=ADDRESS OF LOCK
CAML 1,RESDNT ;RESIDENT?
JRST [SETZM GLOCK ;NO, INDICATE COLLISION
MOVEI 1,SLOCKT ;ACTIVATION ROUTINE
JSYS EDISMS ;SLEEP
MOVNI 1,2 ;BACK UP THE PC
ADDM 1,-2(P)
SUB P,BHC+1 ;ADJUST STACK
POP P,1 ;RESTORE AC1
RET] ;TRY AGAIN
HRLZS 1 ;ADDRESS TO LEFT HALF
HRRI 1,RLOCKT ;ACTIVATION TEST
JSYS EDISMS ;SLEEP
SUB P,BHC+1
POP P,1 ;RESTORE AC1
RET
;ACTIVATION TEST FOR PROCESS WAITING FOR RESIDENT LOCK
RLOCKT: AOSE 0(1)
JRST 0(4)
JRST 1(4)
;ACTIVATION TEST FOR PROCESS WAITING FOR A SWAPPABLE LOCK
SLOCKT: SKIPL GLOCK ;NOW A GOOD TIME TO TRY AGAIN?
JRST 0(4) ;NO
JRST 1(4) ;YES
;HERE ON UNLOCKING A LOCK ON WHICH A CONFLICT HAS OCCURRED
;NOTE THAT SPECIAL CARE IS TAKEN TO PRESERVE THE STATE OF
;ALL ACS BUT P WHEN THE EFFECTIVE ADDRESS COMPUTATION IS DONE
;TO ADDRESS THE LOCK. THUS, TABLES OF LOCKS MAY BE INDEXED VIA
;ANY AC EXCEPT P.
CNFLCT:: PUSH P,1
MOVE 1,-1(P) ;GET PC
MOVE 1,-2(1) ;GET THE SOSL THAT DIDN'T SKIP
EXCH 1,0(P) ;PUT IT ON THE STACK AND RESTORE 1
SETOM @0(P) ;UNLOCK THE LOCK
PUSH P,1
HRRZI 1,@-1(P) ;GET ADDRESS OF LOCK
CAMGE 1,RESDNT ;RESIDNET?
JRST CNFL2 ;YES
SETOM GLOCK ;INDICATE A CONFLICT RESOLVED
SETZM WAITLS ;REQUEST IMMEDIATE FULL WAITLIST SCAN
AOS ISKED ;TO AVOID POSSIBLE DEADLOCK
CNFL2: POP P,1
SUB P,BHC+1
RET
;DISMISS UNTIL WORD .GE. 0
DISGE: PUSH P,1
HRLI 1,DISGET ;GIVEN MON ADDRESS IN 1
DISXE: MOVS 1,1
JSYS EDISMS
POP P,1
RET
DISGET: SKIPGE 0(1)
JRST 0(4)
JRST 1(4)
;DISMISS UNTIL WORD .L. 0
DISL: PUSH P,1
HRLI 1,DISLT
JRST DISXE
DISLT: SKIPL 0(1)
JRST 0(4)
JRST 1(4)
;DISMISS UNTIL WORD .G. 0
DISG: PUSH P,1
HRLI 1,DISGT
JRST DISXE
DISGT: SKIPG 0(1)
JRST 0(4)
JRST 1(4)
;DISMISS UNTIL WORD .LE. 0
DISLE: PUSH P,1
HRLI 1,DISLET
JRST DISXE
DISLET: SKIPLE 0(1)
JRST 0(4)
JRST 1(4)
;DISMISS UNTIL WORD .E. 0
DISE: PUSH P,1
HRLI 1,DISET
JRST DISXE
DISET: SKIPE 0(1)
JRST 0(4)
JRST 1(4)
;DISMISS UNTIL WORD .N. 0
DISN: PUSH P,1
HRLI 1,DISNT
JRST DISXE
DISNT: SKIPN 0(1)
JRST 0(4)
JRST 1(4)
;DISMISS FOR SPECIFIED TIME JSYS
.DISMS: JSYS MENTR
JUMPLE 1,MRETN
CAIL 1,100000 ;LONG OR SHORT TIME?
JRST TDIS1 ;LONG
MOVE 2,TODCLK
ANDI 2,377777
ADDI 2,0(1) ;COMPUTE TIME TO RESTART
CAIGE 1,^D500 ;USE BLOCKW FOR WAIT .L. 500 MS
SKIPA 1,[BLOCKW]
MOVEI 1,BLOCKM ;BLOCKM OTHERWISE
HRLI 1,0(2)
TDIS2: JSYS EDISMS ;DISMISS WITH SPECIFIED TEST
JRST MRETN
TDIS1: CAML 1,BITS+^D9
MOVSI 1,(1B9) ;APPROX 17 HRS IS MAX PERMITTED
MOVE 2,TODCLK
TLZ 2,777000
ADD 2,1 ;COMPUTE TIME TO RESTART
LSH 2,-^D10 ;DIVIDE BY 1024
MOVSI 1,0(2)
HRRI 1,BLOCKT
JRST TDIS2 ;GO COMPLETE DISMISSAL
;SCHEDULER WAIT TEST FOR LONG WAIT
BLOCKT: LSH 1,^D10 ;RESTORE WAKEUP TIME TO FULL SIZE
MOVE 2,TODCLK ;GET TIME NOW
TLZ 2,777000
SUB 1,2 ;DESIRED-NOW = TIME LEFT TO WAIT
BLKT1: JUMPLE 1,1(4) ;WAKEUP IF NEGATIVE
CAMG 1,BITS+^D9 ;VERY LARGE DIFFERENCE?
JRST 0(4) ;NO, KEEP WAITING
SUB 1,BITS+^D8 ;COMPENSATE FOR WRAPAROUND OF TODCLK
JRST BLKT1 ;CHECK AGAIN
;SCHEDULER
SCHED0: SETZ 0, ;FOR PEOPLE WATCHING LIGHTS
SCH0: CONSZ PI,177B27 ;ANY PI IN PROGRESS?
BUG(HLT,<ENTERED SCHEDULER WITH PI IN PROGRESS>)
IFE JTRPSW-1,< ;IF MAPPING RES MON FOR TRAPPED FORK
CONO PGR,6 ;UNMAP IT
>
MOVE 1,NBPROC
ADD 1,NGPROC
LSH 1,^D9
ADD 1,NBPROC
HRLI 0,0(1) ;DISPLAY NRUN, NBPROC IN LH 0
MOVEM 0,22 ;AND 22
MOVSI 16,-NPCLKS ;SCAN PROCESS CLOCKS
SKIPG RJQNT(16) ;EXHAUSTED? (RJQNT IS FIRST IN TABLE)
XCT PCLKT(16) ;YES, SERVICE WHATEVER
AOBJN 16,.-2
SKIPLE TTBIGC ;TTY BIG BUFFER NEED SERVICE?
CALL TTCH7 ;YES
IFDEF TNTCHN,<
SKIPE TNTFLG##
CALL TNTCHK##>
SETZM SKEDF3
MOVE 4,SCDRQO
CAME 4,SCDRQI ;ANY REQUESTS?
CALL SCDRQ1 ;YES
SKIPN ISKED ;ANY REQUESTS FOR RESCHEDULING?
SKIPE PSKED
CALL DISMSJ ;YES
SKIPE ISKED ;SCHED REQUEST?
CALL WTSCAN ;YES, CHECK WAITING FORKS
SKIPE PWRDWN ;POWER FAIL DETECTED?
JRST SCHPRF ;YES
SKIPE 1,20 ;REQUEST FROM SWITCHES?
JSP 4,SWTST ;YES
SKIPGE 7,FORKX ;JOB TO CONTINUE?
CALL SKPROC ;NO, GO FIND ONE.
SCHED3: SKIPGE 1,FKINT(7) ;INTERRUPT REQUEST?
TLNE 1,200000 ;AND NOT ONE IN PROGRESS
JRST SCHED4
MOVSI 1,200000 ;CLEAR WORD EXCEPT FOR PI IN PROG
EXCH 1,FKINT(7)
MOVEM 1,PIMSK ;PASS REQUEST WORD TO SERVICE ROUTINE
MOVEI 1,PIRQ ;PSEUDO-INTERRUPT SERVICE
IFN KIFLG,<
TLO 1,(1B6)> ;SO UXCT GOES TO USER SPACE
EXCH 1,PPC
MOVEM 1,PIPC ;SAVE OLD PC
SCHED4: SETZ 1,
EXCH 1,JOBRTT
AOSG BKGFLG ;COMING OUT OF BACKGROUND?
JRST [ASH 1,-1 ;YES, CHARGE SCHED AND SWAPWT EQUALLY
ADDM 1,SWAPWT
JRST .+1]
ADDM 1,SKMAIN
AOS SKMAIN+1
AOS SKEDF1 ;TELL CH7 TO START PROCESS
ISB SCDCHN ;LET IT START PROCESS
JRST .-1 ;JUST IN CASE ....
DEFINE ECALL (D)
<IFDEF D'CHN,<EXTERN D'CHK
CALL D'CHK
GS D'TIM
>>
;TABLE OF SERVICE CALLS FOR PROCESS CLOCKS
PCLKT: CALL DISMSJ ;RUNNING JOB QUANTUM OVERFLOW
BKGNDT: CALL CLK2 ;SECOND LEVEL CLOCK
NPCLKS==.-PCLKT
NBKR==.-BKGNDT ;THE (LAST) N OF THESE TO RUN ANYTIME
;SECOND PROCESS CLOCK, LESS PRECISE, UPDATES EVERY 100 MS
CLK2: SKIPL SCDRN1 ;RUNNING SPECIFIC JOB?
JRST CLK22 ;YES, DON'T DO JOB 0 CHECK
MOVE 1,TODCLK
CAMGE 1,CHKTM1 ;JOB 0 NOT RUN FOR TWO MINS.?
JRST CLK22 ;EVERYTHING'S OKAY
SKIPGE 7,DDMPFK ;GET INDEX OF DDMP FORK
JRST CLK22 ;DOESN'T EXIST
HLRZ 2,FKQ(7)
JUMPE 2,CLK23 ;FORK ALREADY ON HI-Q
HRRZS FKQ(7) ;PLACE ON Q0
MOVE 2,FKFLGS(7) ;SEE IF CURRENTLY ACTIVE
TLNN 2,RNLS
JRST CLK23 ;NOT ACTIVE
CALL CHGRLS ;MOVE TO NEW RUNLIST
JRST CLK22
CLK23: CAML 1,CHKTIM ;DDMP 4 MIN. OVERDUE?
BUG(HLT,<JOB 0 NOT RUN FOR TOO LONG, PROBABLE SWAPPING HANGUP>)
CLK22: MOVEI 15,^D100
SUBM 15,TIM2 ;ACTUAL TIME SINCE LAST UPDATE
EXCH 15,TIM2
;UPDATE ACTIVE-PROCESS TIME INTEGRAL
MOVE 1,NBPROC
ADD 1,NGPROC
IMUL 1,15
ADDM 1,RJTSUM
MOVN 15,15 ;NEGATE TIME
MOVSI 14,-N2CLKS ;SET TO SCAN SECOND LEVEL CLOCKS
CLK21: ADDM 15,DSKTIM(14) ;UPDATE CLOCK
SKIPG DSKTIM(14) ;COUNTED OUT?
XCT CLK2CL(14) ;YES, DO WHATEVER
AOBJN 14,CLK21
AOS ISKED ;DO COMPLETE RESKED AT LEAST THIS OFTEN
IFDEF DIALLN,<
PUSHJ P,DILCHK##> ; TEMP FOR BBN AUTODIALLER
RET
;TABLE OF CALLS FOR SECOND LEVEL CLOCKS
CLK2CL: ECALL DSK ;DISK RE-QUEUE CHECK
;NOTE THAT DSKCHK MUST BE FIRST --
;SEE CLK21 ABOVE.
IFDEF IMPCHN,<
EXTERN IMPCHK
CALL IMPCHK
GS IMPTM2,1
>
ECALL TNT ;TELENET
ECALL MTA ;MAG TAPE
ECALL PTP ;PAPER TAPE PUNCH
ECALL PLT ;PLOTTER
ECALL DTA ;DECTAPE
ECALL LPT ;PHYSICAL LPT
CALL SKDCHK ;DO PERIODIC SCHEDULER FUNCTIONS
GS SKDTIM,1
IFN PIESLC,<
CALL RESORT ;REORDER COMPUTATION QUEUE
GS RESTIM,1
> ;END PIE-SLICE SCHED CONDITIONAL
CALL DORJAV ;UPDATE LOAD AVS
GS RJATIM,1
ECALL DLS ;DLS (TTY) BACKGROUND STUFF
N2CLKS==.-CLK2CL
RSKP: AOS 0(P) ;RETURN (VIA PDL) SKIPPING
R: RET
PI7P: IOWD NSKDP,SKDPDL
;UPDATE RUNNABLE JOB AVERAGES
DORJAV: MOVEI 2,^D5000
MOVEM 2,RJATIM ;SET TIME OF NEXT UPDATE
MOVE 4,RJTSUM ;CURRENT INTEGRAL OF NBPROC+NGPROC
SUBM 4,RJAVS1 ;DIFFERENCE FROM LAST UPDATE
EXCH 4,RJAVS1
FSC 4,233 ;FLOAT IT
FDVR 4,[5000.0] ;AVERAGE OVER LAST 5000 MS
JOV .+1 ;CLEAR OV FLAG
MOVSI 2,-NRJAVS
SCHC1: MOVE 3,EXPFF(2)
FMPRM 3,RJAV(2) ;SUM*EXP(-T/C) -) SUM
JOV [ SETZM RJAV(2) ;THAT MAY HAVE UNDERFLOWED,
JRST .+1] ;IF SO, CLEAR IT TO 0
MOVE 3,4
FMPR 3,EXPGF(2)
FADRM 3,RJAV(2) ;TERM*(1-EXP(-T/C)) + SUM -) SUM
AOBJN 2,SCHC1
MOVE 2,RJAV ;1 MIN AV.
FADRI 2,(0.5) ;ROUND
MULI 2,400 ;FLOAT-TO-FIX
ASH 3,-243(2)
MOVEM 3,IRJAV ;MAINTAIN INTEGER VALUE
RET
;TABLE OF EXP(-T/C) FOR T = 5 SEC.
EXPFF: EXP 0.920043902 ;C = 1 MIN
EXP 0.983471344 ;C = 5 MIN
EXP 0.994459811 ;C = 15 MIN
;TABLE OF 1-EXP(-T/C) FOR T = 5 SEC
EXPGF: EXP 0.0799560979 ;C = 1 MIN
EXP 0.0165286558 ;C = 5 MIN
EXP 0.00554018893 ;C = 15 MIN
; PERIODIC SCHEDULER FUNCTIONS
;CHECK SUMNRX
SKDCHK: MOVSI 2,-NFKS
SETZ 1,
SUMLUP: MOVE 3,FKFLGS(2)
TLNE 3,BLST
TLNE 3,FORCEM
SKIPA 4,FKWSP(2)
MOVE 4,FKNR(2)
ADDI 1,0(4)
SUMNXT: AOBJN 2,SUMLUP
PIOFF ;PREVENT PNDING FROM CHANGING
ADD 1,PNDING
CAMN 1,SUMNRX
JRST SUMEXT
BUG (NTE,<SUMNRX INCORRECT>)
MOVEM 1,SUMNRX
SUMEXT: PION
;CHECK PNDING
SKIPE IOIP ;DON'T CHECK IF ANY WRITES IN PROGRESS
JRST UDINT
MOVE 1,SWPCOR##
SETZ 2,
MOVSI 4,(77B5) ;MASK TO CHECK AGE
PNDCHK: TDNN 4,CST0(1) ;ON RPLQ??
JRST PNDCH2 ;YES, DON'T INSPECT FURTHER
MOVE 3,CST3(1)
TLC 3,7777
TLNN 3,7777 ;PAGE UNOWNED AND NOT ON RPLQ?
ADDI 2,1 ;YES, COUNT IT
PNDCH2: CAME 1,NHIPG## ;HAVE WE JUST DONE LAST PAGE?
AOJA 1,PNDCHK ;NO, DO ANOTHER
CAMN 2,PNDING ;PNDING OK?
JRST UDINT ;YES
BUG (CHK,<PNDING INCORRECT>) ;NO
SUB 2,PNDING
ADDM 2,PNDING
ADDM 2,SUMNRX ;ADD SAME AMT TO SUMNRX WE JUST ADDED TO PNDING
;UPDATE INTERESTING TIME INTEGRALS
UDINT: MOVEI 1,^D60000 ;UPDATE EVERY 60 SECONDS
SUBM 1,SKDTIM
EXCH 1,SKDTIM
MOVE 2,NBPROC
IMUL 2,1
ADDM 2,BSTSUM ;INTEGRAL NBPROC DT
MOVE 2,NBRUN
IMUL 2,1
ADDM 2,NBRSUM ;INTEGRAL NBRUN DT
; GUARD AGAINST POSSIBLE LOCKUP IN NON-RESIDENT LOCK CONFLICT
; RESOLUTION STRATEGY
SETOM GLOCK ; WAKE EVERYBODY JUST IN CASE
SETZM WAITLS ; A DIRECTORY OR SUBINDEX GOT LOCKED BY
AOS ISKED ; USER CODE WHICH UNLOCKS BUT CAN'T
; SETOM GLOCK
RET
IFN PIESLC,<
;ROUTINE WHICH PERIODICALLY REORDERS THE COMPUTATION QUEUE
RESORT: STMR ;TIME THIS ROUTINE
MOVEI 1,^D1000 ;DO THIS EVERY SECOND
MOVEM 1,RESTIM
SETOM OLDSUM
MOVEI 1,RUNLST+COMPQ ;INIT SRTBEG
MOVEM 1,SRTBEG
MOVEI 1,RUNLSB+COMPQ ;INIT SRTTMP
MOVEM 1,SRTTMP
SETZM PASSES ;ZERO COUNT OF PASSES PER ENTRY
RESOR3: SETOM EXCHGS ;ZERO COUNT OF EXCHANGES PER PASS
MOVE 1,SRTTMP ;INIT SRTEND
MOVEM 1,SRTEND
MOVE 6,SRTBEG ;PLACE TO START THIS PASS
HRLOI 5,377777 ;5=LARGEST FLOATING POINT NUMBER
JRST RESOR8
RESOR6: MOVEI 7,@FKPT6M ;GET FORKX OF LOWER FORK
SKIPN PASSES ;FIRST PASS?
CALL UPDUT ;YES, UPDATE UTILIZATION
CALL CDIST ;AND GET DISTANCE FROM TARGET
CAMGE 5,1 ;IN PROPER ORDER?
JRST RESOR7 ;NO, EXCHANGE THEM
MOVE 5,1 ;YES, MOVE LOWER FORK'S DIST TO AC5
RESOR8: HRRZ 6,0(6) ;NEXT FKPT
CAMN 6,SRTEND ;LAST?
JRST RESOR5 ;YES
JRST RESOR6 ;NO
;EXCHANGE THE FORK POINTED TO BY AC6 WITH THE ONE ABOVE IT.
;DENOTE THE FORK IN 6 BY "C" IN THE SEQUENCE "A,B,C,D".
;WE WISH TO COME OUT WITH "A,C,B,D".
RESOR7: ;6=C
HLRZ 3,(6) ;3=B
HLRZ 2,(3) ;2=A
HRRZ 4,(6) ;4=D
MOVEM 3,SRTTMP ;NEXT PASS MAY END AFTER COMPARING
;A&C IF NO EXCHANGES FOLLOW
AOSN EXCHGS ;FIRST EXCHANGE THIS PASS?
JRST [HLRZ 1,(2) ;YES, NEXT PASS BEGINS BY COMPARING A&C
JUMPE 1,.+1 ;CAN'T DO IF AT TOP OF LIST
MOVEM 1,SRTBEG
JRST .+1]
HRRM 6,(2) ;A FORWARD TO C
HRLZM 2,(6) ;C BACK TO A
HRRM 3,(6) ;C FORWARD TO B
HRLZM 6,(3) ;B BACK TO C
HRRM 4,(3) ;B FORWARD TO D
HRLM 3,(4) ;D BACK TO B
HRRZ 6,0(6) ;6 MUST NOW POINT TO B
JRST RESOR8 ;DONE
RESOR5: SKIPL EXCHGS ;ANY EXCHANGES THIS PASS?
AOSA PASSES ;COUNT PASSES PER ENTRY TO RESORT
CAIA
JRST RESOR3
ETMR SORTIM ;COMPLETE TIMING OF THIS ROUTINE
RET
;ROUTINE TO COMPUTE DISTANCE FROM TARGET. LEAVES RESULT IN AC1.
;SMASHES AC2.
CDIST: HLRZ 2,FKJOB(7) ;GET JOB NO.
MOVE 2,PIEGRP(2) ;GET GROUP INDEX
MOVE 1,DSHARE(2)
FDVR 1,NAPROC(2) ;AC1=TARGET
FSBR 1,FKUTIL(7) ;AC1=TARGET-UTIL
RET
> ;END PIE-SLICE SCHED CONDITIONAL
;TEST WORD DEPOSITED BY SWITCHES IN 20
SWTST: SETZM 20
JFFO 1,.+1
CAIGE 2,NSWTT
XCT SWTT(2)
SWTST1: JRST 0(4) ;RESUME SCHEDULER
SWTT: JRST SWHLT ;HALT T.S.
JRST SWRUN1 ;RUN ONLY SPECIFIED JOB
JRST SWCRSH ;INITIATE JOB0 FUNCTION
NSWTT==.-SWTT
SWHLT: CALL DISMSJ ;DISMISS CURRENT JOB
PUSH P,DCHKSW
SETOM DCHKSW
MOVNI 0,1
BUG(CHK,<SCHED HALTED>) ;GO TO DDT
SWHLT1: POP P,DCHKSW
SKIPE DDTPRS
JRST .
JRST SCHED0
SWRUN1: HRREI 1,0(1) ;-1 OR JOB NUMBER IN RH
JUMPL 1,SWRUN2 ;-1 MEANS RESTORE TO NORMAL
CAIGE 1,NJOBS ;LEGAL JOB NUMBER?
SKIPGE JOBRT(1) ;RIGHT HALF OF SWITCHES SPECIFIES JOB
JRST SWTST1 ;EXCEPT THAT JOB DOESN'T EXIST
SWRUN2: MOVEM 1,SCDRN1 ;ALLOW ONLY THAT JOB TO RUN
CALL DISMSJ ;DISMISS CURRENT FORK
JRST SWTST1
SWCRSH: SETZM NXTDMP ;DO DDMP
SETZB 1,DDTIME## ;HUSTLE UP THE DISK TRICKLER
AOS JB0FLG ;DO JOB 0
JRST SWTST1
;POWER FAIL DETECTED
SCHPRF: CALL DISMSJ ;FLUSH CURRENT FORK
MOVE 1,TODCLK ;WAIT A COUPLE MS FOR IO TO STOP
ADDI 1,2
CAMLE 1,TODCLK
JRST .-1
PIOFF 610000 ;CLEAR WORLD
CONO APR,1B19
SETOM PWRDWN ;SAYS WE FINISHED PWR DWN SEQUENCE
JRST 4,. ;SYSTEM SHOULD BE RESTARTABLE AT SYSRST
;SCHEDULER REQUEST PROCESSOR
;SCDRQ7 CALLED BY ROUTINES HAVING PDL POINTER IN P
SCDRQ7: PIOFF
JSR SCDRQ
PION
RET
;SCDRQ CALLED BY JSR AFTER HAVING TURNED OFF PI SYSTEM
LS SCDRQ,2
SCDRQ0: MOVEM 1,@SCDRQI
AOS 1,SCDRQI
CAIE 1,SCDRQB+NSCDRQ
JRST @SCDRQ
MOVEI 1,SCDRQB
MOVEM 1,SCDRQI
JRST @SCDRQ
;PROCESS SCHEDULER REQUESTS
SCDRQ1: CAMN 4,SCDRQI
RET
MOVE 2,0(4) ;WORD CONTAINS DATA,,DISPATCH ADR
HLRZ 1,2
CALL 0(2)
AOS 4,SCDRQO
CAIE 4,SCDRQB+NSCDRQ
JRST SCDRQ1
MOVEI 4,SCDRQB
MOVEM 4,SCDRQO
JRST SCDRQ1
;SCHEDULER REQUESTS
;PUSHJ TO THESE ROUTINES ON SCHED LEVEL WHEN SCDRQ QUEUE NON-EMPTY.
; CALL WITH DATUM IN AC1
;JOB STARTUP ROUTINE. CALL WITH TTY NUMBER OR 0,,-1 IN AC 1
JOBSRT: HRRE 1,1 ;FULL WORD IN CASE DETACHED STARTUP
MOVE 2,SPTC ;CURRENT SPT COUNT
CAIL 2,SSPT-NOFN-20 ;NEARLY FULL?
JRST JOBSR1 ;YES, DON'T PERMIT LOGIN
MOVE 2,DRMFRE
CAIG 2,100 ;ENOUGH FOR THE EXEC, IF NOT A JOB?
JRST JOBSR2 ;NO
SKIPN FREJOB ;ROOM FOR NEW JOB
JRST JOBSR3 ;NO JOBS
SKIPN FREFK ;AND NEW FORK?
JRST JOBSR4 ;NO
MOVE 2,@FREJOB ;GET THE FREE JOB OFF THE FREE LIST
EXCH 2,FREJOB ; ..
SUBI 2,JOBPT ;CONVERT TO JOB NUMBER FROM LIST ADR
PUSH P,2 ;SAVE JOB NUMBER
PUSH P,1 ;TTY NUMBER (FROM TTY SRV)
CALL ASSFK ;GET A FORK
POP P,1
POP P,2 ;JOB NUMBER AGAIN
HRLM 2,FKJOB(7) ;PUT IT IN FORK TABLE, TO RE-FIND JOB NUMBER
HRLI 1,NEWJBF ;ADD TTY #, NEWJOB TO B0&NEWFKF
IORM 1,FKINT(7) ;LEAVE TTY NUMBER IN RH FOR STARTUP ROUTINE
IFN PIESLC,<
SKIPE 1,SYSIFG ;SYSTEM INITIALIZED?
MOVE 1,DEFGP ;GET DEFAULT PIE-SLICE GROUP
MOVEM 1,PIEGRP(2)
> ;END PIE-SLICE SCHEDULER CONDITIONAL
RET
JOBSR4: MOVEI 2,[ASCIZ / FORKS FULL
/]
JRST JOBSR0
JOBSR3: MOVEI 2,[ASCIZ / JOBS FULL
/]
JRST JOBSR0
JOBSR2: MOVEI 2,[ASCIZ / DRUM FULL
/]
JRST JOBSR0
JOBSR1: MOVEI 2,[ASCIZ / SPT FULL
/]
JOBSR0: JUMPL 1,JOBSRC ;JUMP IF FROM CRJOB
EXCH 1,2 ;MSG TO 1, TTY TO 2
HRRZS 2 ;JUST LINE NUMBER
HRLI 1,440700 ;STRING POINTER
CALL TTEMES ;GIVE USER BAD NEWS
SETOM TTFORK(2) ;CLEAR TTY
RET
JOBSRC: MOVEI 1,CRJBX6## ;FAIL RETURN FROM CRJOB
MOVEM 1,CRJANS ;RETURN IT
RET
;ASSIGN FORK SLOT
ASSFK: HRRZ 7,@FREFK
EXCH 7,FREFK ;GET FORK, UPDATE LIST
SUBI 7,FKPT
MOVEI 1,JSKP
MOVEM 1,FKSTAT(7) ;MAKE STATUS RUNNABLE AT NEXT TEST
IFE PIESLC,<
MOVE 1,QBASE+1
MOVEM 1,FKQ(7) ;ESTABLISH QUEUE
> ;END NON-PIE-SLICE SCHEDULER CONDITIONAL
IFN PIESLC,<
MOVE 1,SOLD
MOVEM 1,FKSOLD(7)
SETZM FKUTIL(7)
SETZM FKPRT(7)
MOVE 1,TODCLK
MOVEM 1,FKUDT(7)
MOVSI 1,NOCNT ;INITIALIZE FKFLGS
HLLM 1,FKFLGS(7)
MOVEI 2,INTERQ
CALL CHGQ
> ;END PIE-SLICE SCHEDULER CONDITIONAL
CALL WTCONC ;PUT ON WAITLIST
MOVSI 1,400000+NEWFKF
MOVEM 1,FKINT(7) ;LEAVE INTERRUPT REQUEST
MOVEI 2,(7) ;PSIR4 TAKES FORKX IN AC2
CALL PSIR4 ;AND GET IT NOTICED
SETZM FKINTB(7)
SETZM FKPGS(7) ;CLEAR PT AND PSB WORD
SETZM FKJOB(7)
SETZM FKWSP(7)
MOVE 1,[XWD 100100,3] ;INIT AGE TO 100, W.S. TO 3
MOVEM 1,FKNR(7)
RET
;PROCESSOR INTERRUPTS REFERRED FROM CHANNEL 1
P7OV: MOVEI 2,6 ;OVERFLOW, FLOATING OVERFLOW
JRST P7PI1
P7FOV: MOVEI 2,7 ;FLOATING OVERFLOW CHANNEL
JRST P7PI1
P7POV: MOVEI 2,^D9 ;PDL OVERFLOW
P7PI1: EXCH 1,2 ;FORK NUMBER LEFT BY APR ROUTINE
CALL PSIRQ
RET
MPEINT: MOVEI 2,^D11 ;GIVES IO ERROR INTERRUPT
JRST P7PI1
;BALANCE SET SCHEDULER
;CALLED TO SELECT PROCESS TO RUN
SKPROC: STMR ;BEGIN TIMING THIS ROUTINE
SETZM 21 ;CLEAR JOB RUNNING DISPLAY
SETZM PSKED
SKIPE FSHBAL ;ANYBODY WANT TO FLUSH THE BALANCE SET?
CALL FSHBS ;YES, GO DO IT
SKPR1: SKIPGE SSKED ;ARE WE NOSKED?
SKIPN NBRUN ;NO, ANYBODY TO RUN?
SKPR5: SKIPA 1,NRPLQ ;NOBODY TO RUN OR NOSKED
JRST SKPR3 ;SOMEBODY TO RUN, GO DO IT.
ADD 1,IOIP
CAMLE 1,NRPMIN
JRST SKPR3 ;NO
ADD 1,POTEN ;ANY POINT IN DOING A GCCOR?
CAMG 1,NRPMIN
JRST SKPR4 ;NO, TRY TO REMOVE A PROCESS
CALL GCCOR ;YES, TRY GARBAGE COLLECTING
MOVE 1,NRPLQ ;DID THAT HELP?
ADD 1,IOIP
CAMLE 1,NRPMIN
JRST SKPR3 ;YES
SKPR4: CALL RMPROC ;NO, REMOVE A PROCESS FROM BALANCE SET
JRST [PUSH P,BSBITS ;COULDN'T FIND ONE, DO PANIC GCCOR
SETZM BSBITS ;MAKE GCCOR COLLECT ALL UNLOCKED PAGES
CALL GCCOR
POP P,BSBITS
AOS PANIC ;COUNT OCCURRENCES
JRST SKPR3]
JRST SKPR5 ;FOUND ONE, RECHECK CORE SITUATION
SKPR3: SKIPL 7,SSKED ;ARE WE NOSKED?
JRST SKPR11 ;YES
SKIPN 1,NGPROC ;ANY PROCS WAITING TO ENTER BAL SET?
JRST SKPR7 ;NO
SKIPL SCDRN1 ;RUNNING SPECIAL JOB?
JRST SKPR6 ;CALL LDPROC
SKIPL 1,OLDSUM ;VALID OLD SUMNRX?
CAMLE 1,SUMNRX ;YES, HAS SUMNRX GOTTEN SMALLER?
SKPR6: CALL LDPROC ;TRY TO LOAD SOME PROCESSES
SKPR7: SKIPN 10,NBPROC ;NUMBER OF BALSET PROCS
JRST BKGND1 ;THERE AREN'T ANY
MOVSI 11,-<MAXQ+1> ;INIT QUEUE LIST LOOP AOBJN POINTER
SKPR8: HRRZ 6,RUNLST(11) ;GET FKPT ADDRESS FOR FIRST FORK
SKPR9: CAIN 6,RUNLSB(11) ;END OF LIST?
JRST SKPR10 ;YES, GO TO NEXT ONE
;WE HAVE A PROCESS TO LOOK AT, GET ITS FORK INDEX
MOVEI 7,@FKPT6M ;FORK INDEX TO 7
HRRZ 6,0(6) ;GET POINTER TO NEXT PROCESS
MOVE 2,FKFLGS(7) ;GET FLAGS
TLNN 2,BLST ;IS IT IN THE BALANCE SET?
JRST SKPR9 ;NO, GET NEXT PROCESS
TLNN 2,WTFK+FORCEM ;RUNNABLE?
JRST SKPR2 ;YES, GIVE HIM CONTROL
CALL WAITT ;INSPECT WAITING FORK
JRST [ SOJG 10,SKPR9 ;GO ON TO NEXT PROC, IF ANYMORE IN BS
JRST BKGND1] ;NO NEED TO LOOK FURTHER, GO IDLE
JRST SKPR2
;MOVE ON TO THE NEXT LIST
SKPR10: AOBJN 11,SKPR8 ;ANOTHER LIST?
JRST BKGND1 ;NO, NOBODY TO RUN
;TEST NOSKED FORK
SKPR11: CALL WAITT ;NOSKED FORK MUST BE WAITING
JRST BKGND1 ;STILL WAITING
SETOM SSKED ;NO LONGER WAITING, RUN HIM
ETMR SKBAL
JRST SETRT
;TEST WAITING BALANCE SET PROCESS
WAITT: HRRZ 2,FKPGST(7) ;GET ADDRESS OF TEST ROUTINE
HLRZ 1,FKPGST(7) ;GET TEST DATA
JSP 4,0(2) ;CALL IT
RET ;STILL WAITING
;FORK NO LONGER WAITING
WAITT2: MOVSI 4,WTFK+NOSK
ANDCAB 4,FKFLGS(7) ;TURN OFF WAIT AND NOSKED BITS
AOS NBRUN ;COUNT RUNNABLE FORKS
TLNN 4,FORCEM ;IS THIS A DELAYED FORCED BS REMOVAL?
JRST WAITT3 ;NO
HRRZ 1,FKPGST(7) ;YES
CAIN 1,SWPINT ;WAS THIS PROCESS ENTERING BALANCE SET?
CALL UDNRP ;YES, CLEAN UP
CALL REMBSF ;KICK HIM OUT
RET
WAITT3: HRRZ 1,FKPGST(7)
CAIE 1,SWPINT ;ENTERING BALANCE SET?
JRST RSKP ;SKIP RETURN
SKIPN PRELDF ;PRELOADING DESIRED?
JRST WAITT4 ;NO
HLRZ 1,FKWSP(7) ;IS THERE ENOUGH CORE TO PRELOAD?
CAMLE 1,NRPLQ
JRST [SKIPG IOIP ;NO, FREE PAGES ON THE WAY?
JRST .+1 ;NO, LOAD AS MUCH AS POSSIBLE NOW
MOVSI 1,WTFK ;YES, MAKE HIM WAIT AGAIN
IORM 1,FKFLGS(7)
SOS NBRUN
RET]
PUSH P,6
PUSH P,10
CALL PRELD ;PRELOAD THE WS
POP P,10
POP P,6
WAITT4: CALL UDNRP
JRST RSKP
;UPDATE FOR PROCESS FINISHED ENTERING BALSET
UDNRP: SKIPN PRELDF ;PRELOADING?
RET
HLRZ 1,FKWSP(7) ;PRELOAD SIZE AS DETERMINED BY LDPROC
MOVN 1,1
ADDM 1,NRPMIN ;REMOVE FROM REPLQ RESERVE
RET
;TEST EDISMS FORK FOR ELAPSED GRACE PERIOD
DISMT: PUSH P,4 ;SAVE AC4
MOVE 2,FKFLGS(7) ;GET FLAG WORD
TLNE 2,FORCEM ;DELAYED FORCED REMOVAL?
JRST DISMT2 ;YES
;SEE IF EDISMS WAIT IS OVER
MOVE 2,FKSTAT(7)
HLRZ 1,2
JSP 4,0(2) ;CALL ACTIVATION ROUTINE
CAIA ;STILL WAITING
JRST RSKP ;NO LONGER WAITING
;STILL BLOCKED, SEE IF PSI IS PENDING
DISMT4: SKIPGE 1,FKINT(7) ;INTERRUPT PENDING?
TLNE 1,(1B1) ;AND ACCEPTABLE?
JRST DISMT3 ;NO, SEE IF GRACE PERIOD IS OVER
MOVSI 1,PSIWTF ;YES, REMEMBER FORK WAS WAITING
IORM 1,FKINT(7)
JRST RSKP
;SEE IF GRACE PERIOD IS OVER
DISMT3: HLRZ 1,FKPGST(7)
JSP 4,BLOCKW ;GRACE PERIOD OVER?
RET ;NO
DISMT2: CALL REMBSJ
RET
;ROUTINE TO DETERMINE WHETHER FORK IS WAITING
; CHECKS FOR FORK ON WAIT-LIST OR BEING HELD IN BALANCE SET
;TAKES FORK INDEX IN AC7. CLOBBERS NO ACS.
;SKIP RETURNS IF FORK IS WAITING, RETURNS +1 OTHERWISE.
CHKWT:: PUSH P,1
MOVE 1,FKFLGS(7) ;SEE IF FORK ON WAIT-LIST
TLNE 1,WTLS
JRST CHKWT1 ;IT IS, SKIP RETURN
;SEE IF BEING HELD IN BALANCE SET
TLC 1,WTFK+RNLS+BLST
TLNE 1,WTFK+RNLS+BLST
JRST CHKWT2 ;NOT BEING HELD
HRRZ 1,FKPGST(7) ;ITS IN BALANCE SET AND NOT RUNNABLE
CAIN 1,DISMT ;BEING HELD?
CHKWT1: AOS -1(P) ;YES
CHKWT2: POP P,1 ;NO, RESTORE AC1
RET ;AND RETURN
;GIVE CONTROL TO SELECTED PROCESS
SKPR2: ETMR SKBAL ;FINISH TIMING BALANCE SET SCHEDULER
SKIPL SCDRN1 ;RUN SPECIAL JOB?
JRST SKDSP ;YES
SETRT: HRRZM 7,FORKX
CALL SETPPG ;SETUP PAGER FOR THIS PROCESS
IFE PIESLC,<
HLRZ 2,FKQ(7) ;SETUP REMAINING TIME
HRRZ 3,FKQ(7)
SUB 3,QBASE(2)
> ;END NON-PIE-SLICE SCHEDULER CONDITIONAL
IFN PIESLC,<
HRRZ 3,FKQ(7) ;GET REMAINING TIME ON THIS QUEUE
> ;END PIE-SLICE SCHEDULER CONDITIONAL
MOVEM 3,RJQNT
SETZM RUNT1
AOS SOLD+1 ;COUNT TOTAL RESCHEDULES
HLRZ 1,FKQ(7) ;QUEUE LEVEL OF FORK
HLRZ 2,FKJOB(7) ;JOB NUMBER OF FORK
CAIG 2,^D32 ;DONT DISPLAY IF GT JOB 32
IOR 1,BITS(2) ;BIT POSITION DESIGNATES JOB NUMBER
MOVEM 1,21 ;FORK NUMBER AND STATUS FOR LIGHTS
IFN KIFLG,<
JRST SETOVF> ;SET UP FLAGS IN OTHER MODULE FOR KI10
IFN KAFLG,<
SETOVF: MOVEI 2,5B29+5B32 ;CLEAR OV AND FOV FLAGS
MOVE 1,PSICHM ;GET THIS FORKS CHANNEL MASK
TLNE 1,(1B6) ;CHANNEL 6?
TRC 2,6B32 ;YES, ENABLE OVERFLOW
TLNE 1,(1B7) ;CHANNEL 7?
TRC 2,6B29 ;YES, ENABLE FLOATING OVERFLOW
CONO APR,APRCHN(2) ;SET APR ACCORDING TO FORGOING
RET ;RETURN (+2 IF CALLED FROM SCHED)
>
;RUN SPECIAL JOB ONLY
SKDSP: HLRZ 1,FKJOB(7) ;GET SELECTED FORK'S JOB NO.
CAMN 1,SCDRN1 ;IS IT THE RIGHT JOB?
JRST SETRT ;YES
CALL RMPROC ;NO, THROW OUT A PROCESS
BUG (HLT,<COULDN'T FLUSH BAL SET FOR SPECIAL JOB>)
;BACKGROUND ACTIVITIES, IF NO PROCESS TO RUN
BKGND1: SKIPGE BKGFLG
JRST .+3 ;ALREADY BACKGROUND
JSP 4,ETIME ;FINISH TIMING ROUTINE
ADDM 1,JOBRTT ;BUT ADD BACK INTO JOBRTT SO SWAPWT GETS CHARGED
MOVSI 16,-NBKR ;PERFORM ANY PERIODIC ROUTINES
XCT BKGNDT(16) ;WHICH CAN BE RUN MORE OFTEN THAN
AOBJN 16,.-1 ;WHEN THEIR CLOCK RUN OUT
SETZ 1,
EXCH 1,JOBRTT ;GET TIME SINCE LAST UPDATE
SKIPG 2,NBPROC ;ANYONE IN BALANCE SET?
IOR 2,NGPROC ;OR WAITING TO ENTER BAL SET?
JUMPE 2,[ADDM 1,IDLE ;NO .. TREAT AS IDLE TIME
SKIPL BKGFLG
AOS IDLE+1
JRST BKGND2]
SKIPL SSKED ;NOSKED?
JRST [SKIPG NBRUN ;ANYBODY RUNNABLE?
JRST .+1 ;NO ,CHARGE TO IOWT
ADDM 1,NSKWT ;CHARGE TO NOSKED WAIT
SKIPL BKGFLG
AOS NSKWT+1
JRST BKGND2]
ADDM 1,SWAPWT ;CHARGE TO SWAPWT
SKIPL BKGFLG
AOS SWAPWT+1
BKGND2: SETOM BKGFLG ;INDICATE BACKGROUND
SETZM WAITLS ;INSURE FULL SCAN OF WAIT LIST
SKIPE CGFLG ;EXPLICIT GCCOR REQUEST?
CALL GCCOR ;YES
SCHRST: MOVE P,PI7P ;REINITIALIZE STACK POINTER
AOJA 0,SCH0 ;TRY AGAIN
;CORE OVER RESERVED, SELECT PROCESS TO REMOVE
;SELECT PROCESS WITH LOWEST PRIORITY THAT CAN BE REMOVED
RMPROC: SKIPL SCDRN1 ;ARE WE RUNNING SCECIAL JOB?
JRST RMPR2 ;YES, DONT CHECK FOR ONE PROCESS
MOVE 1,NBPROC
CAIG 1,1 ;ONLY ONE PROCESS IN BALSET?
JRST RMPR1 ;YES, MUST BE A BIG ONE
RMPR2: MOVEI 1,MAXQ
RMPR3: HLRZ 6,RUNLSB(1)
RMPR4: CAIN 6,RUNLST(1) ;END OF LIST?
JRST RMPR5 ;YES, GO TO NEXT ONE
MOVEI 7,@FKPT6M ;GET FORK INDEX
HLRZ 6,0(6) ;GET POINTER TO NEXT FKPT
MOVE 2,FKFLGS(7)
TLNN 2,BLST ;IS THIS A BAL SET PROCESS?
JRST RMPR4 ;NO
SKIPL SCDRN1 ;ARE WE RUNNING A SPECIAL JOB?
JRST [HLRZ 10,FKJOB(7)
CAMN 10,SCDRN1 ;IS THIS THE RIGHT ONE?
JRST RMPR4 ;YES, DONT KICK HIM OUT
JRST .+1]
TLNE 2,NOSK ;CAN THIS GUY BE KICKED OUT?
JRST RMPR4 ;NO
TLNE 2,FORCEM ;WAITING FOR FORCED BALSET REMOVAL?
JRST [PUSH P,1 ;YES, TRY TO KICK HIM OUT
CALL WAITT
POP P,1
JRST RMPR4]
TLNE 2,WTFK ;IS THIS FORK WAITING?
JRST [MOVSI 10,FORCEM+WTCLCT ;INDICATE FORCED OUT AND AWAITS COLLECTION
IORM 10,FKFLGS(7)
HRRZ 10,FKWSP(7)
ADDM 10,POTEN ;INCREASE POTENTIALLY COLLECTABLE
HRRZ 2,FKNR(7)
SUBI 10,0(2)
ADDM 10,SUMNRX
AOS NREMJ
MOVE 2,FKCNO(7)
MOVE 2,BITS(2)
ANDCAM 2,BSBITS ;BAL SET MASK FOR GCCOR
JRST RSKP]
CALL REMBSF ;KICK HIM OUT NOW
AOS NREMJ
JRST RSKP
;END OF LIST, GO TO NEXT ONE
RMPR5: SOJGE 1,RMPR3
RET
RMPR1: MOVE 1,NRPLQ ;TIGHT ON CORE?
CAMLE 1,NRPMIN
RET ;NO, LEAVE BALANCE SET AS IS
JRST RMPR2
;REMOVE PROCESS FROM BALANCE SET
REMBSF: TDZA 3,3
REMBSJ: SETO 3,
MOVE 1,FKFLGS(7) ;SEE IF DELAYED FORCED REMOVAL
TLNE 1,FORCEM
JRST REMBS4 ;IT IS, FOLLOWING HAS BEEN DONE
TLO 1,WTCLCT ;INDICATE AWAITS COLLECTION
MOVEM 1,FKFLGS(7)
HRRZ 2,FKWSP(7) ;DECREASE SUMNRX BY FKNR-FKWSP
ADDM 2,POTEN ;INCREASE POTENTIALLY COLLECTABLE
MOVE 1,FKNR(7)
SUBI 2,0(1)
ADDM 2,SUMNRX
MOVE 2,FKCNO(7)
MOVE 2,BITS(2)
ANDCAM 2,BSBITS ;MASK FOR GCCOR
;IF NEITHER PRELOADING NOR POSTPURGING, AVOID POSTPG CALL
REMBS4: SKIPN 1,PRELDF
IOR 1,POSPGF
JUMPE 1,REMBS3
PUSH P,10
PUSH P,6
PUSH P,3
MOVE 1,3
CALL POSTPG
POP P,3
POP P,6
POP P,10
REMBS3: HLRZ 2,FKJOB(7) ;MAINTAIN SUBSYS INFO
HRRZ 2,JOBNAM(2)
HRRZ 1,FKNR(7)
HRLI 1,(1B14) ;COUNT REMOVALS FROM BALSET
ADDM 1,SWAKES(2) ;INTEGRATE SIZE
MOVSI 2,-PLKV
SKIPN INDFLG ;ARE WE LOCKING JSB'S?
JRST REMB1 ;NO
HRRZ 1,FKJOB(7) ;GET SPT INDEX OF JSB
MOVE 1,SPT(1) ;GET CORE INDEX
ADDM 2,CST1(1) ;REDUCE LOCK COUNT
REMB1: HRRZ 1,FKPGS(7) ;PSB SPT INDEX
MOVE 1,SPT(1)
ADDM 2,CST1(1) ;UNLOCK PSB
HLRZ 1,FKPGS(7) ;UPT SPT INDEX
JUMPE 1,.+3 ;NO UPT
MOVE 1,SPT(1)
ADDM 2,CST1(1) ;UNLOCK UPT
MOVSI 1,BLST+FORCEM
ANDCAB 1,FKFLGS(7) ;TURN OFF BAL SET BIT
TLZE 1,WTFK ;WAITING FORK?
JRST [ANDM 1,FKFLGS(7) ;TURN OFF WAIT BIT
JRST REMBS2]
SOS NBRUN ;WE ARE REMOVING A RUNNABLE FORK
REMBS2: SOS NBPROC
JUMPGE 3,[AOS NGPROC
RET]
;REMOVE PROCESS FROM RUNLST AND PLACE ON WTLST
CALL REMRUN
SETOM OLDSUM ;INDICATE OLDSUM INVALID
MOVE 1,FKCNO(7)
MOVE 1,BITS(1)
ANDCAM 1,RLBITS ;MAINTAIN MASK FOR GCCOR
IFN PIESLC,<
MOVE 1,FKFLGS(7)
TLNE 1,NOCNT ;ARE WE TO DECREMENT NAPROC?
JRST REMBS5 ;NO
HLRZ 1,FKJOB(7)
MOVE 1,PIEGRP(1) ;GET PIESLICE GROUP INDEX
MOVSI 2,(-1.0)
FADRM 2,NAPROC(1)
REMBS5:
> ; END PIE-SLICE SCHEDULER CONDITIONAL
HRRZ 1,0(P) ;SEE IF WE SHOULD DO WTCONC CALL
CAIE 1,HLTFK3 ;IF FROM HLTFK3 DONT DO IT
CALL WTCONC
RET
;ADD PROCESS TO BALANCE SET IF POSSIBLE
LDPROC: STMR ;BEGIN TIMING THIS ROUTINE
CALL SCDRUN ;SELECT BEST RUNNABLE FORK
JRST LDPRT ;RETURN IF NONE FOUND
;PROMOTE TO THE BALANCE SET
LDPR2: MOVE 3,NRPLQ ;SEE IF ENOUGH PAGES ON RPLQ TO LOAD PSB&UPT
CAIGE 3,MINNR
JRST [ADD 3,IOIP ;ADD WRITES IN PROGRESS
CAIL 3,MINNR ;NNOW ENOUGH?
JRST LDPRT ;YES, JUST RETURN WITHOUT GCCOR
PUSH P,7
CALL GCCOR ;TRY TO GET SOME PAGES
POP P,7
MOVE 3,NRPLQ ;NOW TEST
CAIGE 3,MINNR ;ENOUGH NOW?
JRST LDPRT ;NO
JRST LDPR3]
LDPR3: HRRZ 1,FKNR(7)
SKIPE 2,FKWSP(7)
SUBI 1,0(2) ;FKNR-FKWSP
ADDM 1,SUMNRX ;UPDATE SUMNRX
MOVSI 10,BLST+WTFK+ZIFA ;INDICATE WAITING, BAL SET FORK AND REQUEST
;CLEAR OF IFAV
IORB 10,FKFLGS(7)
TLNN 10,WTCLCT ;FORK BEEN GCCOR'D?
JRST .+3
MOVNI 2,0(2) ;NO
ADDM 2,POTEN
SETOM OLDSUM ;INDICATE OLDSUM INVALID
SOS NGPROC
AOS NBPROC
SKIPN PRELDF ;PRELOADING WANTED?
JRST LDPR4 ;NO
;NOTE THAT IT IS ASSUMED HERE THAT FKNR-FKWSP IS STILL IN AC1
;LEFT OVER FROM LDPR3!!
HRLM 1,FKWSP(7) ;SAVE
ADDM 1,NRPMIN ;MAKE SURE WE HAVE THEM ON RPLQ
LDPR4: HRRZ 1,FKPGS(7) ;GET PSB INTO CORE
JUMPN 1,.+3
CALL ASSPT ;NO PSB, GO ASSIGN ONE
HRRZM 1,FKPGS(7)
CALL SWPIN0
HLRZ 1,FKPGS(7)
JUMPN 1,.+3
CALL ASSPT ;NO UPT, ASSIGN ONE
HRLM 1,FKPGS(7)
CALL SWPIN0
HRRZ 1,FKJOB(7) ;GET JSB SPTN
JUMPN 1,.+3 ;IS ONE?
CALL ASSPT ;NO, ASSIGN FOR NEW JOB
HRRM 1,FKJOB(7)
SKIPE INDFLG ;LOCKING JSB'S?
CALL SWPIN0 ;SWAP IT IN AND LOCK IT
MOVEI 1,SWPINT
MOVEM 1,FKPGST(7) ;SET TEST TO WAIT FOR PSB AND PT
MOVE 1,FKCNO(7)
MOVE 1,BITS(1)
IORM 1,BSBITS ;MAINTAIN MASK FOR GCCOR
ETMR SKLDPR ;FINISH TIMING THIS ROUTINE
RET ;AND EXIT
LDPRT: ETMR SKLDPR ;COMPLETE TIMING OF THIS ROUTINE
RET ;NO SKIP RETURN
;SELECT PROCESS TO PROMOTE TO BALANCE SET
SCDRUN: MOVSI 1,-<MAXQ+1>
SETZ 4, ;4 USED TO ACCUMULATE NR OF BALSET PROCS
SCDR1: MOVEI 6,RUNLST(1) ;ADDRESS OF CURRENT LIST POINTER
SCDR2: HRRZ 6,0(6) ;GET ADDRESS OF NEXT FKPT ENTRY
CAIN 6,RUNLSB(1) ;END OF LIST?
JRST SCDR3 ;YES, GO TO NEXT ONE
MOVEI 7,@FKPT6M ;GET FORK INDEX
MOVE 2,FKFLGS(7) ;GET FLAG WRD
TLNE 2,BLST ;IN BALANCE SET?
JRST [MOVE 2,FKNR(7) ;YES, ACCUMULATE NR
ADDI 4,0(2)
JRST SCDR2]
SKIPL SCDRN1 ;RUNNING A SPECIAL JOB?
JRST [HLRZ 10,FKJOB(7) ;YES, ONLY LOAD ITS FORKS
CAME 10,SCDRN1
JRST SCDR2 ;THIS FORK NOT PART OF SPEC. JOB
JRST .+1]
;SEE IF HE CAN ENTER BY DISPLACING LOWER PRIORITY BALSET FORKS
HRRZ 5,FKNR(7) ;SPACE HE WILL NEED
SKIPE 3,FKWSP(7) ;SPACE HE CURRENTLY OCCUPIES
SUBI 5,0(3) ;DEDUCT FROM FKNR IF NON-ZERO
ADDI 5,0(4) ;PLUS SPACE REQUIRED BY HIGHER PRIORITY FORKS
CAMGE 5,MAXNRX ;DOES THIS EXCEED MAXNRX?
JRST RSKP ;NO, OKAY TO LOAD
SCDR4: MOVE 5,SUMNRX ;YES, SAVE SUMNRX
MOVEM 5,OLDSUM
RET
;NEXT LIST
SCDR3: AOBJN 1,SCDR1
JRST SCDR4
;LIST MAINTENANCE SUBROUTINES
; REMOVE FORK FROM CURRENT LIST
REMOVE: MOVE 3,FKPT(7)
HLLM 3,(3) ;NEXT POINTS BACK TO PREVIOUS
MOVS 3,3
HLRM 3,(3) ;PREVIOUS POINTS FORWARD TO NEXT
RET
; INSERT FORK ON LIST
;RH(3) POINTS TO EXISTING ENTRY ON LIST. FORK WILL BE INSERTED ABOVE IT.
INSERT: HRLI 3,FKPT(7) ;LH(3)=ADDRESS(NEW FKPT)
HLRZ 4,(3) ;RH(4)=ADDRESS(OLD FKPT)
HLLM 3,(3) ;TAIL POINTS BACK TO NEW
HLRM 3,(4) ;OLD POINTS FORWARD TO NEW
HRLZM 4,FKPT(7) ;NEW POINTS BACK TO OLD
HRRM 3,FKPT(7) ;NEW POINTS FORWARD TO TAIL
RET
; REMOVE A FORK FROM THE RUNLIST IT IS ON
REMRUN: CALL REMOVE
MOVSI 3,RNLS
ANDCAM 3,FKFLGS(7)
RET
; REMOVE FORK FROM WAITLIST
REMWT: CALL REMOVE
MOVSI 3,WTLS
ANDCAM 3,FKFLGS(7)
RET
; APPEND FORK TO RUNLIST
APPRUN: HLRZ 3,FKQ(7)
MOVEI 3,RUNLSB(3)
CALL INSERT
MOVSI 3,RNLS
IORM 3,FKFLGS(7)
RET
; APPEND FORK TO WAITLIST
APPWT: MOVEI 3,WTLSTB
CALL INSERT
MOVSI 3,WTLS
IORM 3,FKFLGS(7)
RET
;REMOVE FORK FROM CURRENT RUNLIST AND PLACE ON RUNLIST SPECIFIED BY
;CURRENT CONTENTS FOR FKQ
CHGRLS: CALL REMOVE ;REMOVE FROM CURRENT LIST
HLRZ 3,FKQ(7) ;QUEUE
IFN PIESLC,<
CAIN 3,COMPQ ;GOING TO COMPQ?
JRST REQUE ;PLACE ON COMPQ
> ;END PIE-SLICE CONDITIONAL
MOVEI 3,RUNLSB(3) ;TAIL POINTER
CALL INSERT
RET
IFN PIESLC,<
;TRANSPARENT ROUTINE TO INSERT A FORK IN ITS PROPER PLACE ON THE COMPQ
REQUE: ADD P,BHC+7
JUMPGE P,MSTKOV##
MOVEM 7,-6(P)
MOVEM 6,-5(P)
MOVEM 5,-4(P)
MOVEM 4,-3(P)
MOVEM 3,-2(P)
MOVEM 2,-1(P)
MOVEM 1,(P)
CALL UPDUT ;UPDATE FORK'S UTIL AVERAGE
CALL CDIST ;GET DISTANCE FROM TARGET
MOVE 5,1 ;SAVE IT IN AC5
MOVEI 6,RUNLST+COMPQ ;PREPARE TO CHASE DOWN COMPQ
REQUE1: HRRZ 6,0(6) ;NEXT FORKPT
CAIN 6,RUNLSB+COMPQ ;ARE WE AT THE END?
JRST REQUE2 ;YES
MOVEI 7,@FKPT6M ;NO
CALL CDIST ;GET THIS FORK'S DISTANCE FROM TARGET
CAMGE 5,1 ;SAVED DISTANCE .GE. THIS ONE?
JRST REQUE1 ;NO, DON'T WANT TO PUT HIM HERE
REQUE2: MOVE 3,6 ;GET FKPT TO INSERT AT
MOVE 7,-6(P) ;AND FORKX WE SAVED
CALL INSERT ;AND STUFF HIM ONTO COMPQ
MOVE 1,(P)
MOVE 2,-1(P)
MOVE 3,-2(P)
MOVE 4,-3(P)
MOVE 5,-4(P)
MOVE 6,-5(P)
SUB P,BHC+7
RET
> ;END PIE-SLICE SCHED CONDITIONAL
;PUT PROCESS ON THE WAITLIST
WTCONC: MOVE 1,TODCLK ;SAVE TIME FORK WAS PUT INTO WAITING
MOVEM 1,FKPGST(7)
HRRZ 1,FKWSP(7)
JUMPN 1,WTCON1 ;NO PAGES IN CORE?
HRRZ 1,FKCNO(7) ;YES, HAS CORE NUMBER?
JUMPE 1,WTCON1
MOVE 1,BITS(1) ;YES, DEASSIGN IT
IORM 1,FRECB
HLLZS FKCNO(7)
WTCON1: CALL APPWT ;APPEND TO WAITLIST
RET
;PERFORM WAIT LIST SCAN FOR UNBLOCKED PROCESSES
WTSCAN: STMR ;TIME WAIT LIST SCAN
SETZM ISKED
SETZM WAITFS ;RESET FULL-SCAN FLAG
MOVE 6,TODCLK ;GET TIME-NOW
SUB 6,WAITLS ;LESS TIME OF LAST FULL SCAN
CAIL 6,^D750 ;HAS IT BEEN 3/4 SEC OR MORE?
JRST [AOS WAITFS ;YES, INDICATE FULL SCAN DESIRED
ADDM 6,WAITLS ;WAITLS=TIME NOW
JRST .+1]
HLRZ 6,WTLSTB ;BEGIN BACKWARDS SCAN
WTSCN1: CAIN 6,WTLST ;DONE YET?
JRST WTSCN2 ;YES
MOVEI 7,@FKPT6M ;FORK INDEX
HLRZ 6,0(6) ;GET FKPT ADDRESS OF NEXT ENTRY
MOVE 1,TODCLK ;TIME NOW
SUB 1,FKPGST(7) ;LESS TIME THIS PROCESS ENTERED WAIT LIST
CAIL 1,^D3000 ;HAS HE BEEN THERE 3 SEC OR MORE?
JRST [SKIPN WAITFS ;FULL SCAN REQUIRED?
JRST WTSCN2 ;NO, TERMINATE THE SCAN HERE
JRST .+1] ;YES, CONTINUE
SKIPGE 1,FKINT(7) ;INTERRUPT REQUESTED?
JRST [ TLNE 1,(1B1) ;AND ACCEPTABLE?
JRST .+1 ;NOW DEFERRING...
MOVSI 1,PSIWTF
IORM 1,FKINT(7) ;REMEMBER WAS IN WAIT STATE
IFN PIESLC,<CALL INCNAP>
CALL PSIAWK ;AWAKEN WITHOUT NEWST
JRST WTSCN1] ;NEXT FORK
MOVE 2,FKSTAT(7) ;GET ACTIVATION TEST
HLRZ 1,2
JSP 4,0(2) ;CALL TEST ROUTINE
JRST WTSCN1 ;NO SKIP => STILL NOT RUNNABLE
CALL AWAKEN ;FORK IS ARISING
JRST WTSCN1 ;NEXT ONE
WTSCN2: ETMR SKWAIT ;FINISH TIMING WAITLIST SCAN
RET
;TRANSPARENT CALL TO AWAKEN.
PRWAKE:: PUSH P,1
PUSH P,2
PUSH P,3
PUSH P,4
CALL AWAKEN
POP P,4
POP P,3
POP P,2
POP P,1
RET
;ROUTINE TO MOVE FORK FROM WAIT LIST TO RUNLIST. FORKX IN 7.
;ALTERNATE ENTRY POINT 'PSIAWK' USED FOR SLEEPING FORK THAT'S BEEN
;PSI'D.
AWAKEN:
IFN PIESLC,<CALL INCNAP>
AOS WAKEUP ;NOW RUNNABLE, COUNT UNBLOCKS
CALL NEWST ;ESTABLISH NEW QUEUE STATUS
PSIAWK: MOVSI 1,HOLD ;PREPARE TO SEE IF THIS FORK
;SHOULD BE HELD IN BALSET AFTER NEXT
MOVE 2,TODCLK ;DISMISS
SUB 2,FKPGST(7) ;AC2=DURATION OF DISMISS
CAMLE 2,MAXBSH ;LESS THAN OR EQUAL TO MAXBSH?
ANDCAB 1,FKFLGS(7) ;NO, TURN OFF HOLD BIT
IORB 1,FKFLGS(7) ;YES, SET HOLD BIT. NOP IF WE FALL
;THRU FROM ANDCAB.
TLZN 1,WTLS ;MAKE SURE THIS GUY'S ON WAITLIST
BUG (HLT,<ATTEMPT TO WAKE-UP FORK NOT ON WAITLIST>)
TLO 1,RNLS ;TURN ON RUNLIST BIT
MOVEM 1,FKFLGS(7) ;AND STUFF THE WHOLE THING IN FKFLGS
;PLACE PROCESS ON PROPER QUEUE LIST
CALL CHGRLS ;PLACE ON RUNLIST
AOS NGPROC
SETOM OLDSUM ;INDICATE OLDSUM INVALID
;ASSIGN A CORE NUMBER
HRRZ 2,FKCNO(7)
JUMPN 2,[MOVE 2,BITS(2) ;ALREADY ASSIGNED
IORM 2,RLBITS ;MASK FOR GCCOR
RET]
MOVE 2,FRECB
JFFO 2,.+2 ;NONE AVAILABLE
MOVEI 3,^D35 ;YES, USE 35
MOVE 2,BITS(3)
ANDCAM 2,FRECB
IORM 2,RLBITS ;MASK FOR GCCOR
HRRM 3,FKCNO(7)
RET
IFN PIESLC,<
; ROUTINE TO INCREMENT NAPROC
INCNAP: MOVE 1,FKFLGS(7)
TLNE 1,NOCNT ;ARE WE TO INCREMENT NAPROC
RET ;NO
HLRZ 1,FKJOB(7) ;GET JOB NUMBER
MOVE 1,PIEGRP(1) ;GET PIE-SLICE GROUP INDEX
MOVSI 2,(1.0) ;UPDATE ACTIVE PROCESS COUNT
FADRM 2,NAPROC(1)
RET
> ;END PIE-SLICE SCHEDULER CONDITIONAL
;DISMISS JOB FOR RESCHEDULING OR ON QUANTUM OVERFLOW
DISMSJ: SKIPGE 7,FORKX
RET ;NOTHING IN FORKX
CALL SAVRT ;UPDATE QUEUE AND QUANTUM
SETZ 1,
JRST SCHP2
;UPDATE QUEUE NUMBER AND TIME USED VALUE
SAVRT: MOVE 1,IFTIM ;HAS HE GONE .5 SEC. WITHOUT FAULTING?
CAIGE 1,^D500
JRST SAVRT2 ;NO
MOVE 1,FKFLGS(7)
TLOE 1,NOFLT ;HAVE WE ALREADY DONE FKNR ADJUSTMENT?
JRST SAVRT2 ;YES, DON'T KEEP DOING IT
MOVEM 1,FKFLGS(7) ;REPLACE FKFLGS WITH NOFLT BIT ON
HRRZ 1,FKNR(7) ;MAKE FKNR=FKWSP+1
HRRZ 2,FKWSP(7)
SUBI 2,-1(1)
ADDM 2,SUMNRX
ADDM 2,FKNR(7)
SAVRT2: HLRZ 2,FKQ(7) ;CURRENT QUEUE NUMBER
HRLOI 1,377777 ;LARGEST NUMBER
EXCH 1,RJQNT ;GET REMAINING QUANTUM
MOVE 3,RUNT1 ;RUNTIME THIS RUNNING
ADDM 3,QSUM(2) ;ACCUMULATE STATISTIC
JUMPG 1,DISMJ1 ;NOT EXHAUSTED
SETOM OLDSUM ;INDICATE OLDSUM INVALID
IFE PIESLC,<
MOVE 1,JOBNO
SKIPE JOBCK0 ;GUARANTEE WORDS INIT'ED?
JRST SAVRT7 ;YES, GO CHECK CURRENT PERFORMANCE
MOVE 3,TODCLK ;INITIALIZE TIME QUARANTEE WORDS
MOVEM 3,JOBCK0
MOVE 3,JOBRT(1)
MOVEM 3,JOBCK1
SAVRT7: LDB 3,[POINT 7,JOBBIT,17] ;GET GUARANTEE PERCENTAGE
JUMPE 3,SAVRT8 ;NOT SPECIAL
LDB 4,[POINT 6,JOBBIT,23] ;DOES FORK HAVE TEMP MINQ?
JUMPN 4,SAVRT8 ;YES, IGNORE PCT. PRIORITY
MOVE 4,JOBRT(1) ;COMPUTE RUNTIME DURING TEST INTERVAL
SUB 4,JOBCK1
MOVE 1,TODCLK ;COMPUTE REAL TIME OF TEST INTERVAL
SUB 1,JOBCK0
IMULI 4,^D100 ;COMPUTE RUNTM*100%/PCT TO GET
IDIV 4,3 ;EXPECTED REAL TIME
SUB 4,1 ;EXCESS OR DEFICIT OF REAL TIME
MOVEM 4,23 ;FOR LIGHT WATCHERS
HRLM 2,23 ; ..
JUMPGE 4,SAVRT5 ;.G. 0 MEANS BETTER THAN GUARANTEE
MOVEI 2,0 ;KEEP ON QUEUE 0 TO GET EXCLUSIVE TIME
JRST SAVRT9
SAVRT8: LDB 3,[POINT 6,JOBBIT,35] ;GET MAX Q FOR THIS JOB
CAIL 2,0(3) ;IF REACHED MAX Q, AND
JUMPN 3,[LDB 2,[POINT 6,JOBBIT,29] ;PRIORITY WORD NOT 0,
MOVEM 7,23 ;FOR DISPLAY
JRST SAVRT9] ;SET NEW Q AS SPECIFIED
CAIGE 2,MAXQ ;NOW ON MAX Q?
AOJA 2,SAVRT9 ;NO, GO TO NEXT ONE
SAVRT9: LDB 3,[POINT 6,JOBBIT,23] ;TEMP MINQ??
JUMPE 3,.+3 ;NO
CAILE 2,0(3) ;IS NEW QUEUE HIGHER OR SAME?
MOVEI 2,0(3) ;NO, USE MINQ
MOVSM 2,FKQ(7)
CALL CHGRLS ;APPEND FORK TO APPROPRIATE RUN LIST
SKIPA 1,QBASE+1(2) ;NEXT QUEUE TIME
DISMJ1: ADD 1,QBASE(2) ;INCREASE BY BASE TIME
DISMJ2: HRRM 1,FKQ(7)
RET
SAVRT5: CAIGE 4,LOWQT ;IS HE AHEAD BY MORE THAN LOWQT?
JRST SAVRT8 ;NO
LDB 2,[POINT 6,JOBBIT,35] ;GET MAXQ FOR THIS FORK
SKIPE 2 ;NO MAXQ
CAIL 2,MAXQ
MOVEI 2,MAXQ
MOVSM 2,FKQ(7)
CALL CHGRLS
MOVE 1,QBASE+1 ;GIVE HIM HI-Q QUANTUM
JRST DISMJ2
;THIS ROUTINE ACCEPTS AN ARGUMENT IN AC1. IF THIS ARGUMENT IS
;NON-ZERO, THE CALLING FORK IS PLACED ON QUEUE 0. IN ANY CASE,
;THE ARGUMENT IS DEPOSITED IN JOBBIT IN THE SIX BIT BYTE ENDING AT
;BIT 23. THE SCHEDULER TREATS THIS BYTE AS A MINIMUM QUEUE IF NON-ZERO.
;THIS ROUTINE IS INTENDED FOR USE BY MONITOR ROUTINES WHICH ARE
;ABOUT TO USE A CRITICAL RESOURCE (SUCH AS LOCKING A DIRECTORY)
;AND WANT TO INSURE PROMPT COMPLETION.
STMINQ:: DPB 1,[POINT 6,JOBBIT,23] ;STASH THE ARG
JUMPE 1,R ;IF ZERO NOTHING MORE TO DO.
PUSH P,7 ;SAVE AC7
MOVE 7,FORKX ;GET INDEX OF THIS FORK
NOSKED
HRRZS FKQ(7) ;PUT HIM ON Q 0.
PUSH P,3
PUSH P,4
CALL CHGRLS ;PUT HIM ON Q0 RUNLIST
POP P,4
POP P,3
MOVE 1,QBASE+1 ;GET Q0 QUANTUM
HRRM 1,FKQ(7) ;STASH IN FKQ
MOVEM 1,RJQNT ;AND IN RJQNT
OKSKED
POP P,7 ;RESTORE 7
RET
> ;END NON-PIE-SLICE SCHEDULER CONDITIONAL
IFN PIESLC,<
MOVE 3,FKFLGS(7)
TLNE 3,HIQFK ;FORK NEED SPECIAL SCHEDULING?
JRST [TLNE 3,SPQFK ;SPECIAL QUEUE?
SKIPA 2,[SPECQ] ;YES
MOVEI 2,INTERQ ;NO
TLZE 3,PHIQFK ;FORGET THAT HE WAS ORIGINALLY ON INTERQ
MOVEM 3,FKFLGS(7)
CALL CHGQ ;SET UP FKQ
CALL CHGRLS
RET]
MOVEI 2,COMPQ ;HE GOES ON COMPQ
CALL CHGQ ;SET UP FKQ
CALL CHGRLS ;APPEND TO NEW RUNLST
RET
DISMJ1: HRRM 1,FKQ(7) ;SAVE TIME LEFT ON THIS QUEUE
RET
> ;END PIE-SLICE SCHEDULER CONDITIONAL
;CLOCK ROUTINES
;CALLED FROM APR INTERRUPT, 60 CY CLOCK INITIATES BREAK ON CH7
;FOR SERVICE
APCLK1: CONO APR,1000+APRCHN ;TURN OFF FLAG
AOS APCLKC ;FOR CH7 ROUTINE
ISB 7
SOSLE MSCNT ;THIRD TICK?
JEN @PIAPRX
MOVEM 1,MSCNT ;THIRD TICK (50 MS.), SYNC 1 MS. CLOCK
SKIPE 1,SYNCC ;COUNTED 50 MS.?
JRST [ ADDM 1,TODCLK ;NO, FINISH UP LAST TICKS
ADDM 1,JOBRTT
JRST .+2] ;AND LEAVE IT RUNNING
MSCKON ;TURN CLOCK BACK ON
MOVEI 1,^D50
MOVEM 1,SYNCC ;SET TO SYNC AFTER 50 TICKS
MOVEI 1,3 ;AND 3 TICKS OF 60 HZ CLOCK
EXCH 1,MSCNT
JEN @PIAPRX
;SCHEDULER CLOCK UPDATE
APCLK: SETZM APCLKC ;CLEAR REQUEST FLAG
MOVEM 1,CLKAC1 ;SAVE COUPLE AC'S
MOVEM 2,CLKAC2
MOVE 1,TODCLK ;CLOCK UPDATED BY 1MS INTERRUPT
SUBM 1,OLDTCK ;COMPUT NUMBER MS. SINCE LAST UPDATE
EXCH 1,OLDTCK ;SAVE 'NOW' IN OLDTCK
MOVN 1,1
MOVSI 2,-NPCLKS ;UPDATE PROCESS CLOCKS
APCLK3: ADDM 1,RJQNT(2) ;UPDATE (RJQNT IS FIRST OF TABLE)
SKIPG RJQNT(2) ;TIMED OUT?
AOS SKEDF3 ;YES, NOTIFY SCHED
AOBJN 2,APCLK3
MOVE 2,CLKAC2 ;RESTORE AC2
MOVE 1,CLKAC1
JRST APCLKX
;TIMER ROUTINES
; JSP 4,STIME ;STARTS TIMING
; .. ;PROGRAM
; JSP 4,ETIME ;ENDS TIMING, RETURNS TIME IN 1
; ADDM 1,CLOCK ;ADD TIME TO APPROPRIATE CLOCK
STIME: SETZ 1,
EXCH 1,JOBRTT ;GET AND RESET RUNTIME
PUSH P,1
JRST 0(4)
ETIME: POP P,1 ;OLD RUNTIME
EXCH 1,JOBRTT ;RESTORE OLD RUNTIME, GET RUNTIME OF
JRST 0(4) ;TIMED CODE AND RETURN IT
JSKP: JRST 1(4)
JRET: JRST 0(4)
;FLUSH ALL JOBS FROM THE BALANCE SET, THEN CALL GCALC
FSHBS: SKIPL SSKED ;ARE WE NOSKED?
JRST SKPR1 ;YES, DONT TRY TO FLUSH BALSET
MOVSI 10,-<MAXQ+1>
FSHBS1: MOVEI 6,RUNLST(10)
FSHBS2: HRRZ 6,0(6)
CAIN 6,RUNLSB(10) ;END OF LIST?
JRST FSHNL ;YES, GET NEXT ONE
MOVEI 7,@FKPT6M ;GET FORK INDEX
FSHBSD: MOVE 2,FKFLGS(7) ;GET FLAG WORD
TLNN 2,BLST ;IS PROCESS IN BALANCE SET?
JRST FSHBS2 ;NO
TLNE 2,WTFK ;IS IT WAITING?
JRST FSHBSW ;YES
FSHBSN: CALL REMBSF
JRST FSHBS1
FSHBSW: HRRZ 5,FKPGST(7) ;GET WAIT TEST
CAIE 5,DISMT ;EDISMS?
JRST FSHBSS ;NO
CALL REMBSJ
JRST FSHBS1
FSHBSS: HLRZ 1,FKPGST(7)
JSP 4,0(5) ;CALL WAIT ROUTINE
JRST FSHBSD ;STILL WAITING
CAIN 5,SWPINT ;WAS HE ENTERING BAL SET?
CALL UDNRP ;YES, UPDATE NRPMIN
JRST FSHBSN
;NEXT LIST
FSHNL: AOBJN 10,FSHBS1
JRST GCALC
;QUEUE PARAMETER TABLES
IFE PIESLC,<
RADIX 10
QBASE: 0
100
500
2500
4500
14500
LOWQT=10000 ;TIME QUANTUM OF LOWEST QUEUE
RADIX 8
> ;END NON-PIE-SLICE SCHEDULER CONDITIONAL
IFN PIESLC,<
SPECQ==0 ;QUEUE FOR SPECIAL SCHEDULING
INTERQ==SPECQ+1 ;INTERACTIVE QUEUE
COMPQ==INTERQ+1 ;COMPUTATION QUEUE
QUANTS: ^D100
^D100
777777 ;ESSENTIALLY INFINITE QUANTUM FOR COMPQ
; ROUTINE TO SET UP FKQ WITH PROPER QUANTUM AND QUEUE NUMBER
;QUEUE IN AC2
CHGQ: HRL 2,QUANTS(2) ;GET QUANTUM
MOVSM 2,FKQ(7) ;STASH IT AWAY
RET
> ;END PIE-SLICE SCHEDULER CONDITIONAL
IFE PIESLC,<
;HEURISTIC FOR ADJUSTING QUEUE LEVEL AFTER I/O WAIT
;THIS ROUTINE IS THE PRINCIPLE CONTROL OVER THE EXTENT TO WHICH
;'INTERACTIVE' OR 'COMPUTE-BOUND' JOBS ARE FAVORED. IT GIVES
;PRIORITY 'CREDIT' TO A FORK AS A RESULT OF WAITING. THE MORE
;CREDIT GIVEN FOR A CERTAIN LENGTH WAIT (OR THE SHORTER THE WAIT
;REQUIRED TO BECOME HIGH-Q), THE MORE THE SYSTEM WILL FAVOR
;INTERACTIVE FORKS, AND THE GREATER THE CHANCE THAT FREQUENT OR
;WELL-TIMED INTERACTIONS WILL GIVE A PROCESS AN EXCESSIVELY LARGE
;SHARE OF COMPUTE TIME. IT HAS BEEN DEMONSTRATED HOWEVER, THAT
;A COMPLETELY 'FAIR' ALGORITHM HERE, I.E. ONE WHICH PREVENTS AN
;INTERACTIVE FORK FROM GETTING ANY GREATER SHARE OF THE MACHINE
;THAN A COMPUTE-BOUND FORK, IS HIGHLY UNSATISFACTORY TO INTERACTIVE
;USERS UNDER MEDIUM AND HEAVY LOADS (AND ALL USERS ARE INTERACTIVE
;SOMETIMES), AND RESULTS IN EXPONENTIALLY INCREASING LEVELS OF
;FRUSTRATION, CURSING AND BEATING OF TERMINALS, ETC. THEREFORE
;THIS ROUTINE IS GENUINELY A HEURISTIC, MODIFIED AS A RESULT OF
;PRESSURES BROUGHT TO BEAR ON SYSTEM PROGRAMMERS.
;THE FOLLOWING DESCRIBES THE CURRENT PRACTICE:
; 1. TTY INPUT WAITS OF .GE. 1 SEC GIVE HIGH-Q. GREATLY REDUCES
; USER FRUSTRATION LEVEL.
; 2. WAITS BY FORKS ON QUEUE 0 RESULT IN NO CHANGE TO Q VALUE
; 3. FORKS ON QUEUES 1 TO MAXQ-1 WILL BE HIGH-Q IF WAITING TIME IS
; LONGER THAN LAST RUNTIME AS IMPLIED BY Q LEVEL. THIS LIMITS
; 'WELL-TIMED' INTERACTIONS TO ABOUT 1/2 THE CPU.
; 4. FORKS ON MAXQ WILL BE HIGH-Q IF WAITING TIME IS LONGER THAN
; THE MAXQ QUANTUM, AND WILL BE MOVED UP TO MAXQ-1 IF WAITING
; TIME IS LONGER THAN SOME 'MINIMAL' TIME (500 MS)
;POSSIBLE ADJUSTMENTS INCLUDE:
; 1. INCLUDE NJOB WEIGHTING BY INCLUDING IDIV AT NEWST+3
; 2. REDUCE CPU LIMIT IN 3 ABOVE BY INCLUDING IDIVI 1,M AT NEWST2
; MAX CPU USE WILL THEN BE 1-M/(M+1), E.G. 1/3 FOR M=2
;COMPUTE NEW Q VALUE
NEWST: MOVE 1,TODCLK ;CALCULATE ACTUAL WAITING TIME
SUB 1,FKPGST(7)
; IDIV 1,IRJAV ;DIVIDE BY AV NUMBER RUNNABLE FORKS
CAIGE 1,^D1000 ;ABOVE MIN WAIT TIME?
JRST NEWST2 ;NO FOLLOW REGULAR ALGORITHM
HRRZ 2,FKSTAT(7)
CAIN 2,TCITST ;TTY INPUT?
JRST NEWST1 ;YES, BE MORE GENEROUS
NEWST2: HLRZ 2,FKQ(7) ;CURRENT Q
JUMPE 2,R ;NO CHANGE IF HIGHEST
CAIGE 2,MAXQ ;LOW Q?
JRST NEWST4 ;NO, DO Q'S 1 TO MAXQ-1 ALGORITHM
CAIL 1,LOWQT ;LOW Q - WAITED FULL QUANTUM?
JRST PSSKD1 ;YES, CAN BE HI-Q
CAIGE 1,^D500 ;.GE. MINIMAL WAIT?
RET ;NO
MOVEI 1,MAXQ-1 ;YES, REQUE ON MAXQ-1
JRST NEWST3
NEWST4: HRRZ 3,FKQ(7) ;COMPUTE TIME USED ON CURRENT QUEUE
MOVN 3,3
ADD 3,QBASE+1(2)
CAMGE 1,3 ;WAIT TIME LONGER THAN THAT?
JRST [ ADDM 1,FKQ(7) ;NO, GIVE CREDIT FOR WAIT, LEAVE
RET] ;FORK ON SAME QUEUE
ADD 3,QBASE(2) ;ADD TIME USED ON EARLIER QUEUES
CAML 1,3 ;WAIT TIME LONGER THAN THAT?
JRST PSSKD1 ;YES, HI-QUEUE THE FORK
MOVEI 1,-1(2) ;NO, BUMP FORK UP ONE LEVEL
JRST NEWST3
PSSKD1: ;REINIT ON QUEUE 0 AND CLEAR WS
NEWST1: MOVEI 2,6 ;TTYIN COMPLETED -- IF LONG WAIT,
HRRZ 1,FKWSP(7)
CAIGE 2,0(1) ;CHOOSE MIN(6,SIZE)
MOVEI 2,0(1)
HRRM 2,FKNR(7)
SETZ 1, ;INIT ON QUEUE 0
NEWST3: MOVE 2,QBASE+1(1)
HRLI 2,0(1) ;CONSTRUCT NEW Q WORD
MOVEM 2,FKQ(7)
RET
> ;END NON-PIE-SLICE SCHEDULER CONDITIONAL
IFN PIESLC,<
;ROUTINE CALLED BY PIE-SLICE SCHEDULER FOR DETERMINING QUEUE LEVEL
;AFTER A PERIOD DURING WHICH A PROCESS HAS BLOCKED.
NEWST: MOVEI 2,INTERQ
MOVE 3,FKFLGS(7)
TLNE 3,HIQFK ;SPECIAL SCHEDULING?
JRST [TLNE 3,SPQFK ;SPECIAL QUEUE
MOVEI 2,SPECQ
JRST NEWST2]
HRRZ 3,FKSTAT(7)
CAIE 3,TCITST ;WAS WAITING FOR TTY INPUT?
CAIN 3,TCOTST## ;OR OUTPUT?
JRST NEWST2 ;YES, HI-Q
MOVEI 2,COMPQ
NEWST2: CALL CHGQ ;GET QUANTUM AND UPDATE FKQ
RET
;ROUTINE TO MAINTAIN EXPONENTIAL AVERAGE OF PROCESS UTILIZATION
UPDUT: MOVE 1,FKUTIL(7) ;GET CURRENT VALUE
MOVE 2,TODCLK
SUB 2,FKUDT(7) ;TIME SINCE LAST UPDATE
CAIG 2,<1B<35-<TCEXP-4>>-1> ;ENOUGH TIME ELAPSED TO UPDATE?
RET ;NO, JUST RETURN
ADDM 2,FKUDT(7) ;TODCLK TO FKUDT
CONO APR,APRCHN+550 ;CLEAR AND DISABLE OV AND FOV
SKIPE 4,FKPRT(7) ;GET INCREMENTAL RUNTIME
JRST UPDUT3 ;NON-ZERO
MOVE 3,SOLD
MOVEM 3,FKSOLD(7)
UPDUT2: CALL AVERAG ;COMPUTE AVERAGE
MOVEM 1,FKUTIL(7)
RET
UPDUT3: SETZM FKPRT(7)
FSC 4,233 ;CONVERT TO FLOATING POINT
MOVE 3,SOLD
SUBM 3,FKSOLD(7)
EXCH 3,FKSOLD(7)
FSC 3,233
FDVR 4,3 ;COMPUTE RECENT UTILIZATION
JFCL 11,UPDUTE ;ERROR IF OVERFLOW
JRST UPDUT2
UPDUTE: BUG (CHK,<OVERFLOW COMPUTING PROCESSOR UTILIZATION>)
SETZ 4,
JRST UPDUT2
;ROUTINE TO COMPUTE EXPONENTIAL AVERAGE
;ACCEPTS IN AC1: A(I) FLOATING POINT FORMAT
; AC2: T TIME SINCE LAST UPDATE IN MILLISECS (FIXED PT)
; AC4: K (THE SAMPLE VALUE) IN FLOATING POINT FORMAT
;RETURNS +1 ALWAYS WITH A(I+1)=((A(I)-K)*E^-(T/C))+K IN AC1
; WHERE C=TC*LOG E
; 2
; SMASHES AC3
TCEXP==^D12 ;GIVES ABOUT 4 SEC TIME CONSTANT
TC==1B<35-TCEXP> ; =2^TCEXP
AVERAG: FSBR 1,4 ;AC1=A(I)-K
CAILE 2,<TC-1> ;INTEGER PART NON-ZERO?
JRST [ASHC 2,-TCEXP ;NO, GET INT(T/TC)
MOVNS 2 ;NEGATE
FSC 1,(2) ;AC1=(A(I)-K)*2^-INT(T/TC)
JFCL 11,AVEROV ;FSC CAN UNDERFLOW
ASHC 2,4 ;GET BACK FRACTIONAL PART
ANDI 2,17 ;TAKE OUT ALL OTHER BITS
JRST AVERG2]
ASH 2,-<TCEXP-4> ;GET TOP 4 BITS OF FP
AVERG2: FMPR 1,EXPTAB(2) ;AC1=(A(I)-K)*2^-(T/TC)
JFCL 11,AVEROV ;FMPR COULD HAVE UNDERFLOWED
FADR 1,4 ;PLUS K GIVES A(I+1)
RET
AVEROV: MOVE 1,4 ;NEW VALUE = K
RET
EXPTAB: 1.0 ;2^0
.957603281 ;2^-(1/16)
.917004043 ;2^-(2/16)
.87812608 ;...
.840896415
.805245166
.771105413
.738413073
.707106781
.677127773
.648419777
.620928906
.594603558
.569394317
.545253866
.522136891 ;2^-(15/16)
> ;END PIE-SLICE SCHEDULER CONDITIONAL
;NON-PIESLICE SYSTEM: IF NOT ON ONE OF TOP TWO QUEUES, MOVE PROCESS UP A QUEUE
;PIESLICE SYSTEM: HI-QUEUE THE FORK
PSSKD2: PUSH P,3
PUSH P,4
IFE PIESLC,<
HLRZ 2,FKQ(7) ;GET PRESENT QUEUE
CAIG 2,1 ;.GT. 1?
RET ;NO, DO NOTHING
SUBI 2,1 ;BUMP HIM UP A QUEUE
HRLM 2,FKQ(7)
MOVE 2,QBASE+1(2)
HRRM 2,FKQ(7) ;SET UP HIS QUANTUM
> ;END NON-PIE-SLICE CONDITIONAL
IFN PIESLC,<
MOVSI 2,SPQFK ;SPECIAL QUEUE?
TDNE 2,FKFLGS(7)
SKIPA 2,[SPECQ] ;YES
MOVEI 2,INTERQ ;NO
PSSKD4: CALL CHGQ
> ;END PIE-SLICE CONDITIONAL
NOSKD1
MOVE 2,FKFLGS(7)
TLNN 2,RNLS ;IS FORK NOW ACTIVE?
JRST PSSKD3 ;NO
CALL CHGRLS ;APPEND TO NEW LIST
SKIPN INSKED ;IN SCHEDULER?
CAME 7,FORKX ;NO, PROCESS LEVEL
JRST PSSKD3 ;IN SCHED, OR OBJECT FORK NOT THIS ONE
HRRZ 4,FKQ(7) ;PUT NEW QUANTUM
MOVEM 4,RJQNT ;IN RJQNT
PSSKD3: OKSKD1
POP P,4
POP P,3
RET
IFN PIESLC,<
;ROUTINE TO REPLACE FORK ON COMPQ
SETCQ: PUSH P,3
PUSH P,4
MOVEI 2,COMPQ
JRST PSSKD4
;TRANSPARENT ROUTINE TO PLACE CURRENT FORK ON ONE OF THE HI QUEUES
SETHIQ:: AOSE HIQCNT ;ALREADY HI-QUEUED?
RET ;YES
PUSH P,1
PUSH P,2
PUSH P,7
MOVE 7,FORKX
HLRZ 1,FKQ(7) ;IS HE NOW ON INTERQ?
CAIN 1,INTERQ
JRST [MOVSI 1,PHIQFK ;YES, MAKE NOTE OF THAT
IORM 1,FKFLGS(7)
JRST SETHI2]
MOVSI 2,HIQFK
IORM 2,FKFLGS(7) ;SET IN FKFLGS
CALL PSSKD2
SETHI2: POP P,7
POP P,2
POP P,1
RET
;ROUTINE TO RESUME NORMAL SCHEDULING IF THE OUTERMOST
;SUCH REQUEST
RELHIQ:: SKIPGE HIQCNT
BUG (HLT,<ATTEMPT TO OVERDECREMENT HIQCNT>)
SOSL HIQCNT ;OUTERMOST REQUEST?
RET
PUSH P,1
PUSH P,2
PUSH P,7
MOVE 7,FORKX
RELHI2: MOVSI 1,SPQFK+HIQFK ;TURN OFF SPECIAL SCHED BITS
RELHI3: ANDCAB 1,FKFLGS(7)
TLZN 1,PHIQFK ;WAS HE PREVIOUSLY ON INTERQ?
JRST [CALL SETCQ ;REPLACE ON COMPQ
JRST .+2]
MOVEM 1,FKFLGS(7) ;YES, CLEAR THE BIT BUT NO RESCHED
POP P,7
POP P,2
POP P,1
RET
; ROUTINE TO OBTAIN TOP PRIORITY SCHEDULING FOR RUNNING FORK
SETSPQ:: AOSE SPQCNT ;FIRST SUCH REQUEST?
RET ;NO, JUST NOTE THAT IT HAPPENED
PUSH P,1
PUSH P,2
PUSH P,7
MOVE 7,FORKX
MOVSI 1,SPQFK+HIQFK
IORM 1,FKFLGS(7)
AOSN HIQCNT
JRST [HLRZ 1,FKQ(7) ;IF NOT INTERQ AS A RESULT OF PRIOR
;CALL TO SETHIQ, THEN NOTE IF INTERQ NOW
CAIN 1,INTERQ
JRST [MOVSI 1,PHIQFK
IORM 1,FKFLGS(7)
JRST .+1]
JRST .+1]
CALL PSSKD2
POP P,7
POP P,2
POP P,1
RET
;ROUTINE TO TURN OFF TOP PRIORITY SCHEDULING IF OUTERMOST
;SUCH REQUEST
RELSPQ:: SKIPGE SPQCNT
BUG (HLT,<ATTEMPT TO OVERDECREMENT SPQCNT>)
SOSL SPQCNT
RET ;NESTED REQUEST
PUSH P,1
PUSH P,2
PUSH P,7
MOVE 7,FORKX
SKIPGE HIQCNT
BUG (HLT,<ATTEMPT TO OVERDECREMENT HIQCNT>)
SOSGE HIQCNT
JRST RELHI2
MOVSI 1,SPQFK
JRST RELHI3
>; END PIE-SLICE SCHEDULER CONDITIONAL
;HALT JOB
HLTJB: HRRE 6,CTRLTT
JUMPL 6,HLTJB1 ;IF JOB DETACHED
SETZM TTPSI(6) ;CLEAR TTY WORDS
SETOM TTFORK(6)
HLTJB1: MOVE 5,JOBNO
SETZM CTRLTT ;CLEAR CONTROL TTY WORDS
HRRZS JOBPT(5)
MOVEI 1,400000
SETO 2,
DIC ;DEACTIVATE ALL INTERRUPTS
MOVNI 1,1
CLOSF
JFCL
RELD ;RELEASE ALL DEVICES
JFCL
MOVEI 1,-4
KFORK ;KILL ALL INFERIOR FORKS
IFDEF SIGIPC,< CALL RMJBRF## ;REMOVE REFERENCES TO THIS JOB IN SIGS>
MOVE 7,FORKX ;THIS FORK.
HLLZ 2,FKPGS(7) ;FORKS PT
MOVSI 6,-1000
CALL CLRM0 ;CLEAR UPT
MOVE 6,[XWD PJMPG-PPMPG,JOBMAP-JSB]
HRLZ 2,FKJOB(7) ;GET SPTN OF JSB
CALL CLRM0 ;CLEAR JDV AND PAGES IN JOB AREA
SKIPGE JOBPMF
JRST HLTJB4 ;NO PMF
SETO 1,
HRLZ 2,JOBPMF
MOVSI 6,-1000
HRRI 2,0(6)
PMAP ;DELETE CONTENTS OF PMF
AOBJN 6,.-2
HRRZ 1,JOBPMF
SETOM JOBPMF ;ENABLE CLOSE OF PMF
CLOSF ;CLOSE PMF - NO JSYS' AFTER HERE
JFCL
HLTJB4: MOVE 1,ACBAS
CAIGE 1,<EUACB>B39 ;AC BLOCKS IN PSB?
SETZM PSB+UACPG ;YES, CLEAR MAP ENTRY FOR UACPG
HLTJB5: MOVE 6,[XWD CPTPG+1-UPTPG,CPTPG+1]
HRLZ 2,FKPGS(7)
CALL CLRM0 ;CLEAR PAGES IN PP AREA
HRRZ 1,FKJOB(7) ;JSB
CALL WTSPT ;WAIT FOR IT TO BE UNSHARED
CALL WTFPGS ;WAIT FOR PSB AND UPT TO BE IN NO MAPS
ENTSKD ;ENTER SCHED
MOVE 1,JOBNO ;RELEASE JOB NUMBER
SETZM JOBDIR(1) ;CLEAR DIRECTORY NUMBER
SETOM JOBRT(1) ;INDICATE JOB NUMBER NOT IN USE
ADDI 1,JOBPT
EXCH 1,FREJOB ;PUT SLOT ON FREE LIST
MOVEM 1,@FREJOB
JRST HLTFK2 ;FLUSH THIS LAST FORK
HLTFK1: ENTSKD ;ENTER SCHEDULER
HLTFK2: CALL REMBSJ ;REMOVE FORK FROM BALANCE SET
HLTFK3: ;THIS LABEL MUST IMMEDIATELY FOLLOW CALL TO
;REMBSJ. DO NOT SEPARATE!!!
MOVEI 1,(1B0)
HRLM 1,FKPT(7) ;NOTE FORK NOT IN BALSET
HRRZ 1,FKJOB(7) ;JSB
LDB 2,[POINT 14,SPT(1),13] ;SHARE COUNT NOW 1?
CAIE 2,1 ;LAST USE OF JSB?
JRST [ MOVSI 2,-1B31 ;NO, REDUCE SHARE COUNT
ADDM 2,SPT(1)
JRST .+2]
CALL DESPT ;YES, DELETE IT (LOGOUT CASE)
HLRZ 1,FKPGS(7) ;UPT
CALL DESPT ;DELETE IT
HRRZ 1,FKPGS(7)
CALL DESPT ;DEASSIGN PSB
SETOM FORKX
PUSH P,7
ADDI 7,FKPT
EXCH 7,FREFK ;PUT FORK NUMBER ON FREE LIST
TLO 7,400000
MOVEM 7,@FREFK
CALL GCCOR ;CLEAN UP PAGES
POP P,7
HRRZ 1,FKWSP(7) ;MAKE SURE FORK CLEANED UP
HRRZ 2,FKCNO(7)
CAIN 1,0
CAIE 2,0
BUG(CHK,<FORK NOT PROPERLY DELETED>)
JRST SCHED0 ;NOW THERE IS NOTHING LEFT OF JOB...
CLRM0: SETZ 1,
CLRM1: HRRI 2,0(6) ;PUT PAGE NUMBER WITH PTN
CALL SETPT
AOBJN 6,CLRM1
RET
;WAIT FOR PSB AND UPT TO HAVE SHARE COUNT OF 1
WTFPGS: HRRZ 1,FKPGS(7) ;PSB
CALL WTSPT
HLRZ 1,FKPGS(7) ;UPT
WTSPT: PUSH P,4
WTSPT2: JSP 4,WTSPTT ;TEST PAGE NOW
JRST WTSPT1 ;MUST WAIT
POP P,4 ;NOW OK
RET
WTSPT1: MOVSI 1,0(1)
HRRI 1,WTSPTT
JSYS EDISMS
HLRZ 1,1
JRST WTSPT2
WTSPTT: LDB 2,[POINT 14,SPT(1),13] ;GET SHARE COUNT
CAIE 2,1
JRST 0(4)
JRST 1(4)
;PRELIMINARY FORK INIT
;HERE ON PROCESS LEVEL FROM PIRQ IF NEWFKF IS SET IN FKINT
; IF NEW JOB, TTY # IS IN PIMSK AND RH 7
FKSET: MOVE 1,PSB+PSBPG ;SETUP USER MAP WORD
MOVEM 1,PSB+UACPG ;SAME AS PSB UNTIL OVERFLOW
MOVE 1,[IOWD NUPDL,UPDL]
MOVEM 1,UPP ;MON ROUTINES PDL
MOVE 1,[IOWD 1000,PSIPGA]
MOVEM 1,PSIPT ;PSI STORAGE STACK
MOVEI 1,<UACB>B39 ;SETUP AC BASE
MOVEM 1,ACBAS
MOVEM 1,ACBAS1
SETACB 1
MOVE 1,ICAPT
MOVEM 1,CAPT
MOVE 1,INTDF0 ;INTERRUPT SWITCHES
MOVEM 1,INTDFF
MOVE 1,MJRST0
MOVEM 1,MJRSTF
SETZM NSKED
MOVE 1,RSKEDN
MOVEM 1,RSKED
MOVSI 1,(<MOVEM 1,0>)
MOVEM 1,PATU40 ;SETUP INSTRUCTION PART FOR COMPAT
MOVEM 1,PATUPC ;ENTRY PROCEDURE
IFN PIESLC,<
SETOM HIQCNT ;INIT SPEC SCHEDULING REQUEST COUNTS
SETOM SPQCNT
> ;END PIE-SLICE CONDITIONAL
SETOM SLOWF
SETOM INTDF
SETOM TRAPC
SETOM FKTAB
MOVEI 1,FKTAB+1
HRLI 1,-1(1)
BLT 1,FKTAB+NLFKS/2-1
SETOM JTLCK ;INIT JSYS TRAP LOCK
MOVE 2,[XWD 77,7777] ;INIT JTMNW TO NO CHANNEL, NO MONITOR
MOVEM 2,JTMNW
MOVE 1,JDSPTP
MOVE 2,FORKX
HRL 2,FKPGS(2)
HRRI 2,JDVPG
MOVSI 3,RWX ;SET JSYS DISPATCH TO STANDARD
CALL SETPT ;NON-MONITORED DISPATCH
MOVE 2,FORKX
HLRZ 1,FKPGS(2) ;GET SPTN OF PAGE TABLE
LSH 1,^D9 ;CONSTRUCT SHARE POINTER
TLO 1,RWXB-XCTB+SHRBIT
MOVEM 1,PSB+UPTPG
MOVE 6,FORKX
TLNE 7,NEWJBF ;NEW JOB TOO?
JRST FKSET1 ;YES
HRRZ 1,FKJOB(6) ;GET JSB
MOVSI 2,1B31
ADDM 2,SPT(1) ;BUMP SHARE COUNT
LSH 1,^D9
TLO 1,RWXB+SHRBIT ;CONSTRUCT SHARE POINTER
MOVEM 1,PSB+JSBPG
MOVEI 1,FKSET2
FKSET3: MOVEM 1,PIPC
SETZM PIOLDS
MOVE 1,PSB+JSBPG ;GET JSB POINTER
TLC 1,SHRBIT+INDBIT ;MAKE INTO INDIRECT POINTER
ADDI 1,JOBMAP-JSB+JSBPG-PJMPG+1 ;FIRST WORD OF JOB PT
MOVEI 2,JSBPG+1 ;STARTING AFTER JSB,
MOVEM 1,PSB(2) ;FILL MON MAP WITH IND POINTERS
ADDI 2,1
CAIGE 2,PPMPG
AOJA 1,.-3
JRST PIRQR ;DEBREAK - RUN IN NORMAL MODE
;INIT NEW JOB
FKSET1: HRRE 2,7 ;GET CONTROLLING TTY #, IF ANY
HLRZ 1,FKJOB(6) ;GET JOB NUMBER STORED BY JOBSRT
MOVEM 1,JOBNO
MOVSM 2,JOBPT(1) ;TTY ASSIGNED TO JOB, OR -1, TO LH JOBPT
SKIPL 2 ;UNLESS DETACHED, SET TTFORK TOO
HRLM 1,TTFORK(2) ;JOB CONTROLLED BY TTY
MOVE 2,FORKX
HRRM 2,JOBPT(1) ;TOP FORK OF JOB
SETZM JOBRT(1) ;JOB RUNTIME
HRRZ 1,FKJOB(6) ;JSB
LSH 1,^D9
TLO 1,RWXB+SHRBIT
MOVEM 1,PSB+JSBPG ;SETUP JSB
MOVE 1,JOBNO
HLRE 2,JOBPT(1) ;CONTROLLING TTY OR -1
MOVEM 2,CTRLTT ;IN JSB
MOVEI 1,EXEC0
HRRZS FKTAB ;FORK 400000 IN TOP FK IS JOB FK 0
JRST FKSET3
FKSET2: SETZ 0, ;START WITH 0 AC'S
MOVEI 17,1
BLT 17,16
SETZ 17,
ENTSKD
MOVSI 1,UMODF
MOVEM 1,PPC
MOVEI 1,HALTT
JRST DISMSE
INTDF0: SOS INTDF ;NORMAL CONTENTS OF INTDFF
MJRST0: JRSTF @FPC ;NORMAL CONTENTS OF MJRSTF
CHNSON: EXP 1B9+1B11+1B15+1B16+1B17+1B18+1B20 ;ALWAYS ON PSI CHANS
IFDEF RTISW,<
;SOLO -- CAPTURE THE WHOLE MACHINE
.SOLO: JSYS MENTR
MOVE 1,JOBNO
MOVEM 1,SCDRN1 ;TELL SCHED TO RUN THIS JOB ONLY
MOVEI 1,FSHBAL
MOVEM 1,FSHBAL ;TELL SCHED TO FLUSH BALANCE SET
CALL DISE ;WAIT TIL DONE
JRST MRETN
;TUTTI -- ALLOW ALL JOBS TO RUN
.TUTTI: JSYS MENTR
MOVSI 1,(1B1)
MOVEM 1,CHKTIM
SETOM SCDRN1
JRST MRETN
>;END IFDEF RTISW
;PSEUDO-INTERRUPT SYSTEM
;BITS IN LH FKINT, LH PIMSK
;B18=INT REQUEST
;B19=INTERRUPT HANDLER RUNNING AT PROCESS LEVEL
NEWFKF==:1B20 ;INITIATE NEW FORK - PI FLAG
NEWJBF==1B21 ;INITIATE NEW JOB - PI FLAG
PSIIF==1B22 ;CHANNEL INTERRUPT REQUESTED IN FKINTB
PSIT1F==1B23 ;TERMINAL CODE INTERRUPT, PHASE 1
PSIT2F==1B24 ;TERMINAL CODE INTERRUPT, PHASE 2
SUSFKR==1B25 ;SUSPEND FORK REQUEST
PSIWTF==1B26 ;JOB WAS IN WAIT STATUS
PSILOB==1B27 ;LOGOUT JOB REQUEST
FRZB1==1B28 ;DIRECT FREEZE HAS BEEN DONE
FRZB2==1B29 ;INDIRECT FREEZE HAS BEEN DONE
PSIJTR==1B30 ;JSYS TRAP REQUEST
JTFRZB==1B31 ;JSYS TRAP FREEZE
FRZBB==FRZB1+FRZB2 ;BOTH BITS FOR EXTERNAL REFS
FRZBAL==JTFRZB+FRZBB ;FOR EXTERNAL REFS
;SCHEDULER CAUSES JOB TO BE STARTED HERE ON PI REQUEST
;SAVED PC IN PIPC
;PIMSK CONTAINS INTERRUPT REQUEST WORD
PIRQ: MOVEM P,PIAC+17
MOVEI P,PIAC ;SAVE USER AC'S
BLT P,PIAC+16
MOVE P,PIPDL ;SET UP LOCAL STACK
PUSH P,PGURET ;SAVE UNTRAP RETURN ON LOCAL STACK
MOVE 7,PIMSK ;INTERRUPT REQUEST WORD
MOVE 6,FORKX
SETZ 2,
TLNE 7,PSIWTF ;WAS JOB IN WAIT STATUS?
MOVE 2,FKSTAT(6) ;YES, GET OLD STATUS
MOVEM 2,PIOLDS ;SAVE OLD STATUS, OR 0 IF WAS RUNNING
TLNE 7,NEWFKF ;START NEW FORK?
JRST FKSET ;YES
TLNE 7,PSIT1F
JRST PSIT1 ;TERMINAL, PHASE 1
TLNE 7,PSIT2F
JRST PSIT2 ;TERMINAL, PHASE 2
PSITR1: TLNE 7,PSIIF+SUSFKR+PSILOB+PSIJTR
JRST PSII ;CHANNEL INTERRUPT SPEC. BY FKINTB
PIRQR: JSYS UNPIR ;LEAVE PI STATE
PSIDF1: SKIPN 1,PIOLDS ;WAS RUNNING BEFORE PSI?
JRST SCHED3
JRST DISMSE ;NO, REPLACE ON WAIT LIST
;NOTE THAT THIS ROUTINE IS NOW CALLED VIA
;JSYS UNPIR. THIS WILL NOT WORK ON THE KI10.
UNPIR: XWD ENSKR,.+1
SKIPE INSKED
BUG(HLT,<CALL TO SCHEDULER WHEN ALREADY IN SCHEDULER>)
AOS INSKED ;ENTER SCHEDULER
MOVE P,PI7P ;SCHEDULER STACK
PUSH P,ENSKR
SETZM ENSKR
MOVE 1,PIPDB ;RESTORE PGURET
MOVEM 1,PGURET
MOVE 1,[XWD PIAC,PAC]
BLT 1,PAC+17 ;PUT AC'S BACK
IFN KIFLG,<
JSP 7,KISSAV> ;SAVE STUFF FOR KI-10 HARDWARE
MOVE 1,PIPC
MOVEM 1,PPC
MOVSI 1,200000
MOVE 7,FORKX
ANDCAM 1,FKINT(7)
JRST UCLOCK ;CHARGE PROCESS TIME AND RETURN
PIPDL: IOWD NPIPDL,PIPDB ;INTERRUPT ROUTINES LOCAL PDL
;REQUEST INTERRUPT
;AC1 CONTAINS INTERRUPT NUMBER
;AC2 CONTAINS FORK INDEX
PSIRQ0: MOVE 2,FORKX ;REQUEST INTERRUPT IN CURRENT FORK
PSIRQF: NOSKED ;REQUEST INTERRUPT, FORK IN AC2
CALL PSIRQ
OKSKED
RET
;ENTERED FROM SCHEDULER REQUEST PROCESSOR
PSIRQ: MOVE 1,BITS(1)
PSIRQB: IORM 1,FKINTB(2) ;SET BIT IN INTERRUPT WAITING BUFFER
PSITQ: MOVSI 1,400000+PSIIF ;REGULAR INTERRUPT FLAG
IORM 1,FKINT(2)
CAMN 2,FORKX ;FOR THIS FORK?
RET ;YES
PSIR4: PUSH P,7
MOVEI 7,0(2)
NOSKD1
MOVE 1,FKFLGS(7) ;NO, GET STATUS OF FORK
TLNN 1,WTLS ;ON WAITLIST?
JRST PSIR6 ;NO
MOVSI 1,(1B1) ;INTERRUPT IN PROGRESS?
TDNE 1,FKINT(7)
JRST PSIR6 ;YES, LET WTSCAN PICK HIM UP
MOVSI 1,PSIWTF ;REMEMBER IT WAS WAITING
IORM 1,FKINT(7)
IFN PIESLC,<CALL INCNAP>
PUSH P,3
PUSH P,4
CALL PSIAWK ;AWAKEN HIM
POP P,4
POP P,3
;SET NEW SCHED STATUS FOR PSI'D FORK
PSIR6: OKSKD1
CALL PSSKD2 ;SET SHORT QUANTUM, HIGH PRIORITY
MOVEI 2,0(7)
POP P,7
RET
;TERMINAL INTERRUPT
;PHASE ONE - CALLED FROM TERM SERVICE ROUTINES
; 2/ LINE NO., 3/ INTERRUPT CODE
;SEND TO TOP FORK TO FIND PROPER DESTINATION
TTPSRQ: HLRZ 1,TTFORK(2) ;GET JOB USING THIS TTY
ANDCMI 1,600000 ;FLUSH EXTRANEOUS BITS
HLRZ 4,JOBPT(1) ;4=JOB CTTY LINE #
CAIE 4,0(2) ;LINE PSI REQUESTED?
JRST TTPSR2 ;NO, MUST FIND TOP FORK IN GROUP TO PSI
HRRZ 2,JOBPT(1) ;YES, 2=INDEX OF TOP JOB FORK
TTPSR1:: MOVSI 1,1B18+PSIT1F ;PHASE ONE REQUEST
IORM 1,FKINT(2)
HRRM 3,FKINT(2) ;INTERRUPT CODE
JRST PSIR4 ;SET NEW STATUS
TTPSR2: MOVEI 1,0(2) ;1=LINE #
IDIVI 1,2 ;COMPUTE BYTE PTR TO TTFRK1 ENTRY
ADD 1,TTFRKP(2)
LDB 2,1 ;2=INDEX OF TOP FORK IN GROUP
CAIL 2,NFKS ;RANGE CHECK
RET ;FAILED, ABORT PSI
JRST TTPSR1
TTFRKP: POINT 18,TTFRK1,17 ;POINTERS TO TTFRK1 ENTRIES
POINT 18,TTFRK1,35
;ROUTINES TO HANDLE INTERRUPT CONDITIONS AS SPECIFIED BY BITS
;IN LEFT HALF OF FKINT
;TERMINAL INTERRUPT, PHASE ONE
;THIS CODE RUN IN TOP FORK OF PROCESS GROUP ONLY
PSIT1: MOVE 6,BITS(7)
HRRZ 1,FORKN ;START WITH THIS FORK
HLRZ 4,SYSFK(1) ;4=DESIGNATOR OF TERM PSI SOURCE
SETO 5,
TDNE 6,FKPSIE(1) ;TERM CODE ON IN FORK?
MOVEI 5,0(1) ;YES, REMEMBER FORK
CALL PSIT1A ;LOOK AT ALL INFERIORS
JUMPL 5,PSIT11 ;NOT FOUND, SO TURN OFF CODE
HRRZ 2,SYSFK(5) ;GET SYSTEM INDEX OF FORK TO GET INTERPT
CAMN 2,FORKX ;THIS FORK?
JRST PSIT2 ;YES, GO DIRECTLY TO PHASE TWO
NOSKED
HRRM 7,FKINT(2) ;NO, SETUP TO INTERRUPT PROPER FORK
MOVSI 1,PSIT2F+400000 ;PHASE TWO REQUEST FLAG
IORM 1,FKINT(2)
CALL PSIR4
OKSKED
JRST PSITR1
PSIT11: CAIN 4,-1 ;SOURCE OF PSI=JOB CTTY?
JRST PSIT12 ;YES.
TRZN 4,1B18 ;MAYBE, CONVERT TO LINE #, ASSUMING TTY
JRST PSITR1 ;DESIGNATOR. NOT TTY DES. RETURN.
CAMN 4,CTRLTT ;CTTY OF JOB?
JRST PSIT12 ;YES.
CAIGE 4,NLINES ;NO, RANGE CHECK LINE #
CAIGE 4,0
JRST PSITR1
JRST PSIT13
PSIT12: ANDCAM 6,TTSPSI ;CLEAR PSI CODE FOR JOB
SKIPL 4,CTRLTT
PSIT13: ANDCAM 6,TTPSI(4) ;CLEAR CODE FOR TTY
JRST PSITR1
;SEARCH FORK STRUCTURE FOR FORK TO INTERRUPT
;4/ DESIGNATOR OF SOURCE OF THIS PSI
PSIT1A: ADD 1,INFERP ;LOOK AT INFERIOR LIST
PSIT1B: LDB 1,1 ;GET NEXT IN LIST
JUMPE 1,R ;RETURN AT END OF LIST
HLRZ 2,SYSFK(1) ;2=FORK'S SOURCE OF TTY PSI'S
CAIE 2,0(4) ;=SOURCE OF THIS ONE?
JRST PSIT1E ;NO, CONSIDER FORK NO FURTHER.
HRRZ 2,SYSFK(1) ;CHECK STATE OF FORK
PUSH P,7 ;SEE IF FORK FROZEN OR HALTED
MOVEI 7,0(2)
CALL CHKWT ;SEE IF DISMISSED
JRST [POP P,7 ;ITS NOT
JRST PSIT1D]
POP P,7
HRRZ 3,FKSTAT(2)
CAIN 3,FRZWT ;FROZEN?
JRST PSIT1G ;YES
CAIE 3,HALTT ;HALTED OR FORCED TERM?
CAIN 3,FORCTM
JRST PSIT1C ;YES
PSIT1D: TDNE 6,FKPSIE(1) ;FORK HAS CODE ENABLED?
MOVEI 5,0(1) ;YES, REMEMBER IT
PSIT1E: HRLM 1,0(P) ;REMEMBER CURRENT FORK
CALL PSIT1A ;CHECK INFERIORS
HLRZ 1,0(P) ;RECOVER CURRENT
PSIT1C: ADD 1,PARALP ;DO PARALLELS
JRST PSIT1B
PSIT1G: MOVSI 3,JTFRZB ;JSYS TRAP FREEZE?
TDNN 3,FKINT(2)
JRST PSIT1C ;NO
MOVSI 3,FRZBB
TDNE 3,FKINT(2) ;OTHER FREEZE ALSO?
JRST PSIT1C ;YES.
JRST PSIT1D
;FORK STRUCTURE POINTERS
SUPERP: POINT 12,FKPTRS,11 ;SUPERIOR
PARALP: POINT 12,FKPTRS,23 ;PARALLEL
INFERP: POINT 12,FKPTRS,35
;TERMINAL INTERRUPT, PHASE TWO
PSIT2: MOVEI 1,0(7)
CALL GETCHA
LDB 2,2
MOVE 1,BITS(2) ;AND SET BIT IN INT. WAITING WORD
AND 1,PSICHM ;BUT ONLY FOR ENABLED CHANNELS
IORM 1,PSIBW
JRST PSII ;THEN GO PROCESS IT
;SUSPEND FORK REQUEST
PIRSFK: TLNE 7,PSIJTR ;JSYS TRAP PSI REQUEST ALSO PRESENT?
JRST [ MOVE 2,FORKX ;YES, REMEMBER IT
MOVSI 1,PSIJTR+400000
IORM 1,FKINT(2)
JRST .+1 ]
MOVE 1,PIPC
CALL PITEST ;NOW INTERRUPTABLE?
JRST PIRSF1 ;NO
PIRSK2: MOVEI 3,SUSWT ;SUSPENDED FORK TEST
PIRSK1: MOVE 2,FORKX
MOVSI 1,SUSFKR
ANDCAM 1,FKINT(2)
JSYS UNPIR ;LEAVE INTERRUPT STATE
IORM 1,FKINT(7) ;KEEP INTERRUPT STARTING BIT
MOVEI 1,0(3) ;SUSWT OR FRZWT
HRL 1,PIOLDS ;WITH OLD STATUS
JRST DISMSE ;DISMISS
PIRSF1: MOVE 7,FORKX
NOSKED
HRRZ 1,FKSTAT(7)
CAIN 1,JTQWT ;IN JSYS TRAP QUEUE WAIT?
JRST PIRSF2 ;YES, ALLOW SUSPENSION
OKSKED
MOVSI 1,SUSFKR ;TURN REQUEST BIT BACK ON
IORM 1,FKINT(7)
JRST PSIDFR ;AND SET DEFERRED INTERRUPTS
PIRSF2: MOVEI 1,FKJTQ(7) ;FORK IN JSYS TRAP QUEUE WAIT
CALL JTDEQ ;REMOVE IT FROM QUEUE
MOVEI 1,JTRLCK ;SET RESUME ADDR TO LOCK ROUTINE
SETZM PIOLDS
MOVEM 1,PIPC
OKSKED
JRST PIRSK2
SUSWT: JRST 0(4) ;SCHEDULER TEST FOR SUSPENDED FORK
;LOGOUT REQUEST
PIRLGO: MOVE 1,PIPC
CALL PITEST ;OK TO INTERRUPT?
JRST [ MOVE 7,FORKX ;NO, REMEMBER REQUEST
MOVSI 1,PSILOB
IORM 1,FKINT(7)
JRST PSIDFR]
SETZM PIOLDS ;MAKE FORK RUNNABLE
MOVEI 1,FLOGO
EXCH 1,PIPC
SKIPGE SLOWF
JRST [ MOVEM 1,FPC ;IN USER MODE, SIMULATE JSYS
JRST PIRQR]
MOVE 2,PIAC+17 ;IN MON MODE, SIMULATE PUSHJ
PUSH 2,1
MOVEM 2,PIAC+17
JRST PIRQR
;JSYS TRAP REQUEST
PIRJTP: MOVE 1,PIPC
CALL PITEST ;FORK INTERRUPTABLE?
JRST PIRJT1 ;NO, DEFER IT
MOVSI 1,PSIJTR
MOVE 7,FORKX ;IN CASE THIS PSI WAS DEFERRED
ANDCAM 1,FKINT(7) ;CLEAR IT FROM FKINT
LDB 1,JTMCN ;GET PSI CHANNEL FOR TRAP
MOVE 1,BITS(1)
IORM 1,PSIBW ;SET BIT IN INT WAITING WORD
SETZ 7,
JRST PSII ;GO PROCESS THE TRAP
PIRJT1: MOVE 7,FORKX ;DEFER THE JSYS TRAP PSI
MOVSI 1,PSIJTR
IORM 1,FKINT(7)
JRST PSIDFR
;PROCESS INTERRUPT(S) FOR THIS FORK AS SPECIFIED BY FKINTB
PSII:
IFN KIFLG,<
JRSTF @[1B6+.+1]> ;TURN ON UXCT FLAG
MOVE 1,MJRST0 ;NORMALIZE ALL DEFER TRAPS
MOVEM 1,MJRSTF
MOVE 1,INTDF0
MOVEM 1,INTDFF
TLNE 7,SUSFKR ;FORK SUSPENSION REQUEST?
JRST PIRSFK ;YES
TLNE 7,PSILOB ;LOGOUT REQUEST?
JRST PIRLGO
TLNE 7,PSIJTR ;JSYS TRAP REQUEST?
JRST PIRJTP
MOVE 2,FORKX
MOVEI 1,0
EXCH 1,FKINTB(2) ;RESET FKINTB TO 0
IORM 1,PSIBW ;INCLUDE IN PROCESS WAITING BREAKS
MOVE 1,PSICHM ;USERS ENABLED CHANNELS
IOR 1,CHNSON ;WITH ALWAYS ON CHANNELS
IOR 1,SUPCHN ;WITH SUPERIOR RESERVED CHANNELS
SKIPE 3,PIOLDS ;WAS FORK WAITING?
JRST [ SKIPN FORKN ;AND NOT TOP FORK?
JRST .+1 ;NO
MOVEI 3,0(3) ;YES, HALT OR FORCED TERM?
CAIE 3,HALTT
CAIN 3,FORCTM
SETZ 1, ;YES, FLUSH BREAKS
JRST .+1]
ANDB 1,PSIBW ;FLUSH DISABLED CHANS
JUMPE 1,PIRQR ;RETURN IF NO BREAKS WAITING
MOVE 1,PIPC ;PROCESS PC
CALL PITEST ;CAN PROCESS BE INTERRUPTED NOW?
JRST PSIDFR ;NO, GO SETUP DEFERRED INTERRUPT
PSIS: MOVE 1,PSIBW
TDNE 1,MONCHN ;MONITOR RESERVED CHANNEL?
JRST PSIMB ;YES
AND 1,SUPCHN ;LOOK AT SUPERIOR RESERVED CHANS
JUMPN 1,PSIN1 ;TERMINATE IF ANY
MOVE 1,PSIBW
AND 1,CHNSON ;LOOK AT SPECIAL CHANNELS
SKIPE PSISYS ;IF THIS PROCESS NOT TAKING PSI'S,
JUMPN 1,PSIN1 ;TERMINATE IT IF ANY SPECIALS
ANDCM 1,PSICHM ;AND'ING WITH USER'S 'OFF' CHANNELS
JUMPN 1,PSIN1 ;TERMINATE CAUSE CHANNEL NOT ACTIVE
SKIPE PSISYS ;PSI SYSTEM ON?
JRST PIRQR ;NO
SKIPN LEVCHN ;THIS PROCESS TAKING INTERRUPTS?
JRST PSIN1 ;NO, GO TRANSMIT THE PSI
MOVE 1,PSIBW ;FIND HIGHEST PRIORITY INTERRUPT
MOVEI 2,0 ;NOW WAITING
MOVSI 3,1
PSIS1: JUMPL 1,PSIS2 ;THIS CHANNEL HAS WAITING BREAK?
PSIS4: LSH 1,1 ;NO, SHIFT TO NEXT CHANNEL
ADDI 2,1 ;COUNT CHANNEL NUMBER
JUMPN 1,PSIS1 ;KEEP LOOKING IF ANY BITS LEFT
;AC3 NOW CONTAINS LEVEL OF HIGHEST PRIORITY INTERRUPT FOUND
;AC5 CONTAINS CORRESPONDING CHANNEL NUMBER
JUMPE 3,PSID1 ;NO LEVEL ASSIGNED? GO XMIT INTERRUPT
MOVE 1,BITS(3)
CAMG 1,PSIBIP ;OK TO BREAK ON THIS LEVEL?
JRST [ MOVE 2,PSIBW ;NO, .GE. PRIORITY LEVEL IN PROGRESS
TDNN 2,CHNSON ;BREAKS ON PANIC CHNS WAITING?
JRST PIRQR ;NO, HOLD WAITING BREAKS
JRST PSIN1] ;YES, MUST TERMINATE
IORM 1,PSIBIP ;YES, REMEMBER BREAK THIS LEVEL
HRRZ 1,LEVCHN ;GET ADR OF USER'S CHANNEL TABLE
ADDI 1,0(5) ;COMPUTE ADR OF USER'S CHANNEL WORD
UMOVE 1,0(1) ;GET ADR OF USER'S INT ROUTINE
HRLI 1,UMODF ;USER MODE ON, OTHER FLAGS OFF
EXCH 1,PIPC ;GET OLD PC
TLNN 1,UMODF ;WAS IN USER MODE?
JRST PSISM ;NO, MUST SAVE MONITOR CONTEXT
PSIS5: SETZM PIOLDS
HLRZ 2,LEVCHN ;GET ADR OF USER'S LEVEL TABLE
ADDI 2,-1(3) ;COMPUTE ADR OF USER'S LEVEL WORD
UMOVE 2,0(2) ;GET ADR OF PC WORD FOR THIS LEVEL
TRNN 2,777760 ;ADDRESS IS AC?
MOVEM 1,PIAC(2) ;YES, STRANGE BUT ALLOW IT
TRNE 2,777760 ;NORMALLY,
UMOVEM 1,0(2) ;STORE BREAK PC IN USER'S MEMORY
PSID3: MOVE 1,BITS(5) ;CLEAR WAITING BREAK BIT FOR THIS CHANNEL
ANDCAM 1,PSIBW
JRST PIRQR ;TO USER
PSIS2: HRRZ 4,LEVCHN ;GET ADR OF USER'S CHANNEL TABLE
ADDI 4,0(2) ;COMPUTE ADR OF USER'S CHANNEL WORD
XCTUU [HLRZ 4,0(4)] ;GET LEVEL NUMBER FOR THIS CHANNEL
CAILE 4,NPILEV ;LEGAL LEVEL?
SETZ 4, ;NO, TREAT AS 0
CAIG 3,0(4) ;OLD LEVEL GREATER THAN CURRENT?
JRST PSIS4 ;NO
MOVEI 3,0(4) ;YES, REMEMBER NEW LEVEL
MOVEI 5,0(2) ;AND CHANNEL NUMBER
JRST PSIS4 ;RESUME SCAN
;MONITOR ROUTINE IS SHORTSTOPPING INTERRUPTS - SIMULATE JSYS MONBK
PSIMB: HRRZ 1,MONBK ;ROUTINE ADDRESS
EXCH 1,PIPC ;GET OLD PC
SETZM PIOLDS
HLRZ 2,MONBK ;RET LOC
MOVEM 1,0(2) ;STORE RETURN
JRST PIRQR
;SPECIAL ROUTINE TO SAVE STATE OF INTERRUPTED MONITOR
;ROUTINE
PSISM: MOVS 2,BITS(3) ;NO, REMEMBER MONITOR INTERRUPT
IORM 2,PSIBIP ;IN RH OF BIP WORD
MOVE 7,PSIPT ;STORAGE STACK POINTER
HLRE 6,7 ;SEE IF ENOUGH ROOM
MOVN 6,6 ;GET POSITIVE COUNT
MOVE 2,ACBAS
LSH 2,4 ;2=ADDR OF END OF USER AC BLOCKS
CAIGE 6,NUPDL-UACB+34(2) ;ENOUGH ROOM?
BUG(HLT,<PSI STORAGE STACK OVERFLOW>)
MOVE 10,7 ;SAVE POINTER
PUSH 7,1
MOVSI 6,-NSAVC ;SAVE VULNERABLE CELLS
PUSH 7,@SAVCT(6) ; ..
AOBJN 6,.-1
MOVSI 6,UPDL ;STORE ENTIRE MONITOR STACK
HRRI 6,1(7)
ADD 7,[XWD NUPDL,NUPDL]
BLT 6,0(7)
MOVSI 1,PIAC ;STORE CURRENT MONITOR AC'S
HRRI 1,1(7) ;NOW LIVING IN PIAC
ADD 7,[XWD 20,20]
BLT 1,0(7)
IFN KIFLG,<
MOVEI 1,KIASTK ;MOVE AC BLOCK 1 TO TOP OF STACK
XCTUM [BLT 1,KIASTK+17]>
HRRZ 1,ACBAS ;STORE ALL AC BLOCKS IN USE
HRRZ 2,ACBAS1
SUBI 1,-1(2) ;COMPUTE NUMBER OF BLOCKS IN USE
SUBI 2,<PSB-UACPGA>B39 ;FORCE BLT FROM UACPG
LSH 2,^D18+4
IFN KIFLG,<
MOVSI 2,KIASTK> ;ACTUAL TOP OF AC STACK
MOVE 4,2
HRRI 2,1(7)
LSH 1,4
HRLI 1,0(1) ;NUMBER OF WORDS BOTH HALFS
ADD 7,1
BLT 2,0(7)
PUSH 7,1 ;SAVE COUNT FOR DEBRK
PUSH 7,ACBAS ;AND CURRENT ACBAS
HRRI 4,PIAC ;RECOVER USER AC'S AT TIME OF MON CALL
BLT 4,PIAC+17
HRRZ 1,ACBAS
CAIL 1,<EUACB>B39 ;USING PSB FOR AC BLOCKS?
JRST [ SETZ 1, ;NO, SWITCH BACK TO PSB
MOVEI 2,UACPGA
CALL SETMPG ;UNMAP UACPG
MOVE 1,PSB+PSBPG
MOVEM 1,PSB+UACPG ;RESET MAP ENTRY FOR UACPG
JRST .+1 ] ;TO THAT FOR PSB
MOVE 1,UPDL ;USER PC AT MONITOR CALL
PUSH 7,10 ;PSI STACK BEFORE ALL THIS PUSHING
PUSH 7,1
PUSH 7,PIPDB ;SAVE IN CASE PSI DURING PGUTRP
MOVEM 7,PSIPT
TLZ 1,UMODF ;SO HE CAN TELL IT WAS MON INTERRUPT
SETOM SLOWF
JRST PSIS5 ;FINISH INTERRUPT START
;XMIT INTERRUPT TO SUPERIOR FORK
PSIT: HRRZ 2,FORKN
MOVE 2,FKPTRS(2) ;POINTERS RELATIVE TO FORK
LSH 2,-^D24 ;SUPERIOR FORK POINTER
HRRZ 2,SYSFK(2) ;SYSTEM FORK INDEX
JRST PSIRQF ;REQUEST INTERRUPT
;THIS FORK WON'T TAKE INTERRUPT, DISMISS IT AND RECORD WHY
PSID1: MOVEI 2,0(5) ;CHANNEL WITH NO LEVEL ASSIGNED
JRST PSIN2
PSIN1: MOVE 1,PSIBW ;INTERRUPTS OFF OR NO LEVCHN
JFFO 1,.+1 ;CALCULATE CHANNEL NUMBER
PSIN2: MOVEM 2,FORCTC ;SAVE CHANNEL NUMBER FOR STATUS
MOVE 1,BITS(2) ;JUST ONE CHANNEL AT A TIME
ANDCAM 1,PSIBW ;RESET WAITING BIT
CALL FKTMI ;GIVE FORK TERM INTERRUPT
MOVEI 3,FRZWT ;FORK FROZEN STATE TEST
MOVE 1,CAPENB
TLNE 1,(1B17) ;SUPERIOR WANTS FROZEN STEAD HALT?
JRST PIRSK1 ;YES, GO FREEZE
JSYS UNPIR ;LEAVE PI STATE, MOVE AC'S ETC.
MOVEI 1,FORCTM
JRST DISMSE ;THIS ONE IS BEING DISMISSED
FORCTM: JRST 0(4) ;SCHEDULER TEST FOR FORCED TERM FORK
;INTERRUPT SUPERIOR FORK ON TERMINATION
FKTMI: PUSH P,1
HRRZ 1,FORKN
SKIPN 1
SKIPA 1,[^D35] ;TERMINATING TOP FORK, GIVE CH 35
MOVEI 1,^D19 ;19 IS FORK TERMINATED
CALL PSIT ;TRANSMIT IT
POP P,1
RET
GETCHA: MOVEI 2,0(1)
IDIVI 2,6
ADDI 2,PSICHA
HLL 2,CH6TAB(3)
RET
;DEFERRED INTERRUPT LOGIC
;SET TRAPS TO RECHECK INTERRUPTS WHEN STATE CHANGES
PSIDFR: MOVE 1,MJRST1
MOVEM 1,MJRSTF
MOVE 1,INTDF1
MOVEM 1,INTDFF
JSYS UNPIR ;LEAVE BREAK STARTING STATE
IORM 1,FKINT(7) ;BUT LEAVE PENDING BIT
JRST PSIDF1 ;RESUME
MJRST1: JRSTF @[PSISV0]
INTDF1: JSYS PSISV1
PSISV1: XWD PIPC,.+1
SOSGE INTDF
JRSTF @[PSISV2]
JRSTF @PIPC ;JUST RETURN, HE'S NOT INTERRUPTIBLE
PSISV0: MOVEM 1,PIPC ;SAVE AC1
MOVE 1,FPC ;FPC NOW CONTAINS USER'S PC
EXCH 1,PIPC
PSISV2: MOVEM P,PIAC+17 ;SAVE USER'S AC17
MOVEI P,PIAC ;AND AC'S 0-NSAC
BLT P,PIAC+16
MOVE P,PIPDL ;RESTORE INTERRUPT STARTING STATE
PUSH P,PGURET ;SAVE PGURET
SETZM PIOLDS
PSISV3: MOVE 2,FORKX
MOVE 7,FKINT(2)
JRST PSII ;ENTER MAIN SEQUENCE
;TEST FOR IMMEDIATE OR DEFERRED INTERRUPT
;SKIP => IMMEDIATE
;NOSKIP => DEFERRED
;CALLED WITH TEST USER PC IN AC1
PITEST: TLNE 1,UMODF ;USER MODE?
JRST RSKP ;YES, IMMEDIATE
SKIPL SLOWF ;NO, SLOW CODE?
SKIPL INTDF ;YES, INTERRUPTABLE
RET ;NO, DEFER
SKIPN NSKED ;IN CASE NOSKED W/O NOINT
SKIPL TRAPC ;IN PAGER TRAP, OR
RET ;YES, DEFER
JRST RSKP ;IMMEDIATE
;DEBREAK
.DEBRK: SKIPN PSIBIP ;ANY BREAKS IN PROGRESS?
XCT MJRSTF ;NO, ACTS AS NOP
MOVEM 1,TW1 ;SAVE USER AC1,2
MOVEM 2,TW2
MOVE 2,FORKX
MOVSI 1,200000
IORM 1,FKINT(2) ;SET INTERRUPT STARTING BIT
MOVE 2,TW2
MOVE 1,TW1
MOVEM P,PIAC+17 ;ENTER INTERRUPT STARTING STATE
MOVEI P,PIAC
BLT P,PIAC+16
MOVE P,PIPDL
PUSH P,PGURET ;SAVE PGURET ON LOCAL STACK
SETZM PIOLDS
PSIDBK: MOVE 2,PSIBIP ;BREAKS NOW IN PROGRESS
JFFO 2,.+2 ;FIND HIGHEST ONE
JRST 4,. ;IMPOSSIBLE
HLRZ 1,LEVCHN ;COMPUTE ADDRESS OF RETURN PC
ADDI 1,-1(3)
UMOVE 1,0(1)
TRNN 1,777760 ;ADDRESS IS AC?
SKIPA 1,PIAC(1) ;YES
UMOVE 1,0(1) ;GET RETURN PC FROM USER MEMORY
MOVS 2,BITS(3)
TDNE 2,PSIBIP ;WAS THIS MONITOR INTERRUPT?
JRST PSIS7 ;YES, GO UNWIND
PSIS8:
IFN KAFLG,<
TLZ 1,7637> ;FLUSH TROUBLESOME BITS
IFN KIFLG,<
TLZ 1,7037> ; ..
TLO 1,UMODF ;MAKE SURE USER MODE IS ON
MOVEM 1,PIPC ;SET TO DEBREAK AT THAT ADDRESS
PSIS6: MOVE 1,BITS(3)
ANDCAM 1,PSIBIP ;CLEAR BIP THIS LEVEL
JRST PSISV3 ;GO CHECK FOR OTHER INTERRUPTS AND RETURN
PSIS7: ANDCAM 2,PSIBIP ;CLEAR MON BREAK FLAG FOR THIS LEVEL
MOVE 7,PSIPT
POP 7,PIPDB ;RESTORE PGURET ON LOCAL STACK
POP 7,4 ;PC GIVEN TO USER
POP 7,PSIPT ;TOP OF THIS BLOCK OF PSI STORAGE
TLON 1,UMODF ;IF IT WAS DIDDLED AT ALL,
CAME 1,4
JRST PSIS8 ;DON'T RESUME MON ROUTINE
POP 7,2
MOVEM 2,ACBAS
SETACB 2
POP 7,4
SUB 7,4
HRRZ 5,ACBAS1
LSH 5,4
CAIL 2,<EUACB>B39 ;PREVIOUSLY USING PSB FOR AC BLOCKS?
JRST [ SUBI 5,PSB-UACPGA ;NO, FORCE BLT TO UACPG
SETZM PSB+UACPG ;CLEAR UACPG MAP ENTRY
PUSH P,1
LDB 1,[POINT 13,PSB+PSBPG,26]
MOVE 2,[XWD RCW,UACPGA]
CALL SETMPG ;SET UP MAP FOR UACPG
POP P,1
JRST .+1 ]
IFN KIFLG,<
MOVEI 5,KIASTK> ;ACTUAL TO OF KI STACK
MOVE 2,5 ;SAVE ACB ADDRESS
HRLI 5,1(7)
ADDI 4,0(5)
BLT 5,-1(4) ;RESTORE AC BLOCKS
MOVSI 5,PIAC ;PUT USER CURRENT AC'S INTO TOP BLOCK
HRRI 5,0(2)
BLT 5,17(2)
IFN KIFLG,<
MOVSI 5,KIASTK ;PUT TOP OF STACK BACK IN AC BLOCK 1
XCTMU [BLT 5,17]>
SUB 7,[XWD 20,20]
HRRZ 5,ACBAS
CAIGE 5,<EUACB>B39 ;AC BLOCKS IN PSB?
JRST PSIS7A ;YES
HRRZ 5,ACBAS1 ;NO, MUST COPY THEN FROM UACPG
SUBI 5,<PSB-UACPGA>B39 ;TO PSB
HRL 5,ACBAS1
LSH 5,4
MOVSS 5
BLT 5,EUACB-1 ;COPY AS MANY AS FIT IN PSB
PSIS7A: MOVEI 2,PIAC
HRLI 2,1(7)
BLT 2,PIAC+17 ;RESTORE MONITOR AC'S
SUB 7,[XWD NUPDL,NUPDL]
MOVEI 2,UPDL
HRLI 2,1(7)
BLT 2,UPDL+NUPDL-1 ;RESTORE STACK
MOVEI 2,NSAVC-1 ;RESTORE GROUP OF CELLS
POP 7,@SAVCT(2)
SOJGE 2,.-1
POP 7,PIPC ;ACTUAL MON INTERRUPT PC
SETZM SLOWF
SETOM INTDF
JRST PSIS6 ;NOW DEBRK
;TABLE OF CELLS STORED ON PI STACK AROUND A PSI
SAVCT: 40
60
MPP
FPC
PIOLDS
XMENTR
IFN KIFLG,<
KIMAC1
KIMAC2>
NSAVC==.-SAVCT ;LENGTH OF THIS LIST
;TRAP AND PSI ROUTINE EXECUTED WHEN A FORK EXECUTES A TRAPPED JSYS
JTSRVD==400000 ;TRAP SERVICED FLAG
TRAPSI: XWD JTTMP,TRPSI0
TRPSI0: MOVEM 1,TW1 ;SAVE AC1 AND AC2
MOVEM 2,TW2
MOVSI 1,200000
MOVE 2,FORKX ;PREVENT PSI'S WHILE IN
IORM 1,FKINT(2) ;THIS CODE
HRRZ 1,JTMNW ;GET HANDLE OF IMMED MON.
CAIN 1,7777 ;NULL MON FORK?
BUG(CHK,<UNMONITORED FORK TRAPPED>)
HRRZ 1,JTTMP ;DETERMINE JSYS EXECUTING WHEN
SUBI 1,JTDVC1+1 ;TRAPPED
DPB 1,JTTJSY ;SAVE IT
IFE JTRPSW-1,<
CONO PGR,6 ;UNMAP RES MON
>
HRRZ 1,NJDV(1) ;GET NORMAL DISPATCH
IFE JTRPSW-1,<
CONO PGR,7 ;REMAP RES MON
>
HRRM 1,JTTMP ;AND SAVE IT. AT THIS POINT
MOVE 1,FPC ;RH(JTTMP)=NORM DISP
TLNE 1,UMODF ;CALL FROM USER MODE?
JRST TRPSI1 ;YES, DETERMINE FORK TO PSI
MOVSI 1,200000 ;NO, ACCEPT PSI'S,
MOVE 2,FORKX
ANDCAM 1,FKINT(2)
MOVE 1,TW1 ;RESTORE ACS 1 AND 2,
MOVE 2,TW2
JRST @JTTMP ;AND DO NORMAL DISPATCH
TRPSI1: MOVEM 1,JTFPC
MOVE 1,TW1 ;RESTORE ACS 1 AND 2
MOVE 2,TW2
MOVEM P,XMENT1 ;CALL FROM USER MODE, SET UP
MOVE P,UPP ;STACK, ETC. AS IN BECOMING SLOW
MOVEM P,MPP ;BUT DON'T BE INTERRUPTABLE
MOVE P,ACBAS1
MOVEM P,ACBAS
SETACB P
MOVE P,XMENT1
UMOVEM P,P
SETZ P,
XCTMU [ BLT P,P-1 ]
MOVE P,MPP
PUSH P,FPC ;SAVE USER RETRUN FOR RFSTS,ETC
HRRZ 1,JTMNW ;GET IMMED MONITOR
LDB 2,JTTJSY
IDIVI 2,^D36
MOVE 3,BITS(3) ;2=OFFSET INTO BIT TABLE
TDNE 3,JTBTB(2) ;3=MASK FOR TRAPPED JSYS.
JRST TRPSI3 ;HANDLED BY IMMED MONITOR
TRPSI2: JUMPN 1,TRPSIA ;FORK=TOP JOB FORK?
TRPSIB: TLNN 2,JTSRVD ;YES, TRAP HANDLED YET?
BUG(CHK,<NO MONITOR FOR TRAPPED FORK>)
JRST TRPSI6 ;YES, DO NORMAL DISPATCH.
TRPSIA: MOVEI 11,0(1)
CALL SETLF1 ;MAP MONITOR'S PSB
EXCH 1,11
HRRZ 1,JTMNW(11) ;1=ITS MONITOR
CAIN 1,7777 ;NULL MONITOR?
JRST TRPSIB ;YES.
ADD 11,2
TDNN 3,JTBTB(11) ;IS THAT FORK HANDLING JSYS?
JRST TRPSI2 ;NO, TRY ITS MONITOR.
TRPSI3: TLO 2,JTSRVD ;INDICATE TRAP HANDLED
MOVEI 11,0(1)
CALL SETLF1 ;MAP PSB OF THE MONITOR
EXCH 1,11
TRPSI4: HLRZ 4,JTMNW(11)
TRZ 4,777700
CAIN 4,77 ;IS CHANNEL SPECIFIED?
JRST TRPSI5 ;NO, DON'T PSI
CALL JTLOCK ;SYNCH WITH OTHER TRAPPING FORKS
JRST TRPSI4 ;FORK SUSPENDED AND RESUMED
;WHILE QUEUED, RETRY LOCKING
JSYS FRZPSI ;FREEZE SELF AND PSI MONITOR
;RESUMED HERE AFTER TRAP HANDLED IF MONITOR DOES NOT CHANGE PC
TRPSI5: JUMPE 1,TRPSI6 ;IF TOP JOB FORK DO DISPATCH
JRST TRPSIA ;OTHERWISE LOOK FOR MORE MONS.
TRPSI6: LDB 1,JTTJSY
IFE JTRPSW-1,<
CONO PGR,6 ;UNMAP RES MON
>
HRRZ 1,NJDV(1) ;GET NORMAL DISPATCH
IFE JTRPSW-1,<
HRRZ 2,JTMNW
CAIE 2,7777 ;UNLESS NO LONGER TRAPPED
CONO PGR,7 ;REMAP RES MON
>
MOVEM 1,JTTMP
MOVE P,JTFPC
MOVEM P,FPC ;RESTORE FPC
TLZ P,UMODF
HLLM P,JTTMP
MOVSI 1,200000
MOVE 2,FORKX
ANDCAM 1,FKINT(2) ;ACCEPT PSI'S
SETZ P,
XCTUM [ BLT P,P-1 ] ;RESTORE ACS
UMOVE P,P
JRSTF @JTTMP ;DO NORMAL DISP FOR THIS JSYS
;FREEZE AND PSI ROUTINE - FORK INITIATES JSYS TRAP PSI OF
;FORK TRAPPED TO AND THEN FREEZES ITSELF
;1/ JOB INDEX OF FORK TO PSI
;11/ OFFSET TO FORKS PSB
FRZPSI: XWD FPC,FRZPS0
FRZPS0: LDB 4,JTTJSY
HRL 4,FORKN ;4=FORK INDEX,,JSYS
MOVEM 1,JTTMP ;SAVE FORK TRAPPED FOR UTFRK
MOVEM 4,JTTRW(11) ;SET TRAPPED FORK,,JSYS
HRRZ 4,SYSFK(1)
ENTSKD ;ENTER THE SCHEDULER, SAVE ACS,
;ETC.
MOVEI 2,0(4)
MOVSI 1,400000+PSIJTR
IORM 1,FKINT(2) ;PSI MONITORING FORK
CALL PSIR4 ;MAKE SCHEDULER SEE IT
MOVSI 1,200000+JTFRZB ;DO "JSYS TRAP" FREEZE OF SELF
IORM 1,FKINT(7) ;7=FORKX SET BY ENSKED
SETZM PIOLDS ;"OLD STATE" = RUNNING
MOVEI 1,FRZWT
JRST DISMS1 ;DISMS
JTMCN: POINT 6,JTMNW,17 ;SELECTS PSI CHANNEL
JTTJSY: POINT 9,JTMNW,11 ;PTR TO TEMP STORAGE FOR TRAPPED JSYS
JTDVC1: XLIST ;REPEAT 1000,< JSYS TRAPSI >
REPEAT 1000,< JSYS TRAPSI >
LIST
;THE INTERMEDIATE DISPATCH VECTOR.
;TRAPPED JSYS'S DISPATCH THROUGH IT
;TO TRAPSI ROUTINE
;JSYS TRAP LOCK AND UNLOCK ROUTINES
;WHEN A FORK TRIES JTLOCK AND SOME OTHER FORK HAS THE
;LOCK, THE FORK ADDS ITSELF TO A QUEUE (FKJTQ) AND BECOMES BLOCKED.
;WHEN THE LOCK IS CLEARED (BY A MONITORING FORK) THE QUEUE IS
;SCANNED FOR THE FIRST FORK (IF ANY) WAITING ON THE LOCK. THAT
;FORK IS REMOVED FROM THE QUEUE AND ALLOWED TO RUN.
;LOCK ROUTINE
;ON ENTRY TO JTLOCK:
;1/ JOB FORK INDEX (OF FORK TO FIELD TRAP)
;11/ PTR TO ITS PSB
;RET + 1 IF SUSPENDED AND RESUMED WHILE QUEUED
;RET + 2 WITH LOCK SET
JTLOCK: NOSKED
AOSE JTLCK(11) ;TRY TO SEIZE THE LOCK
JRST JTLOC2 ;SOMEONE ELSE HAS IT
OKSKED ;GOT IT
JTLOC1: AOS 0(P) ;RET + 2
RET
JTLOC2: JSYS JTENQ ;PUT SELF ON JSYS TRAP QUEUE
JRST JTLOC1 ;RETURNS HERE WITH LOCK SEIZED
;IF FORK IS RESUMED AT JTRLCK, IT RETURNS + 1 TO TRAPSI ROUTINE
;FORCING ANOTHER CALL TO JTLOCK AFTER A CHECK TO SEE IF THE TRAP IS
;STILL TO GO TO THE SAME FORK.
JTRLCK: MOVEM 1,TW1 ;SAVE ACS 1 AND 2
MOVEM 2,TW2
MOVSI 1,200000
MOVE 2,FORKX
IORM 1,FKINT(2) ;PREVENT PSI'S BEFORE REENTERING
MOVE 1,TW1 ;TRAP CODE
MOVE 2,TW2
RET
JTENQ: XWD FPC,JTENQ0 ;ROUTINE TO PALCE FORK ON QUEUE
JTENQ0: HRL 1,SYSFK(1) ;1=FORK WAITING ON
ENTSKD ;ENTER SCHEDULER
SOSE NSKED ;MATCHED NOSKED IN JTLOCK
BUG(HLT,<JTENQ WITH BAD NSKED>)
MOVEI 6,FKJTQ(7) ;7=FORKX, SET BY ENSKED
HRRM 6,@JTLSTL ;ADD THIS FORK TO END OF QUEUE
EXCH 6,JTLSTL ;SET NEW END OF QUEUE PTR
MOVSM 6,FKJTQ(7) ;SET BACK PTR TO OLD QUEUE END
HRRI 1,JTQWT
JRST DISMS1 ;DISMS
;JSYS TRAP QUEUE WAIT TEST
JTQWT: MOVE 1,FKINT(7) ;DID A SUSPEND REQUEST OCCUR
TLNN 1,SUSFKR ;BEFORE BLOCKING?
JRST 0(4) ;NO.
MOVSI 1,400000+PSIWTF ;YES, REINITIATE SUSPEND
IORM 1,FKINT(7) ;REQUEST PSI
MOVSI 1,200000
ANDCAM 1,FKINT(7) ;ALLOW PSI'S
HLRZ 1,FKQ(7)
CAIG 1,1
JRST 1(4)
PUSH P,3 ;GIVE FORK SOME PRIORITY
CALL PSSKD2
POP P,3
JRST 1(4)
;UNLOCK ROUTINE
;USES BUT DOES NOT SAVE ACS 1,2,3,4
JTULCK: HRRZ 2,FORKX
NOSKED
MOVE 1,JTLST ;SCAN QUEUE LOOKING FOR FORK
;WAITING ON EXECUIING FORK
JTULC1: JUMPE 1,JTULC3 ;NONE FOUND
MOVEI 4,0(1)
SUBI 4,FKJTQ ;4=FORK INDEX OF QUEUED FORK
HLRZ 3,FKSTAT(4)
CAMN 3,2 ;THIS FORK WAITING ON EX FORK?
JRST JTULC2 ;YES, REMOVE IT FROM QUEUE
HRRZ 1,0(1) ;NO, TRY NEXT FORK
JRST JTULC1
JTULC2: CALL JTDEQ ;REMOVE FORK FROM QUEUE
MOVEI 3,JSKP ;SET WAIT TEST TO SKIP
MOVEM 3,FKSTAT(4) ;CAUSING FORK TO RUN
CAIA
JTULC3: SETOM JTLCK ;NO FORKS ON QUEUE, CLEAR LOCK
OKSKED
RET
;REMOVE FORK WHOSE FKJTQ ENTRY IS PT'D TO BY 1 FROM JSYS TRAP QUEUE
;USES BUT DOES NOT SAVE ACS 2,3
JTDEQ: NOSKED
HRRZ 3,(1) ;3=PTR TO NEXT ITEM ON QUEUE
HLRZ 2,(1) ;2=PTR TO PREV ITEM
HRRM 3,(2)
JUMPE 3,JTDEQ1 ;REMOVING LAST ITEM?
HRLM 2,(3) ;NO
CAIA
JTDEQ1: MOVEM 2,JTLSTL
OKSKED
RET
END ;END OF SCHED.MAC FILE
2. 1992年02月22日的Linux kernel 0.96.a版本中第一次引入这个功能-
https://elixir.bootlin.com/linux/0.96a/source/kernel/sched.c
/kernel/sched.c
344行:
/*
* linux/kernel/sched.c
*
* (C) 1991 Linus Torvalds
*/
/*
* 'sched.c' is the main kernel file. It contains scheduling primitives
* (sleep_on, wakeup, schedule etc) as well as a number of simple system
* call functions (type getpid(), which just extracts a field from
* current-task
*/
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/kernel.h>
#include <linux/sys.h>
#include <linux/fdreg.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <signal.h>
#include <errno.h>
int need_resched = 0;
#define _S(nr) (1<<((nr)-1))
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
void show_task(int nr,struct task_struct * p)
{
int i,j = 4096-sizeof(struct task_struct);
printk("%d: pid=%d, state=%d, father=%d, child=%d, ",nr,p->pid,
p->state, p->p_pptr->pid, p->p_cptr ? p->p_cptr->pid : -1);
i=0;
while (i<j && !((char *)(p+1))[i])
i++;
printk("%d/%d chars free in kstack\n\r",i,j);
printk(" PC=%08X.", *(1019 + (unsigned long *) p));
if (p->p_ysptr || p->p_osptr)
printk(" Younger sib=%d, older sib=%d\n\r",
p->p_ysptr ? p->p_ysptr->pid : -1,
p->p_osptr ? p->p_osptr->pid : -1);
else
printk("\n\r");
}
void show_state(void)
{
int i;
printk("\rTask-info:\n\r");
for (i=0 ; i<NR_TASKS ; i++)
if (task[i])
show_task(i,task[i]);
}
#define LATCH (1193180/HZ)
extern void mem_use(void);
extern int timer_interrupt(void);
extern int system_call(void);
union task_union {
struct task_struct task;
char stack[PAGE_SIZE];
};
static union task_union init_task = {INIT_TASK,};
unsigned long volatile jiffies=0;
unsigned long startup_time=0;
int jiffies_offset = 0; /* # clock ticks to add to get "true
time". Should always be less than
1 second's worth. For time fanatics
who like to syncronize their machines
to WWV :-) */
struct task_struct *current = &(init_task.task);
struct task_struct *last_task_used_math = NULL;
struct task_struct * task[NR_TASKS] = {&(init_task.task), };
long user_stack [ PAGE_SIZE>>2 ] ;
struct {
long * a;
short b;
} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
/*
* 'math_state_restore()' saves the current math information in the
* old math state array, and gets the new ones from the current task
*/
void math_state_restore()
{
if (last_task_used_math == current)
return;
__asm__("fwait");
if (last_task_used_math) {
__asm__("fnsave %0"::"m" (last_task_used_math->tss.i387));
}
last_task_used_math=current;
if (current->used_math) {
__asm__("frstor %0"::"m" (current->tss.i387));
} else {
__asm__("fninit"::);
current->used_math=1;
}
}
/*
* 'schedule()' is the scheduler function. It's a very simple and nice
* scheduler: it's not perfect, but certainly works for most things.
* The one thing you might take a look at is the signal-handler code here.
*
* NOTE!! Task 0 is the 'idle' task, which gets called when no other
* tasks can run. It can not be killed, and it cannot sleep. The 'state'
* information in task[0] is never used.
*/
void schedule(void)
{
int i,next,c;
struct task_struct ** p;
/* check alarm, wake up any interruptible tasks that have got a signal */
need_resched = 0;
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p) {
if ((*p)->timeout && (*p)->timeout < jiffies)
if ((*p)->state == TASK_INTERRUPTIBLE) {
(*p)->timeout = 0;
(*p)->state = TASK_RUNNING;
}
if ((*p)->alarm && (*p)->alarm < jiffies) {
(*p)->signal |= (1<<(SIGALRM-1));
(*p)->alarm = 0;
}
if (((*p)->signal & ~(*p)->blocked) &&
(*p)->state==TASK_INTERRUPTIBLE)
(*p)->state=TASK_RUNNING;
}
/* this is the scheduler proper: */
while (1) {
c = -1;
next = 0;
i = NR_TASKS;
p = &task[NR_TASKS];
while (--i) {
if (!*--p)
continue;
if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
c = (*p)->counter, next = i;
}
if (c) break;
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p)
(*p)->counter = ((*p)->counter >> 1) +
(*p)->priority;
}
switch_to(next);
}
int sys_pause(void)
{
unsigned long old_blocked;
unsigned long mask;
struct sigaction * sa = current->sigaction;
old_blocked = current->blocked;
for (mask=1 ; mask ; sa++,mask += mask)
if (sa->sa_handler == SIG_IGN)
current->blocked |= mask;
current->state = TASK_INTERRUPTIBLE;
schedule();
current->blocked = old_blocked;
return -EINTR;
}
/*
* wake_up doesn't wake up stopped processes - they have to be awakened
* with signals or similar.
*/
void wake_up(struct task_struct **p)
{
struct task_struct * wakeup_ptr, * tmp;
if (p && *p) {
wakeup_ptr = *p;
*p = NULL;
while (wakeup_ptr && wakeup_ptr != task[0]) {
if (wakeup_ptr->state == TASK_ZOMBIE)
printk("wake_up: TASK_ZOMBIE\n");
else if (wakeup_ptr->state != TASK_STOPPED) {
wakeup_ptr->state = TASK_RUNNING;
if (wakeup_ptr->counter > current->counter)
need_resched = 1;
}
tmp = wakeup_ptr->next_wait;
wakeup_ptr->next_wait = task[0];
wakeup_ptr = tmp;
}
}
}
static inline void __sleep_on(struct task_struct **p, int state)
{
unsigned int flags;
if (!p)
return;
if (current == task[0])
panic("task[0] trying to sleep");
__asm__("pushfl ; popl %0":"=r" (flags));
current->next_wait = *p;
task[0]->next_wait = NULL;
*p = current;
current->state = state;
sti();
schedule();
if (current->next_wait != task[0])
wake_up(p);
current->next_wait = NULL;
__asm__("pushl %0 ; popfl"::"r" (flags));
}
void interruptible_sleep_on(struct task_struct **p)
{
__sleep_on(p,TASK_INTERRUPTIBLE);
}
void sleep_on(struct task_struct **p)
{
__sleep_on(p,TASK_UNINTERRUPTIBLE);
}
/*
* OK, here are some floppy things that shouldn't be in the kernel
* proper. They are here because the floppy needs a timer, and this
* was the easiest way of doing it.
*/
static struct task_struct * wait_motor[4] = {NULL,NULL,NULL,NULL};
static int mon_timer[4]={0,0,0,0};
static int moff_timer[4]={0,0,0,0};
unsigned char current_DOR = 0x0C;
int ticks_to_floppy_on(unsigned int nr)
{
extern unsigned char selected;
unsigned char mask = 0x10 << nr;
if (nr>3)
panic("floppy_on: nr>3");
moff_timer[nr]=10000; /* 100 s = very big :-) */
cli(); /* use floppy_off to turn it off */
mask |= current_DOR;
if (!selected) {
mask &= 0xFC;
mask |= nr;
}
if (mask != current_DOR) {
outb(mask,FD_DOR);
if ((mask ^ current_DOR) & 0xf0)
mon_timer[nr] = HZ/2;
else if (mon_timer[nr] < 2)
mon_timer[nr] = 2;
current_DOR = mask;
}
sti();
return mon_timer[nr];
}
void floppy_off(unsigned int nr)
{
moff_timer[nr]=3*HZ;
}
void do_floppy_timer(void)
{
int i;
unsigned char mask = 0x10;
for (i=0 ; i<4 ; i++,mask <<= 1) {
if (!(mask & current_DOR))
continue;
if (mon_timer[i]) {
if (!--mon_timer[i])
wake_up(i+wait_motor);
} else if (!moff_timer[i]) {
current_DOR &= ~mask;
outb(current_DOR,FD_DOR);
} else
moff_timer[i]--;
}
}
#define TIME_REQUESTS 64
static struct timer_list {
long jiffies;
void (*fn)();
struct timer_list * next;
} timer_list[TIME_REQUESTS] = { { 0, NULL, NULL }, };
static struct timer_list * next_timer = NULL;
void add_timer(long jiffies, void (*fn)(void))
{
struct timer_list * p;
if (!fn)
return;
cli();
if (jiffies <= 0)
(fn)();
else {
for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)
if (!p->fn)
break;
if (p >= timer_list + TIME_REQUESTS)
panic("No more time requests free");
p->fn = fn;
p->jiffies = jiffies;
p->next = next_timer;
next_timer = p;
while (p->next && p->next->jiffies < p->jiffies) {
p->jiffies -= p->next->jiffies;
fn = p->fn;
p->fn = p->next->fn;
p->next->fn = fn;
jiffies = p->jiffies;
p->jiffies = p->next->jiffies;
p->next->jiffies = jiffies;
p = p->next;
}
}
sti();
}
#define FSHIFT 11
#define FSCALE (1<<FSHIFT)
/*
* Constants for averages over 1, 5, and 15 minutes
* when sampling at 5 second intervals.
*/
static unsigned long cexp[3] = {
1884, /* 0.9200444146293232 * FSCALE, exp(-1/12) */
2014, /* 0.9834714538216174 * FSCALE, exp(-1/60) */
2037, /* 0.9944598480048967 * FSCALE, exp(-1/180) */
};
unsigned long averunnable[3]; /* fixed point numbers */
void update_avg(void)
{
int i, n=0;
struct task_struct **p;
for(p = &LAST_TASK; p > &FIRST_TASK; --p)
if (*p && ((*p)->state == TASK_RUNNING ||
(*p)->state == TASK_UNINTERRUPTIBLE))
++n;
for (i = 0; i < 3; ++i)
averunnable[i] = (cexp[i] * averunnable[i] +
n * FSCALE * (FSCALE - cexp[i])) >> FSHIFT;
}
unsigned long timer_active = 0;
struct timer_struct timer_table[32];
void do_timer(long cpl)
{
unsigned long mask;
struct timer_struct *tp = timer_table+0;
static int avg_cnt;
for (mask = 1 ; mask ; tp++,mask += mask) {
if (mask > timer_active)
break;
if (!(mask & timer_active))
continue;
if (tp->expires > jiffies)
continue;
timer_active &= ~mask;
tp->fn();
sti();
}
if (cpl)
current->utime++;
else
current->stime++;
if (next_timer) {
next_timer->jiffies--;
while (next_timer && next_timer->jiffies <= 0) {
void (*fn)(void);
fn = next_timer->fn;
next_timer->fn = NULL;
next_timer = next_timer->next;
(fn)();
}
}
if (current_DOR & 0xf0)
do_floppy_timer();
if (--avg_cnt < 0) {
avg_cnt = 500;
update_avg();
}
if ((--current->counter)<=0) {
current->counter=0;
need_resched = 1;
}
}
int sys_alarm(long seconds)
{
int old = current->alarm;
if (old)
old = (old - jiffies) / HZ;
current->alarm = (seconds>0)?(jiffies+HZ*seconds):0;
return (old);
}
int sys_getpid(void)
{
return current->pid;
}
int sys_getppid(void)
{
return current->p_pptr->pid;
}
int sys_getuid(void)
{
return current->uid;
}
int sys_geteuid(void)
{
return current->euid;
}
int sys_getgid(void)
{
return current->gid;
}
int sys_getegid(void)
{
return current->egid;
}
int sys_nice(long increment)
{
if (increment < 0 && !suser())
return -EPERM;
if (increment >= current->priority)
increment = current->priority-1;
current->priority -= increment;
return 0;
}
void sched_init(void)
{
int i;
struct desc_struct * p;
if (sizeof(struct sigaction) != 16)
panic("Struct sigaction MUST be 16 bytes");
set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));
set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
p = gdt+2+FIRST_TSS_ENTRY;
for(i=1 ; i<NR_TASKS ; i++) {
task[i] = NULL;
p->a=p->b=0;
p++;
p->a=p->b=0;
p++;
}
/* Clear NT, so that we won't have troubles with that later on */
__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
ltr(0);
lldt(0);
outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
set_intr_gate(0x20,&timer_interrupt);
outb(inb_p(0x21)&~0x01,0x21);
set_system_gate(0x80,&system_call);
}
3. 1993年09月20日的Linux kernel 0.99.13版本中的sched.c源代码
https://kernelhistory.sourcentral.org/linux-0.99.13/?f=/linux-0.99.13/S/449.html%23L332
/root/kernel/sched.c
338行: (*p)->state == TASK_RUNNING
/*
* linux/kernel/sched.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* 'sched.c' is the main kernel file. It contains scheduling primitives
* (sleep_on, wakeup, schedule etc) as well as a number of simple system
* call functions (type getpid(), which just extracts a field from
* current-task
*/
#include <linux/config.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/kernel.h>
#include <linux/sys.h>
#include <linux/fdreg.h>
#include <linux/errno.h>
#include <linux/time.h>
#include <linux/ptrace.h>
#include <linux/segment.h>
#include <linux/delay.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#define TIMER_IRQ 0
int need_resched = 0;
/*
* Tell us the machine setup..
*/
int hard_math = 0; /* set by boot/head.S */
int x86 = 0; /* set by boot/head.S to 3 or 4 */
int ignore_irq13 = 0; /* set if exception 16 works */
int wp_works_ok = 0; /* not used currently */
extern int _setitimer(int, struct itimerval *, struct itimerval *);
unsigned long * prof_buffer = NULL;
unsigned long prof_len = 0;
#define _S(nr) (1<<((nr)-1))
#define LATCH ((1193180 + HZ/2)/HZ)
extern void mem_use(void);
extern int timer_interrupt(void);
asmlinkage int system_call(void);
static unsigned long init_kernel_stack[1024];
struct task_struct init_task = INIT_TASK;
unsigned long volatile jiffies=0;
unsigned long startup_time=0;
int jiffies_offset = 0; /* # clock ticks to add to get "true
time". Should always be less than
1 second's worth. For time fanatics
who like to syncronize their machines
to WWV :-) */
struct task_struct *current = &init_task;
struct task_struct *last_task_used_math = NULL;
struct task_struct * task[NR_TASKS] = {&init_task, };
long user_stack [ PAGE_SIZE>>2 ] ;
struct {
long * a;
short b;
} stack_start = { & user_stack [PAGE_SIZE>>2] , KERNEL_DS };
/*
* 'math_state_restore()' saves the current math information in the
* old math state array, and gets the new ones from the current task
*
* Careful.. There are problems with IBM-designed IRQ13 behaviour.
* Don't touch unless you *really* know how it works.
*/
asmlinkage void math_state_restore(void)
{
__asm__ __volatile__("clts");
if (last_task_used_math == current)
return;
timer_table[COPRO_TIMER].expires = jiffies+50;
timer_active |= 1<<COPRO_TIMER;
if (last_task_used_math)
__asm__("fnsave %0":"=m" (last_task_used_math->tss.i387));
else
__asm__("fnclex");
last_task_used_math = current;
if (current->used_math) {
__asm__("frstor %0": :"m" (current->tss.i387));
} else {
__asm__("fninit");
current->used_math=1;
}
timer_active &= ~(1<<COPRO_TIMER);
}
/*
* 'schedule()' is the scheduler function. It's a very simple and nice
* scheduler: it's not perfect, but certainly works for most things.
* The one thing you might take a look at is the signal-handler code here.
*
* NOTE!! Task 0 is the 'idle' task, which gets called when no other
* tasks can run. It can not be killed, and it cannot sleep. The 'state'
* information in task[0] is never used.
*
* The "confuse_gcc" goto is used only to get better assembly code..
* Djikstra probably hates me.
*/
asmlinkage void schedule(void)
{
int c;
struct task_struct * p;
struct task_struct * next;
/* check alarm, wake up any interruptible tasks that have got a signal */
sti();
need_resched = 0;
p = &init_task;
for (;;) {
if ((p = p->next_task) == &init_task)
goto confuse_gcc1;
if (p->state != TASK_INTERRUPTIBLE)
continue;
if (p->signal & ~p->blocked) {
p->state = TASK_RUNNING;
continue;
}
if (p->timeout && p->timeout < jiffies) {
p->timeout = 0;
p->state = TASK_RUNNING;
}
}
confuse_gcc1:
/* this is the scheduler proper: */
#if 0
/* give processes that go to sleep a bit higher priority.. */
/* This depends on the values for TASK_XXX */
/* This gives smoother scheduling for some things, but */
/* can be very unfair under some circumstances, so.. */
if (TASK_UNINTERRUPTIBLE >= (unsigned) current->state &&
current->counter < current->priority*2) {
++current->counter;
}
#endif
c = -1;
next = p = &init_task;
for (;;) {
if ((p = p->next_task) == &init_task)
goto confuse_gcc2;
if (p->state == TASK_RUNNING && p->counter > c)
c = p->counter, next = p;
}
confuse_gcc2:
if (!c) {
for_each_task(p)
p->counter = (p->counter >> 1) + p->priority;
}
switch_to(next);
}
asmlinkage int sys_pause(void)
{
current->state = TASK_INTERRUPTIBLE;
schedule();
return -ERESTARTNOHAND;
}
/*
* wake_up doesn't wake up stopped processes - they have to be awakened
* with signals or similar.
*
* Note that this doesn't need cli-sti pairs: interrupts may not change
* the wait-queue structures directly, but only call wake_up() to wake
* a process. The process itself must remove the queue once it has woken.
*/
void wake_up(struct wait_queue **q)
{
struct wait_queue *tmp;
struct task_struct * p;
if (!q || !(tmp = *q))
return;
do {
if ((p = tmp->task) != NULL) {
if ((p->state == TASK_UNINTERRUPTIBLE) ||
(p->state == TASK_INTERRUPTIBLE)) {
p->state = TASK_RUNNING;
if (p->counter > current->counter)
need_resched = 1;
}
}
if (!tmp->next) {
printk("wait_queue is bad (eip = %08x)\n",((unsigned long *) q)[-1]);
printk(" q = %p\n",q);
printk(" *q = %p\n",*q);
printk(" tmp = %p\n",tmp);
break;
}
tmp = tmp->next;
} while (tmp != *q);
}
void wake_up_interruptible(struct wait_queue **q)
{
struct wait_queue *tmp;
struct task_struct * p;
if (!q || !(tmp = *q))
return;
do {
if ((p = tmp->task) != NULL) {
if (p->state == TASK_INTERRUPTIBLE) {
p->state = TASK_RUNNING;
if (p->counter > current->counter)
need_resched = 1;
}
}
if (!tmp->next) {
printk("wait_queue is bad (eip = %08x)\n",((unsigned long *) q)[-1]);
printk(" q = %p\n",q);
printk(" *q = %p\n",*q);
printk(" tmp = %p\n",tmp);
break;
}
tmp = tmp->next;
} while (tmp != *q);
}
static inline void __sleep_on(struct wait_queue **p, int state)
{
unsigned long flags;
struct wait_queue wait = { current, NULL };
if (!p)
return;
if (current == task[0])
panic("task[0] trying to sleep");
current->state = state;
add_wait_queue(p, &wait);
save_flags(flags);
sti();
schedule();
remove_wait_queue(p, &wait);
restore_flags(flags);
}
void interruptible_sleep_on(struct wait_queue **p)
{
__sleep_on(p,TASK_INTERRUPTIBLE);
}
void sleep_on(struct wait_queue **p)
{
__sleep_on(p,TASK_UNINTERRUPTIBLE);
}
static struct timer_list * next_timer = NULL;
void add_timer(struct timer_list * timer)
{
unsigned long flags;
struct timer_list ** p;
if (!timer)
return;
timer->next = NULL;
p = &next_timer;
save_flags(flags);
cli();
while (*p) {
if ((*p)->expires > timer->expires) {
(*p)->expires -= timer->expires;
timer->next = *p;
break;
}
timer->expires -= (*p)->expires;
p = &(*p)->next;
}
*p = timer;
restore_flags(flags);
}
int del_timer(struct timer_list * timer)
{
unsigned long flags;
unsigned long expires = 0;
struct timer_list **p;
p = &next_timer;
save_flags(flags);
cli();
while (*p) {
if (*p == timer) {
if ((*p = timer->next) != NULL)
(*p)->expires += timer->expires;
timer->expires += expires;
restore_flags(flags);
return 1;
}
expires += (*p)->expires;
p = &(*p)->next;
}
restore_flags(flags);
return 0;
}
unsigned long timer_active = 0;
struct timer_struct timer_table[32];
/*
* Hmm.. Changed this, as the GNU make sources (load.c) seems to
* imply that avenrun[] is the standard name for this kind of thing.
* Nothing else seems to be standardized: the fractional size etc
* all seem to differ on different machines.
*/
unsigned long avenrun[3] = { 0,0,0 };
/*
* Nr of active tasks - counted in fixed-point numbers
*/
static unsigned long count_active_tasks(void)
{
struct task_struct **p;
unsigned long nr = 0;
for(p = &LAST_TASK; p > &FIRST_TASK; --p)
if (*p && (*p)->state == TASK_RUNNING)
nr += FIXED_1;
return nr;
}
static inline void calc_load(void)
{
unsigned long active_tasks; /* fixed-point */
static int count = LOAD_FREQ;
if (count-- > 0)
return;
count = LOAD_FREQ;
active_tasks = count_active_tasks();
CALC_LOAD(avenrun[0], EXP_1, active_tasks);
CALC_LOAD(avenrun[1], EXP_5, active_tasks);
CALC_LOAD(avenrun[2], EXP_15, active_tasks);
}
/*
* The int argument is really a (struct pt_regs *), in case the
* interrupt wants to know from where it was called. The timer
* irq uses this to decide if it should update the user or system
* times.
*/
static void do_timer(struct pt_regs * regs)
{
unsigned long mask;
struct timer_struct *tp = timer_table+0;
struct task_struct * task_p;
jiffies++;
calc_load();
if ((VM_MASK & regs->eflags) || (3 & regs->cs)) {
current->utime++;
/* Update ITIMER_VIRT for current task if not in a system call */
if (current->it_virt_value && !(--current->it_virt_value)) {
current->it_virt_value = current->it_virt_incr;
send_sig(SIGVTALRM,current,1);
}
} else {
current->stime++;
#ifdef CONFIG_PROFILE
if (prof_buffer && current != task[0]) {
unsigned long eip = regs->eip;
eip >>= 2;
if (eip < prof_len)
prof_buffer[eip]++;
}
#endif
}
if (current == task[0] || (--current->counter)<=0) {
current->counter=0;
need_resched = 1;
}
/* Update ITIMER_REAL for every task */
for_each_task(task_p) {
if (!task_p->it_real_value)
continue;
if (--task_p->it_real_value)
continue;
send_sig(SIGALRM,task_p,1);
task_p->it_real_value = task_p->it_real_incr;
need_resched = 1;
}
/* Update ITIMER_PROF for the current task */
if (current->it_prof_value && !(--current->it_prof_value)) {
current->it_prof_value = current->it_prof_incr;
send_sig(SIGPROF,current,1);
}
for (mask = 1 ; mask ; tp++,mask += mask) {
if (mask > timer_active)
break;
if (!(mask & timer_active))
continue;
if (tp->expires > jiffies)
continue;
timer_active &= ~mask;
tp->fn();
sti();
}
cli();
while (next_timer && next_timer->expires == 0) {
void (*fn)(unsigned long) = next_timer->function;
unsigned long data = next_timer->data;
next_timer = next_timer->next;
sti();
fn(data);
cli();
}
if (next_timer)
next_timer->expires--;
sti();
}
asmlinkage int sys_alarm(long seconds)
{
struct itimerval it_new, it_old;
it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0;
it_new.it_value.tv_sec = seconds;
it_new.it_value.tv_usec = 0;
_setitimer(ITIMER_REAL, &it_new, &it_old);
return(it_old.it_value.tv_sec + (it_old.it_value.tv_usec / 1000000));
}
asmlinkage int sys_getpid(void)
{
return current->pid;
}
asmlinkage int sys_getppid(void)
{
return current->p_pptr->pid;
}
asmlinkage int sys_getuid(void)
{
return current->uid;
}
asmlinkage int sys_geteuid(void)
{
return current->euid;
}
asmlinkage int sys_getgid(void)
{
return current->gid;
}
asmlinkage int sys_getegid(void)
{
return current->egid;
}
asmlinkage int sys_nice(long increment)
{
int newprio;
if (increment < 0 && !suser())
return -EPERM;
newprio = current->priority - increment;
if (newprio < 1)
newprio = 1;
if (newprio > 35)
newprio = 35;
current->priority = newprio;
return 0;
}
static void show_task(int nr,struct task_struct * p)
{
int i, j;
unsigned char * stack;
printk("%d: pid=%d, state=%d, father=%d, child=%d, ",(p == current)?-nr:nr,p->pid,
p->state, p->p_pptr->pid, p->p_cptr ? p->p_cptr->pid : -1);
i = 0;
j = PAGE_SIZE;
if (!(stack = (unsigned char *) p->kernel_stack_page)) {
stack = (unsigned char *) init_kernel_stack;
j = sizeof(init_kernel_stack);
}
while (i<j && !*(stack++))
i++;
printk("%d/%d chars free in kstack\n",i,j);
printk(" PC=%08X.", *(1019 + (unsigned long *) p));
if (p->p_ysptr || p->p_osptr)
printk(" Younger sib=%d, older sib=%d\n",
p->p_ysptr ? p->p_ysptr->pid : -1,
p->p_osptr ? p->p_osptr->pid : -1);
else
printk("\n");
}
void show_state(void)
{
int i;
printk("Task-info:\n");
for (i=0 ; i<NR_TASKS ; i++)
if (task[i])
show_task(i,task[i]);
}
void sched_init(void)
{
int i;
struct desc_struct * p;
if (sizeof(struct sigaction) != 16)
panic("Struct sigaction MUST be 16 bytes");
set_tss_desc(gdt+FIRST_TSS_ENTRY,&init_task.tss);
set_ldt_desc(gdt+FIRST_LDT_ENTRY,&default_ldt,1);
set_system_gate(0x80,&system_call);
p = gdt+2+FIRST_TSS_ENTRY;
for(i=1 ; i<NR_TASKS ; i++) {
task[i] = NULL;
p->a=p->b=0;
p++;
p->a=p->b=0;
p++;
}
/* Clear NT, so that we won't have troubles with that later on */
__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
load_TR(0);
load_ldt(0);
outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
if (request_irq(TIMER_IRQ,(void (*)(int)) do_timer)!=0)
panic("Could not allocate timer IRQ!");
}
4. 1993年11月29日的Linux kernel 0.99.14版本中的sched.c源代码
/root/kernel/sched.c
https://kernelhistory.sourcentral.org/linux-0.99.14/?f=/linux-0.99.14/S/323.html%23L412
418行: ((p)->state == TASK_RUNNING || (p)->state == TASK_UNINTERRUPTIBLE || (*p)->state == TASK_SWAPPING)
/*
* linux/kernel/sched.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* 'sched.c' is the main kernel file. It contains scheduling primitives
* (sleep_on, wakeup, schedule etc) as well as a number of simple system
* call functions (type getpid(), which just extracts a field from
* current-task
*/
#include <linux/config.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/kernel.h>
#include <linux/sys.h>
#include <linux/fdreg.h>
#include <linux/errno.h>
#include <linux/time.h>
#include <linux/ptrace.h>
#include <linux/segment.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#define TIMER_IRQ 0
#include <linux/timex.h>
/*
* kernel variables
*/
long tick = 1000000 / HZ; /* timer interrupt period */
volatile struct timeval xtime; /* The current time */
int tickadj = 500/HZ; /* microsecs */
/*
* phase-lock loop variables
*/
int time_status = TIME_BAD; /* clock synchronization status */
long time_offset = 0; /* time adjustment (us) */
long time_constant = 0; /* pll time constant */
long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */
long time_precision = 1; /* clock precision (us) */
long time_maxerror = 0x70000000;/* maximum error */
long time_esterror = 0x70000000;/* estimated error */
long time_phase = 0; /* phase offset (scaled us) */
long time_freq = 0; /* frequency offset (scaled ppm) */
long time_adj = 0; /* tick adjust (scaled 1 / HZ) */
long time_reftime = 0; /* time at last adjustment (s) */
long time_adjust = 0;
int need_resched = 0;
/*
* Tell us the machine setup..
*/
int hard_math = 0; /* set by boot/head.S */
int x86 = 0; /* set by boot/head.S to 3 or 4 */
int ignore_irq13 = 0; /* set if exception 16 works */
int wp_works_ok = 0; /* set if paging hardware honours WP */
extern int _setitimer(int, struct itimerval *, struct itimerval *);
unsigned long * prof_buffer = NULL;
unsigned long prof_len = 0;
#define _S(nr) (1<<((nr)-1))
extern void mem_use(void);
extern int timer_interrupt(void);
asmlinkage int system_call(void);
static unsigned long init_kernel_stack[1024];
struct task_struct init_task = INIT_TASK;
unsigned long volatile jiffies=0;
struct task_struct *current = &init_task;
struct task_struct *last_task_used_math = NULL;
struct task_struct * task[NR_TASKS] = {&init_task, };
long user_stack [ PAGE_SIZE>>2 ] ;
struct {
long * a;
short b;
} stack_start = { & user_stack [PAGE_SIZE>>2] , KERNEL_DS };
/*
* int 0x80 entry points.. Moved away from the header file, as
* iBCS2 may also want to use the '<linux/sys.h>' headers..
*/
#ifdef __cplusplus
extern "C" {
#endif
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,
sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,
sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,
sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,
sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_olduname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,
sys_setreuid,sys_setregid, sys_sigsuspend, sys_sigpending,
sys_sethostname, sys_setrlimit, sys_getrlimit, sys_getrusage,
sys_gettimeofday, sys_settimeofday, sys_getgroups, sys_setgroups,
sys_select, sys_symlink, sys_lstat, sys_readlink, sys_uselib,
sys_swapon, sys_reboot, sys_readdir, sys_mmap, sys_munmap, sys_truncate,
sys_ftruncate, sys_fchmod, sys_fchown, sys_getpriority, sys_setpriority,
sys_profil, sys_statfs, sys_fstatfs, sys_ioperm, sys_socketcall,
sys_syslog, sys_setitimer, sys_getitimer, sys_newstat, sys_newlstat,
sys_newfstat, sys_uname, sys_iopl, sys_vhangup, sys_idle, sys_vm86,
sys_wait4, sys_swapoff, sys_sysinfo, sys_ipc, sys_fsync, sys_sigreturn,
sys_clone, sys_setdomainname, sys_newuname, sys_modify_ldt,
sys_adjtimex, sys_mprotect, sys_sigprocmask };
/* So we don't have to do any more manual updating.... */
int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr);
#ifdef __cplusplus
}
#endif
/*
* 'math_state_restore()' saves the current math information in the
* old math state array, and gets the new ones from the current task
*
* Careful.. There are problems with IBM-designed IRQ13 behaviour.
* Don't touch unless you *really* know how it works.
*/
asmlinkage void math_state_restore(void)
{
__asm__ __volatile__("clts");
if (last_task_used_math == current)
return;
timer_table[COPRO_TIMER].expires = jiffies+50;
timer_active |= 1<<COPRO_TIMER;
if (last_task_used_math)
__asm__("fnsave %0":"=m" (last_task_used_math->tss.i387));
else
__asm__("fnclex");
last_task_used_math = current;
if (current->used_math) {
__asm__("frstor %0": :"m" (current->tss.i387));
} else {
__asm__("fninit");
current->used_math=1;
}
timer_active &= ~(1<<COPRO_TIMER);
}
#ifndef CONFIG_MATH_EMULATION
asmlinkage void math_emulate(long arg)
{
printk("math-emulation not enabled and no coprocessor found.\n");
printk("killing %s.\n",current->comm);
send_sig(SIGFPE,current,1);
schedule();
}
#endif /* CONFIG_MATH_EMULATION */
/*
* 'schedule()' is the scheduler function. It's a very simple and nice
* scheduler: it's not perfect, but certainly works for most things.
* The one thing you might take a look at is the signal-handler code here.
*
* NOTE!! Task 0 is the 'idle' task, which gets called when no other
* tasks can run. It can not be killed, and it cannot sleep. The 'state'
* information in task[0] is never used.
*
* The "confuse_gcc" goto is used only to get better assembly code..
* Djikstra probably hates me.
*/
asmlinkage void schedule(void)
{
int c;
struct task_struct * p;
struct task_struct * next;
/* check alarm, wake up any interruptible tasks that have got a signal */
sti();
need_resched = 0;
p = &init_task;
for (;;) {
if ((p = p->next_task) == &init_task)
goto confuse_gcc1;
if (p->state != TASK_INTERRUPTIBLE)
continue;
if (p->signal & ~p->blocked) {
p->state = TASK_RUNNING;
continue;
}
if (p->timeout && p->timeout <= jiffies) {
p->timeout = 0;
p->state = TASK_RUNNING;
}
}
confuse_gcc1:
/* this is the scheduler proper: */
#if 0
/* give processes that go to sleep a bit higher priority.. */
/* This depends on the values for TASK_XXX */
/* This gives smoother scheduling for some things, but */
/* can be very unfair under some circumstances, so.. */
if (TASK_UNINTERRUPTIBLE >= (unsigned) current->state &&
current->counter < current->priority*2) {
++current->counter;
}
#endif
c = -1;
next = p = &init_task;
for (;;) {
if ((p = p->next_task) == &init_task)
goto confuse_gcc2;
if (p->state == TASK_RUNNING && p->counter > c)
c = p->counter, next = p;
}
confuse_gcc2:
if (!c) {
for_each_task(p)
p->counter = (p->counter >> 1) + p->priority;
}
switch_to(next);
/* Now maybe reload the debug registers */
if(current->debugreg[7]){
loaddebug(0);
loaddebug(1);
loaddebug(2);
loaddebug(3);
loaddebug(6);
};
}
asmlinkage int sys_pause(void)
{
current->state = TASK_INTERRUPTIBLE;
schedule();
return -ERESTARTNOHAND;
}
/*
* wake_up doesn't wake up stopped processes - they have to be awakened
* with signals or similar.
*
* Note that this doesn't need cli-sti pairs: interrupts may not change
* the wait-queue structures directly, but only call wake_up() to wake
* a process. The process itself must remove the queue once it has woken.
*/
void wake_up(struct wait_queue **q)
{
struct wait_queue *tmp;
struct task_struct * p;
if (!q || !(tmp = *q))
return;
do {
if ((p = tmp->task) != NULL) {
if ((p->state == TASK_UNINTERRUPTIBLE) ||
(p->state == TASK_INTERRUPTIBLE)) {
p->state = TASK_RUNNING;
if (p->counter > current->counter)
need_resched = 1;
}
}
if (!tmp->next) {
printk("wait_queue is bad (eip = %08lx)\n",((unsigned long *) q)[-1]);
printk(" q = %p\n",q);
printk(" *q = %p\n",*q);
printk(" tmp = %p\n",tmp);
break;
}
tmp = tmp->next;
} while (tmp != *q);
}
void wake_up_interruptible(struct wait_queue **q)
{
struct wait_queue *tmp;
struct task_struct * p;
if (!q || !(tmp = *q))
return;
do {
if ((p = tmp->task) != NULL) {
if (p->state == TASK_INTERRUPTIBLE) {
p->state = TASK_RUNNING;
if (p->counter > current->counter)
need_resched = 1;
}
}
if (!tmp->next) {
printk("wait_queue is bad (eip = %08lx)\n",((unsigned long *) q)[-1]);
printk(" q = %p\n",q);
printk(" *q = %p\n",*q);
printk(" tmp = %p\n",tmp);
break;
}
tmp = tmp->next;
} while (tmp != *q);
}
static inline void __sleep_on(struct wait_queue **p, int state)
{
unsigned long flags;
struct wait_queue wait = { current, NULL };
if (!p)
return;
if (current == task[0])
panic("task[0] trying to sleep");
current->state = state;
add_wait_queue(p, &wait);
save_flags(flags);
sti();
schedule();
remove_wait_queue(p, &wait);
restore_flags(flags);
}
void interruptible_sleep_on(struct wait_queue **p)
{
__sleep_on(p,TASK_INTERRUPTIBLE);
}
void sleep_on(struct wait_queue **p)
{
__sleep_on(p,TASK_UNINTERRUPTIBLE);
}
static struct timer_list * next_timer = NULL;
void add_timer(struct timer_list * timer)
{
unsigned long flags;
struct timer_list ** p;
if (!timer)
return;
timer->next = NULL;
p = &next_timer;
save_flags(flags);
cli();
while (*p) {
if ((*p)->expires > timer->expires) {
(*p)->expires -= timer->expires;
timer->next = *p;
break;
}
timer->expires -= (*p)->expires;
p = &(*p)->next;
}
*p = timer;
restore_flags(flags);
}
int del_timer(struct timer_list * timer)
{
unsigned long flags;
unsigned long expires = 0;
struct timer_list **p;
p = &next_timer;
save_flags(flags);
cli();
while (*p) {
if (*p == timer) {
if ((*p = timer->next) != NULL)
(*p)->expires += timer->expires;
timer->expires += expires;
restore_flags(flags);
return 1;
}
expires += (*p)->expires;
p = &(*p)->next;
}
restore_flags(flags);
return 0;
}
unsigned long timer_active = 0;
struct timer_struct timer_table[32];
/*
* Hmm.. Changed this, as the GNU make sources (load.c) seems to
* imply that avenrun[] is the standard name for this kind of thing.
* Nothing else seems to be standardized: the fractional size etc
* all seem to differ on different machines.
*/
unsigned long avenrun[3] = { 0,0,0 };
/*
* Nr of active tasks - counted in fixed-point numbers
*/
static unsigned long count_active_tasks(void)
{
struct task_struct **p;
unsigned long nr = 0;
for(p = &LAST_TASK; p > &FIRST_TASK; --p)
if (*p && ((*p)->state == TASK_RUNNING ||
(*p)->state == TASK_UNINTERRUPTIBLE ||
(*p)->state == TASK_SWAPPING))
nr += FIXED_1;
return nr;
}
static inline void calc_load(void)
{
unsigned long active_tasks; /* fixed-point */
static int count = LOAD_FREQ;
if (count-- > 0)
return;
count = LOAD_FREQ;
active_tasks = count_active_tasks();
CALC_LOAD(avenrun[0], EXP_1, active_tasks);
CALC_LOAD(avenrun[1], EXP_5, active_tasks);
CALC_LOAD(avenrun[2], EXP_15, active_tasks);
}
/*
* this routine handles the overflow of the microsecond field
*
* The tricky bits of code to handle the accurate clock support
* were provided by Dave Mills (Mills@UDEL.EDU) of NTP fame.
* They were originally developed for SUN and DEC kernels.
* All the kudos should go to Dave for this stuff.
*
* These were ported to Linux by Philip Gladstone.
*/
static void second_overflow(void)
{
long ltemp;
/* last time the cmos clock got updated */
static long last_rtc_update=0;
extern int set_rtc_mmss(unsigned long);
/* Bump the maxerror field */
time_maxerror = (0x70000000-time_maxerror < time_tolerance) ?
0x70000000 : (time_maxerror + time_tolerance);
/* Run the PLL */
if (time_offset < 0) {
ltemp = (-(time_offset+1) >> (SHIFT_KG + time_constant)) + 1;
time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
time_offset += (time_adj * HZ) >> (SHIFT_SCALE - SHIFT_UPDATE);
time_adj = - time_adj;
} else if (time_offset > 0) {
ltemp = ((time_offset-1) >> (SHIFT_KG + time_constant)) + 1;
time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
time_offset -= (time_adj * HZ) >> (SHIFT_SCALE - SHIFT_UPDATE);
} else {
time_adj = 0;
}
time_adj += (time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE))
+ FINETUNE;
/* Handle the leap second stuff */
switch (time_status) {
case TIME_INS:
/* ugly divide should be replaced */
if (xtime.tv_sec % 86400 == 0) {
xtime.tv_sec--; /* !! */
time_status = TIME_OOP;
printk("Clock: inserting leap second 23:59:60 GMT\n");
}
break;
case TIME_DEL:
/* ugly divide should be replaced */
if (xtime.tv_sec % 86400 == 86399) {
xtime.tv_sec++;
time_status = TIME_OK;
printk("Clock: deleting leap second 23:59:59 GMT\n");
}
break;
case TIME_OOP:
time_status = TIME_OK;
break;
}
if (xtime.tv_sec > last_rtc_update + 660)
if (set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_sec;
}
static int lost_ticks = 0;
/*
* disregard lost ticks for now.. We don't care enough.
*/
static void timer_bh(void * unused)
{
cli();
while (next_timer && next_timer->expires == 0) {
void (*fn)(unsigned long) = next_timer->function;
unsigned long data = next_timer->data;
next_timer = next_timer->next;
sti();
fn(data);
cli();
}
sti();
}
/*
* The int argument is really a (struct pt_regs *), in case the
* interrupt wants to know from where it was called. The timer
* irq uses this to decide if it should update the user or system
* times.
*/
static void do_timer(struct pt_regs * regs)
{
unsigned long mask;
struct timer_struct *tp = timer_table+0;
struct task_struct * task_p;
long ltemp;
/* Advance the phase, once it gets to one microsecond, then
* advance the tick more.
*/
time_phase += time_adj;
if (time_phase < -FINEUSEC) {
ltemp = -time_phase >> SHIFT_SCALE;
time_phase += ltemp << SHIFT_SCALE;
xtime.tv_usec += tick - ltemp;
}
else if (time_phase > FINEUSEC) {
ltemp = time_phase >> SHIFT_SCALE;
time_phase -= ltemp << SHIFT_SCALE;
xtime.tv_usec += tick + ltemp;
} else
xtime.tv_usec += tick;
if (time_adjust)
{
/* We are doing an adjtime thing.
*/
/* Limit the amount of the step for *next* tick to be
* in the range -tickadj .. +tickadj
*/
if (time_adjust > tickadj)
ltemp = tickadj;
else if (time_adjust < -tickadj)
ltemp = -tickadj;
else
ltemp = time_adjust;
/* Reduce the amount of time left by this step */
time_adjust -= ltemp;
/* Modify the value of the tick for next time.
* Note that a positive delta means we want the clock
* to run fast. This means that the tick should be bigger
*/
tick = 1000000/HZ + ltemp;
}
else
tick = 1000000/HZ;
if (xtime.tv_usec >= 1000000) {
xtime.tv_usec -= 1000000;
xtime.tv_sec++;
second_overflow();
}
jiffies++;
calc_load();
if ((VM_MASK & regs->eflags) || (3 & regs->cs)) {
current->utime++;
/* Update ITIMER_VIRT for current task if not in a system call */
if (current->it_virt_value && !(--current->it_virt_value)) {
current->it_virt_value = current->it_virt_incr;
send_sig(SIGVTALRM,current,1);
}
} else {
current->stime++;
#ifdef CONFIG_PROFILE
if (prof_buffer && current != task[0]) {
unsigned long eip = regs->eip;
eip >>= 2;
if (eip < prof_len)
prof_buffer[eip]++;
}
#endif
}
if (current == task[0] || (--current->counter)<=0) {
current->counter=0;
need_resched = 1;
}
/* Update ITIMER_REAL for every task */
for_each_task(task_p) {
if (!task_p->it_real_value)
continue;
if (--task_p->it_real_value)
continue;
send_sig(SIGALRM,task_p,1);
task_p->it_real_value = task_p->it_real_incr;
need_resched = 1;
}
/* Update ITIMER_PROF for the current task */
if (current->it_prof_value && !(--current->it_prof_value)) {
current->it_prof_value = current->it_prof_incr;
send_sig(SIGPROF,current,1);
}
for (mask = 1 ; mask ; tp++,mask += mask) {
if (mask > timer_active)
break;
if (!(mask & timer_active))
continue;
if (tp->expires > jiffies)
continue;
timer_active &= ~mask;
tp->fn();
sti();
}
cli();
if (next_timer) {
if (next_timer->expires) {
next_timer->expires--;
if (!next_timer->expires)
mark_bh(TIMER_BH);
} else {
lost_ticks++;
mark_bh(TIMER_BH);
}
}
sti();
}
asmlinkage int sys_alarm(long seconds)
{
struct itimerval it_new, it_old;
it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0;
it_new.it_value.tv_sec = seconds;
it_new.it_value.tv_usec = 0;
_setitimer(ITIMER_REAL, &it_new, &it_old);
return(it_old.it_value.tv_sec + (it_old.it_value.tv_usec / 1000000));
}
asmlinkage int sys_getpid(void)
{
return current->pid;
}
asmlinkage int sys_getppid(void)
{
return current->p_opptr->pid;
}
asmlinkage int sys_getuid(void)
{
return current->uid;
}
asmlinkage int sys_geteuid(void)
{
return current->euid;
}
asmlinkage int sys_getgid(void)
{
return current->gid;
}
asmlinkage int sys_getegid(void)
{
return current->egid;
}
asmlinkage int sys_nice(long increment)
{
int newprio;
if (increment < 0 && !suser())
return -EPERM;
newprio = current->priority - increment;
if (newprio < 1)
newprio = 1;
if (newprio > 35)
newprio = 35;
current->priority = newprio;
return 0;
}
static void show_task(int nr,struct task_struct * p)
{
static char * stat_nam[] = { "R", "S", "D", "Z", "T", "W" };
printk("%-8s %3d ", p->comm, (p == current) ? -nr : nr);
if (((unsigned) p->state) < sizeof(stat_nam)/sizeof(char *))
printk(stat_nam[p->state]);
else
printk(" ");
/* this prints bogus values for the current process */
printk(" %08lX ", ((unsigned long *)p->tss.esp)[2]);
printk("%5lu %5d %6d ",
p->tss.esp - p->kernel_stack_page, p->pid, p->p_pptr->pid);
if (p->p_cptr)
printk("%5d ", p->p_cptr->pid);
else
printk(" ");
if (p->p_ysptr)
printk("%7d", p->p_ysptr->pid);
else
printk(" ");
if (p->p_osptr)
printk(" %5d\n", p->p_osptr->pid);
else
printk("\n");
}
void show_state(void)
{
int i;
printk(" free sibling\n");
printk(" task PC stack pid father child younger older\n");
for (i=0 ; i<NR_TASKS ; i++)
if (task[i])
show_task(i,task[i]);
}
void sched_init(void)
{
int i;
struct desc_struct * p;
bh_base[TIMER_BH].routine = timer_bh;
if (sizeof(struct sigaction) != 16)
panic("Struct sigaction MUST be 16 bytes");
set_tss_desc(gdt+FIRST_TSS_ENTRY,&init_task.tss);
set_ldt_desc(gdt+FIRST_LDT_ENTRY,&default_ldt,1);
set_system_gate(0x80,&system_call);
p = gdt+2+FIRST_TSS_ENTRY;
for(i=1 ; i<NR_TASKS ; i++) {
task[i] = NULL;
p->a=p->b=0;
p++;
p->a=p->b=0;
p++;
}
/* Clear NT, so that we won't have troubles with that later on */
__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
load_TR(0);
load_ldt(0);
outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
if (request_irq(TIMER_IRQ,(void (*)(int)) do_timer)!=0)
panic("Could not allocate timer IRQ!");
}
5. 2022年02月22日的最新版Linux kernel中的loadavg.c源代码
https://github.com/torvalds/linux/blob/master/kernel/sched/loadavg.c
注意看loadavg.c文件中的第一段注释:其实这是一个很SB的指标,但是不知道为什么他们都认为这玩意很重要。
349行:void calc_global_load(void)
// SPDX-License-Identifier: GPL-2.0
/*
* kernel/sched/loadavg.c
*
* This file contains the magic bits required to compute the global loadavg
* figure. Its a silly number but people think its important. We go through
* great pains to make it work on big machines and tickless kernels.
*/
/*
* Global load-average calculations
*
* We take a distributed and async approach to calculating the global load-avg
* in order to minimize overhead.
*
* The global load average is an exponentially decaying average of nr_running +
* nr_uninterruptible.
*
* Once every LOAD_FREQ:
*
* nr_active = 0;
* for_each_possible_cpu(cpu)
* nr_active += cpu_of(cpu)->nr_running + cpu_of(cpu)->nr_uninterruptible;
*
* avenrun[n] = avenrun[0] * exp_n + nr_active * (1 - exp_n)
*
* Due to a number of reasons the above turns in the mess below:
*
* - for_each_possible_cpu() is prohibitively expensive on machines with
* serious number of CPUs, therefore we need to take a distributed approach
* to calculating nr_active.
*
* \Sum_i x_i(t) = \Sum_i x_i(t) - x_i(t_0) | x_i(t_0) := 0
* = \Sum_i { \Sum_j=1 x_i(t_j) - x_i(t_j-1) }
*
* So assuming nr_active := 0 when we start out -- true per definition, we
* can simply take per-CPU deltas and fold those into a global accumulate
* to obtain the same result. See calc_load_fold_active().
*
* Furthermore, in order to avoid synchronizing all per-CPU delta folding
* across the machine, we assume 10 ticks is sufficient time for every
* CPU to have completed this task.
*
* This places an upper-bound on the IRQ-off latency of the machine. Then
* again, being late doesn't loose the delta, just wrecks the sample.
*
* - cpu_rq()->nr_uninterruptible isn't accurately tracked per-CPU because
* this would add another cross-CPU cacheline miss and atomic operation
* to the wakeup path. Instead we increment on whatever CPU the task ran
* when it went into uninterruptible state and decrement on whatever CPU
* did the wakeup. This means that only the sum of nr_uninterruptible over
* all CPUs yields the correct result.
*
* This covers the NO_HZ=n code, for extra head-aches, see the comment below.
*/
/* Variables and functions for calc_load */
atomic_long_t calc_load_tasks;
unsigned long calc_load_update;
unsigned long avenrun[3];
EXPORT_SYMBOL(avenrun); /* should be removed */
/**
* get_avenrun - get the load average array
* @loads: pointer to dest load array
* @offset: offset to add
* @shift: shift count to shift the result left
*
* These values are estimates at best, so no need for locking.
*/
void get_avenrun(unsigned long *loads, unsigned long offset, int shift)
{
loads[0] = (avenrun[0] + offset) << shift;
loads[1] = (avenrun[1] + offset) << shift;
loads[2] = (avenrun[2] + offset) << shift;
}
long calc_load_fold_active(struct rq *this_rq, long adjust)
{
long nr_active, delta = 0;
nr_active = this_rq->nr_running - adjust;
nr_active += (int)this_rq->nr_uninterruptible;
if (nr_active != this_rq->calc_load_active) {
delta = nr_active - this_rq->calc_load_active;
this_rq->calc_load_active = nr_active;
}
return delta;
}
/**
* fixed_power_int - compute: x^n, in O(log n) time
*
* @x: base of the power
* @frac_bits: fractional bits of @x
* @n: power to raise @x to.
*
* By exploiting the relation between the definition of the natural power
* function: x^n := x*x*...*x (x multiplied by itself for n times), and
* the binary encoding of numbers used by computers: n := \Sum n_i * 2^i,
* (where: n_i \elem {0, 1}, the binary vector representing n),
* we find: x^n := x^(\Sum n_i * 2^i) := \Prod x^(n_i * 2^i), which is
* of course trivially computable in O(log_2 n), the length of our binary
* vector.
*/
static unsigned long
fixed_power_int(unsigned long x, unsigned int frac_bits, unsigned int n)
{
unsigned long result = 1UL << frac_bits;
if (n) {
for (;;) {
if (n & 1) {
result *= x;
result += 1UL << (frac_bits - 1);
result >>= frac_bits;
}
n >>= 1;
if (!n)
break;
x *= x;
x += 1UL << (frac_bits - 1);
x >>= frac_bits;
}
}
return result;
}
/*
* a1 = a0 * e + a * (1 - e)
*
* a2 = a1 * e + a * (1 - e)
* = (a0 * e + a * (1 - e)) * e + a * (1 - e)
* = a0 * e^2 + a * (1 - e) * (1 + e)
*
* a3 = a2 * e + a * (1 - e)
* = (a0 * e^2 + a * (1 - e) * (1 + e)) * e + a * (1 - e)
* = a0 * e^3 + a * (1 - e) * (1 + e + e^2)
*
* ...
*
* an = a0 * e^n + a * (1 - e) * (1 + e + ... + e^n-1) [1]
* = a0 * e^n + a * (1 - e) * (1 - e^n)/(1 - e)
* = a0 * e^n + a * (1 - e^n)
*
* [1] application of the geometric series:
*
* n 1 - x^(n+1)
* S_n := \Sum x^i = -------------
* i=0 1 - x
*/
unsigned long
calc_load_n(unsigned long load, unsigned long exp,
unsigned long active, unsigned int n)
{
return calc_load(load, fixed_power_int(exp, FSHIFT, n), active);
}
#ifdef CONFIG_NO_HZ_COMMON
/*
* Handle NO_HZ for the global load-average.
*
* Since the above described distributed algorithm to compute the global
* load-average relies on per-CPU sampling from the tick, it is affected by
* NO_HZ.
*
* The basic idea is to fold the nr_active delta into a global NO_HZ-delta upon
* entering NO_HZ state such that we can include this as an 'extra' CPU delta
* when we read the global state.
*
* Obviously reality has to ruin such a delightfully simple scheme:
*
* - When we go NO_HZ idle during the window, we can negate our sample
* contribution, causing under-accounting.
*
* We avoid this by keeping two NO_HZ-delta counters and flipping them
* when the window starts, thus separating old and new NO_HZ load.
*
* The only trick is the slight shift in index flip for read vs write.
*
* 0s 5s 10s 15s
* +10 +10 +10 +10
* |-|-----------|-|-----------|-|-----------|-|
* r:0 0 1 1 0 0 1 1 0
* w:0 1 1 0 0 1 1 0 0
*
* This ensures we'll fold the old NO_HZ contribution in this window while
* accumulating the new one.
*
* - When we wake up from NO_HZ during the window, we push up our
* contribution, since we effectively move our sample point to a known
* busy state.
*
* This is solved by pushing the window forward, and thus skipping the
* sample, for this CPU (effectively using the NO_HZ-delta for this CPU which
* was in effect at the time the window opened). This also solves the issue
* of having to deal with a CPU having been in NO_HZ for multiple LOAD_FREQ
* intervals.
*
* When making the ILB scale, we should try to pull this in as well.
*/
static atomic_long_t calc_load_nohz[2];
static int calc_load_idx;
static inline int calc_load_write_idx(void)
{
int idx = calc_load_idx;
/*
* See calc_global_nohz(), if we observe the new index, we also
* need to observe the new update time.
*/
smp_rmb();
/*
* If the folding window started, make sure we start writing in the
* next NO_HZ-delta.
*/
if (!time_before(jiffies, READ_ONCE(calc_load_update)))
idx++;
return idx & 1;
}
static inline int calc_load_read_idx(void)
{
return calc_load_idx & 1;
}
static void calc_load_nohz_fold(struct rq *rq)
{
long delta;
delta = calc_load_fold_active(rq, 0);
if (delta) {
int idx = calc_load_write_idx();
atomic_long_add(delta, &calc_load_nohz[idx]);
}
}
void calc_load_nohz_start(void)
{
/*
* We're going into NO_HZ mode, if there's any pending delta, fold it
* into the pending NO_HZ delta.
*/
calc_load_nohz_fold(this_rq());
}
/*
* Keep track of the load for NOHZ_FULL, must be called between
* calc_load_nohz_{start,stop}().
*/
void calc_load_nohz_remote(struct rq *rq)
{
calc_load_nohz_fold(rq);
}
void calc_load_nohz_stop(void)
{
struct rq *this_rq = this_rq();
/*
* If we're still before the pending sample window, we're done.
*/
this_rq->calc_load_update = READ_ONCE(calc_load_update);
if (time_before(jiffies, this_rq->calc_load_update))
return;
/*
* We woke inside or after the sample window, this means we're already
* accounted through the nohz accounting, so skip the entire deal and
* sync up for the next window.
*/
if (time_before(jiffies, this_rq->calc_load_update + 10))
this_rq->calc_load_update += LOAD_FREQ;
}
static long calc_load_nohz_read(void)
{
int idx = calc_load_read_idx();
long delta = 0;
if (atomic_long_read(&calc_load_nohz[idx]))
delta = atomic_long_xchg(&calc_load_nohz[idx], 0);
return delta;
}
/*
* NO_HZ can leave us missing all per-CPU ticks calling
* calc_load_fold_active(), but since a NO_HZ CPU folds its delta into
* calc_load_nohz per calc_load_nohz_start(), all we need to do is fold
* in the pending NO_HZ delta if our NO_HZ period crossed a load cycle boundary.
*
* Once we've updated the global active value, we need to apply the exponential
* weights adjusted to the number of cycles missed.
*/
static void calc_global_nohz(void)
{
unsigned long sample_window;
long delta, active, n;
sample_window = READ_ONCE(calc_load_update);
if (!time_before(jiffies, sample_window + 10)) {
/*
* Catch-up, fold however many we are behind still
*/
delta = jiffies - sample_window - 10;
n = 1 + (delta / LOAD_FREQ);
active = atomic_long_read(&calc_load_tasks);
active = active > 0 ? active * FIXED_1 : 0;
avenrun[0] = calc_load_n(avenrun[0], EXP_1, active, n);
avenrun[1] = calc_load_n(avenrun[1], EXP_5, active, n);
avenrun[2] = calc_load_n(avenrun[2], EXP_15, active, n);
WRITE_ONCE(calc_load_update, sample_window + n * LOAD_FREQ);
}
/*
* Flip the NO_HZ index...
*
* Make sure we first write the new time then flip the index, so that
* calc_load_write_idx() will see the new time when it reads the new
* index, this avoids a double flip messing things up.
*/
smp_wmb();
calc_load_idx++;
}
#else /* !CONFIG_NO_HZ_COMMON */
static inline long calc_load_nohz_read(void) { return 0; }
static inline void calc_global_nohz(void) { }
#endif /* CONFIG_NO_HZ_COMMON */
/*
* calc_load - update the avenrun load estimates 10 ticks after the
* CPUs have updated calc_load_tasks.
*
* Called from the global timer code.
*/
void calc_global_load(void)
{
unsigned long sample_window;
long active, delta;
sample_window = READ_ONCE(calc_load_update);
if (time_before(jiffies, sample_window + 10))
return;
/*
* Fold the 'old' NO_HZ-delta to include all NO_HZ CPUs.
*/
delta = calc_load_nohz_read();
if (delta)
atomic_long_add(delta, &calc_load_tasks);
active = atomic_long_read(&calc_load_tasks);
active = active > 0 ? active * FIXED_1 : 0;
avenrun[0] = calc_load(avenrun[0], EXP_1, active);
avenrun[1] = calc_load(avenrun[1], EXP_5, active);
avenrun[2] = calc_load(avenrun[2], EXP_15, active);
WRITE_ONCE(calc_load_update, sample_window + LOAD_FREQ);
/*
* In case we went to NO_HZ for multiple LOAD_FREQ intervals
* catch up in bulk.
*/
calc_global_nohz();
}
/*
* Called from scheduler_tick() to periodically update this CPU's
* active count.
*/
void calc_global_load_tick(struct rq *this_rq)
{
long delta;
if (time_before(jiffies, this_rq->calc_load_update))
return;
delta = calc_load_fold_active(this_rq, 0);
if (delta)
atomic_long_add(delta, &calc_load_tasks);
this_rq->calc_load_update += LOAD_FREQ;
}
6. 测试代码及结果
写一段程序,生成N个一直占用CPU资源的进程,运行T秒,然后观察load-average的值。
可以看到运行1分钟以上后,load-average的第一个值和进程数接近。
6.1 测试代码
# cat forkpid.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
//define a signal handler function to terminate child processes
void handler(int sig)
{
printf("Child process %d received signal %d\n", getpid(), sig);
exit(0);
}
int main(int argc, char *argv[])
{
if(argc != 3) //check the number of arguments
{
printf("Usage: %s <number of processes> <time to run>\n", argv[0]); //print the usage
exit(1);
}
int N = atoi(argv[1]); //convert the first argument to an integer, indicating the number of processes to create
int T = atoi(argv[2]); //convert the second argument to an integer, indicating the time to run
int i;
for(i = 0; i < N; i++) //loop N times
{
pid_t pid; //define a process ID variable
pid = fork(); //create a child process
if(pid == -1) //if creation fails, return -1
{
perror("fork error");
exit(1);
}
else if(pid == 0) //if creation succeeds, return 0, indicating child process
{
printf("This is child process %d, pid is %d\n", i+1, getpid()); //print the child process number and ID
signal(SIGALRM, handler); //register the signal handler function to receive the SIGALRM signal sent by the parent process
for(;;) //execute an infinite loop to consume CPU resources
{
double x = 3.14159; //define a double variable
x = x * x; //calculate the square of x
}
}
}
//parent process waits for T seconds and then sends SIGALRM signal to all child processes to terminate them
sleep(T); //wait for T seconds
kill(0, SIGALRM); //send SIGALRM signal to all processes in the same process group
printf("Parent process sent signal %d to all child processes\n", SIGALRM);
//parent process waits for all child processes to end and gets their return values
int status; //define a status variable
while(wait(&status) != -1) //if there are still child processes not finished, continue to wait
{
printf("Child process exited with status %d\n", status); //print the exit status of the child process
}
return 0;
}
6.2 测试结果
# gcc -o forkpid forkpid.c
# ./forkpid 500 180
在这里运行了500个进程,运行了180秒,可以看到top中的load-average的第一值为482。
# top
top - 09:32:50 up 13:16, 5 users, load average: 482.00, 283.16, 128.12
Tasks: 771 total, 501 running, 270 sleeping, 0 stopped, 0 zombie
%Cpu(s): 99.5 us, 0.2 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st
KiB Mem : 7989848 total, 5106864 free, 1373304 used, 1509680 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 6162116 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
26609 root 20 0 4216 88 0 R 1.5 0.0 0:01.55 forkpid
26503 root 20 0 4216 88 0 R 1.2 0.0 0:01.56 forkpid
26505 root 20 0 4216 88 0 R 1.2 0.0 0:01.55 forkpid
26506 root 20 0 4216 88 0 R 1.2 0.0 0:01.55 forkpid
26507 root 20 0 4216 88 0 R 1.2 0.0 0:01.55 forkpid
26509 root 20 0 4216 88 0 R 1.2 0.0 0:01.55 forkpid