📄 exec.cpp
字号:
//*************************************************************************// MODULE : Exec - Process creation and management module *// AUTHOR : Ron Chernich *// PURPOSE: This object contains an array of process descriptors and a *// number of queues containing array indicies, plus the means *// to create, destroy and move them around. *// HISTORY: *// 21-APR-93 First version *// 14-MAY-93 Process animation "calls" inserted *// 02-JUN-93 "Kill" not de-allocating memory *// 03-FEB-94 Allow un-blocking process to pre-empt current on priority *// 18-APR-94 Kill and Fork modified to accomodate shared memory *// 01-SEP-94 File object support added to Fork and Kill *// 17-SEP-94 LoSked allowed unallocated PID to be scheduled! *// 21-MAR-94 Quantum made a constructor parameter *//*************************************************************************#include "exec.hpp" // related header#include "kernel.hpp" // can't put this in header due recursion#include "fsiface.hpp" // ..or this (see header about cheshier cat)#define PROC0_TEXT 8 // Minimum requirement for process zero code#define PROC0_STACK 60 // Minimum stack allocation for process zero#define PROC0_NAME "CONX" // Name for console process///////////////////////////////////////////////////////////////////////////// Class Member Functions for the Process Control Stuff//-------------------------------------------------------------------------// Initialize Process descriptors to empty, then create (but don't start)// the primary process - this should be customizable and the Prime Proc// should be a trusted p-code program - but in this release, for simplicity,// the process is fixed and coded in C++.//Exec::Exec (Knl *pK, UINT16 nMs) : PqReady(TRUE){ pTx = pK; uProcCnt = 0; nQuantum = nMs; uCurProc = NO_PROC; memset(arrPCB, 0, sizeof(PCB) * MAX_PROC); for (INT16 i = 0; i < MAX_PROC; i++) { memset(&arrPCB[i], 0, sizeof(PCB)); arrPCB[i].uPid = NO_PROC; for (INT16 j = 0; j < MAX_DEV; j++) arrPCB[i].arrDev[j] = NO_PROC; } CreateCon();}//////////////////// Somewhat gracefull death for the Execution unit - really the operator// should delete any running programs, so the only operation required is// to kill off the console process, but..//Exec::~Exec (void){ for (INT16 idx = 0; idx < MAX_PROC; idx++) if (arrPCB[idx].uPid != NO_PROC) Kill(idx);}/////////////////////// Call here when we want to execute some application code. Run the// scheduler if there is no current process, otherwise simply call// the p-code interpreter UNLESS the PID is ZERO (which is special, being// the first process) in which case we call the specified operator proc.// After the process has run we do accumulated and quantum time accounting.//// If the process has done something causing it to yield, it is put into// the Blocked Q. If it has run out of Quantum allocation, it gets put// back into the Ready Q (this process temporarily reduces its priority// ensures that processes at equal priority run round-robin).//// RETURNS: TRUE .. something got run (used by single-step logic)// FALSE .. no go//BOOL Exec::Dispatch (void){ MSG msg; if (uCurProc == NO_PROC) Sked(); else { UINT32 lMark = Clock.GetTicks(); if (arrPCB[uCurProc].uPid == 0) RunCon(arrPCB[uCurProc]); else RunProc(arrPCB[uCurProc], arrMCB[uCurProc]); UINT16 uIntVal = MAX(1, (UINT16)(Clock.GetTicks() - lMark)); arrPCB[uCurProc].luTicks += (UINT32)uIntVal; arrPCB[uCurProc].nQuantum -= uIntVal; switch (arrPCB[uCurProc].uStatus) { case PS_Halted: case PS_Illegal: msg = message(ID_Kernel, ((arrPCB[uCurProc].uStatus = PS_Illegal) ? ANI_HALT_BAD : ANI_HALT_OK), uCurProc); pTx->SendMsg(ID_ANIM, &msg); Kill(uCurProc); uCurProc = NO_PROC; break; case PS_Ready: if (arrPCB[uCurProc].nQuantum <= 0) { PqReady.PqAdd(arrPCB[uCurProc]); if (arrPCB[uCurProc].nQuantum > PCD_PREEMPT) { msg = message(ID_Kernel, ANI_RUN_RDY, uCurProc); pTx->SendMsg(ID_ANIM, &msg); } uCurProc = NO_PROC; } break; default: PqBlocked.PqAdd(arrPCB[uCurProc]); msg = message(ID_Kernel, ANI_RUN_BLK, uCurProc); pTx->SendMsg(ID_ANIM, &msg); uCurProc = NO_PROC; } return TRUE; } return FALSE;}////////////////////// Basic Prime Proc initialization must stop short of allocating memory// because we won't have a MMU device registered yet!//void Exec::CreateCon (void){ if (arrPCB[0].uPid == NO_PROC) { arrPCB[0].uStatus = PS_Zombie; arrPCB[0].uPid = arrPCB[0].uPidp = 0; arrPCB[0].nPriority = MAX_PRIORITY; arrPCB[0].pReply = NULL; arrPCB[0].arrDev[0] = CLS_VDU; arrPCB[0].uBP = 1; if (arrPCB[0].pszName = new char[strlen(PROC0_NAME)+1]) strcpy(arrPCB[0].pszName, PROC0_NAME); arrPCB[0].uStatus = PS_Created; PqIn.PqAdd(arrPCB[0]); ++uProcCnt; }}//////////////////// Called when the supervisor "RUN" button has been clicked for the first// time - we must complete the Prime Proc initializations that could not// be done before (we should now have a MMU and FS), then call the Low// Level Scheduler which should be able to allocate a TTY and line driver// to the console and move it into the Ready Queue.//void Exec::StartCon (void){ MSG mess(ID_Kernel, KM_Open, PROC0_TEXT); pTx->SendMsg(ID_MMU, &mess); arrMCB[0].hText = mess.wParam; mess = message(ID_Kernel, KM_Open, PROC0_STACK); pTx->SendMsg(ID_MMU, &mess); arrMCB[0].hStack = mess.wParam; if(arrMCB[0].hText && arrMCB[0].hStack) { MMU_MSG mms; INT16 ret_frame[3] = {0, 0, -1 }; UINT16 csp_exec[2] = { 0x08, 0x06 }; mms.pData = (UINT16*)&csp_exec[0], mms.uOffset = 0, mms.uLen = 2; mess = message(ID_CON, KM_WriteBlk, arrMCB[0].hText, &mms); pTx->SendMsg(ID_MMU, &mess); mms.pData = (UINT16*)&ret_frame[0], mms.uOffset = 0, mms.uLen = 3; mess = message(ID_CON, KM_WriteBlk, arrMCB[0].hStack, &mms); pTx->SendMsg(ID_MMU, &mess); arrPCB[0].uSP = 3; if (LoSked(0)) InitCon(); }}////////////////////// Low level scheduler starts the passed process provided it is able to// allocate the resources contained in the PID's device list. Called in// response to a console or batch GO command or to start the Prime Proc.//// Note that VDU devices are not allocated direct - we merely see if one// is free and instantiate a Line Protocol Driver (which will get the TTY).//// *** INCOMPLETE: failed startup does not release allocated devices! ***//BOOL Exec::LoSked (UINT16 uGo){ INT16 idx = 0; BOOL bCanStart; bCanStart = (NO_PROC == arrPCB[uGo].uPid) ? FALSE : TRUE; while (bCanStart && (idx < MAX_DEV) && (arrPCB[uGo].arrDev[idx] != NO_PROC)) { MSG msg; UINT16 uNeed = arrPCB[uGo].arrDev[idx]; switch (uNeed) { case CLS_VDU: msg = message(uGo, KM_Open, CLS_VDU); pTx->SendMsg(ID_Kernel, &msg); if (msg.wParam == 0) bCanStart = FALSE; else { msg.wMsgType = KM_Close; pTx->SendMsg(ID_Kernel, &msg); arrPCB[uGo].pDev = new LnDrv(arrPCB[uGo].uPid, ID_LNDRV+arrPCB[uGo].uPid, pTx); bCanStart = (BOOL)(arrPCB[uGo].pDev != NULL); } break; case CLS_MTU: break; case CLS_PRN: break; case CLS_FSF: break; } ++idx; } if (bCanStart) { arrPCB[uGo].uStatus = PS_Ready; PqReady.PqAdd(arrPCB[PqIn.PqGet(uGo)]); MSG msg(ID_Kernel, ANI_IN_RDY, uGo); pTx->SendMsg(ID_ANIM, &msg); } return bCanStart;}//////////////////////// Process scheduler - first, see if there are any blocked processes that// have become unblocked and if so, feed them back into the right Queue.// Then, if there is anything at the head of the priority Ready Queue,// make its context current, preparing it for dispatch to the CPU.//// If an unblocking process was blocked on a semaphore wait, we temporarily// elevate its priority to ensure it will re-schedule ahead of the proc// that has just signaled and hence been pre-empted (assuming they are at// the same priority) - so, after insretion to the ready Q, return its// priority to original.//void Exec::Sked (void){ INT16 i; UINT16 arrProcs[MAX_PROC]; PqBlocked.PqImage(arrProcs); for (i = 0; i < MAX_PROC; i++) { if (arrProcs[i] == NO_PROC) break; if (!(arrPCB[arrProcs[i]].uStatus & PS_Blocked)) { INT16 idx = PqBlocked.PqGet(arrProcs[i]); arrPCB[idx].uStatus |= PS_Ready; if (arrPCB[idx].uSemSet) ++arrPCB[idx].nPriority; PqReady.PqAdd(arrPCB[idx]); if (arrPCB[idx].uSemSet) --arrPCB[idx].nPriority; MSG msg(ID_Kernel, ANI_BLK_RDY, arrPCB[idx].uPid); pTx->SendMsg(ID_ANIM, &msg); } } PqSusBlock.PqImage(arrProcs); for (i = 0; i < MAX_PROC; i++) { if (arrProcs[i] == NO_PROC) break; if (!(arrPCB[arrProcs[i]].uStatus & PS_Blocked)) { INT16 idx = PqSusBlock.PqGet(arrProcs[i]); arrPCB[idx].uStatus |= PS_Ready; PqSusReady.PqAdd(arrPCB[idx]); MSG msg(ID_Kernel, ANI_SBLK_SRDY, arrPCB[idx].uPid); pTx->SendMsg(ID_ANIM, &msg); } } PqReady.PqImage(arrProcs); uCurProc = PqReady.PqGet(); if (uCurProc != NO_PROC) { arrPCB[uCurProc].nQuantum = nQuantum; MSG msg(ID_Kernel, ANI_RDY_RUN, uCurProc); pTx->SendMsg(ID_ANIM, &msg); }}//////////////////////// The current process has posted a message which the Kernel has decided// cannot be serviced immediatly. Remove Ready Status and assert Blocked.// The Dispatcher will do the Queue movements later.//void Exec::Block (void){ if (uCurProc != NO_PROC) { arrPCB[uCurProc].uStatus &= ~PS_Ready; arrPCB[uCurProc].uStatus |= PS_Blocked; arrPCB[uCurProc].pReply = NULL; }}////////////////// Suspend this process - always an operator initiated action. Find the// process and suspend the bugger (can't suspend the running process, only// ready or blocked ones).// RETURNS: TRUE .. process suspemded// FALSE .. unknown process or already suspended!//BOOL Exec::Suspend (UINT16 uTarg){ if (uTarg == PqReady.PqGet(uTarg)) { arrPCB[uTarg].uStatus |= PS_Suspended; PqSusReady.PqAdd(arrPCB[uTarg]); MSG msg(ID_Kernel, ANI_RDY_SRDY, uTarg); pTx->SendMsg(ID_ANIM, &msg); return TRUE; } if (uTarg == PqBlocked.PqGet(uTarg)) { arrPCB[uTarg].uStatus |= PS_Suspended; PqSusBlock.PqAdd(arrPCB[uTarg]); MSG msg(ID_Kernel, ANI_BLK_SBLK, uTarg); pTx->SendMsg(ID_ANIM, &msg); return TRUE; } return FALSE;}////////////////// Resume a Suspend process - always an operator initiated action.// Find the process and move her back to the appropriate queue.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -