Nachos architecture - PowerPoint PPT Presentation

1 / 54
About This Presentation
Title:

Nachos architecture

Description:

All threads within a process share the same ... decodes and executes it. This fetch-decode-execute cycle is repeated until either an illegal operation is ... – PowerPoint PPT presentation

Number of Views:70
Avg rating:3.0/5.0
Slides: 55
Provided by: Test281
Category:

less

Transcript and Presenter's Notes

Title: Nachos architecture


1
Nachos architecture
2
Threads What are they?
  • A traditional process has a single thread of
    control and a single program counter.
  • All threads within a process share the same
    address space
  • Each, however, has its own
  • program counter,
  • stack for procedure calls of the kernel code
  • and register set for context switch between
    threads
  • threads can be in different states - running,
    ready or blocked
  • All kernel threads share the kernel code

3
Creating a thread
  • Thread t
  • char id2, name "t"
  • int i, NumThreads 4
  • for(i0iltNumThreadsi)
  • sprintf(id,i)
  • strcat(name,id)
  • t new Thread(name)
  • creates 4 threads using the constructor function
    for the class Thread
  • The constructor function simply allocates space
    for thread and sets its status to JUST_CREATED
  • in Nachos, a "main" thread is created as part of
    system initialization
  • the ready list remains empty upon creation of
    these threads.
  • the global variable currentThread always points
    to the thread currently occupying the CPU

4
Forking a thread
  • Thread t
  • char id2, name "t"
  • int i, NumThreads 4
  • for(i0iltNumThreadsi)
  • sprintf(id,i)
  • strcat(name,id)
  • t new Thread(name)
  • t-gtFork("somefunction",0)
  • Fork() allocates a stack for the thread which
    invokes it, and adds it to the ready list
    maintained by the scheduler
  • StackAllocate() allocates and initializes the
    execution stack for the thread.
  • A C routine ThreadRoot() is called which calls
    the function "somefunction" and upon its return,
    calls ThreadFinish().
  • now, we have all four threads in the ready list
    with their status set to READY. currentThread
    still points to "main".

5
Thread Switching
  • CPU switching between threads t0 and t1
  • After the switch, t0 is back in the ready list
    and t1 occupies the CPU.
  • The switch takes place when the routine Run() is
    invoked. This routine is part of the Scheduler
    class (see scheduler.cc in the threads
    directory). Upon invocation, the routine saves
    the state of the thread currently occupying the
    CPU and loads the state of the thread being
    switched to, into the machine registers.
  • The actual switch takes place by calling the
    machine dependent context switch routine, SWITCH,
    defined in switch.s in the threads directory.
  • An important point to note in the Run() routine
    is what happens after the SWITCH routine returns.
    All the statements in the Run() routine after the
    call to SWITCH are executed by the new thread we
    just switched to! Thus, the thread that invoked
    the Run() routine is not the same as the one that
    finishes its execution.

6
Thread Yield
  • Yield() is invoked by a thread when it wants to
    relinquish the CPU, leaving it up to the
    scheduler to decide which thread to run next.
  • Assume the CPU is occupied by thread t1 and the
    following code is executed
  • currentThread-gtYield()
  • If the ready list is not empty, the Yield()
    routine appends this thread to the ready list and
    assigns the CPU to the thread at the head of the
    ready list. If there is no other thread on the
    ready queue, the routine returns without changing
    the system.
  • Thus, if the ready list is non-empty, a switch
    occurs as a result of a thread invoking Yield().
    As a result of t1 invoking Yield() , t2 occupies
    the CPU and t1 returns to the ready list.

7
Threadtest.cc
  • //Loop 5 times, yielding the CPU to another ready
    thread each iteration
  • Void SimpleThread(int which)
  • int num
  • for (num 0 num lt 5 num)
  • printf(" thread d looped d times\n", (int)
    which, num)
  • currentThread-gtYield()
  • // Set up a ping-pong between two threads, by
    forking a thread
  • // to call SimpleThread, and then calling
    SimpleThread ourselves.
  • voidThreadTest()
  • DEBUG('t', "Entering SimpleTest")
  • Thread t new Thread("forked thread")
  • t-gtFork(SimpleThread, 1)
  • SimpleThread(0)

