예제 #1
0
  /**
   * Suspends the thread that is currently on the processor on the specified event.
   *
   * <p>Note that the thread being suspended doesn't need to be running. It can also be waiting for
   * completion of a pagefault and be suspended on the IORB that is bringing the page in.
   *
   * <p>Thread's status must be changed to ThreadWaiting or higher, the processor set to idle, the
   * thread must be in the right waiting queue, and dispatch() must be called to give CPU control to
   * some other thread.
   *
   * @param event - event on which to suspend this thread. @OSPProject Threads
   */
  public void do_suspend(Event event) {
    // To suspend a thread, we must first figure out which state to suspend it to. As can
    // be seen from Figure 3.1, there are two candidates:

    // If the thread is running, then it is suspended to ThreadWaiting.
    if (this.getStatus() == ThreadRunning) {
      // Again steps 1-3 for changing ThreadRunning to another status.
      MMU.getPTBR().getTask().setCurrentThread(null);
      MMU.setPTBR(null);
      this.setStatus(ThreadWaiting);
    }

    // If it is already waiting, then the status is incremented
    // by 1. For instance, if the current status of the thread is ThreadWaiting then it should
    // become ThreadWaiting+1.
    else // if (this.getStatus() == ThreadWaiting) only accounted for threadWaiting not +1, +2, etc
    this.setStatus(this.getStatus() + 1); // Accounts for higher cases than +1; such as +2

    // We now must set the new thread status using the method
    // setStatus() and place it on the waiting queue to the event.
    // IF and ONLY IF it does not already exist on the queue.
    if (!(lowPriorityQueue.contains(this)) || !(highPriorityQueue.contains(this))) {
      // Move to waiting queue
      event.addThread(this);
    }

    // Finally, a new thread must be dispatched using a call to dispatch()
    dispatch();
  }
예제 #2
0
 /**
  * Frees up main memory occupied by the task. Then unreserves the freed pages, if
  * necessary. @OSPProject Memory
  */
 public void do_deallocateMemory() {
   int s = MMU.getFrameTableSize();
   for (int i = 0; i < s; i++) {
     FrameTableEntry frame = MMU.getFrame(i);
     PageTableEntry page = frame.getPage();
     if (page != null && getTask() == page.getTask()) {
       frame.setPage(null);
       frame.setDirty(false);
       frame.setReferenced(false);
       if (frame.getReserved() == getTask()) {
         frame.setUnreserved(getTask());
       }
     }
   }
 }
예제 #3
0
 /**
  * The page table constructor. Must call
  *
  * <p>super(ownerTask)
  *
  * <p>as its first statement. @OSPProject Memory
  */
 public PageTable(TaskCB ownerTask) {
   super(ownerTask);
   size = (int) Math.pow(2, MMU.getPageAddressBits());
   pages = new PageTableEntry[size];
   for (int i = 0; i < size; i++) {
     pages[i] = new PageTableEntry(this, i);
   }
 }
예제 #4
0
  // This method destroys threads. To destroy a thread, its status must be set to ThreadKill
  // and a number of other actions must be performed depending on the current status of
  // the thread. (The status of a thread can be obtained via the method getStatus().)
  // If the thread is ready, then it must be removed from the ready queue. If a running
  // thread is being destroyed, then it must be removed from controlling the CPU, as
  // described earlier.
  public void do_kill() {
    if (this.getStatus() == ThreadReady) {
      ThreadCB.lowPriorityQueue.remove(this);
      ThreadCB.highPriorityQueue.remove(this);
    }

    if (this.getStatus() == ThreadRunning) {
      // Following steps 1-3 change the current status from ThreadRunning to
      // ThreadKill pages 55-56 of the manual
      // Step 2
      MMU.setPTBR(null);
      // Step 3
      this.getTask().setCurrentThread(null);
    }

    if (this.getStatus() == ThreadWaiting) {
      // Cancel the I/O request by removing the corresponding
      // IORB from its device queue. This can be done by scanning all devices in the device
      // table and executing the method cancelPendingIO() on each device. The device table
      // is an array of size Device.getTableSize() (starting with device 0), where device i
      // can be obtained with a call to Device.get().
      for (int device = 0; Device.getTableSize() > device; ++device)
        Device.get(device).cancelPendingIO(this);

      this.setStatus(ThreadKill);
    }

    // Includes threadWaiting
    this.setStatus(ThreadKill);

    // When a thread is killed, those resources must be released into the common
    // pool so that other threads could use them. This is done using the static method
    // giveupResources() of class ResourceCB, which accepts the thread be killed as a
    // parameter.
    this.getTask().removeThread(this);

    ResourceCB.giveupResources(this);

    // Two things remain to be done now. First, we must dispatch a new thread, since we
    // should use every interrupt or a system call as an opportunity to optimize CPU usage.
    dispatch();

    // Second, since we have just killed a thread, we must check if the corresponding task still
    // has any threads left. A task with no threads is considered dead and must be destroyed
    // with the kill() method of class TaskCB. To find out the number of threads a task
    // has, use the method getThreadCount() of TaskCB
    if (this.getTask().getThreadCount() == 0) this.getTask().kill();
  }
