home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume44 / toy_os / part02 / oper_system.cc < prev   
C/C++ Source or Header  |  1994-09-05  |  11KB  |  433 lines

  1. // This may look like C code, but it is really -*- C++ -*-
  2. /*
  3.  ************************************************************************
  4.  *
  5.  *               UNT Virtual Machine
  6.  *
  7.  *         This is the core of the operating system
  8.  *
  9.  * The functions defined in the present file are executed within the
  10.  * TRAP-handler thread that gets control if CPU raised the trap signal.
  11.  * While CPU is stopped during the trap handling, the trap thread
  12.  * handles traps and system requests (SVC traps) and performs all the
  13.  * high level process scheduling.
  14.  *
  15.  ************************************************************************
  16.  */
  17.  
  18. #pragma implementation "oper_system.h"
  19. #include "oper_system.h"
  20. #include "myenv.h"
  21.  
  22. /*
  23.  *------------------------------------------------------------------------
  24.  *            Initializing the operating system
  25.  */
  26.  
  27. OperatingSystem::OperatingSystem(void)
  28.     : Halt("Computer halt",0),
  29.       ProcessTable(20)
  30. {
  31.   semas = new SemaphoreTable(20);
  32.   running_pcb = 0;
  33.   if( !newproc("TRAP handling",1) )
  34.   {                    // This is a CPU trap handler
  35.     for(;;)
  36.     {
  37.       CPU.wait_for_trap();        // Wait for the trap
  38.       if( trap_handler(CPU.q_trap_code()) == DISPATCH )
  39.     dispatch();
  40.       CPU.start();
  41.     }
  42.   }
  43. }
  44.  
  45.  
  46.                 // Load a program from the drum and run it
  47. void OperatingSystem::commence(void)
  48. {
  49.   running_pcb = &(*this)[new_pid()];
  50.   running_pcb->status = PCB::Ok;
  51.   running_pcb->pc = memory.drum_load(0);
  52.   prepare_for_running();
  53.   CPU.start();
  54. }
  55.  
  56. /*
  57.  *------------------------------------------------------------------------
  58.  *                TRAP handler
  59.  */
  60.  
  61. HANDLER_STATUS
  62. OperatingSystem::trap_handler(const CentralProcessingUnit::TRAPCODE trap_code)
  63. {
  64.   save_context();            // Save the CPU context anyway
  65.   switch(trap_code)
  66.   {
  67.     case CentralProcessingUnit::ILLEGOP:
  68.          if( CPU.q_opcode() == HALT )
  69.        console("Current process has terminated normally (by HALT)");
  70.      else
  71.        console("Illegal operation (dec code %d)",CPU.q_opcode());
  72.      return terminate();
  73.     
  74.     case CentralProcessingUnit::SVCCALL:
  75.      return svc_handler((SVCCODE)CPU.q_svcop());
  76.  
  77.     case CentralProcessingUnit::CLOCKINT:
  78.      single_message("Clock Interrupt");
  79.      if( readyPCBs.is_empty() )
  80.        return RESUME;        // We've got only a single process
  81.      readyPCBs.append(*running_pcb);
  82.      return DISPATCH;        // Else pick up smth else
  83.  
  84.     case CentralProcessingUnit::MEMORY:
  85.      single_message("Memory Fault");
  86.      return DISPATCH;
  87.  
  88.     default:
  89.          _error("FATAL: Trap handler has been entered with illegal "
  90.         "trap code %d",trap_code);
  91.   }
  92.   return DISPATCH;
  93. }
  94.  
  95. /*
  96.  *------------------------------------------------------------------------
  97.  *                SVC Handler
  98.  */
  99.  
  100. HANDLER_STATUS OperatingSystem::svc_handler(const SVCCODE svc_code)
  101. {
  102.   single_message("SVC interrupt %d",svc_code);
  103.   switch(svc_code)
  104.   {
  105.     case DUMP_STATUS:            // Dump the OS status
  106.          dump();
  107.          return RESUME;
  108.     
  109.     case FORK:                // Fork a process.
  110.                     // reg_a := PID of a kid or parent
  111.      return fork(CPU.q_RegA(),CPU.q_EA());    // EA = start address of a kid
  112.  
  113.     case WAIT_KIDS:            // Wait for kid processes to terminate
  114.      return wait_for_kids();
  115.  
  116.     case SEMINIT:            // Create a semaphore for a process
  117.          return create_semaphore(CPU.q_RegA(),CPU.q_EA());
  118.  
  119.     case SEM_P:                // P-operation on semaphore
  120.      return semaphore_p((SID)CPU[CPU.q_RegA()]);
  121.  
  122.     case SEM_V:                // V-operation on semaphore
  123.      return semaphore_v((SID)CPU[CPU.q_RegA()]);
  124.  
  125.     case KILL:                // Try to kill the process
  126.      return shoot((PID)CPU[CPU.q_RegA()]);
  127.  
  128.     default:
  129.          console("Illegal SVC call %d, program is being tossed out",
  130.          svc_code);
  131.      return terminate();
  132.   }
  133.   return DISPATCH;
  134. }
  135.  
  136. /*
  137.  *------------------------------------------------------------------------
  138.  *          Elementary process scheduling functions
  139.  */
  140.  
  141.                 // Prepare the process for running
  142.                 // on the CPU
  143. void OperatingSystem::prepare_for_running(void)
  144. {
  145.   assert( running_pcb != 0 );
  146.   assert( !CPU.is_running() );
  147.   CPU.load_context(*running_pcb);
  148.   CPU.clock = Time_quant;
  149. }
  150.  
  151.                 // Save the status of the currently
  152.                 // running process as the process is
  153.                 // going to lose the CPU control
  154. void OperatingSystem::save_context(void)
  155. {
  156.   assert( running_pcb != 0 );
  157.   assert( !CPU.is_running() );
  158.   (*running_pcb).load_context((Context&)CPU);
  159. }
  160.  
  161.                 // Terminate the currently running process
  162. HANDLER_STATUS OperatingSystem::terminate(void)
  163. {
  164.   console("Terminating the process PID %d",running_pcb->id);
  165.   (*running_pcb).dump();
  166.   kill(*running_pcb);
  167.   return DISPATCH;            // Pick up a new process to run
  168. }
  169.  
  170.                 // Pick up a new process to run and make
  171.                 // it current
  172. void OperatingSystem::dispatch(void)
  173. {
  174.   if( readyPCBs.is_empty() )
  175.     Halt++;
  176.   running_pcb = &(*this)[readyPCBs.get_from_head()->id];
  177.   prepare_for_running();
  178. }
  179.  
  180. /*
  181.  *------------------------------------------------------------------------
  182.  *              Dump whatever goes on in the system
  183.  */
  184.  
  185. void OperatingSystem::dump(void)
  186. {
  187.   begin_printing();
  188.   message("\n%s\n\n\t\t\tOperating System Status\n",_Minuses);
  189.   message("\nCurrent process\n");
  190.   (*running_pcb).dump();
  191.   ProcessTable::dump();
  192.   (*semas).dump();
  193.   end_printing();
  194. }
  195.  
  196. /*
  197.  *------------------------------------------------------------------------
  198.  *               Making new processes
  199.  */
  200.  
  201.                 // Create a child process at EA of
  202.                 // the currently running process
  203.                 // Put PID of the son into the parent rega,
  204.                 // and PID of the parent into the son rega
  205. HANDLER_STATUS OperatingSystem::fork(const int rega,Address EA)
  206. {
  207.   assert( running_pcb != 0 );
  208.   if( running_pcb -> lchild != NIL_pid && running_pcb -> rchild != NIL_pid )
  209.   {
  210.     console("ABEND: attempt to create the 3d child");
  211.     return terminate();
  212.   }
  213.  
  214.   PID son_id = new_pid();
  215.   if( son_id == NIL_pid )
  216.   {
  217.     console("ABEND: can't create a process - too many are running");
  218.     return terminate();
  219.   }
  220.  
  221.   PCB& son_pcb = (*this)[son_id];
  222.   PCB& dad_pcb = *running_pcb;
  223.   single_message("Creating a child %d for a parent %d",son_pcb.id,dad_pcb.id);
  224.  
  225.   son_pcb.status = PCB::Ok;
  226.   son_pcb.parent = dad_pcb.id;
  227.   son_pcb.lchild = son_pcb.rchild = NIL_pid;
  228.   son_pcb.load_context(dad_pcb);
  229.   son_pcb[rega] = dad_pcb.id;
  230.   son_pcb.pc    = EA;
  231.  
  232.   if( dad_pcb.lchild == NIL_pid )
  233.     dad_pcb.lchild = son_pcb.id;
  234.   else
  235.     dad_pcb.rchild = son_pcb.id;
  236.   dad_pcb[rega] = son_pcb.id;
  237.  
  238.   readyPCBs.append(dad_pcb);
  239.   readyPCBs.append(son_pcb);
  240.   return DISPATCH;
  241. }
  242.  
  243.                 // Wait until all the kids of the
  244.                 // running process are through.
  245.                 // Return RESUME if the running process
  246.                 // has got no kids.
  247. HANDLER_STATUS OperatingSystem::wait_for_kids(void)
  248. {
  249.   assert( running_pcb != 0 );
  250.   if( running_pcb->lchild == NIL_pid && running_pcb->rchild == NIL_pid )
  251.     return RESUME;                // No kids
  252.   running_pcb->status = PCB::Wait_for_kids;    // The PCB remains unqueued
  253.   return DISPATCH;
  254. }
  255.  
  256.                 // Try to kill the process
  257. HANDLER_STATUS OperatingSystem::shoot(const PID pid)
  258. {
  259.   single_message("An attempt to shoot a process %d",pid);
  260.   if( pid == running_pcb->id )
  261.   {
  262.     console("The current process %d shot himself",pid);
  263.     return terminate();
  264.   }
  265.  
  266.   if( pid == NIL_pid )
  267.   {
  268.     console("An attempt to kill a nonexistent process");
  269.     return terminate();
  270.   }
  271.  
  272.   if( pid != running_pcb->lchild && pid != running_pcb->rchild )
  273.   {
  274.     console("Process %d has no right to shoot %d",running_pcb->id,pid);
  275.     return terminate();
  276.   }
  277.  
  278.   kill((*this)[pid]);
  279.   return RESUME;
  280. }
  281.  
  282. /*
  283.  *------------------------------------------------------------------------
  284.  *                Kill the process
  285.  * The program performs the clean-up and releases all the resources
  286.  * that process occupied.
  287.  *     - Kids are terminated
  288.  *     - Parent is notified and turned ready if has been waiting
  289.  *     - Owned semaphores are destroyed and all the processes being 
  290.  *      waiting on it are terminated
  291.  *     - the process is purged of all semaphore waiting lists if
  292.  *      it has been waiting on P operation
  293.  */
  294.  
  295. void OperatingSystem::kill(PCB& pcb)
  296. {
  297.   assert(pcb.status != PCB::Dead);
  298.  
  299.   readyPCBs.purge_id(pcb.id);        // Purge from the ready queue
  300.                     // (if any)
  301.   if( pcb.lchild != NIL_pid )
  302.     kill((*this)[pcb.lchild]);
  303.  
  304.   if( pcb.rchild != NIL_pid )
  305.     kill((*this)[pcb.rchild]);
  306.  
  307.   if( pcb.parent != NIL_pid )
  308.   {
  309.     PCB& dad_pcb = (*this)[pcb.parent];
  310.     if( dad_pcb.lchild == pcb.id )
  311.       dad_pcb.lchild = NIL_pid;
  312.     else if( dad_pcb.rchild == pcb.id )
  313.       dad_pcb.rchild = NIL_pid;
  314.     else
  315.       _error("FATAL: parent doesn't know of his son");
  316.  
  317.                 // If dad has been waiting for kids, it may go
  318.     if( dad_pcb.lchild == NIL_pid && dad_pcb.rchild == NIL_pid &&
  319.        dad_pcb.status == PCB::Wait_for_kids )
  320.       dad_pcb.status = PCB::Ok,
  321.       readyPCBs.append(dad_pcb);
  322.     pcb.parent = NIL_pid;
  323.   }
  324.  
  325.                     // Release all owned semaphores
  326.   SID sid;
  327.   while( (sid = (*semas).owned_by(pcb.id)) != NIL_sid )
  328.   {
  329.     (*semas).dispose(sid);
  330.   }
  331.  
  332.   if( pcb.status == PCB::Wait_on_sema )
  333.     (*semas).purge(pcb.id);
  334.  
  335.   dispose(pcb.id);
  336. }
  337.  
  338. /*
  339.  *------------------------------------------------------------------------
  340.  *            Processes and Semaphores
  341.  */
  342.                 // Create a new semaphore with initial value EA
  343.                 // Put SID in rega
  344.                 // Return RESUME if everything is fine
  345. HANDLER_STATUS OperatingSystem::create_semaphore(const int rega,Address EA)
  346. {
  347.   assert( running_pcb != 0 );
  348.  
  349.   SID semid = (*semas).new_semaphore(EA,running_pcb->id);
  350.   if( semid == NIL_sid )
  351.   {
  352.     console("ABEND: Can't create a semaphore - too many are in use");
  353.     return terminate();
  354.   }
  355.  
  356.   single_message("Semaphore %d (in_value %d) has been allocated for PID %d",
  357.          semid,EA,running_pcb->id);
  358.  
  359.   CPU[rega] = semid;
  360.   return RESUME;
  361. }
  362.  
  363.                 // Check to see that the Sema is valid
  364.                 // to perform P or V operation on.
  365.                 // It should be active and belong to
  366.                 // the running process or its ancestor
  367. Sema * OperatingSystem::valid_semaphore(const SID sid)
  368. {
  369.   if( !(*semas).is_active(sid) )
  370.     return 0;
  371.   Sema& sem = (*semas)[sid];
  372.   PID owner = sem.q_owner();            // Check ownership
  373.   PID id    = running_pcb->id;            // through the chain of
  374.   for(; id != NIL_pid; id = (*this)[id].parent)    // ancestorship
  375.     if( id == owner )
  376.       return &sem;
  377.   return 0;
  378. }
  379.  
  380.                 // Semaphore P-operation
  381.                 // Return RESUME if the process is to
  382.                 // be resumed
  383. HANDLER_STATUS OperatingSystem::semaphore_p(const SID sid)
  384. {
  385.   single_message("\nP-operation on semaphore %d",sid);
  386.   assert( running_pcb != 0 );
  387.   Sema * semp;
  388.   if( (semp = valid_semaphore(sid)) == 0 )
  389.   {
  390.     console("Semaphore %d may not be used by process %d",sid,
  391.         running_pcb->id);
  392.     return terminate();
  393.   }
  394.  
  395.   if( !(*semp).p(*running_pcb) )
  396.   {                    // Suspend the current process
  397.     single_message("Process %d should wait on semaphore %d",
  398.            running_pcb->id,sid);
  399.     running_pcb->status = PCB::Wait_on_sema;
  400.     return DISPATCH;
  401.   }  
  402.  
  403.   return RESUME;
  404. }
  405.  
  406.  
  407.                 // Semaphore V-operation
  408.                 // Return RESUME if the process is to
  409.                 // be resumed
  410. HANDLER_STATUS OperatingSystem::semaphore_v(const SID sid)
  411. {
  412.   single_message("V-operation on semaphore %d",sid);
  413.   assert( running_pcb != 0 );
  414.   Sema * semp;
  415.   if( (semp = valid_semaphore(sid)) == 0 )
  416.   {
  417.     console("Semaphore %d may not be used by process %d",sid,
  418.         running_pcb->id);
  419.     return terminate();
  420.   }
  421.  
  422.   PID pid_to_wake;
  423.   if( (pid_to_wake = (*semp).v()) != NIL_pid )
  424.   {                    // Wake up pid_to_wake
  425.     single_message("Process %d is being woken up",pid_to_wake);
  426.     PCB& pcb_to_wake = (*this)[pid_to_wake];
  427.     pcb_to_wake.status = PCB::Ok;
  428.     readyPCBs.append(pcb_to_wake);
  429.   }  
  430.  
  431.   return RESUME;
  432. }
  433.