Nachos Instructional OS: Part 2 CS 270, Tao Yang, Spring 2011

advertisement
Nachos Instructional OS: Part 2
CS 270, Tao Yang, Spring 2011
Topics

Program execution in Nachos


Given a C program, produce binary MIPS code.
Execute this MIPS code.





Machine object
How to load a program
How to execute a program: process-level flow.
Demo of single-process execution flow.
Programming Assignment 2.



Support multiprogramming.
System calls for process execution.
System calls for a simple file system interface.
7/26/2016
2
System Layers
User process
Thread 1
User process
Nachos kernel threads
Thread 2
Thread N
Nachos OS modules
(Threads mgm, File System, Code execution/memory mapping,
System calls/Interrupt)
Simulated MIPS Machine
(CPU, Memory, Disk, Console)
Base Operating System
(Linux for our class)
Machine





Source code: under machine subdirectory.
Roughly approximates the MIPS architecture
Machine has registers, memory, a CPU and a
clock.
Simulated clock used for event scheduling
and execution (interrupts).
Can execute an arbitrary program with a
sequence of MIPS instructions.
7/26/2016
4
Machine: Code execution Steps






Load instructions into the machine's memory.
Initialize registers (including the program counter
PCReg).
Tell the machine to start executing instructions.
The machine then fetches the instruction PCReg
points at, decodes it, and executes it.
The process is repeated until an illegal operation is
performed or an interrupt is generated.
When a trap or interrupt takes place, an interrupt
service routine deals with the condition.
7/26/2016
5
Two modes of executions

User mode (executing MIPS instructions of a user
program)


Example: Halt code in test directory
User-programs can only access the memory
associated with the simulated machine.
7/26/2016
6
Two modes of executions

Kernel mode


kernel executes when Nachos first starts up
or when a user-program executes an instruction
that causes a ``hardware’’ trap



illegal instruction,
page fault
system call
7/26/2016
7
Machine Object: Implement a MIPS
machine


an instance created when Nachos starts up.
Supported public variables:



Registers: 40 registers.
Memory: Byte-addressable.
Virtual memory: use a single linear page table or
a software-managed TLB.
7/26/2016
8
Machine Object: Supported
operations








Machine(bool debug).
Translate(int virtAddr, int* physAddr, int size,
bool writing).
OneInstruction().
Run()
ReadRegister(int num)
WriteRegister(int num, int value)
ReadMem(int addr, int size, int* value)
WriteMem(int addr, int size, int value)
7/26/2016
9
Machine Object: Supported
operations








Machine(bool debug).
Translate(int virtAddr, int* physAddr, int size,
bool writing).
OneInstruction().
Run()
ReadRegister(int num)
WriteRegister(int num, int value)
ReadMem(int addr, int size, int* value)
WriteMem(int addr, int size, int value)
7/26/2016
10
Interrupt Object


Maintain an event queue together with a simulated clock.
Supported operations:
 Schedule(VoidFunctionPtr handler, int arg, int when, IntType
type) Schedule a future event to take place at time
``when''.
 Usage: schedule a yield at random interval.
 SetLevel(IntStatus level). Used to temporarily disable and
re-enable interrupts. Two levels are supported: IntOn and
IntOff.
 OneTick()- CheckIfDue(bool advanceClock). Examines if some event
should be serviced.

Idle(). ``advances'' to the clock to the time of the next
scheduled event
7/26/2016
11
Interrupt::OneTick()

Software managed clock.
 The clock advances 1 tick for user mode, 10 for
system mode
 after every restored interrupt (disable/enable
Interrupt)
 or after the MIPS simulator executes one
instruction.
 When the ready list is empty, fast-advance ticks
until the next scheduled event happens.

7/26/2016
12
Timer object



Generate interrupts at regular or random intervals
Then Nachos invokes the predefined clock event
handling procedure.
Supported operation:


Timer(VoidFunctionPtr timerHandler, int callArg, bool
doRandom).
Create a real-time clock that interrupts


every TimerTicks (100) time units
Or set this a random number for random mode
7/26/2016
13
Console Object




