//----------------------------------------------------------------------
//
//	ProcessSuspend
//
//	Place a process in suspended animation until it's
//	awakened by Processwakeup.
//
//	NOTE: This must only be called from an interrupt or trap.  It
//	should be immediately followed by ProcessSchedule().
//
//----------------------------------------------------------------------
void
ProcessSuspend (PCB *suspend)
{

    // Make sure it's already a runnable process.
    dbprintf ('p', "Suspending PCB 0x%x (%s) at %d.\n", suspend, suspend->name,my_timer_get());
    suspend->sleeptime = my_timer_get();
    ASSERT (suspend->flags & PROCESS_STATUS_RUNNABLE,
            "Trying to suspend a non-running process!\n");
    ProcessSetStatus (suspend, PROCESS_STATUS_WAITING);
    QueueRemove (&suspend->l);
    QueueInsertLast (&waitQueue, &suspend->l);
}
//----------------------------------------------------------------------
//
//	ProcessWakeup
//
//	Wake up a process from its slumber.  This only involves putting
//	it on the run queue; it's not guaranteed to be the next one to
//	run.
//
//	NOTE: This must only be called from an interrupt or trap.  It
//	need not be followed immediately by ProcessSchedule() because
//	the currently running process is unaffected.
//
//----------------------------------------------------------------------
void
ProcessWakeup (PCB *wakeup)
{
    int i, sleeptime_inseconds;
    float temp_estcpu, power;

    dbprintf ('p',"Waking up PCB 0x%x.\n", wakeup);
    // Make sure it's not yet a runnable process.
    ASSERT (wakeup->flags & PROCESS_STATUS_WAITING,
            "Trying to wake up a non-sleeping process!\n");

    sleeptime_inseconds = (my_timer_get() - wakeup->sleeptime)/1000;

    // Adjust the estimated CPU time
    if(sleeptime_inseconds >= 1) {
        temp_estcpu = ((((float)2 * wakeup->load)/((float)2 * wakeup->load + 1)) * wakeup->estcpu); // Base
        power = temp_estcpu;
        for(i = 1; i < sleeptime_inseconds; i++) {
            temp_estcpu *= power;  // To the power of sleeptime_inseconds
        }
        wakeup->estcpu = (int)temp_estcpu;
    }

    // Recalculate priority
    wakeup->prio = PUSER + (wakeup->estcpu/4) + (2*wakeup->p_nice);
    wakeup->runQueueNum = wakeup->prio/4;

    // Put the process on a run queue based on Priority
    ProcessSetStatus (wakeup, PROCESS_STATUS_RUNNABLE);
    QueueRemove (&wakeup->l);
    QueueInsertLast (&runQueue[wakeup->runQueueNum], &wakeup->l);

}
Example #3
0
//----------------------------------------------------------------------
//
//	ProcessWakeup
//
//	Wake up a process from its slumber.  This only involves putting
//	it on the run queue; it's not guaranteed to be the next one to
//	run.
//
//	NOTE: This must only be called from an interrupt or trap.  It
//	need not be followed immediately by ProcessSchedule() because
//	the currently running process is unaffected.
//
//----------------------------------------------------------------------
void
ProcessWakeup (PCB *wakeup)
{
  uint32 currTime;
  int i;
  dbprintf ('p',"Waking up PCB 0x%x.\n", wakeup);
  // Make sure it's not yet a runnable process.
  ASSERT (wakeup->flags & PROCESS_STATUS_WAITING,
          "Trying to wake up a non-sleeping process!\n");
  ProcessSetStatus (wakeup, PROCESS_STATUS_RUNNABLE);
  QueueRemove (&wakeup->l);
  
  currTime = my_timer_get();
  if (currTime - wakeup->sleeptime >= 10) {
    //printf("Updating prio after long wakeup");
    for (i = 0; i < (currTime - wakeup->sleeptime); i++) 
        wakeup->estcpu *= (2.0f/(2 + 1));
    wakeup->prio = PUSER + (int)(wakeup->estcpu/4) + 2*wakeup->p_nice;
  }
  wakeup->processed = 1 - processedFlag;
  QueueInsertLast (&runQueue[wakeup->prio/4], &wakeup->l);
}
Example #4
0
int TimerGet(void)
{
    return my_timer_get();
}
Example #5
0
//----------------------------------------------------------------------
//
//	ProcessFork
//
//	Create a new process and make it runnable.  This involves the
//	following steps:
//	* Allocate resources for the process (PCB, memory, etc.)
//	* Initialize the resources
//	* Place the PCB on the runnable queue
//
//	NOTE: This code has been tested for system processes, but not
//	for user processes.
//
//----------------------------------------------------------------------
int
ProcessFork (VoidFunc func, uint32 param, int p_nice, int p_info,char *name, int isUser)
{
  int		i, j, fd, n;
  Link		*l;
  int		start, codeS, codeL, dataS, dataL;
  uint32	*stackframe;
  int		newPage;
  PCB		*pcb;
  int	addr = 0;
  int		intrs;
  unsigned char buf[100];
  uint32 dum[MAX_ARGS+8], count, offset;
  char *str;


  intrs = DisableIntrs ();
  dbprintf ('I', "Old interrupt value was 0x%x.\n", intrs);
  dbprintf ('p', "Entering ProcessFork args=0x%x 0x%x %s %d\n", func,
	    param, name, isUser);
  // Get a free PCB for the new process
  if (QueueEmpty (&freepcbs)) {
    printf ("FATAL error: no free processes!\n");
    exitsim ();	// NEVER RETURNS!
  }
  l = QueueFirst (&freepcbs);
  dbprintf ('p', "Got a link @ 0x%x\n", l);
  QueueRemove (l);
  pcb = (PCB *)(l->object);
  // This prevents someone else from grabbing this process
  ProcessSetStatus (pcb, PROCESS_STATUS_RUNNABLE);

  // At this point, the PCB is allocated and nobody else can get it.
  // However, it's not in the run queue, so it won't be run.  Thus, we
  // can turn on interrupts here.
  dbprintf ('I', "Before restore interrupt value is 0x%x.\n", CurrentIntrs());
  RestoreIntrs (intrs);
  dbprintf ('I', "New interrupt value is 0x%x.\n", CurrentIntrs());

  // Copy the process name into the PCB.
  dstrcpy (pcb->name, name);

  //----------------------------------------------------------------------
  // This section initializes the memory for this process
  //----------------------------------------------------------------------
  // For now, we'll use one user page and a page for the system stack.
  // For system processes, though, all pages must be contiguous.
  // Of course, system processes probably need just a single page for
  // their stack, and don't need any code or data pages allocated for them.
  pcb->npages = 1;
  newPage = MemoryAllocPage ();
  if (newPage == 0) {
    printf ("aFATAL: couldn't allocate memory - no free pages!\n");
    exitsim ();	// NEVER RETURNS!
  }
  pcb->pagetable[0] = MemorySetupPte (newPage);
  newPage = MemoryAllocPage ();
  if (newPage == 0) {
    printf ("bFATAL: couldn't allocate system stack - no free pages!\n");
    exitsim ();	// NEVER RETURNS!
  }
  pcb->sysStackArea = newPage * MEMORY_PAGE_SIZE;


  //---------------------------------------
  // Lab3: initialized pcb member for your scheduling algorithm here
  //--------------------------------------
  pcb->p_nice = p_nice < 0 ? 0 : p_nice;
  pcb->p_info = p_info;
  pcb->sleeptime = my_timer_get();
  pcb->estcpu = 0;
  pcb->prio = PUSER;
  pcb->processed = 1 - processedFlag;
  pcb->estcputime = 0;
  //----------------------------------------------------------------------
  // Stacks grow down from the top.  The current system stack pointer has
  // to be set to the bottom of the interrupt stack frame, which is at the
  // high end (address-wise) of the system stack.
  stackframe = ((uint32 *)(pcb->sysStackArea + MEMORY_PAGE_SIZE)) -
    (PROCESS_STACK_FRAME_SIZE + 8);
  // The system stack pointer is set to the base of the current interrupt
  // stack frame.
  pcb->sysStackPtr = stackframe;
  // The current stack frame pointer is set to the same thing.
  pcb->currentSavedFrame = stackframe;

  dbprintf ('p',
	    "Setting up PCB @ 0x%x (sys stack=0x%x, mem=0x%x, size=0x%x)\n",
	    pcb, pcb->sysStackArea, pcb->pagetable[0],
	    pcb->npages * MEMORY_PAGE_SIZE);

  //----------------------------------------------------------------------
  // This section sets up the stack frame for the process.  This is done
  // so that the frame looks to the interrupt handler like the process
  // was "suspended" right before it began execution.  The standard
  // mechanism of swapping in the registers and returning to the place
  // where it was "interrupted" will then work.
  //----------------------------------------------------------------------

  // The previous stack frame pointer is set to 0, meaning there is no
  // previous frame.
  stackframe[PROCESS_STACK_PREV_FRAME] = 0;

  // Set the base of the level 1 page table.  If there's only one page
  // table level, this is it.  For 2-level page tables, put the address
  // of the level 1 page table here.  For 2-level page tables, we'll also
  // have to build up the necessary tables....
  stackframe[PROCESS_STACK_PTBASE] = (uint32)&(pcb->pagetable[0]);

  // Set the size (maximum number of entries) of the level 1 page table.
  // In our case, it's just one page, but it could be larger.
  stackframe[PROCESS_STACK_PTSIZE] = pcb->npages;

  // Set the number of bits for both the level 1 and level 2 page tables.
  // This can be changed on a per-process basis if desired.  For now,
  // though, it's fixed.
  stackframe[PROCESS_STACK_PTBITS] = (MEMORY_L1_PAGE_SIZE_BITS
					  + (MEMORY_L2_PAGE_SIZE_BITS << 16));


  if (isUser) {
    dbprintf ('p', "About to load %s\n", name);
    fd = ProcessGetCodeInfo (name, &start, &codeS, &codeL, &dataS, &dataL);
    if (fd < 0) {
      // Free newpage and pcb so we don't run out...
      ProcessFreeResources (pcb);
      return (-1);
    }
    dbprintf ('p', "File %s -> start=0x%08x\n", name, start);
    dbprintf ('p', "File %s -> code @ 0x%08x (size=0x%08x)\n", name, codeS,
	      codeL);
    dbprintf ('p', "File %s -> data @ 0x%08x (size=0x%08x)\n", name, dataS,
	      dataL);
    while ((n = ProcessGetFromFile (fd, buf, &addr, sizeof (buf))) > 0) {
      dbprintf ('p', "Placing %d bytes at vaddr %08x.\n", n, addr - n);
      // Copy the data to user memory.  Note that the user memory needs to
      // have enough space so that this copy will succeed!
      MemoryCopySystemToUser (pcb, buf, addr - n, n);
    }
    FsClose (fd);
    stackframe[PROCESS_STACK_ISR] = PROCESS_INIT_ISR_USER;
    // Set the initial stack pointer correctly.  Currently, it's just set
    // to the top of the (single) user address space allocated to this
    // process.
    str = (char *)param;
    stackframe[PROCESS_STACK_IREG+29] = MEMORY_PAGE_SIZE - SIZE_ARG_BUFF;
    // Copy the initial parameter to the top of stack

    MemoryCopySystemToUser (pcb, (char *)str,
			    (char *)stackframe[PROCESS_STACK_IREG+29],
			    SIZE_ARG_BUFF-32);

    offset = get_argument((char *)param);

    dum[2] = MEMORY_PAGE_SIZE - SIZE_ARG_BUFF + offset;
    for(count=3;;count++)
    {
      offset=get_argument(NULL);
      dum[count] = MEMORY_PAGE_SIZE - SIZE_ARG_BUFF + offset;
      if(offset==0)
      {
        break;
      }
    }
    dum[0] = count-2;
    dum[1] = MEMORY_PAGE_SIZE - SIZE_ARG_BUFF - (count-2)*4;
    MemoryCopySystemToUser (pcb, (char *)dum,
			    (char *)(stackframe[PROCESS_STACK_IREG+29]-count*4),
			    (count)*sizeof(uint32));
    stackframe[PROCESS_STACK_IREG+29] -= 4*count;
    // Set the correct address at which to execute a user process.
    stackframe[PROCESS_STACK_IAR] = (uint32)start;
    pcb->flags |= PROCESS_TYPE_USER;
  } else {
    // Set r31 to ProcessExit().  This will only be called for a system
    // process; user processes do an exit() trap.
    stackframe[PROCESS_STACK_IREG+31] = (uint32)ProcessExit;

    // Set the stack register to the base of the system stack.
    stackframe[PROCESS_STACK_IREG+29]=pcb->sysStackArea + MEMORY_PAGE_SIZE-32;

    // Set the initial parameter properly by placing it on the stack frame
    // at the location pointed to by the "saved" stack pointer (r29).
    *((uint32 *)(stackframe[PROCESS_STACK_IREG+29])) = param;

    // Set up the initial address at which to execute.  This is done by
    // placing the address into the IAR slot of the stack frame.
    stackframe[PROCESS_STACK_IAR] = (uint32)func;

    // Set the initial value for the interrupt status register
    stackframe[PROCESS_STACK_ISR] = PROCESS_INIT_ISR_SYS;

    // Mark this as a system process.
    pcb->flags |= PROCESS_TYPE_SYSTEM;
  }

  // Place the PCB onto the run queue.
  intrs = DisableIntrs ();
  QueueInsertLast (&runQueue[pcb->prio/4], l);
  RestoreIntrs (intrs);

  // If this is the first process, make it the current one
  if (currentPCB == NULL) {
    dbprintf ('p', "Setting currentPCB=0x%x, stackframe=0x%x\n",
	      pcb, pcb->currentSavedFrame);
    currentPCB = pcb;
  }
  dbprintf ('p', "Leaving ProcessFork (%s)\n", name);
  // Return the process number (found by subtracting the PCB number
  // from the base of the PCB array).
  return (pcb - pcbs);
}
//----------------------------------------------------------------------
//
//	ProcessSchedule
//
//	Schedule the next process to run.  If there are no processes to
//	run, exit.  This means that there should be an idle loop process
//	if you want to allow the system to "run" when there's no real
//	work to be done.
//
//	NOTE: the scheduler should only be called from a trap or interrupt
//	handler.  This way, interrupts are disabled.  Also, it must be
//	called consistently, and because it might be called from an interrupt
//	handler (the timer interrupt), it must ALWAYS be called from a trap
//	or interrupt handler.
//
//	Note that this procedure doesn't actually START the next process.
//	It only changes the currentPCB and other variables so the next
//	return from interrupt will restore a different context from that
//	which was saved.
//
//----------------------------------------------------------------------
// You should modify this function to use 4.4BSD scheduling policy
//
void
ProcessSchedule ()
{
    PCB           *pcb;
    int           i, j, n;
    int           atEndOfQueue = FALSE; // To be used as a boolean value
    Link          *links[32];

    // The OS exits if there's no runnable process.  This is a feature, not a
    // bug.  An easy solution to allowing no runnable "user" processes is to
    // have an "idle" process that's simply an infinite loop.
    /*  if (QueueEmpty (&runQueue)) {
        printf ("No runnable processes - exiting!\n");
        exitsim (); // NEVER RETURNS
        }*/
    dbprintf('p', "Entering ProcessSchedule [context switch] with current PCB: %p\n",currentPCB);

    currentPCB->p_quanta++;

    totalQuanta++;

    pcb = ProcessHighestPriority();

    currentPCB->runtime += my_timer_get() - startTime;

    if (currentPCB->p_info == 1) {
        printf(TIMESTRING1, currentPCB - pcbs);
        printf(TIMESTRING2, currentPCB->runtime / (float)1000);
        printf(TIMESTRING3, currentPCB - pcbs, currentPCB->prio);
    }

    startTime = my_timer_get();

    if (!pcb) {
        printf ("No runnable processes - exiting!\n");
        exitsim ();
    }

    dbprintf('p', "PCB (%p) currentPCB (%p)\n",pcb,currentPCB);


    // If last run process is still the highest priority (ie. not asleep/a zombie)
    if (pcb == currentPCB) {
        currentPCB->estcpu++;

        QueueRemove (&pcb->l);
        QueueInsertLast (&runQueue[pcb->runQueueNum], &pcb->l);

        dbprintf('p', "\tProcess quanta: %i estcpu: %i\n",currentPCB->p_quanta,currentPCB->estcpu);
        if((currentPCB->p_quanta % PROCESS_QUANTA_MAX) == 0) {
            dbprintf('p', "Recalculating priority of currentPCB\n");
            currentPCB->prio = PUSER + (currentPCB->estcpu/4) + (2*currentPCB->p_nice);
            dbprintf('p', "run queue: %i new run queue: %i prio: %i\n",currentPCB->runQueueNum,currentPCB->prio/4,currentPCB->prio);

            currentPCB->runQueueNum = currentPCB->prio/4;
            QueueRemove(&currentPCB->l);
            QueueInsertLast(&runQueue[currentPCB->runQueueNum], &currentPCB->l);

            dbprintf('p', "Recalculated priority\n");
        }
    }

    if(totalQuanta % TOTAL_QUANTA_MAX == 0) {
        dbprintf('p', "Full reshuffle\n");
        // dbprintf('p', "Process quanta exceeded max\n");
        // Store all the tails of all of the RunQueues
        for(i = 0; i < 32; i++) {
            links[i] = QueueLast(&runQueue[i]);
        }
        dbprintf('p', "Last links registered\n");

        for(i = 0; i < 32; i++) {
            //        if(QueueEmpty(&runQueue[i])) atEndOfQueue = TRUE;
            n = (&runQueue[i])->nitems;

            //        while(!atEndOfQueue) {
            for (j = 0; j < n; j++) {
                pcb = (PCB *)((QueueFirst(&runQueue[i]))->object);
                pcb->estcpu = (int)((((float)2 * pcb->load)/((float)2 * pcb->load + 1)) * pcb->estcpu) + pcb->p_nice;  // Decay the estimated CPU time of all processes
                pcb->prio = PUSER + (pcb->estcpu/4) + (2 * pcb->p_nice);                            // Recalculate the priority of all processes
                dbprintf('p', "\tRun queue shift (%p->%d)\n",pcb,pcb->prio);

                dbprintf('p', "At link: %p for last link: %p\n",&pcb->l,links[i]);
                if(links[i] == &pcb->l || (&pcb->l)->next == NULL) atEndOfQueue = TRUE;

                pcb->runQueueNum = pcb->prio/4;
                QueueRemove(&pcb->l);
                QueueInsertLast(&runQueue[pcb->runQueueNum], &pcb->l);
            }
        }

        pcb = ProcessHighestPriority();
        if (currentPCB == pcb) {
            QueueRemove(&currentPCB->l);
            QueueInsertLast(&runQueue[currentPCB->runQueueNum], &currentPCB->l);
        }
    }
    //}

    pcb = ProcessHighestPriority();

    //  }

    currentPCB = pcb;

    // currentPCB = ProcessHighestPriority();

    // Move the front of the queue to the end, if it is the running process.
    /*
       pcb = (PCB *)((QueueFirst (&runQueue))->object);
       if (pcb == currentPCB)
       {
       QueueRemove (&pcb->l);
       QueueInsertLast (&runQueue, &pcb->l);
       }

    // Now, run the one at the head of the queue.
    pcb = (PCB *)((QueueFirst (&runQueue))->object);
    currentPCB = pcb;
    dbprintf ('p',"About to switch to PCB 0x%x,flags=0x%x @ 0x%x\n",
    pcb, pcb->flags,
    pcb->sysStackPtr[PROCESS_STACK_IAR]);
     */

    // Clean up zombie processes here.  This is done at interrupt time
    // because it can't be done while the process might still be running
    while (!QueueEmpty (&zombieQueue)) {
        pcb = (PCB *)(QueueFirst (&zombieQueue)->object);
        dbprintf ('p', "Freeing zombie PCB 0x%x.\n", pcb);
        QueueRemove (&pcb->l);
        ProcessFreeResources (pcb);
    }
    // Set the timer so this process gets at most a fixed quantum of time.
    TimerSet (processQuantum);
    dbprintf ('p', "Leaving ProcessSchedule (cur=0x%x)\n", currentPCB);
}