8
Threadtest results
  • thread 0 looped 0 times
  • thread 1 looped 0 times
  • thread 0 looped 1 times
  • thread 1 looped 1 times
  • thread 0 looped 2 times
  • thread 1 looped 2 times
  • thread 0 looped 3 times
  • thread 1 looped 3 times
  • thread 0 looped 4 times
  • thread 1 looped 4 times
  • No threads ready or runnable, and no pending
    interrupts.
  • Assuming the program completed.
  • Machine halting!

9
Thread Finish
  • Finish() is called when a thread is done
    executing its forked procedure. The thread ceases
    to exist and the thread at the head of the ready
    list is assigned the CPU.
  • The global variable threadToBeDestroyed always
    points to the thread that is to be deallocated.
    Its default value is NULL and is set by a thread
    that invokes Finish().
  • Assume the CPU is occupied by thread t2 and the
    following code is executed
  • currentThread-gtFinish()
  • As a first step, the thread that invokes
    Finish(), sets the variable threadToBeDestroyed
    to itself and then invokes Sleep .

10
Thread Sleep
  • sets the status of the invoking thread to BLOCKED
    and invokes Run() to switch to the thread at the
    front of the ready list.
  • The thread is actually deallocated inside the
    Run() routine since it can only be deallocated
    after it has given up the CPU.
  • Sleep() can be invoked by either a thread that
    wants to finish or when it is blocked waiting on
    a synchronization variable.
  • In the first case, the finishing thread sets
    threadToBeDestroyed causing it to be deallocated
    by the thread being switched to inside the Run()
    routine.
  • In the second case, the blocked thread will
    eventually be woken up by some other thread and
    put back on the ready list.

11
Synchronization
  • where two or more processes are reading or
    writing some shared data and the final result
    depends on who runs precisely when, are called
    race conditions.
  • The key to avoiding race conditions is to provide
    mutual exclusion - some way of making sure that
    if one process is accessing a shared variable,
    other processes will be excluded from doing the
    same thing.
  • The part of the program where shared variables
    are accessed is called the critical region or
    critical section.
  • If execution of processes is arranged in a manner
    such that no two processes were in their critical
    section at the same time, race conditions are
    avoided

12
Semaphores
  • built on the hardware solutions
  • A semaphore S is an integer variable that can be
    accessed only through two atomic operations - P
    and V (Dutch for wait and signal respectively).
    These are defined as follows
  • P(S) while S lt 0 do no-op S S-1
  • V(S) S S 1
  • The testing and modification of the value of the
    semaphore in both the operations must be done
    atomically

13
synchronize two processes
  • The condition is that B S2 can be executed only
    after A S1.
  • This can be achieved by having a semaphore, synch
    initialized to 0.
  • A and B will now execute the following code
  • A S1 V(synch)
  • B P(synch) S2
  • Because synch is initialized to 0, B will execute
    S2 only after A has signalled it by invoking
    V(synch) which is after S1

14
semaphore in Nachos
  • in the files synch.cc and synch.h in the threads
    directory
  • P() and V() are made atomic by interrupts
    disabled
  • Each semaphore has a queue associated with it -
    the list class
  • To grab a semaphore, P() operation.
  • If the semaphore is not available, the process
    goes to sleep by invoking the Sleep operation on
    itself
  • Upon a signal, it decrements the value of the
    semaphore and executes its critical section.
  • Before a process exits its critical section, it
    invokes V().
  • As part of this operation, a process waiting in
    the semaphore queue is removed and scheduled to
    start executing.
  • This is done by placing it in the Ready List
    maintained by the scheduler. The process is
    appended to this list for later assignment to the
    CPU (ReadyToRun() in scheduler.cc. ).

15
Locks
  • a synchronization primitive similar to
    semaphores, for providing mutual exclusion
  • A lock can either be free or busy initially, a
    lock is free
  • two atomic operations provided are Acquire and
    Release
  • Before accessing a shared variable, a process
    acquires the lock, and releases it after it is
    done accessing that variable
  • two processes A and B accessing a shared variable
    would execute the following code to synchronize
    their access to their critical sections
  • Lock-gtAcquire()
  • critical section
  • Lock-gtRelease()

