Main Page | Alphabetical List | Class List | File List | Class Members | File Members

interrupt.cc

Go to the documentation of this file.
00001 // interrupt.cc 00002 // Routines to simulate hardware interrupts. 00003 // 00004 // The hardware provides a routine (SetLevel) to enable or disable 00005 // interrupts. 00006 // 00007 // In order to emulate the hardware, we need to keep track of all 00008 // interrupts the hardware devices would cause, and when they 00009 // are supposed to occur. 00010 // 00011 // This module also keeps track of simulated time. Time advances 00012 // only when the following occur: 00013 // interrupts are re-enabled 00014 // a user instruction is executed 00015 // there is nothing in the ready queue 00016 // 00017 // DO NOT CHANGE -- part of the machine emulation 00018 // 00019 // Copyright (c) 1992-1993 The Regents of the University of California. 00020 // All rights reserved. See copyright.h for copyright notice and limitation 00021 // of liability and disclaimer of warranty provisions. 00022 00023 #include "copyright.h" 00024 #include "interrupt.h" 00025 #include "system.h" 00026 #include "scheduler.h" 00027 #include <unistd.h> 00028 #include <stdio.h> 00029 #include <signal.h> 00030 #include <pthread.h> 00031 #include <unistd.h> 00032 00033 00034 // String definitions for debugging messages 00035 00036 static char *intLevelNames[] = { "off", "on"}; 00037 static char *intTypeNames[] = { "timer", "disk", "console write", 00038 "console read", "network send", "network recv"}; 00039 00040 //---------------------------------------------------------------------- 00041 // PendingInterrupt::PendingInterrupt 00042 // Initialize a hardware device interrupt that is to be scheduled 00043 // to occur in the near future. 00044 // 00045 // "func" is the procedure to call when the interrupt occurs 00046 // "param" is the argument to pass to the procedure 00047 // "time" is when (in simulated time) the interrupt is to occur 00048 // "kind" is the hardware device that generated the interrupt 00049 //---------------------------------------------------------------------- 00050 00051 PendingInterrupt::PendingInterrupt(VoidFunctionPtr func, int param, int time, 00052 IntType kind) 00053 { 00054 handler = func; 00055 arg = param; 00056 when = time; 00057 type = kind; 00058 } 00059 00060 //---------------------------------------------------------------------- 00061 // Interrupt::Interrupt 00062 // Initialize the simulation of hardware device interrupts. 00063 // 00064 // Interrupts start disabled, with no interrupts pending, etc. 00065 //---------------------------------------------------------------------- 00066 00067 Interrupt::Interrupt() 00068 { 00069 level = IntOff; 00070 pending = new List(); 00071 inHandler = FALSE; 00072 yieldOnReturn = FALSE; 00073 status = SystemMode; 00074 } 00075 00076 //---------------------------------------------------------------------- 00077 // Interrupt::~Interrupt 00078 // De-allocate the data structures needed by the interrupt simulation. 00079 //---------------------------------------------------------------------- 00080 00081 Interrupt::~Interrupt() 00082 { 00083 while (!pending->IsEmpty()) 00084 delete pending->Remove(); 00085 delete pending; 00086 } 00087 00088 //---------------------------------------------------------------------- 00089 // Interrupt::ChangeLevel 00090 // Change interrupts to be enabled or disabled, without advancing 00091 // the simulated time (normally, enabling interrupts advances the time). 00092 00093 //---------------------------------------------------------------------- 00094 // Interrupt::ChangeLevel 00095 // Change interrupts to be enabled or disabled, without advancing 00096 // the simulated time (normally, enabling interrupts advances the time). 00097 // 00098 // Used internally. 00099 // 00100 // "old" -- the old interrupt status 00101 // "now" -- the new interrupt status 00102 //---------------------------------------------------------------------- 00103 void Interrupt::ChangeLevel(IntStatus old, IntStatus now) 00104 { 00105 level = now; 00106 DEBUG('i',"\tinterrupts: %s -> %s\n",intLevelNames[old],intLevelNames[now]); 00107 } 00108 00109 //---------------------------------------------------------------------- 00110 // Interrupt::SetLevel 00111 // Change interrupts to be enabled or disabled, and if interrupts 00112 // are being enabled, advance simulated time by calling OneTick(). 00113 // 00114 // Returns: 00115 // The old interrupt status. 00116 // Parameters: 00117 // "now" -- the new interrupt status 00118 //---------------------------------------------------------------------- 00119 IntStatus Interrupt::SetLevel(IntStatus now) 00120 { 00121 IntStatus old = level; 00122 00123 ASSERT((now == IntOff) || (inHandler == FALSE));// interrupt handlers are 00124 // prohibited from enabling 00125 // interrupts 00126 00127 if (now == IntOff) 00128 { 00129 sigprocmask(SIG_BLOCK, &timer_mask, 0); 00130 } 00131 else 00132 { 00133 sigprocmask(SIG_UNBLOCK, &timer_mask, 0); 00134 } 00135 00136 00137 ChangeLevel(old, now); // change to new state 00138 if ((now == IntOn) && (old == IntOff)) 00139 OneTick(); // advance simulated time 00140 return old; 00141 } 00142 00143 //---------------------------------------------------------------------- 00144 // Interrupt::Enable 00145 // Turn interrupts on. Who cares what they used to be? 00146 // Used in ThreadRoot, to turn interrupts on when first starting up 00147 // a thread. 00148 //---------------------------------------------------------------------- 00149 void Interrupt::Enable() 00150 { 00151 (void) SetLevel(IntOn); 00152 } 00153 00154 //---------------------------------------------------------------------- 00155 // Interrupt::OneTick 00156 // Advance simulated time and check if there are any pending 00157 // interrupts to be called. 00158 // 00159 // Two things can cause OneTick to be called: 00160 // interrupts are re-enabled 00161 // a user instruction is executed 00162 //---------------------------------------------------------------------- 00163 void Interrupt::OneTick() 00164 { 00165 MachineStatus old = status; 00166 00167 // advance simulated time 00168 if (status == SystemMode) 00169 { 00170 stats->totalTicks += SystemTick; 00171 stats->systemTicks += SystemTick; 00172 } 00173 else 00174 { // USER_PROGRAM 00175 stats->totalTicks += UserTick; 00176 stats->userTicks += UserTick; 00177 } 00178 DEBUG('i', "\n== Tick %d ==\n", stats->totalTicks); 00179 00180 // check any pending interrupts are now ready to fire 00181 ChangeLevel(IntOn, IntOff); // first, turn off interrupts 00182 // (interrupt handlers run with 00183 // interrupts disabled) 00184 while (CheckIfDue(FALSE)) // check for pending interrupts 00185 ; 00186 ChangeLevel(IntOff, IntOn); // re-enable interrupts 00187 if (yieldOnReturn) 00188 { // if the timer device handler asked 00189 // for a context switch, ok to do it now 00190 yieldOnReturn = FALSE; 00191 status = SystemMode; // yield is a kernel routine 00192 currentThread->Yield(); 00193 status = old; 00194 } 00195 } 00196 00197 //---------------------------------------------------------------------- 00198 // Interrupt::YieldOnReturn 00199 // Called from within an interrupt handler, to cause a context switch 00200 // (for example, on a time slice) in the interrupted thread, 00201 // when the handler returns. 00202 // 00203 // We can't do the context switch here, because that would switch 00204 // out the interrupt handler, and we want to switch out the 00205 // interrupted thread. 00206 //---------------------------------------------------------------------- 00207 00208 void Interrupt::YieldOnReturn() 00209 { 00210 //ASSERT(inHandler == TRUE); 00211 yieldOnReturn = TRUE; 00212 } 00213 00214 //---------------------------------------------------------------------- 00215 // Interrupt::Idle 00216 // Routine called when there is nothing in the ready queue. 00217 // 00218 // Since something has to be running in order to put a thread 00219 // on the ready queue, the only thing to do is to advance 00220 // simulated time until the next scheduled hardware interrupt. 00221 // 00222 // If there are no pending interrupts, stop. There's nothing 00223 // more for us to do. 00224 //---------------------------------------------------------------------- 00225 void Interrupt::Idle() 00226 { 00227 DEBUG('i', "Machine idling; checking for interrupts.\n"); 00228 status = IdleMode; 00229 if (CheckIfDue(TRUE)) 00230 { // check for any pending interrupts 00231 while (CheckIfDue(FALSE)) // check for any other pending 00232 ; // interrupts 00233 yieldOnReturn = FALSE; // since there's nothing in the 00234 // ready queue, the yield is automatic 00235 status = SystemMode; 00236 return; // return in case there's now 00237 // a runnable thread 00238 } 00239 00240 // if there are no pending interrupts, and nothing is on the ready 00241 // queue, it is time to stop. If the console or the network is 00242 // operating, there are *always* pending interrupts, so this code 00243 // is not reached. Instead, the halt must be invoked by the user program. 00244 00245 DEBUG('i', "Machine idle. No interrupts to do.\n"); 00246 printf("No threads ready or runnable, and no pending interrupts.\n"); 00247 printf("Assuming the program completed.\n"); 00248 Halt(); 00249 } 00250 00251 //---------------------------------------------------------------------- 00252 // Interrupt::Halt 00253 // Shut down Nachos cleanly, printing out performance statistics. 00254 //---------------------------------------------------------------------- 00255 void Interrupt::Halt() 00256 { 00257 printf("Machine halting!\n\n"); 00258 stats->Print(); 00259 Cleanup(); // Never returns. 00260 } 00261 00262 //---------------------------------------------------------------------- 00263 // Interrupt::Schedule 00264 // Arrange for the CPU to be interrupted when simulated time 00265 // reaches "now + when". 00266 // 00267 // Implementation: just put it on a sorted list. 00268 // 00269 // NOTE: the Nachos kernel should not call this routine directly. 00270 // Instead, it is only called by the hardware device simulators. 00271 // 00272 // "handler" is the procedure to call when the interrupt occurs 00273 // "arg" is the argument to pass to the procedure 00274 // "fromNow" is how far in the future (in simulated time) the 00275 // interrupt is to occur 00276 // "type" is the hardware device that generated the interrupt 00277 //---------------------------------------------------------------------- 00278 void Interrupt::Schedule(VoidFunctionPtr handler, int arg, int fromNow, IntType type) 00279 { 00280 int when = stats->totalTicks + fromNow; 00281 PendingInterrupt *toOccur = new PendingInterrupt(handler, arg, when, type); 00282 00283 DEBUG('i', "Scheduling interrupt handler the %s at time = %d\n", 00284 intTypeNames[type], when); 00285 ASSERT(fromNow > 0); 00286 00287 pending->SortedInsert(toOccur, when); 00288 } 00289 00290 //---------------------------------------------------------------------- 00291 // Interrupt::CheckIfDue 00292 // Check if an interrupt is scheduled to occur, and if so, fire it off. 00293 // 00294 // Returns: 00295 // TRUE, if we fired off any interrupt handlers 00296 // Params: 00297 // "advanceClock" -- if TRUE, there is nothing in the ready queue, 00298 // so we should simply advance the clock to when the next 00299 // pending interrupt would occur (if any). If the pending 00300 // interrupt is just the time-slice daemon, however, then 00301 // we're done! 00302 //---------------------------------------------------------------------- 00303 bool Interrupt::CheckIfDue(bool advanceClock) 00304 { 00305 MachineStatus old = status; 00306 int when; 00307 00308 //ASSERT(level == IntOff); // interrupts need to be disabled, 00309 // to invoke an interrupt handler 00310 if (DebugIsEnabled('i')) 00311 DumpState(); 00312 PendingInterrupt *toOccur = (PendingInterrupt *)pending->SortedRemove(&when); 00313 00314 if (toOccur == NULL) // no pending interrupts 00315 return FALSE; 00316 00317 if (advanceClock && when > stats->totalTicks) 00318 { // advance the clock 00319 stats->idleTicks += (when - stats->totalTicks); 00320 stats->totalTicks = when; 00321 } 00322 else if (when > stats->totalTicks) 00323 { // not time yet, put it back 00324 pending->SortedInsert(toOccur, when); 00325 return FALSE; 00326 } 00327 00328 // Check if there is nothing more to do, and if so, quit 00329 if ((status == IdleMode) && (toOccur->type == TimerInt) && pending->IsEmpty()) 00330 { 00331 pending->SortedInsert(toOccur, when); 00332 return FALSE; 00333 } 00334 00335 DEBUG('i', "Invoking interrupt handler for the %s at time %d\n", 00336 intTypeNames[toOccur->type], toOccur->when); 00337 #ifdef USER_PROGRAM 00338 if (machine != NULL) 00339 machine->DelayedLoad(0, 0); 00340 #endif 00341 inHandler = TRUE; 00342 status = SystemMode; // whatever we were doing, 00343 // we are now going to be 00344 // running in the kernel 00345 (*(toOccur->handler))(toOccur->arg); // call the interrupt handler 00346 status = old; // restore the machine status 00347 inHandler = FALSE; 00348 delete toOccur; 00349 return TRUE; 00350 } 00351 00352 //---------------------------------------------------------------------- 00353 // PrintPending 00354 // Print information about an interrupt that is scheduled to occur. 00355 // When, where, why, etc. 00356 //---------------------------------------------------------------------- 00357 00358 static void PrintPending(int arg) 00359 { 00360 PendingInterrupt *pend = (PendingInterrupt *)arg; 00361 00362 printf("Interrupt handler %s, scheduled at %d\n", 00363 intTypeNames[pend->type], pend->when); 00364 } 00365 00366 //---------------------------------------------------------------------- 00367 // DumpState 00368 // Print the complete interrupt state - the status, and all interrupts 00369 // that are scheduled to occur in the future. 00370 //---------------------------------------------------------------------- 00371 00372 void Interrupt::DumpState() 00373 { 00374 printf("Time: %d, interrupts %s\n", stats->totalTicks, 00375 intLevelNames[level]); 00376 printf("Pending interrupts:\n"); 00377 fflush(stdout); 00378 pending->Mapcar(PrintPending); 00379 printf("End of pending interrupts\n"); 00380 fflush(stdout); 00381 }

Generated on Thu Sep 16 12:33:45 2004 for NachOS by doxygen 1.3.8