Simulates the behavior of a character-oriented CRT device
Data can be written to the device one character at a time
through the PutChar() routine.
Input characters arrive one-at-a-time. They can be retrieved by
GetChar().
Supported operations:

Console(char *readFile, char *writeFile, VoidFunctionPtr
readAvail,VoidFunctionPtr writeDone, int callArg).
Create a console instance.``readFile'' is the Unix file of where
the data is to be read from; if NULL, standard input is
assumed.


PutChar(char ch)
GetChar()
7/26/2016
14
Disk Object


Simulates the behavior of a real disk.

The disk has only a single platter, with multiple tracks (32).

Each track contains the same number of sectors (32).

Allow only one pending operation at a time.

Contain a ``track buffer'' cache. Immediately after seeking to a
new track, the disk starts reading sectors, placing them in the track
buffer.
Supported operations:

Disk(char *name, VoidFunctionPtr callWhenDone, int callArg)

ReadRequest(int sectorNumber, char *data)

WriteRequest(int sectorNumber, char *data)

ComputeLatency(int newSector, bool writing)
7/26/2016
15
Executing a user program
halt
shell
user space
data MIPS instructions executed
data
by the emulator
ExceptionHandler()
MIPS emulator
Machine::Run()
fetch/execute
examine/deposit
Machine
object
kernel
SaveState/RestoreState
examine/deposit
Rn
SP
PC
registers
Nachos
page
table
memory
process
page
tables
From C program to MIPS binary
myprogram.c
int j;
char* s = “hello\n”;
myprogram.o
assembler
data
int p() {
j = write(1, s, 6);
return(j);
}
data
data
data
p:
gcc
compiler
object
file
…..
libraries
and
other
objects
linker
store this
store that
push
jsr _write
ret
etc.
myprogram.s
data
program
myprogram
(executable file)
Binary code format (Noff)
Source code: under userprog subdirectory.
 The current Nachos can run a single MIPS binary
(Noff format) e.g. type ``nachos -x ../test/halt''.
 A user program must be compiled using a crossplatform gcc compiler that generates MIPS code.
 A Noff-format file contains (bin/noff.h)




the Noff header, describes the contents of the rest of the file
executable code segment (TEXT)
initialized data segment (DATA)
uninitialized data segment (BSS).
7/26/2016
18
Space usage during execution of a C
program
Stack grows from top-down.
STACK
Heap grows bottom-up
Uninitialized data
HEAP
BSS
DATA
Initialized data
Code
TEXT
TEXT, DATA, BSS, HEAP, STACK in C
Int f3=3; /* DATA segment */
Int f1; /*BSS segment*/
def[] = "1"; /* DATA segment */
int main(void)
{
static char abc[12], /* BSS segment */
static float pi = 3.14159; /* DATA segment */
int i = 3; /* Stack*/
char *cp; /*stack*/
cp= malloc(10); /*malloc allocates space from HEAP*/
f1= i+f3; /* code is in TEXT*/
strcpy(abc , "Test" ); /* “Test” is located in DATA segment */
}
7/26/2016
20
Noff format
.
Each segment has the following information:

virtualAddr: virtual address that segment
begins at.
 inFileAddr: Pointer within the Noff file
where that section actually begins.

The size (in bytes) of that segment.
7/26/2016
21
User process for executing a program
A Nachos thread is extended as a process

Each process has its own address space containing
 Executable code (Code segment)
 Initialized data (Data segment)
 Uninitialized data (BSS)
 Stack space for function calls/local variables
 how big is address space?
size = noffH.code.size + noffH.initData.size
+ noffH.uninitData.size+ UserStackSize

A process owns some other objects, such as open file
descriptors.
7/26/2016
Steps in User process creation
Currently only execute a single user program.

Create an address space.

Zero out all of physical memory (machine->mainMemory)

Read the binary into physical memory and initialize data
segment.

Initialize the translation tables to do a one-to-one mapping
between virtual and physical addresses.

Zero all registers, setting PCReg and NextPCReg to 0 and 4
respectively.

