0%

more system calls in Nachos

Multi-thread

Currently Nachos does not support multi-thread (or multi-process), so we need a pid to note the process.

Modify bar.c :

int main() {
SpaceId pid = Exec("../test/exec.noff");
Halt();
}

SpaceId

In exception.cc , add :

void StartProcess(int spaceID) {
currentThread->space->InitRegisters();
currentThread->space->RestoreState();
machine->Run();
ASSERT(FALSE);
}

addrspace.h :

private:
int spaceID;
static BitMap* userMap, pidMap;

addrspace.cc :

#define Max_UserProcess 256

BitMap* AddrSpace::userMap = new BitMap(NumPhysPages);
BitMap* AddrSpace::pidMap = new BitMap(Max_UserProcess);

int AddrSpace::getSpaceId() {
return spaceID;
}

AddrSpace::AddrSpace(OpenFile *executable) {
spaceID = pidMap->Find()+100; // find a pid for the process
// ...........
}

AddrSpace::~AddrSpace() {
pidMap->Clear(spaceID-100); // remember to remove it
for (int i = 0; i < numPages; i++) {
userMap->Clear(pageTable[i].physicalPage);
}
delete [] pageTable;
}

To test whether the space id works, print the pid in StartProcess :

printf("PID: %d\n", space->getSpaceId());
space->print();

Then make clean, make, ./nachos -d -x ../test/bar.noff :

PID: 100
page table dump: 11 pages in total
============================================
VirtPage, PhysPage
0, 0
1, 1
2, 2
3, 3
4, 4
5, 5
6, 6
7, 7
8, 8
9, 9
10, 10
============================================

[1 2], SC_Exec
../test/exec.noff
Initializing code segment, at 0x580, size 256
EXEC: PID: 101
[1 0], SC_Halt
Machine halting!

We see the main process with pid 100 and the exec process with pid 101.

Exit()

	.globl Exit
.ent Exit
Exit:
addiu $2,$0,SC_Exit
syscall
j $31
.end Exit

Create a file named exit.c in test directory :

#include "syscall.h"

int main() {
Exit(1);
}

In Makefile, add exit to target in line 53, then make.

targets = exit exec bar halt shell matmult sort

To generate a .s file:

/usr/local/mips/bin/decstation-ultrix-gcc -I ../userprog -I ../threads -S exit.c
	.file	1 "exit.c"
gcc2_compiled.:
__gnu_compiled_c:
.text
.align 2
.globl main
.ent main
main:
.frame $fp,24,$31 # vars= 0, regs= 2/0, args= 16, extra= 0
.mask 0xc0000000,-4
.fmask 0x00000000,0
subu $sp,$sp,24
sw $31,20($sp)
sw $fp,16($sp)
move $fp,$sp
jal __main
li $4,1 # 0x00000001
jal Exit
$L1:
move $sp,$fp
lw $31,20($sp)
lw $fp,16($sp)
addu $sp,$sp,24
j $31
.end main

We see li $4,1 , which means it passes the argument to Register 4. And the argument is the exit code.

We need to save the exit code, so modify exception.cc :

else if (type == SC_Exit) {
int exitCode = machine->ReadRegister(4);
machine->WriteRegister(2, exitCode);
printf("--------[SC_Exit] PID: %d exit_code: %d--------\n", (currentThread->space)->getSpaceId(), exitCode);
delete currentThread->space;
currentThread->Finish();
advancePC();
}

New file test_exit.c :

#include "syscall.h"

int main(){
SpaceId pid1 = Exec("../test/exit.noff");
SpaceId pid2 = Exec("../test/halt.noff");
Exit(0);
}

After make (Don’t forget to add it in Makefile), test it by ./nachos -d -x ../test/test_exit.noff

