---
# System prepended metadata

title: OS lab1

---

# Team10
## Team member:李侑庭(110062172), 盧思樺(110062272)

| 部分 | 李侑庭 | 盧思樺 |
| -------- | -------- | -------- |
| Trace code     | 50%     | 50%    |
| Implement     | 60%     | 40%     |
| Report     | 70%     | 30%     |
| Testing     | 30%     | 70%     |
# Part II-1:Trace code
## (a)SC_Halt
![](https://hackmd.io/_uploads/rk50zjIbp.png =50%x)
When user program calls the system call SC_Halt, start.S places the code for SC_Halt in r2.If ExceptionHandler is called from Machine::OneInstruction() to execute SC_Halt in kernal mode, then it will call SysHalt.
### 1.Machine::Run():
* Execute instructions one by one in user-level after leaving the kernel-level program, and advance the simulated time.
```clike
void Machine::Run()
{
	Instruction *instr = new Instruction;    //storage for decoded instruction
	kernel->interrupt->setStatus(UserMode);  //transfer control to user mode
	for (;;)
	{
	    OneInstruction(instr);               //execute one decoded instruction from a user-level program
	    kernel->interrupt->OneTick();        //advance simulated time
	    if (singleStep && (runUntilTime <= kernel->stats->totalTicks))
			Debugger();
	}
}
```
### 2.Machine::OneInstruction(Instruction *instr):
* Fetch, decode and execute an instruction.If there is an exception or interrupt, this function will call RaiseException function to invoke exception handler. 
* In the case of a system call instruction, this function will also call RaiseException function but with different arguments.
* When call RaiseException function, it will pass the exception type and the virtual address causing the trap as arguments.
### 3.Machine::RaiseException(ExceptionType which, int badVAddr):
* Store the virtual address causing the trap in a register, transfer control to kernel mode, invoke the ExceptionHandler function to deal with the system call or exception.
* After ExceptionHandler function retruns, control returns to the user mode.
```clike
void Machine::RaiseException(ExceptionType which, int badVAddr)
{
    registers[BadVAddrReg] = badVAddr;    //store the virtual address causing the trap in a register 
    DelayedLoad(0, 0);			// finish anything in progress
    kernel->interrupt->setStatus(SystemMode);    //leave user-level program
    ExceptionHandler(which);		// interrupts are enabled at this point
    kernel->interrupt->setStatus(UserMode);    //leave kernel-level program
}
```
### 4.ExceptionHandler(ExceptionType which):
* Handle exceptions based on the 'which' argument and the register values. 
* In the case of a system call, this function will utilize the code stored in register 2 to determine how to handle this system call and put back the result into register 2. Otherwise, it will only output messages.
* For the SC_Halt system call, it will call the SysHalt function.
### 5.SysHalt():
* Call the Halt function which in Interrupt.
```clike
void SysHalt()
{
  kernel->interrupt->Halt();
}
```
### 6.Interrupt::Halt():
* Implement the Halt function.Print some message and delete kernel to shut down Nachos.
```clike
void Interrupt::Halt()
{
    cout << "Machine halting!\n\n";
    cout << "This is halt\n";
    kernel->stats->Print();
    delete kernel; // Never returns.
}
```
## (b)SC_Create
![](https://hackmd.io/_uploads/B1jxmsUWp.png =80%x)
When user program calls the system call SC_Create, start.S places the code for SC_Halt in r2 and places a pointer to filename.If ExceptionHandler is called from Machine::OneInstruction() to execute SC_Create in kernal mode, then it will call SysCreate with the pointer as a parameter.
### 1.ExceptionHandler(ExceptionType which):
* Handle exceptions based on the 'which' argument and the register values. 
* In the case of a system call, this function will utilize the code stored in register 2 to determine how to handle this system call and put back the result into register 2. Otherwise, it will only output messages.
* For the SC_Create
(1) Find the filename by using the value in register 4 as an address to access the filename in main memory.
(2) Calling the SysCreate function with the filename as a parameter and get the status.
(3) Storing this status in register 2 and update program counter.
### 2.SysCreate(char *filename):
* Calling Create function in fileSystem with the filename as a parameter.
```clike
int SysCreate(char *filename)
{
	// return value
	// 1: success
	// 0: failed
	return kernel->fileSystem->Create(filename);
}
```
### 3.FileSystem::Create(char *name):
* Calling OpenForWrite function in fileSystem with the name as a parameter.
* OpenForWrite function will create file with name if it doesn't exist, or truncate it if it does.Store the return value as fileDescriptor.
* If fileDescriptor is -1, then return false.Otherwise,close fileDescriptor and return true.
```clike
bool Create(char *name)
{
    int fileDescriptor = OpenForWrite(name);

    if (fileDescriptor == -1)
        return FALSE;
    Close(fileDescriptor);
    return TRUE;
}
```
## (C\)SC_PrintInt
![](https://hackmd.io/_uploads/SyQzXjIb6.png =80%x)
When user program calls the system call SC_PrintInt, start.S places the code for SC_Halt in r2 and places interger in r4.If ExceptionHandler is called from Machine::OneInstruction() to execute SC_Create in kernal mode, then it will call SC_PrintInt with the interger as a parameter.
### 1.ExceptionHandler(ExceptionType which):
* Handle exceptions based on the 'which' argument and the register values. 
* In the case of a system call, this function will utilize the code stored in register 2 to determine how to handle this system call and put back the result into register 2. Otherwise, it will only output messages..
* For the SC_PrintInt
(1) find the val in register 4
(2) Call SysPrintInt with the val as a parameter
(3) Update program counter.
### 2.SysPrintInt()
* Call PutInt with the val as a parameter.
### 3.SynchConsoleOutput::PutInt(int value)
* Change the type of the value to char and store the value in the str.
* Use lock->Acquire to lock resource.Only the calling thread can use resource, the other threads will be blocked until lock release.
* Call PutChar function for each character in 'str' until the character is '\0'.
* Use waitfor->P() function to make the subsequent characters wait.
* Release lock.
### 4.SynchConsoleOutput::PutChar(char ch)
* Like PutInt function but only call PutChar function once with the character ch.
### 5.ConsoleOutput::PutChar(char ch)
* Call WriteFile function to write ch into a file.
* Set putBusy true to avoid prevent call to PutChar in the same time.
* Call Schedule function to schedule this function with the ConsoleTime and the type as parameter.
### 6.Schedule(CallBackObj *toCall, int fromNow, IntType type)
* Schedule for the CPU to be interrputed when simulated time reaches "now + when" .
* Create PendingInterrupt object with parameter and insert it to pending interrupts list.
* When interrputed toCall object need to be called. 
### 7.Run()
* Execute instructions one by one in user-level after leaving the kernel-level program, and advance the simulated time.
### 8.OneTick()
* Advance simulated time by the status.
* Turn off interrupts.
* Call CheckIfDue function to check pending interrupts with false as parameter.
* Turn on interrupts.
* If the timer device want to a context switch, do it.
### 9.CheckIfDue(bool advanceClock)
* If the pending interrupts list is empty, then return false.
* Check if it's time for the first pending interrupt. If it's not and the ready queue is empty, then advance the clock to the next interrupt. Otherwise, return false.
* Set inHandler true.
* Remove the first pending interrupt from the list and call CallBack function until pending interrupts list is empty or it's not yet time for them.
* Set inHandler false.
* Return true.
### 10.ConsoleOutput::CallBack()
* Set putBusy false to allow the next character can be output to the display.
* Update numConsoleCharsWritten.
* Call SynchConsoleOutput::CallBack().
### 11.SynchConsoleOutput::CallBack()
* Use waitFor->V() to make the subsequent characters can be sent to display.
## (d)Makefile
* target file: the name before colon represents the file we want to create.
* dependency file: the names after the colon represent the files needed to create the target file.
* rule : on the next line of target and after 'tab', the instructions we use to create target file. 

If we write a test file in fileIO_test3, for which the executable is to be called fileIO_test3.Then makefile must be add:
```
fileIO_test3.o: fileIO_test3.c
	$(CC) $(CFLAGS) -c fileIO_test3.c
fileIO_test3: fileIO_test3.o start.o
	$(LD) $(LDFLAGS) start.o fileIO_test3.o -o fileIO_test3.coff
	$(COFF2NOFF) fileIO_test3.coff fileIO_test3
```
And change the PROGRAMS definition
```
PROGRAMS = halt shell matmult sort fileIO_test3 
```
# Part II-2:Implement four I/O system calls in NachOS
## syscall.h
Uncomment the system call which we want to use.
```
#define SC_Open 6
#define SC_Read 7
#define SC_Write 8
#define SC_Close 10
```
## start.S
Make user system calls to the Nachos kernel and place the code and arguments.We add SC_Open, SC_Write, SC_Close and SC_Read here.
```
  	.globl Open
  	.ent	Open
Open:
	addiu $2,$0,SC_Open
	syscall
	j	$31
	.end Open

	.globl Write
	.ent	Write
Write:
	addiu $2,$0,SC_Write
	syscall
	j	$31
	.end Write

	.globl Close
	.ent	Close
Close:
	addiu $2,$0,SC_Close
	syscall
	j	$31
	.end Close

	.globl Read
	.ent	Read
Read:
	addiu $2,$0,SC_Read
	syscall
	j	$31
	.end Read
```
## exception.cc
We include our system calls in the SyscallException and pass the parameters stored in registers to the corresponding function in ksyscall.h.After that, update the program counter value.
```clike
	//system call code in r2, arg1 in r4, arg2 in r5, arg3 in r6
case SC_Open:
	val = kernel->machine->ReadRegister(4);
	{
		char *filename = &(kernel->machine->mainMemory[val]);
		status = SysOpen(filename);
		kernel->machine->WriteRegister(2, (int)status);
	}
	kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg));
	kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4);
	kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg) + 4);
	return;
	ASSERTNOTREACHED();
	break;
			
case SC_Write:
	val = kernel->machine->ReadRegister(4);
	{
		char *filename = &(kernel->machine->mainMemory[val]);
		status = SysWrite(filename, (int)kernel->machine->ReadRegister(5), (OpenFileId)kernel->machine->ReadRegister(6));
		kernel->machine->WriteRegister(2, (int)status);
	}
	kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg));
	kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4);
	kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg) + 4);
	return;
	ASSERTNOTREACHED();
	break;

case SC_Read:
	val = kernel->machine->ReadRegister(4);
	{
		char *filename = &(kernel->machine->mainMemory[val]);
		status = SysRead(filename, (int)kernel->machine->ReadRegister(5), (OpenFileId)kernel->machine->ReadRegister(6));
		kernel->machine->WriteRegister(2, (int)status);
	}
	kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg));
	kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4);
	kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg) + 4);
	return;
	ASSERTNOTREACHED();
	break;

case SC_Close:
	{
		// cout << filename << endl;
		status = SysClose((OpenFileId)kernel->machine->ReadRegister(4));
		kernel->machine->WriteRegister(2, (int)status);
	}
	kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg));
	kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4);
	kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg) + 4);
	return;
	ASSERTNOTREACHED();
	break;
```
## ksyscall.h
Interface for the system calls.We only call the function and pass the parameters to filesys.h.
```clike
OpenFileId SysOpen(char *filename)
{
	return kernel->fileSystem->OpenAFile(filename);
}

int SysWrite(char *buffer, int size, OpenFileId id)
{
	return kernel->fileSystem->WriteFile(buffer, size, id);
}

int SysRead(char *buffer, int size, OpenFileId id)
{
	return kernel->fileSystem->ReadFile(buffer, size, id);
}

int SysClose(OpenFileId id)
{
	return kernel->fileSystem->CloseFile(id);
}
```
## filesys.h
Implement our file operations in filesys.h via OpenFile data structure.
### (1)OpenAFile:
* We check the idx to determine the number of opened files.If the idx exceeds 19, we return -1.
* Otherwise, create an OpenFile data structure in OpenFileTable and return the idx.
### (2)WriteFile/ReadFile
* We check the id to determine the number of opened files.If the id exceed 19, less than 0 or the file is closed, then return -1.
* Use the function of OpenFile in the OpenFileTable to write or read a file.
* Bacause this function will return the number of characters written or read from the file, we return it.
### (3)CloseFile
* Check the id to determine the number of opened files.If the id exceed 19, less than 0 or the file is closed, then return -1.
* Otherwise, delete the OpenFile with the id from the OpenFileTable and return 1.
```clike
idx = 0    
OpenFileId OpenAFile(char *name)
{
    int fileDescriptor = OpenForReadWrite(name, false);
    if (idx >= 20 || fileDescriptor < 0)
    {
        return -1;
    }
    OpenFileTable[idx] = new OpenFile(fileDescriptor);
    fid[idx++] = fileDescriptor;
    return idx - 1;
}

int WriteFile(char *buffer, int size, OpenFileId id)
{
    if (id >= 20 || id < 0 || OpenFileTable[id] == NULL)
    {
        return -1;
    }
    return OpenFileTable[id]->Write(buffer, size);
}

int ReadFile(char *buffer, int size, OpenFileId id)
{
    if (id >= 20 || id < 0 || OpenFileTable[id] == NULL)
    {
        return -1;
    }
    return OpenFileTable[id]->Read(buffer, size);
}

int CloseFile(OpenFileId id)
{
    if (id >= 20 || id < 0 || OpenFileTable[id] == NULL)
    {
        return -1;
    }
    delete OpenFileTable[id];
    OpenFileTable[id] = NULL;
    return 1;
}
```
#  What difficulties did you encounter when implementing this assignment?
## 李侑庭
由於一開始trace code時，沒注意到filesys中有OpenFile的object，對如何維護OpenFileTable困擾了很久，還有原本以為fileDescriptor就會記錄已打開的檔案數量，但在testing中與實際數量不一樣，於是才修改code。
## 盧思樺
一開始沒發現要修改start.S，所以有點找不到方向，後來不知道為甚麼編譯不過，才發現必須在start.S新增要實作的system call。