16
Locks in Nachos
  • Implementation of locks and condition variables
    (see section below) is part of a lab.
  • A skeleton definition of a Lock class is provided
    in synch.h,
  • There are three issues to consider here
  • Atomicity of Acquire() and Release().
  • disabling interrupts
  • Provision of some wait mechanism if a process
    trying to acquire the lock cannot do so.
  • implement a queue for each lock
  • Making sure that only the process that acquires
    the lock will release the lock.
  • Use the function isHeldByCurrentThread()

17
Monitors
  • A monitor is a high level primitive, provided as
    a programming language construct.
  • a collection of procedures, variables, and data
    structures grouped together in a special module
    or package
  • monitor synch
  • integer i condition c
  • procedure producer(x) . . end
  • procedure consumer(x) . . end
  • end monitor
  • A process may call the procedures within a
    monitor but cannot directly access the monitor's
    internal data structures
  • only one process can be active in a monitor at
    any instant

18
Condition Variables
  • Monitors need to provide a way for the processes
    to block themselves when they cannot proceed, use
    Condition variables
  • two operations are provided - wait and signal
  • When a monitor procedure finds that it cannot
    continue, it does a wait on some condition
    variable
  • This causes the calling process A to block. It
    also allows another process, B, that had
    previously been prohibited from entering the
    monitor to enter now. B can now wake up another
    process by doing a signal on the condition
    variable that A was waiting on
  • To avoid having two processes active inside the
    monitor at the same time, we need a rule
    specifying what happens after a signal.
  • Should B wait till A leaves the monitor or should
    A wait until B leaves the monitor. Two schemes
    have been proposed - the Hoare style and the Mesa
    style.
  • In the former, the signalling process gives up
    control over the monitor and the CPU to the woken
    process which starts running immediately and
    gives back control of the monitor to the
    signaller when it leaves its critical section. In
    the Mesa style, the woken thread is simply put on
    the ready list and it becomes the responsibility
    of the woken thread to re-enter the monitor.

19
Implement condition variables
  • part of synch.h it is your job to implement it
  • All operations on a condition variable in Nachos
    must be made while the current process/thread has
    acquired a lock (see parameters to the functions
    Wait(), Signal() and Broadcast()).
  • All accesses to a given condition variable must
    be protected by the same lock. This means that
    your implementation must enforce mutual exclusion
    among processes invoking the operations on
    condition variables.
  • Wait(), Signal() and Broadcast() have the
    following semantics
  • Wait() releases the lock, gives up the CPU until
    signalled and then re-acquire the lock.
  • Signal() wakes up a thread if there are any
    waiting on the condition variable.
  • Broadcast() wakes up all threads waiting on the
    condition.
  • In Nachos, condition variables obey the Mesa
    semantics. When a Signal() or Broadcast() wakes
    up another thread/process, it simply puts the
    thread on the ready list, and it is the
    responsibility of the woken thread to re-acquire
    the lock. Note that this re-acquire should be
    taken care of within your implementation of
    Wait().
  • Your implementation for condition variables must
    include a queue for processes waiting on the
    condition variable
  • in your implementation for Wait(), the calling
    process should release the lock, append itself to
    the queue and sleep until it is woken up by
    another thread. Upon waking up, it should
    re-acquire the lock.
  • For Signal(), the calling process should remove a
    thread from the condition queue and place it on
    the scheduler's ready list.
  • The Broadcast() operation must do the same but
    for all the threads on the condition queue

20
Interrupt management
  • Types of interrupt
  • the completion of an I/O operation,
  • Associated with each I/O device class, e.g.,
    floppy disks, hard disks, timers, console, is a
    location at the bottom of memory called the
    interrupt vector.
  • This contains the addresses of the interrupt
    service routines for various devices.
  • When an interrupt occurs, the interrupt hardware
    pushes the program counter, program status word,
    and possibly one or more registers onto the
    current stack
  • an error in a user program such as division by
    zero,
  • an invalid memory access,
  • a software request for operating system service
    such as a system call operation