--------[SC_Exec] PID: 100 filename: ../test/exit.noff--------
Initializing code segment, at 0x580, size 256
--------[SC_Exec] PID: 100 filename: ../test/halt.noff--------
Initializing code segment, at 0xa80, size 320
--------[SC_Exit] PID: 100 exit_code: 0--------
--------[SC_Exit] PID: 101 exit_code: 1--------
--------[SC_Halt] PID: 102--------
Machine halting!

The main process with PID = 100, executes exit.noff and halt.noff, then exits with code 0; while the exit.noff gets it PID = 101, exits with code 1, and the halt.noff with PID = 102 halts.

So there is a little problem: the father process won’t wait until its child process exits. That’s what Join does.

Join()

This is similar to pthread_join(tid) in Linux, waiting for a process then it continues.

Here is how it works:

  1. The child process is in a waiting queue.
  2. Join() sleeps the current process.
  3. Get a process from waiting queue, give it CPU.
  4. After it ends, Finish it, then wake up father process.
  5. The father process get the Join code, wake up.

In scheduler.h in threads directory:

#ifdef USER_PROGRAM
List* getWaitingList() { return waitingList; }
#endif

private:
List *readyList; // queue of threads that are ready to run, but not running
#ifdef USER_PROGRAM
List *waitingList;
#endif

In scheduler.cc :

Scheduler::Scheduler() { 
readyList = new List;
#ifdef USER_PROGRAM
waitingList = new List;
#endif
}
Scheduler::~Scheduler() {
delete readyList;
#ifdef USER_PROGRAM
delete waitingList;
#endif
}

In thread.h in threads directory, add:

void Join(SpaceId id);

Forget it. Too complex and too hard.

Yield()

Just use the original Yield() :

else if (type == SC_Yield) {
printf("--------[SC_Yield] PID: %d--------\n", (currentThread->space)->getSpaceId());
currentThread->Yield();
advancePC();
}

New a file named yield.c in test directory :

#include "syscall.h"

int main(){
SpaceId pid = Exec("../test/exit.noff");
Yield();
Exit(0);
}

./nachos -d -x ../test/yield.noff :

--------[SC_Exec] PID: 100 filename: ../test/exit.noff--------
Initializing code segment, at 0x580, size 256
--------[SC_Yield] PID: 100--------
--------[SC_Exit] PID: 101 exit_code: 1--------
--------[SC_Exit] PID: 100 exit_code: 0--------
No threads ready or runnable, and no pending interrupts.
Assuming the program completed.
Machine halting!

First, exec, then yield, then the child exits. At last it exits.

File Operation

Create()

Obviously, create need to read a filename, then create an empty file:

else if (type == SC_Create) {
int address = machine->ReadRegister(4);
char filename[128];
for(int i = 0; i < 128; i++) {
machine->ReadMem(address+i, 1, (int *)&filename[i]);
if (filename[i] == '\0')
break;
}
printf("--------[SC_Create] PID: %d, filename: %s--------\n", (currentThread->space)->getSpaceId(), filename);
int fileDescriptor = OpenForWrite(filename);
if (fileDescriptor == -1)
printf("create file %s failed!\n", filename);
else
printf("create file %s succeed!, the file id is %d\n", filename, fileDescriptor);
Close(fileDescriptor);
//machine->WriteRegister(2,fileDescriptor);
advancePC();
}

Open()

It opens a file, then returns the file id(write to register 2 for future use):

else if (type == SC_Open) {
int address = machine->ReadRegister(4);
char filename[128];
for (int i = 0; i < 128; i++) {
machine->ReadMem(address+i, 1, (int *)&filename[i]);
if (filename[i] == '\0')
break;
}
printf("--------[SC_Open] PID: %d, filename: %s--------\n", (currentThread->space)->getSpaceId(), filename);
int fileDescriptor = OpenForWrite(filename);
if (fileDescriptor == -1)
printf("open file %s failed!\n", filename);
else
printf("open file %s succeed!, the file id is %d\n", filename, fileDescriptor);
machine->WriteRegister(2, fileDescriptor);
advancePC();
}