Set the stackpointer to the largest virtual address of the
process (stack grows downward).
7/26/2016
23
Key Calling graph when Nachos
executes under userprog directory
Space=
New AddrSpace()
in addrspace.cc
Initialize()
in system.cc
main() in
main.cc
StartProcess ()
in progtest.cc
Space->
InitRegisters()
Space
->RestoreState()
Machine->Run ()
in mipssim.cc
7/26/2016
Executable file
ReadAt()
Machine->
WriteRegister()
Machine->
OneInstruction()
Interupt->
OneTick()
In Interupt.cc
24
Creating a Nachos Process
(userprog/progtest.cc)
void StartProcess(char *filename) {
OpenFile *executable;
AddrSpace *space;
executable = fileSystem->Open(filename);
if (executable == NULL) {
printf("Unable to open file %s\n", filename);
return;
}
space = new AddrSpace(executable);
currentThread->space = space;
delete executable; // close file
space->InitRegisters();
space->RestoreState();
machine->Run();
ASSERT(FALSE);
}
Create a handle for
reading text and initial
data out of the
executable file.
Create an AddrSpace
object, allocating
physical
memory and setting up
the process page table.
Set address space of
current thread/process.
Initialize registers, load
pagetable, and begin
execution in user
mode.
Creating a Nachos Address Space
(userprog/addrspace.cc)
AddrSpace::AddrSpace(OpenFile *executable) {
NoffHeader noffH;
Read the header of binary file
unsigned int i, size;
executable->ReadAt((char *)&noffH, sizeof(noffH), 0);
Compute address space need
// how big is address space?
size = noffH.code.size + noffH.initData.size + noffH.uninitData.size
+ UserStackSize; // we need to increase the size to leave room for the
stack
numPages = divRoundUp(size, PageSize);
size = numPages * PageSize;
Setup a page table for
pageTable = new TranslationEntry[numPages];
address translation
for (i = 0; i < numPages; i++) {
pageTable[i].virtualPage = i; // for now, virtual page # = phys page #
pageTable[i].physicalPage = i;
pageTable[i].valid = TRUE;
}
....
Initializing a Nachos Address Space
Zero out memory allocated
bzero(machine->mainMemory, size);
// copy in the code and data segments into memory
Copy code segment to
if (noffH.code.size > 0) {
memory
executable->ReadAt(&(machine->mainMemory[noffH.code.virtualAddr]),
noffH.code.size, noffH.code.inFileAddr);
}
Copy initialized data segment
if (noffH.initData.size > 0) {
to memory
executable->ReadAt(&(machine->mainMemory[noffH.initData.virtualAddr]),
noffH.initData.size, noffH.initData.inFileAddr);
}
System Calls & Exception Handling
User programs invoke system calls by executing the MIPS
``syscall'' instruction

``syscall'' generates a hardware trap into the Nachos kernel.

A trap is implemented by invoking RaiseException() with arguments
indicating the trap cause.

RaiseException() calls ExceptionHandler() to take care of the

specific problem.

The system call number is stored in Register 2 and the return
address is in Register 31.
Assembly code for Halt():
Halt:
addiu $2,$0,SC_Halt
syscall
j $31
.end Halt

7/26/2016
28
When typing “nachos –x halt”



The main thread starts by running function
StartProcess() in file progtest.cc. This thread is used
to run halt binary.
StartProcess() allocates a new address space and
loads the halt binary. It also initializes registers and
sets up the page table.
Call Machine::Run() to execute the halt binary using
the MPIS emulator.


The halt binary invokes the system call Halt(), which causes
a trap back to the Nachos kernel via functions
RaiseException() and ExceptionHandler().
The exception handler determines that a Halt()
system call was requested from user mode, and it
halts Nachos.
7/26/2016
29
Nachos –x halt using halt.c in test
directory
Machine->Run ()
in mipssim.cc
Machine->
OneInstruction()
Machine->
RaiseException(SyscallException)
ExceptionHandler(SyscallException)
Interrupt->
Halt()
In Interupt.cc
7/26/2016
30
Assignment 2:
Multiprogramming&System Calls



Modify source code under userprog subdirectory.
 ~1200 lines of code.
The crossplatform compiler is under ~cs270t/gcc.
 This compiler on x86 machines produces a.out with the coff
format.
 Use utility coff2noff (under nachos’ bin directory) to convert
it as Noff.
 Check the makefile under test subdirectory on how to use
gcc and coff2noff.
System calls to be implemented:
 Multiprogramming: Fork(), Yield(), Exit(), Exec() and Join().
 File and console I/O: Creat(), Open(), Read(), Write(), and
Close().
7/26/2016
31
Multi-Processes and the Kernel
0
2n-1
text
0
text
text
data
data
data
BSS
BSS
BSS
user stack
user stack
user stack
args/env
args/env
args/env
kernel area
kernel area
2n-1
Nachos kernel
2n-1
0
kernel area
To run multiple processes
Nachos should
 Provide the physical memory management;
 Set up an address translation table with linear page
tables;
 Save/restore address-space related state during
process switching (AddrSpace::SaveUserState() and
AddrSpace:RestoreUserState() are called).
7/26/2016
33
Address translation with linear page
tables

A virtual address is split into page number and page
offset components





Machine->pageTable points to the page table to be used.
Machine->pageTableSize -- the actual size of the page table.
Process switching requires to set the above pointer
properly for each user.
Physical page 0 starts at machine-> mainMemory.
Each page has 128-bytes. The actual number of
physical pages is NumPhysPages (32).
7/26/2016
34
A Simple Page Table
Each process/VAS
has its own page
table. Virtual
addresses are
translated relative to
the current page
table.
process page table
PFN 0
PFN 1
In this example, each
VPN j maps to PFN j,
but in practice any
physical frame may be
used for any virtual
page.
PFN i
PFN i
+
offset
page #i
offset
user virtual address
physical memory
page frames
The page tables are
themselves stored in
memory; a protected
register holds a pointer
to the current page
table.
Assignment 2: Files involved


Key files.

progtest.cc -- test routines to run user code.

addrspace.h addrspace.cc -- create an address space and load the
program from disk.

syscall.h -- the system call interface.

exception.cc -- the handler for system calls and other user-level
exceptions such as page faults.

filesys.h, openfile.h console.h -- interface to the Nachos file system
and console.
Other related files:

bitmap.h bitmap.cc -- manipulate bitmpas (useful for keeping track
of physical page frames).

translate.h, translate.cc -- translation tables.

machine.h, machine.cc -- emulates main memory, processor, etc.

mipsim.cc -- emulates MPIS R2/3000 instructions.

console.cc -- emulates a terminal using UNIX files.
7/26/2016
36
Questions

Process management

How to let two processes run in parallel? What are fundamental
mechanisms to enable this functionality?




Another Nachos Process? Thread?
How to execute Machine->Run?
How to set PC (program counter) correctly in the new process?
How to invoke Machine->Run() from the current execution
process?
7/26/2016
37
Assignment 2: Implementation
Notes
Tao Yang
Part I: Multiprogramming





Fork(func) creates a new user-level (child) process,
whose address space starts out as an exact copy of
that of the caller (the parent),
Yield(): temporarily relinquish the CPU to another
process.
Exit(int) call takes a single argument, which is an
integer status value as in Unix. The currently
executing process is terminated..
Exec(filename) spawns a new user-level thread
(process), but creates a new address space. It should
return to the parent a SpaceId.
Join(ID) call waits and returns only after a process
with the specified ID has finished.
Getting Started


Review start.s (under test directory) which includes
all system call stubs, following the style of Halt.
Modify ExceptionHandler() in exception.cc to
include all system call entries.

After each system call, increment PC registers so that
ExceptionHandler() execution flow returns back to next
instruction after user’s system call place.





Arguments of a system call are in Registers 4, 5, 6 etc.


counter = machine->ReadRegister(PCReg);
machine->WriteRegister(PrevPCReg,counter);
counter = counter + 4;machine>WriteRegister(PCReg,counter);
counter = counter + 4; machine>WriteRegister(NextPCReg,counter);
how to verify? You may review MPIS assembly code produced for a
test C program using gcc -S.
If needed, return result is register 2

machine->WriteRegister(2,result);
PCB (Process Control Block)


Write the PCB and a process manager. Create
a PCB class that will store the necessary
information about a process. Don't worry
about putting everything in it right now, you
can always add more as you go along. To
start, it should have a PID, parent PID, and
Thread*.
The process manager- it can have getPID and
clearPID methods, which return an unused
process id and clear a process id respectively.
Memory Manager

Write a Memory Manager that will be used to
facilitate memory allocation:




Track memory page usage.
Allocate a page
Free a page
Modify AddrSpace:AddrSpace (addrspace.cc)
to use the memory manager.


Modify the page table constructors to use pages
allocated by your memory manager
Create a PCB (process control block) also for each
process to include key control information.
AddSpace.cc


Write a function (e.g. AddrSpace::Translate),
which converts a virtual address to a physical
address. It does so by breaking the virtual
address into a page table index and an offset.
Write a function( e.g. AddrSpace::ReadFile),
which loads the code and data segments into
the translated memory, instead of at position
0.


Read data into a system buffer (diskBuffer).
Copy buffered data into proper memory locations
(e.g. at machine->mainMemory[physAddr].)
Implement Fork() in
ExceptionHandler()

FuncEntry = Value of register 4 ( sys call
argu)





Target function to be executed in the new space.
Create a new kernel thread.
Create a new AddrSpace to be a duplicate of
the CurrentThread's space and get a new
PCB.
The current thread calls Yield() so the new
thread can run.
The new thread runs a dummy function that
creates a bridge for execution of the user
function).