예제 #5
0
  /**
   * Sets the properties of a new task, passed as an argument.
   *
   * <p>Creates a new thread list, sets TaskLive status and creation time, creates and opens the
   * task's swap file of the size equal to the size (in bytes) of the addressable virtual memory.
   *
   * @return task or null @OSPProject Tasks
   */
  public static TaskCB do_create() {
    TaskCB new_task = new TaskCB(); // criação da nova task
    HClock new_clock = new HClock(); // instância do relógio do sistema
    String path = SwapDeviceMountPoint + String.valueOf(new_task.getID()); // path do swap file

    new_task.new_thread = new ArrayList<ThreadCB>();
    new_task.new_port = new ArrayList<PortCB>();
    new_task.new_file = new ArrayList<OpenFile>();

    new_task.setPageTable(new PageTable(new_task));

    new_task.setCreationTime(
        new_clock.get()); // usa a instância do relógio, para setar o horário de criação da task

    new_task.setStatus(TaskLive); // seta o estado atual da task (TaskLive)
    new_task.setPriority(1); // como não foi especificado nenhuma prioridade na descrição, usamos 1

    new_task.new_swap.create(
        path,
        (int)
            Math.pow(
                2.0D,
                MMU.getVirtualAddressBits())); // tamanho da página deve ser 2^(número de bits do
    // ambiente)
    OpenFile save_swap = OpenFile.open(path, new_task); // abrimos o swap file

    new_task.setSwapFile(
        save_swap); // dizemos que o swap criado acima deve ser alocado a nossa task

    ThreadCB.create(new_task); // criamos a thread inicial

    if (save_swap == null) { // verificamos se o swap foi criado com sucesso
      new_task
          .new_thread
          .get(0)
          .dispatch(); // caso não tenha, dispachamos a thread criada acima (posição 0 do vetor)
      return null; // e retornamos uma task nula
    }

    return new_task; // se tudo ocorreu como esperado, retornamos a task criada inicialmente
  }
