//----------------------------------------------------------------------
//
//	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.
//
//----------------------------------------------------------------------
void
ProcessSchedule ()
{
  PCB		*pcb;
  int		i;

  dbprintf ('p', "Now entering ProcessSchedule (cur=0x%x, %d ready)\n",
	    currentPCB, QueueLength (&runQueue));
  // 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
  }

// 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);
}
Exemplo n.º 2
0
PCB *
ProcessHighestPriority() {
    PCB *ret = NULL;
    int i;

    for (i = 0; i < 32; i++) {
        if ((&runQueue[i])->nitems != 0) {
            dbprintf('p', "Found process to run in queue [%i]\n",i);
            ret = (PCB *)(QueueFirst(&runQueue[i])->object);
            break;
        }
    }

    return ret;
}
//----------------------------------------------------------------------
//
//	SemSignal
//
//	Signal on a semaphore.  Again, details are in Section 6.4 of
//	_OSC_.
//
//----------------------------------------------------------------------
void
SemSignal (Sem *sem)
{
    Link	*l;
    int		intrs;

    intrs = DisableIntrs ();
    dbprintf ('s', "Signalling on sem 0x%x, count=%d.\n", sem, sem->count);
    sem->count += 1;
    if (sem->count <= 0) {
	l = QueueFirst (&sem->waiting);
	QueueRemove (l);
	dbprintf ('s', "Waking up PCB 0x%x.\n", l->object);
	ProcessWakeup ((PCB *)(l->object));
	QueueFreeLink (l);
    }
    RestoreIntrs (intrs);
}
Exemplo n.º 4
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);
}
Exemplo n.º 5
0
//----------------------------------------------------------------------
//
//	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;
  int empty;
  int count = 0;
  float pinfotime;
  PCB* tailPtrs[32];
  PCB *curr; 
  total_num_quanta++;
  currentPCB->estcpu++;
  currentPCB->estcputime++;

  if (currentPCB->p_info) {  
    pinfotime = currentPCB->estcputime *.1;
    printf(TIMESTRING1, (int)GetCurrentPid());
    printf(TIMESTRING2, pinfotime);
    printf(TIMESTRING3, (int)GetCurrentPid());
    printf(TIMESTRING4, currentPCB->prio);
  }

  empty = 1;
  for (i = 0; i < 32; i++) {
    count += QueueLength(&runQueue[i]);     
  }
  dbprintf ('p', "Now entering ProcessSchedule (cur=0x%x, %d ready)\n",
            currentPCB, count);
  // 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 (!count) {
    printf ("No runnable processes - exiting!\n");
    exitsim (); // NEVER RETURNS
  }
  dbprintf('p', "About to check if estcpu is divisible by 4 (cur=0x%x\n", currentPCB);
  if ((int)currentPCB->estcputime % 4 == 0) {
    //printf("Updating prio");
    currentPCB->prio = PUSER + (int)(currentPCB->estcpu/4) + 2*currentPCB->p_nice;
  }
dbprintf('p', "About to move to end of queue (cur=0x%x\n", currentPCB);

  // Move the front of the queue to the end, if it is the running process.
  for (i = 0; i < 32; i++) {
    pcb = (PCB *)((QueueFirst (&runQueue[i]))->object);
    if (pcb == currentPCB)
    {
      QueueRemove(&pcb->l);
      QueueInsertLast(&runQueue[pcb->prio/4], &pcb->l);
      break;
    }
  }
dbprintf('p', "Before decay (cur=0x%x\n", currentPCB);


  //decay
  if (!(total_num_quanta % 10)) {
    //printf("Looping through all to update everything");
    for (i = 0; i < 32; i++) { // loop through each queue
      if (!QueueLength(&runQueue[i]))
          continue;
      curr = QueueFirst(&runQueue[i])->object;
      dbprintf('p', "Processing PCB in queue %d: 0x%x\n", i, curr);
      while (curr && curr->processed != processedFlag) { // all the elements not previously processed in the queue
        curr->estcpu = 2.0f/(2 + 1) *curr->estcpu + curr->p_nice;
        curr->prio = PUSER + (int)(curr->estcpu/4) + 2*curr->p_nice;
        curr->processed = processedFlag;
        QueueRemove(&curr->l);
        QueueInsertLast(&runQueue[curr->prio/4], &curr->l);
        curr = QueueFirst(&runQueue[i])->object;
        dbprintf('p', "Processing PCB in queue %d: 0x%x\n", i, curr);
      }
    }
  
    processedFlag = 1 - processedFlag;
  }
  // Now, run the one at the head of the queue.
  dbprintf ('p',"About to switch 0x%x", currentPCB);

  for (i = 0; i < 32; i++) {
    if (!QueueLength(&runQueue[i]))
      continue;
    pcb = (PCB *)((QueueFirst (&runQueue[i]))->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]);
    break;
  }

  // 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);
}
Exemplo n.º 6
0
//----------------------------------------------------------------------
//
//	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);
}