Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

47KB

Developing Your Own 32-Bit Operating System — Chapter-22: Job Management Code

  • Source: /library/Developing Your Own 32-Bit Operating System/source-file.pdf
  • PDF pages: 461–492
  • Pages without text: 461, 492

Job Management Code

I
440 Part PV ■ The Operating System Source Code After you get the kernel out of the way and when you want to begin loading things to run on your system, this is where you'll end up. The program loader is in this chapter. Before I wrote this code, pictures of the complications of writing a loader floated in my head for quite a while. Writing the loader was every bit as difficult as I imagined. The process of allocating all the resources, making sure that each was correctly set and filled out, and finally starting the new task can be complicated. I have tried to break down the process logically and keep it all in a high-level language. Reclamation of Resources Equally as difficult was the death of a job and the reclamation of its valuable resources. In the functions ExitJobO andchain(), I drift in and out of assembly language to do things such as switch to a temporary stack when the running task no longer has a valid stack. When things don't work right, it is usually quite a mess. The resources you reclaim include all the memory, exchanges, link blocks, request blocks, the JCB, TSSs, and finally the operating system memory. Pieces of related code in the kernel, the monitor, and other places provide assistance with resource reclamation. Job Management Helpers The file JobCode.ASM provides many small helper functions for job management. These calls, many of them public functions, are used by almost every module in the operating system. See Listing 22.1. Listing 22.1. Job management subroutines. .DATA .INCLUDE MOSEDF.INC .INCLUDE JOB. INC .INCLUDE TSS.INC PUBLIC pFreeJCB DD 0 ; Ptr to free Job Control Blocks PUBLIC pJCBs DD 0 ; JCBs are in allocated memory PUBLIC nJCBLeft DD nJCBs ; For Monitor stats EXTRN MonJCB DB ; Monitor JCB reference EXTRN _BootDrive DD ; From Main. asm ;======= End data, Begin Code .CODE InitNewJCB is used initially by the OS to fill in the first two jobs (Monitor & Debugger)

Chapter 22 ■ Job Management Code
PUBLIC InitNewJCB:
INPUT
OUTPUT :
USED :
MODIFIES
EAX
EBX
ESI
ECX
Ptr to JCB that is to be filled in
Linear Ptr to Page Directory for Job
pbJobName
cbJobName
EDX — Pointer to Job Virtual Video Buffer (all jobs have one!)
JOB Number in EAX
EAX, EBX, ECX, EDX, EDI, ESI, EFlags
JCB pointed to in EBX This fills in a JCB with new information. This is used to initilaize a new JCB during OS init and when a new Job is loaded and run. MOV [EAX+JcbPD] ,EBX MOV EDI, EAX ADD EDI, sbJobName MOV BYTE PTR [EDI], CL INC EDI REP MOVSB MOV [EAX+pVirtVid], EDX MOV DWORD PTR [EAX+nCols], 80 MOV DWORD PTR [EAX+nLines] , 25 MOV DWORD PTR [EAX+NormAttr] , 1 MOV EAX, [EAX+JobNum] RETN Put Ptr to PD into JCB EDI points to JCB Now to JobName size is filled in first byte of name Move it in Video number is in JCB InitFreeJCB INPUT OUTPUT : USED: MODIFIES: Address of JCBs to be initialized Count of JCBs Size of JCBs EAX ECX EDX NONE EAX, EBX, ECX, EDX, ESI EFLAGS pFreeJCB, pJCBs This routine will initialize the free pool of Job Control Blocks (JCBs) EAX points to the first JCB, ECX is count of JCBs, EDX is size of each JCB. The pFreeJCB pointer is set to address the first element in rgJCBs. Each element of rgJCBs is set to point to the next element of rgJCBs. The last element of rgJCBs is set to point to nothing (NIL) . The JCBs are also sequentially numbered. We can't use its position in the array because some JCBs are static (Mon and Debugger), while others (the ones we are initializing now) are dynamicly allocated. PUBLIC InitFreeJCB: MOV pFreeJCB, EAX MOV pJCBs, EAX MOV EBX, 3 JCB_Loop: MOV ESI, EAX ADD EAX, EDX MOV [ESI+NextJCB],EAX MOV [ESI+JobNum], EBX ;Set up OS pointer to list ;Set up global ptr to first JCB ;lst number for Dynamic JCBs ;EBX has pointer to current one ;EAX points to next one ;Make current point to next ;Number it
I
442
Part PV ■ The Operating System Source Code
Listing 22.1. continued
INC EBX
LOOP JCB_Loop ;Go back till done
MOV DWORD PTR [ESI+NextJCB] , 0 ;Make last one NIL
RETN ;
Allocate 4 pages (16384 bytes) for 32 Job Control Blocks (structures)
Then call InitFreeJCB
PUBLIC InitDynamicJCBs:
PUSH 4
MOV EAX, OFFSET pJCBs
PUSH EAX
CALL FWORD PTR _AllocOSPage
; 4 pages for 32 JCBs (16384 bytes)
; Returns ptr to allocated mem in pJCBs
Get ‘em! XOR EAX, EAX MOV ECX, 4096 MOV EDI, pJCBs REP STOSD Clear allocated memory for JCBs (4*4096=16384 - DWORDS!) where to store 0s Do it MOV EAX, pJCBs MOV ECX, nJCBs MOV EDX, sJCB CALL InitFreeJCB RETN Ptr to JCBs Count of Job Control Blocks EDX is size of a JCB Init the array of JCBs PUBLIC NewJCB: INPUT : NONE OUTPUT : EAX REGISTERS : EAX , EBX , FLAGS MODIFIES : pFreeJCB This routine will return to the caller a pointer to the next free jcb. The data used in this algorithm is the free jcb pointer (pFreeJCB) . This routine will return in EAX register the address of the next free jcb. If none exists, then EAX will contain NIL (0). This routine will also update the value of pFreeJCB to point to the next “unused” JCB in the free pool. MOV EAX, pFreeJCB CMP EAX.0 JE NewJCBDone MOV EBX,[EAX+NextJCB] MOV pFreeJCB, EBX DEC DWORD PTR _nJCBLeft NewJCBDone: RETN Get OS pointer to JCBs IF pFreeJCB=NIL THEN Return; Get pointer to next free one Put it in OS pointer PUBLIC DisposeJCB: ; INPUT : EAX ; OUTPUT : NONE ; REGISTERS : EBX, FLAGS