Write()

/* Write "size" bytes from "buffer" to the open file. */
void Write(char *buffer, int size, OpenFileId id);

3 arguments, obviously.

else if (type == SC_Write) {
int address = machine->ReadRegister(4);
int bufsize = machine->ReadRegister(5);
int fileID = machine->ReadRegister(6);
printf("--------[SC_Write] PID: %d, fileid: %d, buffer address: %d with size=%d--------\n", (currentThread->space)->getSpaceId(), fileID, address, bufsize);
OpenFile* openfile = new OpenFile(fileID);
ASSERT(openfile != NULL);

char buffer[128];
for (int i = 0; i < bufsize; i++) {
machine->ReadMem(address+i, 1, (int *)&buffer[i]);
}
buffer[bufsize] = '\0';
printf("the buffer is %s\n", buffer);

int WritePosition;
if (fileID == 1)
WritePosition = 0;
else
WritePosition = openfile->Length();

int writtenBytes = openfile->WriteAt(buffer, bufsize, WritePosition);
if ((writtenBytes) == 0)
printf("Writing file failed!\n");
else
printf("\"%s\" has been written in file %d successfully!\n", buffer, fileID);
advancePC();
}

Read()

/* Read "size" bytes from the open file into "buffer".  
* Return the number of bytes actually read -- if the open file isn't long enough, or if it is an I/O device, and there aren't enough characters to read, return whatever is available (for I/O devices, you should always wait until you can return at least one character). */
int Read(char *buffer, int size, OpenFileId id);

Similar to write() :

else if (type == SC_Read) {
int address = machine->ReadRegister(4);
int bufsize = machine->ReadRegister(5);
int fileID = machine->ReadRegister(6);
printf("--------[SC_Read] PID: %d, fileid: %d, buffer address: %d with size=%d--------\n", (currentThread->space)->getSpaceId(), fileID, address, bufsize);

OpenFile* openfile = new OpenFile(fileID);
ASSERT(openfile != NULL);
char buffer[128];
int readnum = openfile->Read(buffer, bufsize);

for (int i = 0; i < bufsize; i++) {
if (!machine->WriteMem(address, 1, buffer[i]))
printf("There is something wrong.\n");
}
buffer[bufsize] = '\0';
printf("Read successfully! The content is \"%s\" , length = %d\n", buffer, bufsize);
machine->WriteRegister(2, readnum);
advancePC();
}

Close()

/* Close the file, we're done reading and writing to it. */
void Close(OpenFileId id);

Quite simple:

else if (type == SC_Close) {
int fileID = machine->ReadRegister(4);
printf("--------[SC_Close] PID: %d, fileid: %d--------\n", (currentThread->space)->getSpaceId(), fileID);
Close(fileID);
printf("Close file[%d] successfully!\n", fileID);
advancePC();
}

Test

New file named filetest.c in test directory :

#include "syscall.h"

int main() {
OpenFileId file;
char buffer[50];
int readsz;
Create("Test");
file = Open("Test");
Write("something to test.", 15, file);
readsz = Read(buffer, 10, file);
Close(file);
Exit(0);
}

./nachos -d -x ../test/filetest.noff :

--------[SC_Create] PID: 100, filename: Test--------
create file Test succeed!, the file id is 3
--------[SC_Open] PID: 100, filename: Test--------
open file Test succeed!, the file id is 3
--------[SC_Write] PID: 100, fileid: 3, buffer address: 360 with size=15--------
the buffer is something to te
"something to te" has been written in file 3 successfully!
--------[SC_Read] PID: 100, fileid: 3, buffer address: 1320 with size=10--------
Read successfully! The content is "something " , length = 10
--------[SC_Close] PID: 100, fileid: 3--------
Close file[3] successfully!
--------[SC_Exit] PID: 100 exit_code: 0--------
No threads ready or runnable, and no pending interrupts.
Assuming the program completed.
Machine halting!

Nothing wrong.