21
Interrupt Management
  • interrupt vector
  • For each I/O device class, e.g., floppy disks,
    hard disks, timers, console, is a location at the
    bottom of memory called the interrupt vector.
  • contains the addresses of the interrupt service
    routines for various devices.
  • When an interrupt occurs, the interrupt hardware
    pushes the program counter, program status word,
    and possibly one or more registers onto the
    current stack.
  • The computer then jumps to the address specified
    in the appropriate entry in the interrupt vector
    and starts execution from there.
  • to determine which entry in the interrupt vector
    to use, a unique device number provided with the
    interrupt request is used to index into the
    vector.
  • The interrupt service routine
  • The interrupt service routine starts out by
    saving all the registers in the process table
    entry for the current process.
  • The instructions in the service routine are then
    executed.
  • Upon completion, registers and other data
    structures for the current process are re-loaded
    and the process starts running again.
  • Usually, interrupts are disabled while an
    interrupt is being serviced, delaying any other
    incoming interrupts. Thus, it is important that
    interrupt service routines be short

22
Nachos Interrupt
  • interrupt management are in interrupt.h and
    interrupt.cc in the machine directory.
  • The class PendingInterrupt defines an interrupt
    that is scheduled to occur in the future.
  • The main routines are
  • Schedule() schedules a future event to take place
    at a time specified in the one of the parameters
    to this routine. This is done by creating a
    PendingInterrupt object and placing it in the
    event queue. When it is time for the scheduled
    interrupt to take place, Nachos calls the handler
    routine.
  • SetLevel() changes the interrupt mask from its
    current status to an argument specified as a
    parameter to this routine. This routine is used
    to temporarily disable and re-enable interrupts
    for the purposes of mutual exclusion. Only two
    interrupt levels are supported IntOn and IntOff.
  • OneTick() advances the clock one tick and
    services any pending interrupts by calling the
    routine CheckIfDue(). This routine is called from
    the Run() routine which is part of the Machine
    class, after each user-level instruction is
    executed, and also by SetLevel() when the
    interrupts are restored.
  • CheckIfDue() examines the event queue for events
    that need servicing now. If it finds any, it
    services them by calling the appropriate handler
    routine.
  • Idle() "advances" the clock to the time of the
    next scheduled event. It is called by the
    scheduler when there is nothing in the ready
    queue.
  • The Timer class defined in timer.h(cc) in the
    machine directory simulates a real-time clock in
    Nachos by generating interrupts at regular
    intervals. The constructor creates a real-time
    clock that interrupts the CPU every TimerTicks
    units. This constant is defined in stats.h to be
    100

23
Nachos simulates interrupts
  • by maintaining an event queue together with a
    simulated clock.
  • As the clock ticks, the event queue is examined
    to find events scheduled to take place at that
    time.
  • The clock is maintained entirely in software and
    advances under the following conditions
  • Every time interrupts are restored (and the
    restored interrupt mask has interrupts enabled).
    Nachos code frequently disables and enables
    interrupts for the purposes of mutual exclusion
    by explicitly calling the routine SetLevel().
  • Whenever the MIPS simulator executes one
    instruction, the clock advances one tick.
  • Whenever the ready list is empty, the clock
    advances however many ticks are needed to
    fast-forward the current time to that of the next
    scheduled event.
  • Whenever the clock advances, the event queue is
    examined and any pending interrupts are serviced
    by invoking the interrupt service routine.
  • All interrupt service routines are run with
    interrupts disabled,
  • the service routine may not re-enable them.

24
User process
  • A process running a user program in its own
    address space
  • Has its own
  • user stack
  • User register state
  • Runs its own code in user mode
  • Invokes system calls to run kernel code in system
    mode
  • User process kernel thread address space
    user register set

25
Nachos user process
  • Class Thread
  • private
  • int stackTop
  • int machineStateMachineStateSize
  • public
  • .
  • int userRegistersNumTotalRegs
  • public
  • void SaveUserState()
  • void RestoreUserState()
  • AddrSpace space