Chapter 22 ■ Job Management Code |
443 MODIFIES : pFreeJCB This routine will place the jcb pointed to by EAX back into the free pool of JCBs pointed to by (pFreeJCB) if EAX is not NIL. This invalidates the JCB by placing 0 in JcbPD. CMP EAX, 0 JE DispJCBDone MOV DWORD PTR [EAX+JcbPD] MOV EBX, pFreeJCB MOV [EAX+NextJCB],EBX MOV pFreeJCB, EAX INC DWORD PTR _nJCBLeft DispJCBDone: RETN : If pJCBin = NIL THEN Return; Invalidate JCB EBX has OS ptr to free list Move it into newly freed JCB Move ptr to newly frred JCB to OS GetpCrntJCB Returns a pointer to the current Job Control Block in EAX. This is based on which Task is executing. All TSSs are assigned to a Job. A Job may have more than one Task. INPUT: Nothing OUTPUT: EAX — Linear Address of current JCB USED: EAX, EFlags 2PUBLIC GetpCrntJCB: MOV EAX, pRunTSS ; Current Task State Segment MOV EAX, [EAX+TSS_pJCB] ; Pointer to JCB RETN GetCrntJobNum Many OS functions deal with the Job number. The Job number is a field in the JCB structure. Returns the Job number for the currently executing task. This is based on which Task is executing. All TSSs are assigned to a Job! A Job may have more than one Task. INPUT: Nothing OUTPUT: EAX — Current Job Number USED: EAX, EFlags PUBLIC GetCrntJobNum: CALL GetpCrntJCB MOV EAX, [EAX+JobNum] ;Current JCB RETN GetpJCB Returns a pointer to a Job Control Block identified by number in EAX. All TSSs are assigned to a Job. INPUT: EAX - Job Number of desired pJCB

Part PV ■ The Operating System Source Code Listing 22.1. continued OUTPUT: EAX — Linear Address of the JCB or 0 for invalid number USED: EAX, EFlags PUBLIC GetpJCB: PUSH EDX CMP EAX, 1 JNE GetpJCBI MOV EAX, OFFSET MonJCB POP EDX RETN GetpJCBI : CMP EAX, 2 JNE GetpJCB2 MOV EAX, OFFSET DbgJCB POP EDX RETN GetpJCB2: CMP EAX, nJCBs+2 JLE GetpJCB3 XOR EAX, EAX POP EDX RETN GetpJCB3: SUB EAX, 3 MOV EDX, SJCB MUL EDX ADD EAX, pJCBs POP EDX RETN ;Add in two static JCBs ;Within range of JCBs ;Take off static JCBs+1 (make it an offset) ;Times size of JCB ;Now points to desired JCB GetJobNum Many OS functions deal with the Job number. The Job number is a field in the JCB structure. Returns the Job number for the pJCB in EAX in EAX. INPUT: EAX pJCB we want job number from. OUTPUT: EAX - Current Job Number USED: EAX, EFlags PUBLIC GetJobNum: MOV EAX, [EAX+JobNum] RETN jCurrent JCB AllOCJCB (NEAR) This allocates a new JCB (from the pool). This is a NEAR call to support the public job management calls in high level languages. Procedural Interface : AllocJCB(pdJobNumRet, ppJCBRet) :ercType

Chapter 22 ■ Job Management Code S pdJobNumRet is the number of the new JCB. pJCBRet is a pointer where you want the pointer to the new JCB is returned. ErcNoMoreJCBs will be returned if no more JCBs are avaialble. ; pdJobNum EQU [EBP+12] ; pJCBRet EQU [EBP+8] PUBLIC AllocJCB: PUSH EBP MOV EBP, ESP CALL NewJCB Get a new JCB OR EAX, EAX JNZ SHORT AJCB01 We got one! MOV EAX, ErcNoMoreJCBs Sorry, out of them! MOV ESP, EBP POP EBP RETN 8 AJCB01: MOV ESI, [EBP+8] pJCBRet MOV [ESI], EAX MOV ESI, [EBP+12] Job Num CALL GetJobNum MOV [ESI], EAX XOR EAX, EAX No error MOV ESP, EBP POP EBP RETN 8 DeAllocJCB (NEAR) This Deallocates a JCB (returns it to the pool). This is a NEAR call to support the public job management calls in high level languages in the OS code. Procedural Interface : DeAllocJCB(pJCB) :ercType pJCB is a pointer the JCB to be deallocated. ErcNoMoreJCBs will be returned if no more JCBs are avaialble. pJCB EQU [EBP+8] PUBLIC _DeAllocJCB: PUSH EBP MOV EBP, ESP MOV EAX, [EBP+8] CALL DisposeJCB XOR EAX, EAX MOV ESP, EBP POP EBP pJCB Get a new JCB

I
446 Part IV ■ The Operating System Source Code Listing 22.1. continued BEGIN PUBLIC FAR JOB CALLS GetpJCB This PUBLIC returns a pointer to the JCB for the JobNum you specifiy. Procedural Interface : GetpJCB(dJobNum, pJCBRet) :ercType dJobNum is the number of the JCB you want. pJCBRet is a pointer where you want the JCB returned. ErcBadJobNum will be returned if dJobNum is out of range ErcBadJobNum will be returned if dJobNum is invalid or 0 will be returned with the data. dJobNum EQU [EBP+16] pJCBRet EQU [EBP+12] PUBLIC _GetpJCB: ; PUSH EBP ; MOV EBP, ESP ; MOV EAX, [EBP+16] ;Job Num OR EAX, EAX JZ GetpJcbBad CMP EAX, nJCBs + 2; JBE GetpJcbOK GetpJcbBad: MOV EAX, ErcBadJobNum MOV ESP, EBP POP EBP RETF 8 GetpJcbOk: CALL GetpJCB MOV ESI, [EBP+12] MOV [ESI], EAX CMP DWORD PTR [EAX+JcbPD], 0 JNE GetpJCBOkl MOV EAX, ErcInvalidJCB MOV ESP, EBP POP EBP RETF 8 GetpJcbOkl : XOR EAX, EAX 8 MOV ESP, EBP POP EBP RETF 8 0 is invalid Dynamic + 2 static ;puts address of JCB in EAX ; pJCBRet Is this a valid JCB JCB we are pointing to is unused