예제 #6
0
  /**
   * Selects a thread from the run queue and dispatches it.
   *
   * <p>If there is just one theread ready to run, reschedule the thread currently on the processor.
   *
   * <p>In addition to setting the correct thread status it must update the PTBR.
   *
   * @return SUCCESS or FAILURE @OSPProject Threads
   */
  public static int do_dispatch() {
    // Context switching. Passing control of the CPU from one thread to another is called
    // context switching. This has two distinct phases: preempting the currently running
    // thread and dispatching another thread. Preempting a thread involves the following steps:
    // 1. Changing of the state of the currently running thread from ThreadRunning to whatever
    // is appropriate in the particular case. For instance, if a thread looses control of the CPU
    // because it has to wait for I/O, then its status might become ThreadWaiting. If the
    // thread has used up its time quantum, then the new status should become ThreadReady.
    // Changing the status is done using the method setStatus() described later.
    // This step requires knowing the currently running thread. The call MMU.getPTBR()
    // (described below) lets you find the page table of the currently scheduled task. The
    // task itself can be obtained by applying the method getTask() to this page table. The
    // currently running thread is then determined using the method getCurrentThread().

    // 2. Setting the page table base register (PTBR) to null. PTBR is a register of the
    // memory management unit (a piece of hardware that controls memory access), or MMU,
    // which always points to the page table of the running thread. This is how MMU knows
    // which page table to use for address translation. In OSP 2 , PTBR can be accessed
    // using the static methods getPTBR() and setPTBR() of class MMU.

    // 3. Changing the current thread of the previously running task to null. The current
    // thread of a task can be set using the method setCurrentThread().

    // When a thread, t, is selected to run, it must be given control of the CPU. This is called
    // dispatching a thread and involves a sequence of steps similar to the steps for preempting
    // threads:
    // 1. The status of t is changed from ThreadReady to ThreadRunning.

    // 2. PTBR is set to point to the page table of the task that owns t. The page table of a
    // task can be obtained via the method getPageTable(), and the PTBR is set using the
    // method setPTBR() of class MMU.

    // 3. The current thread of the above task must be set to t using the method setCurrentThread().
    // In practice, context switch is performed as part of the dispatch() operation, and steps 2
    // and 3 in the first list above can be combined with steps 2 and 3 of the second list.
    // In the degenerate case, when the running thread, t, is suspended and no other thread
    // takes control of the CPU, consider it as a context switch from t to the imaginary “null
    // thread.” Likewise, if no process is running and the dispatcher chooses some ready-to-run
    // thread for execution, we can view it as a context switch from the null thread to t.

    ThreadCB queueThread = null;

    // First, some thread should be chosen from the ready queue (or the currently running
    // thread can be allowed to continue).
    // highPriorityQueue is not empty and it has been selected to retrieve queue item randomly

    // Pick an item from a queue. First case is when both queues are not empty. Choose a thread
    // from one of the queues where 90% of the time the one selected will be from highPriority.
    if (!(ThreadCB.lowPriorityQueue.isEmpty()) && !(ThreadCB.highPriorityQueue.isEmpty())) {
      if ((randomGeneratedNumber.nextInt((10 - 1) + 1) > 1))
        queueThread = ThreadCB.highPriorityQueue.elementAt(0);
      else queueThread = ThreadCB.lowPriorityQueue.elementAt(0);
    }

    // Case 2 only low priority has anything in its queue. Thread will be selected from the
    // lowPriority queue.
    else if (!(ThreadCB.lowPriorityQueue.isEmpty()) && ThreadCB.highPriorityQueue.isEmpty())
      queueThread = ThreadCB.lowPriorityQueue.elementAt(0);

    // High priority queue is the only queue containing any threads. Thread chosen from it.
    else if (ThreadCB.lowPriorityQueue.isEmpty() && !(ThreadCB.highPriorityQueue.isEmpty()))
      queueThread = ThreadCB.highPriorityQueue.elementAt(0);

    // Process currently in CPU and in a queue. Context switch with use of timer at end of time
    // quantum.
    if (MMU.getPTBR() != null
        && queueThread
            != null /*(!(ThreadCB.highPriorityQueue.isEmpty()) || !(ThreadCB.lowPriorityQueue.isEmpty()))*/) {
      // Context switching with steps 1-3 given above. We already know that a thread is running on
      // the CPU from getPTBR()
      // so we need to switch the currently running thread with the one on the queue after time
      // quantum has ended.
      if (HTimer.get() <= 0) {
        // Add the thread currently on the CPU to the end of the readyQueue
        if (MMU.getPTBR().getTask().getPriority() > 1)
          ThreadCB.highPriorityQueue.addElement(MMU.getPTBR().getTask().getCurrentThread());
        else ThreadCB.lowPriorityQueue.addElement(MMU.getPTBR().getTask().getCurrentThread());

        MMU.getPTBR().getTask().getCurrentThread().setStatus(ThreadReady);
        MMU.getPTBR().getTask().setCurrentThread(null);
        MMU.setPTBR(null);

        MMU.setPTBR(queueThread.getTask().getPageTable());

        queueThread.getTask().setCurrentThread(queueThread);
        MMU.getPTBR().getTask().getCurrentThread().setStatus(ThreadRunning);
        // Which queue contains the thread? Remove recently set thread to CPU from readyQueue
        if (ThreadCB.highPriorityQueue.contains(queueThread)) ThreadCB.highPriorityQueue.remove(0);
        else ThreadCB.lowPriorityQueue.remove(0);
      }
    }

    // There is a thread running in the CPU, but there is nothing left in the queues. Continue
    // running thread. Return SUCCESS
    // No switch needed as only thread is already in the CPU.
    else if (
    /*ThreadCB.highPriorityQueue.isEmpty() && ThreadCB.lowPriorityQueue.isEmpty()*/ queueThread
            == null
        && !(MMU.getPTBR() == null)) return SUCCESS;

    // Process is in a queue but not in the CPU
    else if (MMU.getPTBR() == null
        && queueThread
            != null /*(!(ThreadCB.highPriorityQueue.isEmpty()) || !(ThreadCB.lowPriorityQueue.isEmpty()))*/) {
      MMU.setPTBR(queueThread.getTask().getPageTable());
      MMU.getPTBR().getTask().setCurrentThread(queueThread);
      MMU.getPTBR().getTask().getCurrentThread().setStatus(ThreadRunning);
      // Which queue contains the thread? Remove recently set thread to CPU from readyQueue
      if (ThreadCB.highPriorityQueue.contains(queueThread))
        ThreadCB.highPriorityQueue.remove(queueThread);
      else ThreadCB.lowPriorityQueue.remove(queueThread);

      return SUCCESS;
    }

    // No thread is running and there are no threads in the queues. Return FAILURE
    else if (
    /*ThreadCB.highPriorityQueue.isEmpty() && ThreadCB.lowPriorityQueue.isEmpty()*/ queueThread
            == null
        && MMU.getPTBR() == null) return FAILURE;

    return FAILURE; // None of the above cases were true. Something went wrong.
  }