26
System Calls
  • System calls are an interface between the
    operating system and its application programs
  • consider the open system call in UNIX
  • open(char path, int flags, int mode)
  • A system call is treated as a software interrupt
    by the hardware.
  • Control passes through an interrupt vector to a
    service routine in the operating system.
  • The kernel examines the interrupting instruction
    to determine the type of system call, verifies
    the correctness of the parameters and executes
    the request, returning control to the instruction
    following the system call.
  • The parameters to the system call may be passed
    in registers, on the stack, or in memory with
    pointers to memory locations passed in registers.

27
Exceptions
  • Exceptions
  • caused by the occurrence of unusual conditions
    during a process' execution
  • often are the result of programming errors, such
    as division by zero or integer overflow.
  • exception causes a trap to the operating system
  • transfers control through the interrupt vector to
    the operating system.
  • servicing of an exception works just like an
    interrupt.
  • operating system may abnormally terminate the
    program
  • an error message is displayed
  • a memory dump of the program is saved in a core
    file
  • user can then examine this memory dump and fix
    the problem

28
Nachos System calls/Exception Handling
  • List of Nachos system calls and UNIX equivalent

29
Syscall code
  • User programs invoke system calls by executing
    the MIPS syscall instruction,
  • generates a trap into the Nachos kernel.
  • The MIPS simulator implements traps by invoking
    the routine RaiseException() (see exception.cc)
    to take care of the problem.
  • ExceptionHandler() is passed an argument
    indicating the kind of exception that has been
    raised
  • A list of exceptions that can be raised within
    Nachos is in machine.h.
  • The list of system calls in Nachos is in
    syscall.h.
  • The actual instructions for making system calls
    are found in start.s .
  • At runtime, a code indicating the type of system
    call is placed in register 2 and the syscall
    instruction is executed.
  • Additional arguments to the system call are
    placed in registers 4-7, following standard C
    procedure call linkage conventions.
  • Any return values are expected to be in register
    2.
  • The syscall instruction is a trap instruction,
    meaning the next instruction to be executed is
    the first instruction is the trap handler.
  • In Nachos, the trap handler is the routine
    ExceptionHandler()

30
Syscall sequence
  • 1 include "syscall.h"
  • 2 .text
  • 3 .align 2
  • 4
  • 5 .globl __start
  • 6 .ent __start
  • 7 __start
  • 8 jal main / Call the procedure "main" /
  • 9 move 4,0 / R4 gets 0 /
  • 10 jal Exit / If we return from main, exit(0) /
  • 11 .end __start
  • 12
  • 13 .globl Halt
  • 14 .ent Halt
  • 15 Halt
  • 16 addiu 2,0,SC_Halt
  • 17 syscall
  • 18 j 31
  • 19 .end Halt

31
run user programs in Nachos
  • Nachos can run arbitrary user programs as long as
    they make only those system calls that are
    understood by Nachos
  • Nachos executables are in the Noff format
  • Eg to compile halt.c
  • halt.o
  • halt.c (CC) (CFLAGS) -c halt.c
  • halt halt.o start.o
  • (LD) (LDFLAGS) start.o halt.o -o halt.coff
  • ../bin/coff2noff halt.coff halt

32
Noff format
  • Noff format files consist of four parts.
  • Noff Header. This resides at the beginning of the
    file and describes the contents of the rest of
    the file, giving information about the program's
    instructions, initialized variables and
    uninitialized variables. In the variable
    noffMagic , the header maintains a reserved magic
    number indicating that the file is in the Noff
    format. Before attempting to execute a
    user-program, Nachos checks for the existence of
    this magic number to make sure the file is a
    Nachos executable.
  • The executable code segment.
  • Initialized data segment.
  • Uninitialized data segment.
  • For each of the last three sections of the file,
    the Noff header contains information in the
    following variables
  • virtualAddr - The virtual address that segment
    begins at (usually zero).
  • inFileAddr - Pointer within the Noff file where
    that section actually begins so that Nachos can
    read it into memory before execution.
  • size - The size of that segment.

33
Makefile revisited
  • 1 all halt shell matmult sort
  • 2 start.o start.s ../userprog/syscall.h
  • 3 (CPP) (CPPFLAGS) start.s gt strt.s
  • 4 (AS) (ASFLAGS) -o start.o strt.s
  • 5 rm strt.s
  • 6 shell.o shell.c
  • 7 (CC) (CFLAGS) -c shell.c
  • 8 shell shell.o start.o
  • 9 (LD) (LDFLAGS) start.o shell.o -o
    shell.coff
  • 10 ../bin/coff2noff shell.coff shell