Call NewThread->Fork(ForkBridge, FuncEntry)
Why? It needs to set program counter properly in
a new space.
ForkBridge() : Key parts


Set counter = FuncEntry
Initialize and restore the registers. For
example,






currentThread->RestoreUserState();
currentThread->space->RestoreState();
machine->WriteRegister(PCReg, counter);
machine->WriteRegister(PrevPCReg,counter-4);
machine->WriteRegister(NextPCReg,counter+4);
Call machine->Run() which executes the
forked user process in the desired FuncEntry
address.
Implement Exec()

Exec is creating a new process with new code and
data segments from a file.

Allocate a new address space which fits this file.





In order to get that file name you will have to write a function that
copies over the string from user space.
Allocate a new kernel thread and a PCB to execute with the above
space.
Fork the new thread to run a dummy bridge function
that sets the machine registers straight and runs the
code


Load data/code from an OpenFile object constructed from the
filename passed in by the user.
Call NewThread->Fork(ExecBridge ,NULL);
The calling thread should yield to give control to the newly
spawned thread.
Return the process ID that executes this binary.
ExecBridge(): Key parts

Initialize registers/restore state.



(currentThread->space)->InitRegisters();
(currentThread->space)->RestoreState();
Machine->run();
System Calls for File System

For the entire system, maintain a set of
objects (SysOpenFile class) representing
system-wide opened files.