Chapter 22 ■ Job Management Code H GetJobNum This PUBLIC returns the number for the current Job. This is the job that the task that called this belongs to. Procedural Interface : GetJobNum (pJobNumRet) :ercType pJCBRet is a pointer where you want the JCB returned. pJobNumRet EQU [EBP+12] PUBLIC _GetJobNum: PUSH EBP MOV EBP, ESP CALL GetCrntJobNum MOV ESI, [EBP+12] MOV [ESI], EAX XOR EAX, EAX POP EBP RETF 4 Leave jobnum in EAX pJobNumRet No Error GetSystemDisk This PUBLIC returns a single byte which represents the disk that the system booted from. This is from the public variable _BootDrive defined in Main. asm. It returns 0-n (which corresponds to drive A-x) This code is here for lack of a better place. It's really not a filesystem function either. And will still be needed if a loadable filesystem is installed. Procedural Interface : GetSystemDisk (pSysDiskRet) :ercType pSysDiskRet is a pointer to a byte where the number representing the system disk is returned. pSysDiskRet EQU [EBP+12] PUBLIC _GetSystemDisk PUSH EBP MOV EBP, ESP MOV EAX, _BootDrive AND EAX, 7Fh MOV ESI, [EBP+12] MOV [ESI], AL XOR EAX, EAX POP EBP RETF 4 . ================= MODULE END get rid of high bits pJobNumRet No Error

aaq I Part IV ■ The Operating System Source Code Job Management Listing This file, Jobc.c, contains all the major high-level functions for job management. Functions for starting and ending jobs and public functions for working with the job control block are contained in this module. See Listing 22.2. Listing 22.2. Job management code. /* This file contains functions and data used to support loading or terminating jobs (or services). It contains the public functions: Chain () Loads new job run file in current JCB & PD LoadNewJobO Loads a new job into a new JCB & PD ExitJob() Exits current job, loads ExitJob if specified GetExitJob() SetExitJob() Gets run file name that will be loaded upon ExitJob Sets run file name to load upon ExitJob SetCmdLine( ) GetCmdLine() Sets the command line for next job Gets the command line for the current job GetPath() Setpath() Gets the path prefix for the current job Sets the path prefix for the current job SetUserName( ) GetUserName() Sets the User-name for the current job Gets the Username for the current job #define U32 unsigned long #define S32 long #define U16 unsigned int #define S16 int #define U8 unsigned char #define S8 char #define TRUE 1 #define FALSE 1 #include “MKernel.h” #include “MMemory.h” #include “MData.h” #include “MTimer.h” #include “MVid.h” #include “MKbd.h” ^include “MJob.h” ^include “MFiles.h” ^include “runfile.h” #define MEMUSERD 7 /* User Writable (Data) */ ^define ErcOpCancel 4 /’ ^define ErcOutOfRange 10 /’ ^define ErcBadJobNum 70 /’ Operator cancel */ Bad Exchange specified in OS call */ A Bad job number was specified */

Chapter 22 ■ Job Management Code |
449 | #define ErcNoExitJob 76 /* No ExitJob specified on ExitJob(n) */ #define ErcBadRunFile 74 /* Couldn't load specified ExitRunFile */ /* Near Support Calls from JobCode.ASM */ extern long AllocJCB(long *pdJobNumRet , char *ppJCBRet); extern long RemoveRdyJob(char *pJCB) ; extern long GetExchOwner(long Exch, char *pJCBRet); extern long SetExchOwner(long Exch, char *pNewJCB); extern long SendAbort(long JobNum, long Exch); /* Temporary NEAR externals from the monitor program for debugging */ extern long xprintf(char *fmt, ...); extern U32 Dump(unsigned char *pb, long cb) ; /* We switch to this stack when we clean out a user PD before we rebuild it. */ static long TmpStackf 128] ; /* 512 byte temporary stack */ /* Used for allocating/filling in new JCB */ static struct JCBRec *pNewJCB; /* Used to access a JCB */ static struct JCBRec *pTmpJCB; static struct JCBRec *pCrntJCB; static long JobNum; /* For ExitJob and Chain cause They can't have local vars! */ static long JobNumE, job_fhE; static long ExchE, ercE, iE; static long BogusMsg[2]; static long *pPDE; static char *pExchJCBE static long KeyCodeE; static char aFileE[80]; static long cbFileE; extern unsigned long KillExch; /* From the Monitor */ /* Run file data */ static char *pCode, *pData, *pStack; /* Ptrs in User mem to load to */ static long sCode, sData, sStack; /* Size of segments */ static unsigned long oCode, oData; /* Offset in file to Code & Data */ static long offCode = 0, /* Virtual Offset for Code & Data Segs */ offData = 0; static unsigned long nCDFIX = 0, oCDFIX = 0, nCCFIX = 0, OCCFIX = 0, nDDFIX = 0,