34
The Nachos MIPS CPU
  • loading instructions for the user programs into
    Nachos main memory
  • initializing registers (including the program
    counter)
  • asking the machine to start executing those
    instructions
  • The machine
  • fetches the instruction that is pointed to by
    PCReg (the register containing the program
    counter),
  • decodes and executes it.
  • This fetch-decode-execute cycle is repeated until
    either an illegal operation is performed or a
    hardware interrupt is generated.
  • In such an event, the execution of the MIPS
    instructions is suspended and a Nachos interrupt
    service routine is invoked to deal with the
    condition

35
Machine object
  • Registers
  • Memory
  • MMU
  • Virtual Memory
  • a linear page table
  • or translation lookaside buffer
  • Devices
  • The Console Device
  • The Disk Device
  • Exception Handling
  • The MIPS simulator

36
MIPS
  • Class Machine
  • public
  • char mainMemory
  • int registersNumTotalRegs
  • TranslationEntry tlb
  • TranslationEntry pageTable
  • unsigned int pageTableSize
  • private

37
Machine Setup
38
MIPS machine
  • class Machine
  • public
  • void Run() // run a user program
  • void OneInstruction(Instruction instr) // run
    one instruction of user program
  • int ReadRegister(int num)
  • void WriteRegister(int num, int value)
  • bool ReadMem(int addr, int size, int value)
  • bool WriteMem(int addr, int size, int value)
  • ExceptionType Translate(int virtAddr, int
    physAddr, int size)
  • void RaiseException(ExceptionType which, int
    badVAddr)
  • char mainMemory
  • int registersNumTotalRegs
  • When a program is loaded into memory the Nachos
    operating system reads the contents of the
    program's executable file from disk, then writes
    those contents into main memory. This is done one
    byte at a time.
  • Once the contents of the executable are in memory
    and the program counter is set, the machine
    begins execution by calling MachineRun().

39
MachineRun()
  • When a program is loaded into memory the Nachos
    operating system reads the contents of the
    program's executable file from disk, then writes
    those contents into main memory. This is done one
    byte at a time. Once the contents of the
    executable are in memory and the program counter
    is set, the machine begins execution by calling
    MachineRun().
  • Void MachineRun()
  • Instruction instr new Instruction
  • if (DebugIsEnabled(n)) printf()
  • interrupt-gtsetStatus(UserMode)
  • for()
  • OneInstruction(instr)
  • interrupt-gtOneTick()
  • if(singleStep (runUntilTime lt
    status-gttotalTicks))
  • Debugger

