home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume44
/
toy_os
/
part02
/
oper_system.cc
< prev
Wrap
C/C++ Source or Header
|
1994-09-05
|
11KB
|
433 lines
// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
*
* UNT Virtual Machine
*
* This is the core of the operating system
*
* The functions defined in the present file are executed within the
* TRAP-handler thread that gets control if CPU raised the trap signal.
* While CPU is stopped during the trap handling, the trap thread
* handles traps and system requests (SVC traps) and performs all the
* high level process scheduling.
*
************************************************************************
*/
#pragma implementation "oper_system.h"
#include "oper_system.h"
#include "myenv.h"
/*
*------------------------------------------------------------------------
* Initializing the operating system
*/
OperatingSystem::OperatingSystem(void)
: Halt("Computer halt",0),
ProcessTable(20)
{
semas = new SemaphoreTable(20);
running_pcb = 0;
if( !newproc("TRAP handling",1) )
{ // This is a CPU trap handler
for(;;)
{
CPU.wait_for_trap(); // Wait for the trap
if( trap_handler(CPU.q_trap_code()) == DISPATCH )
dispatch();
CPU.start();
}
}
}
// Load a program from the drum and run it
void OperatingSystem::commence(void)
{
running_pcb = &(*this)[new_pid()];
running_pcb->status = PCB::Ok;
running_pcb->pc = memory.drum_load(0);
prepare_for_running();
CPU.start();
}
/*
*------------------------------------------------------------------------
* TRAP handler
*/
HANDLER_STATUS
OperatingSystem::trap_handler(const CentralProcessingUnit::TRAPCODE trap_code)
{
save_context(); // Save the CPU context anyway
switch(trap_code)
{
case CentralProcessingUnit::ILLEGOP:
if( CPU.q_opcode() == HALT )
console("Current process has terminated normally (by HALT)");
else
console("Illegal operation (dec code %d)",CPU.q_opcode());
return terminate();
case CentralProcessingUnit::SVCCALL:
return svc_handler((SVCCODE)CPU.q_svcop());
case CentralProcessingUnit::CLOCKINT:
single_message("Clock Interrupt");
if( readyPCBs.is_empty() )
return RESUME; // We've got only a single process
readyPCBs.append(*running_pcb);
return DISPATCH; // Else pick up smth else
case CentralProcessingUnit::MEMORY:
single_message("Memory Fault");
return DISPATCH;
default:
_error("FATAL: Trap handler has been entered with illegal "
"trap code %d",trap_code);
}
return DISPATCH;
}
/*
*------------------------------------------------------------------------
* SVC Handler
*/
HANDLER_STATUS OperatingSystem::svc_handler(const SVCCODE svc_code)
{
single_message("SVC interrupt %d",svc_code);
switch(svc_code)
{
case DUMP_STATUS: // Dump the OS status
dump();
return RESUME;
case FORK: // Fork a process.
// reg_a := PID of a kid or parent
return fork(CPU.q_RegA(),CPU.q_EA()); // EA = start address of a kid
case WAIT_KIDS: // Wait for kid processes to terminate
return wait_for_kids();
case SEMINIT: // Create a semaphore for a process
return create_semaphore(CPU.q_RegA(),CPU.q_EA());
case SEM_P: // P-operation on semaphore
return semaphore_p((SID)CPU[CPU.q_RegA()]);
case SEM_V: // V-operation on semaphore
return semaphore_v((SID)CPU[CPU.q_RegA()]);
case KILL: // Try to kill the process
return shoot((PID)CPU[CPU.q_RegA()]);
default:
console("Illegal SVC call %d, program is being tossed out",
svc_code);
return terminate();
}
return DISPATCH;
}
/*
*------------------------------------------------------------------------
* Elementary process scheduling functions
*/
// Prepare the process for running
// on the CPU
void OperatingSystem::prepare_for_running(void)
{
assert( running_pcb != 0 );
assert( !CPU.is_running() );
CPU.load_context(*running_pcb);
CPU.clock = Time_quant;
}
// Save the status of the currently
// running process as the process is
// going to lose the CPU control
void OperatingSystem::save_context(void)
{
assert( running_pcb != 0 );
assert( !CPU.is_running() );
(*running_pcb).load_context((Context&)CPU);
}
// Terminate the currently running process
HANDLER_STATUS OperatingSystem::terminate(void)
{
console("Terminating the process PID %d",running_pcb->id);
(*running_pcb).dump();
kill(*running_pcb);
return DISPATCH; // Pick up a new process to run
}
// Pick up a new process to run and make
// it current
void OperatingSystem::dispatch(void)
{
if( readyPCBs.is_empty() )
Halt++;
running_pcb = &(*this)[readyPCBs.get_from_head()->id];
prepare_for_running();
}
/*
*------------------------------------------------------------------------
* Dump whatever goes on in the system
*/
void OperatingSystem::dump(void)
{
begin_printing();
message("\n%s\n\n\t\t\tOperating System Status\n",_Minuses);
message("\nCurrent process\n");
(*running_pcb).dump();
ProcessTable::dump();
(*semas).dump();
end_printing();
}
/*
*------------------------------------------------------------------------
* Making new processes
*/
// Create a child process at EA of
// the currently running process
// Put PID of the son into the parent rega,
// and PID of the parent into the son rega
HANDLER_STATUS OperatingSystem::fork(const int rega,Address EA)
{
assert( running_pcb != 0 );
if( running_pcb -> lchild != NIL_pid && running_pcb -> rchild != NIL_pid )
{
console("ABEND: attempt to create the 3d child");
return terminate();
}
PID son_id = new_pid();
if( son_id == NIL_pid )
{
console("ABEND: can't create a process - too many are running");
return terminate();
}
PCB& son_pcb = (*this)[son_id];
PCB& dad_pcb = *running_pcb;
single_message("Creating a child %d for a parent %d",son_pcb.id,dad_pcb.id);
son_pcb.status = PCB::Ok;
son_pcb.parent = dad_pcb.id;
son_pcb.lchild = son_pcb.rchild = NIL_pid;
son_pcb.load_context(dad_pcb);
son_pcb[rega] = dad_pcb.id;
son_pcb.pc = EA;
if( dad_pcb.lchild == NIL_pid )
dad_pcb.lchild = son_pcb.id;
else
dad_pcb.rchild = son_pcb.id;
dad_pcb[rega] = son_pcb.id;
readyPCBs.append(dad_pcb);
readyPCBs.append(son_pcb);
return DISPATCH;
}
// Wait until all the kids of the
// running process are through.
// Return RESUME if the running process
// has got no kids.
HANDLER_STATUS OperatingSystem::wait_for_kids(void)
{
assert( running_pcb != 0 );
if( running_pcb->lchild == NIL_pid && running_pcb->rchild == NIL_pid )
return RESUME; // No kids
running_pcb->status = PCB::Wait_for_kids; // The PCB remains unqueued
return DISPATCH;
}
// Try to kill the process
HANDLER_STATUS OperatingSystem::shoot(const PID pid)
{
single_message("An attempt to shoot a process %d",pid);
if( pid == running_pcb->id )
{
console("The current process %d shot himself",pid);
return terminate();
}
if( pid == NIL_pid )
{
console("An attempt to kill a nonexistent process");
return terminate();
}
if( pid != running_pcb->lchild && pid != running_pcb->rchild )
{
console("Process %d has no right to shoot %d",running_pcb->id,pid);
return terminate();
}
kill((*this)[pid]);
return RESUME;
}
/*
*------------------------------------------------------------------------
* Kill the process
* The program performs the clean-up and releases all the resources
* that process occupied.
* - Kids are terminated
* - Parent is notified and turned ready if has been waiting
* - Owned semaphores are destroyed and all the processes being
* waiting on it are terminated
* - the process is purged of all semaphore waiting lists if
* it has been waiting on P operation
*/
void OperatingSystem::kill(PCB& pcb)
{
assert(pcb.status != PCB::Dead);
readyPCBs.purge_id(pcb.id); // Purge from the ready queue
// (if any)
if( pcb.lchild != NIL_pid )
kill((*this)[pcb.lchild]);
if( pcb.rchild != NIL_pid )
kill((*this)[pcb.rchild]);
if( pcb.parent != NIL_pid )
{
PCB& dad_pcb = (*this)[pcb.parent];
if( dad_pcb.lchild == pcb.id )
dad_pcb.lchild = NIL_pid;
else if( dad_pcb.rchild == pcb.id )
dad_pcb.rchild = NIL_pid;
else
_error("FATAL: parent doesn't know of his son");
// If dad has been waiting for kids, it may go
if( dad_pcb.lchild == NIL_pid && dad_pcb.rchild == NIL_pid &&
dad_pcb.status == PCB::Wait_for_kids )
dad_pcb.status = PCB::Ok,
readyPCBs.append(dad_pcb);
pcb.parent = NIL_pid;
}
// Release all owned semaphores
SID sid;
while( (sid = (*semas).owned_by(pcb.id)) != NIL_sid )
{
(*semas).dispose(sid);
}
if( pcb.status == PCB::Wait_on_sema )
(*semas).purge(pcb.id);
dispose(pcb.id);
}
/*
*------------------------------------------------------------------------
* Processes and Semaphores
*/
// Create a new semaphore with initial value EA
// Put SID in rega
// Return RESUME if everything is fine
HANDLER_STATUS OperatingSystem::create_semaphore(const int rega,Address EA)
{
assert( running_pcb != 0 );
SID semid = (*semas).new_semaphore(EA,running_pcb->id);
if( semid == NIL_sid )
{
console("ABEND: Can't create a semaphore - too many are in use");
return terminate();
}
single_message("Semaphore %d (in_value %d) has been allocated for PID %d",
semid,EA,running_pcb->id);
CPU[rega] = semid;
return RESUME;
}
// Check to see that the Sema is valid
// to perform P or V operation on.
// It should be active and belong to
// the running process or its ancestor
Sema * OperatingSystem::valid_semaphore(const SID sid)
{
if( !(*semas).is_active(sid) )
return 0;
Sema& sem = (*semas)[sid];
PID owner = sem.q_owner(); // Check ownership
PID id = running_pcb->id; // through the chain of
for(; id != NIL_pid; id = (*this)[id].parent) // ancestorship
if( id == owner )
return &sem;
return 0;
}
// Semaphore P-operation
// Return RESUME if the process is to
// be resumed
HANDLER_STATUS OperatingSystem::semaphore_p(const SID sid)
{
single_message("\nP-operation on semaphore %d",sid);
assert( running_pcb != 0 );
Sema * semp;
if( (semp = valid_semaphore(sid)) == 0 )
{
console("Semaphore %d may not be used by process %d",sid,
running_pcb->id);
return terminate();
}
if( !(*semp).p(*running_pcb) )
{ // Suspend the current process
single_message("Process %d should wait on semaphore %d",
running_pcb->id,sid);
running_pcb->status = PCB::Wait_on_sema;
return DISPATCH;
}
return RESUME;
}
// Semaphore V-operation
// Return RESUME if the process is to
// be resumed
HANDLER_STATUS OperatingSystem::semaphore_v(const SID sid)
{
single_message("V-operation on semaphore %d",sid);
assert( running_pcb != 0 );
Sema * semp;
if( (semp = valid_semaphore(sid)) == 0 )
{
console("Semaphore %d may not be used by process %d",sid,
running_pcb->id);
return terminate();
}
PID pid_to_wake;
if( (pid_to_wake = (*semp).v()) != NIL_pid )
{ // Wake up pid_to_wake
single_message("Process %d is being woken up",pid_to_wake);
PCB& pcb_to_wake = (*this)[pid_to_wake];
pcb_to_wake.status = PCB::Ok;
readyPCBs.append(pcb_to_wake);
}
return RESUME;
}