Part PV ■ The Operating System Source Code Listing 22.2. continued ODDFIX = 0, nDCFIX = 0, ODCFIX = 0; static char pStart, filetype; static struct tagtype tag; /************ INTERNAL SUPPORT CALLS ******************/ This deallocates all user memory in a PD. This is used when we ExitJob to free up all the memory. We get the pointer to the PD and “walk” through all the User PTs deallocating linear memory. When all PTs have been cleaned, we eliminate the user PDEs. This leaves a blank PD for reuse if needed. NOTE: This must be called from a task that is running in the JCB that owns the memory! static void CleanUserPD(long *pPD) { long i, j , k, ere; unsigned long *pPT; char *pMem; for (i=768; i<1024; i++) { /* Look at each shadow PDE */ if (pPD[i]) { /* If it's a linear address (non 0)*/ pPT = pPD[i] & 0xFFFFF000; /* Point to Page Table */ for (j=0; j<1024; j++) { /* Get Beginning address for each run to deallocate */ k = 0; /* nPages to deallocate */ pMem = ((i-512) * 0x400000) + (J * 4096); while ((pPT[j++]) && (j<1024)) k++; /* one more page */ if (k) { /* we have pages (one or more) */ ere = DeAllocPage(pMem, k); } This opens, reads and validates the run file. If all is well, it leaves it open and returns the file handle, else it closes the run file and returns the error to the caller. The caller is responsible for closing the file 1 1 1

Chapter 22 ■ Job Management Code static long GetRunFile(char *pFileName, long cbFileName, long *pfhRet) { long ere, i, fh, dret; char fDone, junk; off Code = 0; offData = 0; nCDFIX = 0 oCDFIX = 0 nCCFIX = 0 oCCFIX = 0 nDDFIX = 0 ODDFIX = 0 nDCFIX = 0 oDCFIX = 0 pfhRet = 0; / default to 0 / / Mode Read, Stream type / ere = OpenFile(pFileName, cbFileName, 0, 1, &fh); if (!erc) { / File opened OK / fDone = 0; while ((!erc) && (IfDone)) { tag. id = 0; ere = ReadBytes (fh, &tag, 5, &dret); switch (tag. id) { case IDTAG: ere = ReadBytes (fh, &filetype, 1, &dret); if ((filetype < 1) || (filetype > 3)) ere = ErcBadRunFile; break; case SEGTAG: ere = ReadBytes (fh, &sStack, 4, &dret); if (!erc) ere = ReadBytes (fh, &sCode, 4, &dret); if (!erc) ere = ReadBytes (fh, SsData, 4, &dret); break; case DOFFTAG: ere = ReadBytes (fh, &offData, 4, &dret); break; case COFFTAG: ere = ReadBytes (fh, &offCode, 4, &dret); break; case STRTTAG: ere = ReadBytes (fh, SpStart, 4, &dret); break; case CODETAG: ere = GetFileLFA(fh, &oCode); if (!erc) ere = SetFileLFA(f h, oCode+tag.len) ; / skip it / break; case DAT AT AG: ere = GetFileLFA(fh, &oData); if (!erc) ere = SetFileLFA(fh, oData+tag.len) ; / skip it */ continues

452| Part IV ■ The Operating System Source Code Listing 22.2. continued break; case CDFIXTAG: ere = GetFileLFA(fh, &0CDFIX); nCDFIX = tag.len/4; if (!erc) ere = SetFileLFA(fh, oCDFIX+tag.len) ; /* skip it */ break; case CCFIXTAG: ere = GetFileLFA(fh, &0CCFIX); nCCFIX = tag.len/4; if (!erc) ere = SetFileLFA(fh, oCCFIX+tag.len) ; /* skip it */ break; case DDFIXTAG: ere = GetFileLFA(fh, &0DDFIX); nDDFIX = tag.len/4; if (!erc) ere = SetFileLFA(fh, oDDFIX+tag.len) ; /* skip it */ break; case DCFIXTAG: ere = GetFileLFA(fh, &0DCFIX); nDCFIX = tag.len/4; if (!erc) ere = SetFileLFA(fh, oDCFIX+tag.len) ; /* skip it */ break; case ENDTAG: fDone = TRUE; break; default: ere = GetFileLFA(fh, &i); if (tag.len > 1024) ere = ErcBadRunFile; if (!erc) ere = SetFileLFA(fh, i+tag.len); /* skip it */ if (ere) ere = ErcBadRunFile; break; } if (ere) CloseFile(fh); } if (!erc) *pfhRet = fh; if (ere) xprintf(“Erc from GetRunFile in JobC: %d\r\n”, ere); return (ere); } /*********** PUBLIC CALLS FOR JOB MANAGEMENT ************/ /••••••••••••••••• .••.•.•..*••**.*.**.*. /

Chapter 22 ■ Job Management Code |453 long far _SetExitJob(char pRunFile, long dcbRunFile) { long JobNum; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); / Get pJCB to current Job / if (dcbRunFile > 79) return (ErcBadJobParam) ; else if ( IdcbRunFile) pTmpJCB->JcbExitRF[0] = 0; else { CopyData(pRunFile, &pTmpJCB->JcbExitRF[ 1 ] , dcbRunFile); pTmpJCB->JcbExitRF[0] = dcbRunFile; } return(0) : long far _GetExitJob(char pRunRet, long pdcbRunRet) { long JobNum, i; GetJobNum(&JobNum) ; GetpJCB (JobNum, &pTmpJCB); / Get pJCB to current Job / i = pTmpJCB->JcbExitRF[0] ; if (i) CopyData(&pTmpJCB->JcbExitRF[1 ] , pRunRet, i) ; ♦pdcbRunRet = i; return(0) ; } long far _SetPath(char pPath, long dcbPath) { long JobNum; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); / Get pJCB to current Job / if (dcbPath > 69) return (ErcBadJobParam); else if (IdcbPath) pTmpJCB->sbPath[0] = 0; else { CopyData(pPath, &pTmpJCB->sbPath[ 1 ] , dcbPath); pTmpJCB->sbPath[0] = dcbPath; } return(0) ; } /it*********************************************/ long far _GetPath(long JobNum, char *pPathRet, long *pdcbPathRet) { long i, ere; ere = GetpJCB(JobNum, &pTmpJCB); /* Get pJCB to JobNum */ if (lerc) { i = pTmpJCB->sbPath[0];

ac / Part TV ■ The Operating System Source Code Listing 22.2. continued if (i) CopyData(&pTmpJCB->sbPath[1 ] , pPathRet, i) ; *pdcbPathRet = i; } return(erc) ; } long far _SetCmdLine(char pCmd, long dcbCmd) { long JobNum; GetJobNum(&JobNum) ; GetpJCB (JobNum, &pTmpJCB) ; / Get pJCB to current Job */ if (dcbCmd > 79) return (ErcBadJobParam) ; else if ( IdcbCmd) pTmpJCB->JcbCmdLine[0] = 0; else { CopyData(pCmd, &pTmpJCB->JcbCmdLine[1 ] , dcbCmd); pTmpJCB->JcbCmdLine[0] = dcbCmd; } return(0) ; } long far _GetCmdLine(char *pCmdRet, long pdcbCmdRet) { long JobNum, i; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB) ; / Get pJCB to current Job */ i = pTmpJCB->JcbCmdl_ine[0] ; if (i) CopyData(&pTmpJCB->JcbCmdLine[1 ] , pCmdRet, i); *pdcbCmdRet = i; return(0) ; long far _SetUserName(char pUser, long dcbUser) { long JobNum; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); / Get pJCB to current Job */ if (dcbUser > 29) return (ErcBadJobParam); else if ( IdcbUser) pTmpJCB->sbUserName[0] = 0; else { CopyData(pUser, &pTmpJCB->sbUserName[ 1 ] , dcbUser); pTmpJCB->sbUserName[0] = dcbUser; return(0)

Chapter 22 M Job Management Code s long far _GetUserName(char pUserRet, long pdcbUserRet) { long JobNum, i; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); / Get pJCB to current Job / i = pTmpJCB->sbUserName[0] ; if (i) CopyData(&pTmpJCB->sbUserName[1 ] , pUserRet, i); pdcbUserRet = i; return(0) ; } long far _SetSysIn(char pName, long dcbName) { long JobNum; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); / Get pJCB to current Job / if ((dcbName > 49) J| (IdcbName)) return (ErcBadJobParam) ; else { CopyData(pName, &pTmpJCB->JcbSysIn[1 ] , dcbName); pTmpJCB->JcbSysIn[0] = dcbName; } return(0) ; } /A*********************************************/ long far _GetSysIn(char *pFileRet, long *pdcbFileRet) { long JobNum, i; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); /* Get pJCB to current Job */ i = pTmpJCB->JcbSysIn[0] ; if (i) CopyData(&pTmpJCB->JcbSysIn[1 ] , pFileRet, i); *pdcbFileRet = i; return(0) ; } long far _SetSysOut (char *pName, long dcbName) { long JobNum; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); /* Get pJCB to current Job */ if ((dcbName > 49) \ (IdcbName)) return (ErcBadJobParam); else { CopyData(pName, &pTmpJCB->JcbSys0ut[1 ] , dcbName); pTmpJCB->JcbSysOut[0] = dcbName;

|436 Part IV ■ The Operating System Source Code Listing 22.2. continued } return(0) ; } long far _GetSysOut(char *pFileRet, long pdcbFileRet) { long JobNum, i; GetJobNumf&JobNum) ; GetpJCB (JobNum, &pTmpJCB) ; / Get pJCB to current Job */ i = pTmpJCB->JcbSysOut[0] ; if (i) CopyData(&pTmpJCB->JcbSys0ut[1 ] , pFileRet, i); *pdcbFileRet = i; return (0) ; } long far _SetJobName(char pName, long dcbName) { long JobNum; GetJobNum(&JobNum); GetpJCB(JobNum, &pTmpJCB); / Get pJCB to current Job */ if (dcbName > 13) dcbName = 13; if (dcbName) CopyData(pName, &pTmpJCB->sbJobName[ 1 ] , dcbName); pTmpJCB->sbJobName[0] = dcbName; return(0) ; } This creates and loads a new Job from a RUN file. This returns ErcOK and new job number if loaded OK else an error is returned. long far _LoadNewJob(char *pFileName, long cbFileName, long *pJobNumRet) { long ere, i, fh, dret, nPages; unsigned long *pPD, *pPT, *pVid, *pOSPD; long pFix; U32 PhyAdd; ere = GetRunFile(pFileName, cbFileName, &fh); if (!erc) { / We set these to zero so we can tell if they have been allocated in case we fail so we know what to deallocate */ JobNum ■ 0; pNewJCB = 0;

Chapter 22 ■ Job Management Code pPD = 0; pPT = 0; pVid = 0; ere = AllocJCB(&JobNum, &pNewJCB); /* Alloc OS memory pages required for new job */ if (!erc) ere = AllocOSPage(3, &pPD) ; /* Job's PD, PT * pVirtVid */ pPT = pPD + 4096; /* 1 page later */ pVid = pPD +8192; /* 2 pages later */ if (!erc) { FillData(pPT, 4096, 0); /* Zero user PT */ FillData(pVid, 4096, 0); /* Zero user video */ GetpJCB(1, &pTmpJCB); /* Get OS pJCB */ pOSPD = pTmpJCB->pJcbPD; /* Pointer to OS PD */ GetPhyAdd(1, pPT, &PhyAdd); /* Get Phy Add for new PT */ PhyAdd |= MEMUSERD; /* Set user code bits in PDE */ pOSPD[256] = PhyAdd; /* Make User PDE in OS PD */ pOSPD[768] = pPT; /* Shadow Linear Address of PT */ /* xprintf (“pOSPD : %08x\r\n”, pOSPD) ; xprintf (“pUserPD: %08x\r\n”, pPD) ; xprintf (“pUserPT: %08x\r\n”, pPT); xprintf (“PhyAdd : %08x\r\n”, PhyAdd); ReadKbd(&KeyCodeE, 1); */ /* Now we can allocate User Job Memory */ /* Allocate user memory for Stack Code and Data */ /* This is STILL done in the OS PD */ nPages = sStack/4096; if (sStack%4096) nPages++; ere = AllocPage(nPages, SpStack); sStack = nPages * 4096; /* set to whole pages */ nPages = sCode/4096; if (sCode%4096) nPages++; if (!erc) ere = AllocPage(nPages, &pCode); nPages = sData/4096; if (sData%4096) nPages++; if (!erc) ere = AllocPage( nPages, &pData); /* Right now, the OS PD looks exacly like we want the User PD to look. We will now copy the entire OS PD into the User's New PD.

/col Part IV ■ The Operating System Source Code Listing 22.2. continued / CopyData(pOSPD, pPD, 4096); / Copy OS PD to User PD / / All Job memory is now allocated, so let's LOAD IT! / if (!erc) ere = SetFileLFA(fh, oCode); if (!erc) ere = ReadBytes (fh, pCode, sCode, &dret); if (!erc) ere = SetFileLFA(fh, oData); if (!erc) ere = ReadBytes (fh, pData, sData, &dret); / Now that we have read in the code and data we apply fixups to these segments from the runfile / if (!erc) { if (nCDFIX) { ere = SetFileLFA(fh, oCDFIX); / back to fixups / while ((nCDFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pCode + i; / Where in CSeg */ *pFix = pFix - offData + pData; } } if (nCCFIX) { ere = SetFileLFA(fh, oCCFIX); / back to fixups / while ((nCCFIX-) && (!erc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pCode + i; / Where in CSeg */ *pFix = pFix - off Code + pCode; } } if (nDCFIX) { ere = SetFileLFA(fh, oDCFIX); / back to fixups / while ((nDCFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pData + i; / Where in DSeg */ *pFix = pFix - off Code + pCode; } } if (nDDFIX) { ere = SetFileLFA(f h, oDDFIX); / back to fixups / while ((nDDFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pData + i; / Where in DSeg */ •pFix = *pFix - offData + pData; }

Chapter 22 ■ Job Management Code

/* Clean the OS PD of User memory */ FillData(&pOSPD[256], 1024, 0); /* Clean OS PD of User PDEs */ FillData(&pOSPD[768], 1024, 0); /* Clean OS PD of User Shadow */ /* Now we fill in the rest of the User's JCB */ pNewJCB->pJcbPD = pPD; /* Lin Add of PD */ pNewJCB->pJcbStack = pStack; /* Add of Stack */ pNewJCB->sJcbStack = sStack; /* Size of Code */ pNewJCB->pJcbCode = pCode; /* Add of Code */ pNewJCB->sJcbCode = sCode; /* Size of Code */ pNewJCB->pJcbData = pData; /* Add of Code */ pNewJCB->sJcbData = sData; /* Size of Code */ pNewJCB->sbUserName[0] pNewJCB->sbPath[0] = 0 pNewJCB->JcbExitRF[0] ■ pNewJCB ->JcbCmdLine [ 0 ] /* Zero UserName */ /* No Default path */ /* No Exit Run File */ /* No Cmd Line */ CopyData(“KBD”, &pNewJCB->JcbSysIn[1 ] , 3); pNewJCB->JcbSysIn[0] = 3; /* Size */ CopyData(“VID”, 4pNewJCB->JcbSys0ut[1 ] , 3): pNewJCB->JcbSysOut[0] = 3; /* Size */ pNewJCB->pVidMem = pVid; pNewJCB->pVirtVid = pVid; pNewJCB->CrntX = 0; pNewJCB->CrntY = 0; pNewJCB->nCols = 80; pNewJCB->nLines = 25; /* Default to Virt Vid */ /* Virtual Video memory */ /* Vid X Position */ /* Vid Y Position */ /* Columns */ /* Lines */ pNewJCB->VidMode = 0; pNewJCB->fCursOn = 1; pNewJCB->fCursType = 0; /* 80x25 VGA Color Text */ /* Cursor On */ /* UnderLine */ /* Finally, we crank up the new task and schedule it for execution! */ if (!erc) ere = AllocExch(&i) ; if (!erc) ere = NewTask(JobNum, 0x18, 25, 0, i, pStack+sStack-4, pStart+pCode -off Code) ; if (I ere) SetExchOwner(i, pNewJCB); /* Exch now belongs to new JCB */ CloseFile(fh); /* read run file OK */

I
460 I Part W ■ The Operating System Source Code Listing 22.2. continued
if (!erc) *pJobNumRet = JobNum; return(erc) ; } This loads a job into an existing PD and JCB. This is called by Chain() and may also be called by ExitJob() if an ExitJob was specified in the current JCB. The PD, pVid & first PT still exist in OS memory. (CleanPD left the first PT for us). ExitJob and Chain are responsible for opening and validating the runfile and setting up the run file variables. static long LoadJob(char *pJCB, long fh) { long ere, i, dret, nPages; long pFix; pNewJCB = pJCB; / Allocate user memory for Stack, Code and Data / / This is done in the context of the USER PD / nPages = sStack/4096; if (sStack%4096) nPages++; ere = AllocPage( nPages, &pStack); sStack = nPages * 4096; / set to whole pages / nPages = sCode/4096; if (sCode%4096) nPages++; if (lerc) ere = AllocPage(nPages, &pCode); nPages = sData/4096; if (sData%4096) nPages++; ere = AllocPage (nPages, &pData); / All Job memory is now allocated, so let's LOAD IT! / if (lerc) ere = SetFileLFA(fh, oCode); if (lerc) ere = ReadBytes (fh, pCode, sCode, &dret); if (lerc) ere = SetFilel_FA(fh, oData); if (lerc) ere = ReadBytes (fh, pData, sData, &dret); / Now that we have read in the code and data we apply fixups to these segments from the runfile

Chapter 22 Job Management Code H (lerc) { if (nCDFIX) { ere = SetFileLFA(fh, oCDFIX); /* back to fixups */ while ((nCDFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pCode + i; /* Where in CSeg */ *pFix = *pFix - off Data + pData; } (nCCFIX) { ere = SetFileLFA(fh, oCCFIX); /* back to fixups */ while ((nCCFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pCode + i; /* Where in CSeg */ *pFix = *pFix - off Code + pCode; } /* back to fixups */ if (nDCFIX) { ere = SetFileLFA(fh, oDCFIX) while ((nDCFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pData + i; /* Where in DSeg */ *pFix = *pFix - off Code + pCode; } } if (nDDFIX) { ere = SetFileLFA(fh, oDDFIX) ; /* back to fixups */ while ((nDDFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pData + i; /* Where in DSeg */ *pFix = *pFix - off Data + pData; } /* Now we fill in the rest of the User's JCB */ pNewJCB->pJcbStack = pStack; /* Add of Stack */ pNewJCB->sJcbStack = sStack; /* Size of Code */ pNewJCB->pJcbCode = pCode; /* Add of Code */ pNewJCB->sJcbCode = sCode; /* Size of Code */ pNewJCB->pJcbData = pData; /* Add of Code */ pNewJCB->sJcbData = sData; /* Size of Code */ } CloseFile(fh); return(erc) ; This is started as a new task in the context of a job that is being killed off. This is done to allow memory access and also reuse the code for ExitJob in the monitor. This task, its exchange, and TSS

/^2 Part IV ■ The Operating System Source Code Listing 22.2. continued will be reclaimed by the monitor along with the JCB and all OS memory pages for the PD.PT and video. void _KillTask(void) { GetJobNum(&JobNumE) ; GetpJCB(JobNumE, &pTmpJCB) ; /* Get pJCB to this Job */ /* Clean the PD of all user memory leaving OS memory to be deallocated by the caller (monitor or whoever). */ pPDE = pTmpJCB->pJcbPD; CleanUserPD(pPDE); GetTSSExch(&ExchE); ercE = 0; while(lercE) /* clear the exchange */ ercE = CheckMsg(ExchE, BogusMsg); ISendMsg(KillExch, ExchE, ErcOpCancel) ; SetPriority(31); WaitMsg( ExchE, BogusMsg); White(1); /*In case we get scheduled again*/ /* He's History! */ This is called from one job to kill another job or service. This cleans up ALL resources that the job had allocated. This is used to kill run-away jobs, or terminate a job just for the fun of it. It results in a violent death for the job specified. This must never be called from a task within the job to be killed. A job may terminate itself with ExitJob(). long far _KillJob(long JobNum) { long ere; /* Make sure it's not the Monitor, Debugger or the current job. */ GetJobNum(&JobNumE) ; if ((JobNum == JobNumE) | \ (JobNum ==1) ! | (JobNum == 2)) return(ErcBadJobNum) ; ere = GetpJCB(JobNum, &pTmpJCB) ; /* Get pJCB to the Job */ if (ere)

Chapter 22 ■ Job Management ( .ode |
463 return(erc) ; pTmpJCB->ExitError = ErcOpCancel; /* Operator said DIE! */ /* Remove ALL tasks for this job that are at the ReadyQue. The task we are in does not belong to the job we are killing so we can remove them all. */ RemoveRdyJob(pTmpJCB) ; /* It always returns ErcOk */ /* Deallocate ALL exchanges for this job */ ercE = 0; iE = 0; while (ercE != ErcOutOfRange) { ercE = GetExchOwner(iE, &pExchJCBE); if ((lercE) && (pExchJCBE == pTmpJCB)) DeAllocExch(iE); iE++; } ercE = 0; /* Clear the error */ /* Now that the user can't make anymore requests, We send “Abort” messages to all services. This closes all files that were opened by the Job and frees up any other resources held for this job by any service. We will allocate one exchange for the job that is being killed so we can use it for SendAbort and also as the exchange number we send to the KillExch in the monitor which will kill of the JCB completely (he also switches video and keyboard if needed) . */ ere = AllocExch(&ExchE) ; /* Get an Exch */ SetExchOwner(ExchE, pTmpJCB) ; /* make him the owner */ SendAbort (JobNum, ExchE); /* Notify all services */ /*JobNum, CodeSeg, Priority, fDebug, Exch, ESP, EIP */ ere = NewTask (JobNum, 0x08, 3, 0, ExchE, &TmpStack[127] , &_KillTask) return(erc) ; /* He's History! */ This is called from Exit() in C or directly from a user job or service. This cleans up ALL resources that the job had allocated. This also checks for an exit run file to load if one is specified. If no exit run file is specified we just kill the JCB entirely and if video and keyboard are assigned we assign them to the Monitor.

asa I Part IV ■ The Operating System Source Code Listing 22.2. continued void far _ExitJob(long dError) { /* NO LOCAL VARIABLES BECAUSE WE SWITCH STACKS!! */ GetJobNum(&JobNumE) ; GetpJCB(JobNumE, SpCrntJCB); /* Get pJCB to current Job */ pCrntJCB->ExitError = dError; /* Remove ALL tasks for this job that are at the ReadyQue. The task we are in won't be removed because it's RUNNING! */ RemoveRdyJob(pCrntJCB) ; /* It always returns ErcOk */ /* Deallocate all exchanges for this job except the one belonging to current TSS! The Dealloc Exchange call will invalidate all TSSs found at exchanges belonging to this user, and will also free up RQBs and Link Blocks. The job will not be able to initiate requests or send messages after this unless it is done with the TSSExchange because it will get a kernel error (invalid exchange). /* Find out what our TSS exchange is so we don't deallocate it too! */ GetTSSExch(&ExchE); ercE = 0; iE = 0; while (ercE != ErcOutOfRange) { ercE = GetExchOwner(iE, &pExchJCBE); if ((!ercE) && (iE != ExchE) && (pExchJCBE == pCrntJCB)) DeAllocExch(iE); iE++; } /* Now that the user can't make anymore requests, Send Abort messages to all services. This closes all files that were opened by the Job and frees up any other resources held for this job by any service. SendAbort(JobNumE, ExchE); ercE = 0; /* Clear the error */ while(lercE) /* clear the exchange */ ercE ■ CheckMsg(ExchE, BogusMsg); ercE ■ 0; /* Clear the error */ /* We must now switch to a temporary stack so we can clean out the user PD (we are on his stack right now!) */ #asm MOV EAX, OFFSET _TmpStack

Chapter 22 M Job Management Code ADD EAX, 508 MOV ESP, EAX MOV EBP, EAX #endasm /* Clean the PD of all user memory leaving OS memory for next job if there is one. */ pPDE = pCrntJCB->pJcbPD; CleanUserPD(pPDE); /* Look for Exit Run file to load if any exists. If no exit run file, we deallocate the PD and JCB then return to JOB 1. */ GetExitJob(aFileE, AcbFileE); /* Exit Run File!! */ if (IcbFileE) ercE = ErcNoExitJob; if (!ercE) ercE = GetRunFile(aFileE, cbFileE, &job_fhE); if (lercE) ercE = LoadJob(pCrntJCB, job_fhE); if (lercE) { pStart = pStart+pCode -off Code; /* Now we RETURN to new job's address after we put him on his new stack. */ #asm MOV EAX, _pStack MOV EBX, sStack ADD EAX, EBX SUB EAX, 4 MOV ESP, EAX MOV EBP, EAX PUSH 18h MOV EAX, pStart PUSH EAX RETF #endasm } if (ercE) { /* somet ;We are history! something failed or we don't have an ExitRF */ /* In case there is no job to run or a fatal error has happened we send a message (ISendMsg) to the monitor status task with our TSSExch and the Error. Then he will WIPE US OUT! We use ISend (vice send) so he can't run before we get to the exchange otherwise we will be placed back on the readyQueue! */ ISendMsg(KillExch, ExchE, ercE);

Part IV ■ The Operating System Source Code Listing 22.2. continued SetPriority(31); WaitMsg(ExchE, BogusMsg); while(l); /In case we get scheduled again/ /* We are NO MORE */ } } This is called to execute a program without changing the ExitJob. This is so you can run program B from program A and return to program A when program B is done. This runs Job B in the “context” of Job A which means Job B inherits the JCB and PD of job A so it can use things like the command line and path that were set up by A. Information can be passed to Job B by calling SetCmdLine (if Job B reads it), and also by setting the ExitError value in the parameter. Chain will only return to you if there was an error loading the Job. In other words, if Chain fails in a critial section we try to load the ExitJob and pass it the error. long far _Chain(char *pFileName, long cbFileName, long dExitError) { /* NO LOCAL VARIABLES BECAUSE WE SWITCH STACKS!! */ CopyData(pFileName, aFileE, cbFileName); cbFileE = cbFileName; ercE = GetRunFile(pFileName, cbFileName, &job_fhE); if (ercE) { CloseFile( job_fhE) ; /* if it had a handle at all */ return(ercE) ; } CloseFile( job_fhE) ; /* we will open it again after SendAbort */ 2 GetJobNum(&JobNumE) ; GetpJCB(JobNumE, &pCrntJCB) ; /* Get pJCB to current Job */ pCrntJCB->ExitError = dExitError; /* Remove ALL tasks for this job that are at the ReadyQue. The task we are in won't be removed because it's RUNNING! */ RemoveRdyJob(pCrntJCB) ; /* It always returns ErcOk */ /* Deallocate all exchanges for this job except the one belonging to current TSS! The Dealloc Exchange call will invalidate all TSSs found at exchanges belonging to this user, and will also free up RQBs and Link Blocks. The job will not be able to initiate requests or send messages after this unless it is done with the TSSExchange because it will get a kernel

Chapter 22 ■ Job Management Code H error (invalid exchange). / / Find out what our TSS exchange is so we don't deallocate it too! / GetTSSExch(&ExchE); ercE = 0; iE = 0; while (ercE != ErcOutOfRange) { ercE = GetExchOwner(iE, &pExchJCBE); if ((lercE) && (iE != ExchE) && (pExchJCBE == pCrntJCB)) DeAllocExch(iE); iE++; } / Now that the user can't make anymore requests, Send Abort messages to all services. This closes all files that were opened by the Job and frees up any other resources held for this job by any services. / SendAbort(JobNumE, ExchE); ercE = 0; / Clear the error / while(lercE) / clear the exchange of abort responses*/ ercE = CheckMsg(ExchE, BogusMsg); ercE = 0; /* Clear the error */ /* We must now switch to a temporary stack so we can clean out the user PD (we are on his stack right now!). */ #asm MOV EAX, OFFSET _TmpStack ADD EAX, 508 MOV ESP, EAX MOV EBP, EAX #endasm /* Clean the PD of all user memory leaving OS memory for next job if there is one. */ pPDE = pCrntJCB->pJcbPD; CleanUserPD(pPDE); /* Try to load the Chain file. Don't bother checking error cause it was valid if we got here! */ GetRunFile(aFileE, cbFileE, &job_fhE); /* it was valid before!*/ ercE = LoadJob(pCrntJCB, job_fhE); if (ercE) {

I
468 Part P/ ■ The Operating System Source Code Listing 22.2. continued /* We have errored in a critical part of Chain (LoadJob). The original user's job is destroyed, and we can't run the chain file (bummer). The only thing left to do is Look for Exit Run file to load if any exists. If no exit run file, we kill this guy and return to the monitor if he had the screen. */ GetExitJob(aFileE, &cbFileE); /* Exit Run File!! */ if (IcbFileE) ercE = ErcNoExitJob; if (lercE) ercE = GetRunFile(aFileE, cbFileE, &job_fhE); if (lercE) ercE = LoadJob(pCrntJCB, job_fhE); } if (lercE) { /* No error */ pStart = pStart+pCode -off Code; /* Now we RETURN to new job's address after we put him on his new stack. */ MOV EAX, _pStack MOV EBX, sStack ADD EAX, EBX SUB EAX, 4 MOV ESP, EAX MOV EBP, EAX PUSH 18h MOV EAX, _pStart PUSH EAX RETF #endasm } if (ercE) { /* S ;We are history! Something failed loading the job (Chain or Exit) */ /* In case there is no job to run or a fatal error has happened we send a message (ISendMsg) to the monitor status task with our TSSExch and the Error. Then he will WIPE US OUT! We use ISend (vice send) so he can't run before we get to the exchange otherwise we will be placed back on the readyQueuel */ ISendMsg(KillExch, ExchE, ercE); /* ISend clears intsl */ #asm ST I flendasm SetPriority(31 ) ;

Chapter 22 ■ Job Management Code } } WaitMsg(ExchE, BogusMsg); while(1); /In case we get scheduled again/ /* We are NO MORE */ r*** End of Module

Powered by TurnKey Linux.