40
Executing one instruction
  • Class Instruction
  • public
  • void Decode()
  • unsigned int value // binary representation of
    the instruction
  • char opCode
  • char rs,rt,rd
  • int extra // immediate, etc
  • Void MachineOneInstruction(Instruction instr)
  • if (!machine-gtReadMem(registersPCreg,4,raw)
    return
  • instr-gtvalueraw
  • instr-gtDecode()
  • switch(instr-gtopCode)
  • ...
  • ...
  • ...
  • case OP_SYSCALL RaiseException(SyscallException,
    0) break
  • case OP_XOR registersinstr-gtrd
    registersinstr-gtrs ... break
  • case OP_XORI registersinstr-gtrt
    registersinstr-gtrs ... break

41
Exception Handling Simulation
  • Change to system mode
  • Call corresponding exception handler
  • Void machine RaiseException(ExceptionType
    which, int badVAddr)
  • DEBUG(..)
  • registerpBadVAddrRegbadVAddr
  • DelayedLoad(0,0)
  • interrupt-gtsetStatus(SystemMode)
  • ExceptionHandler(which)
  • interrupt-gtsetStatus(UserMode)

42
trap
43
Main memory
44
Main Memory
  • main memory is implemented as an array of bytes.
    This array is written to by the instruction
    WriteMem(), and is read by the instruction
    ReadMem().
  • page table, which is a table of translations from
    virtual addresses to physical addresses.
  • An executing instruction which references a
    virtual address must first have that virtual
    address translated into a physical address via
    Translate() (which refers to the page table)
    before it can be executed.

45
Thread
  • From code/threads/thread.h
  • class Thread
  • public
  • FDTEntry FDTable MAX\_FD
  • int ThreadID
  • ThreadStatus status
  • unsigned int stack
  • char name MAXFILENAMELENGTH 1
  • int userRegistersNumTotalRegs
  • void SaveUserState()
  • void RestoreUserState()
  • AddrSpace space
  • FDTEntry getFD (int fd)
  • private
  • unsigned int machineStateMachineStateSize

46
Context Switching
47
SchedulerRun(),
  • void SchedulerRun (Thread nextThread)
  • Thread oldThread currentThread
  • if (currentThread-gtspace ! NULL)
  • currentThread-gtSaveUserState()
  • currentThread-gtspace-gtSaveState()
  • currentThread nextThread
  • currentThread-gtsetStatus(RUNNING)
  • SWITCH(oldThread, nextThread)
  • if (threadToBeDestroyed ! NULL)
  • delete threadToBeDestroyed
  • threadToBeDestroyed NULL
  • if (currentThread-gtspace ! NULL)
  • currentThread-gtRestoreUserState()
  • currentThread-gtspace-gtRestoreState()

48
Running User program
49
Memory management
  • Paging to give its threads contiguous logical
    address spaces
  • logical address spaces are 128 kilobytes in size,
    even though the simulated MIPS processor on which
    they run has only 16 kilobytes of main memory
  • MemoryManager and Machine objects together serve
    as the MMU of the nachos system
  • The MemoryManager object keeps track of which
    physical memory pages are free, which are used,
    and the owners of the used pages. As such, it is
    primarily involved in memory resource allocation.
    The Machine object, among other things, holds the
    page table of the currently running Nachos
    thread.
  • AddrSpace object. This object is simply an
    abstraction of the thread's logical address
    space, and contains a pointer to the thread's
    page table. When the CPU is given to the thread,
    the page table in the Machine object is loaded
    using this pointer

50
Page table
  • implemented as an array of TranslationEntry
    objects, one object for each page of the thread's
    logical address space.
  • class TranslationEntry
  • public
  • unsigned int virtualPage
  • unsigned int physicalPage
  • bool valid // page is not swapped out
  • bool readOnly // program text
  • bool use // set when referenced by hardware
  • bool dirty // modified
  • OpenFile File
  • size_t offset
  • bool zero
  • bool cow

51
Running user program
  • the thread object has a pointer to an address
    space object, which has a page table inside it.
  • To run a user program, the thread must have
    control of the MIPS simulator. When this is the
    case, the thread's user program will be resident
    in main memory and the machine's program counter
    will be pointing to an instruction somewhere in
    that memory.
  • Then the machine invokes Run(), which simply
    loops through a fetch-and-execute cycle, reading
    instructions from main memory and executing them
    until the threads quantum expires and the
    scheduler swaps it out.

52
Memory management
53
Paging and page fault
54
Demand paging
  • When a Nachos program is loaded, only its first
    and last pages are brought into main memory
  • The first page will be the first page of the
    program's text segment, and the last page will be
    its stack
  • Nachos handles page faults by loading the
    referenced page into main memory, and restarting
    the instruction that generated the page fault.
  • Pages are swapped in using the MemoryManagerpage
    in() function. In order to swap a page in, this
    function may need to swap another page out (if
    there are no free pages). This is done using the
    MemoryManagerpageout() function.
  • The file from which a page is read when being
    swapped into memory will vary depending on the
    nature of the page. The TranslationEntry object
    of each page contains a pointer to an OpenFile
    object.
  • OpenFile object member points
  • to the file from which the page can be read if it
    gets swapped out disk and has to be swapped back
    into main memory.
  • This will point to the swap file, if the page had
    been modified before it was swapped out,
  • or the original executable file if it has not
    been modified.
  • If the page contained was contained only
    uninitialized data, the pointer to the OpenFile
    object is set to NULL
Write a Comment
User Comments (0)
About PowerShow.com