Each file may be opened by many user processes.
Call it fileOpenTable[];
For each process, maintain a set of objects in
PCB (UserOpenFile class) representing files
opened by this process.


Each file has filename, index to the system-wide
open table entry, and offset for the current
read/write pointer
Implement create()

Nachos already has a simple file system
implemented


filesys.cc and openfile.cc under filesys directory.
Use Nachos fileSystem to add a new file to its
directory.
Implement open()


Use fileSystem->Open() (nachos’open) to
locate the file object.
Add the opened file object to the systemwide fileOpenTable.


If it is already opened by some other user
process, just share the entry.
Else insert to the local file open table in PCB.
Implement read()


Allocate an internal buffer.
If Inputfile ID is ConsoleInput



Else, find the current read offset.


Then use getchar() and save data in the internal
buffer.
Read until EOF or \n.
Use openFile:ReadAt (defined in openfile.cc) to
read data from the nachos file.
Copy data from the internal buffer to the
destination memory location.

Need to copy one by one since the address needs
to be translated one by one.
Implement write()


Allocate an internal buffer.
Copy data from the source memory location
to the internal buffer.


If Outputfile ID is ConsoleOutput


Need to copy one by one since the address needs
to be translated one by one.
Then use printf (assuming string type).
Else, find the current write offset.

Use openFile:WriteAt (defined in openfile.cc) to
write data to the nachos file.
Download