From 51bb0c6dab08e511604c3704108b380e3c308517 Mon Sep 17 00:00:00 2001 From: Armin Langhofer Date: Tue, 13 Oct 2015 18:07:07 +0200 Subject: [PATCH 1/8] AUTHORS: added armin langhofer as an author --- AUTHORS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index f8c27e6..a50423f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,4 +2,5 @@ Martin Aigner Christian Barthel Christoph Kirsch Michael Lippautz -Simone Oblasser \ No newline at end of file +Simone Oblasser +Armin Lanhgofer From 640920f8a6bf0beaf6c3dece59f5be7b138f4cf3 Mon Sep 17 00:00:00 2001 From: Armin Langhofer Date: Wed, 14 Oct 2015 01:18:42 +0200 Subject: [PATCH 2/8] assignment 0: readme for instructions how to compile and run, impl in linkedlist.c --- README | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ linkedlist.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 README create mode 100644 linkedlist.c diff --git a/README b/README new file mode 100644 index 0000000..385eae8 --- /dev/null +++ b/README @@ -0,0 +1,79 @@ +######################################### +## Assignment 0: Basic data structures ## +######################################### +https://github.com/cksystemsteaching/AOS-Winter-2015 +Author: armin langhofer + +This is my implementation of a linked list in C*. + +To Compile, please follow these steps: +====================================== +1. install ubuntu 32bit lts server edition, apt-get install gcc. +2. see https://github.com/mistay/AOS-Winter-2015/tree/selfie-master for how to get selfie up and running +3. git clone repro from https://github.com/mistay/AOS-Winter-2015.git or, if pull request was accepted from + https://github.com/cksystemsteaching/AOS-Winter-2015.git +4. change into cloned directory and do the following: + +$ gcc -o selife selfie.c -fno-builtin +$ touch out +$ ./selfie -c < selfie.c +$ mv out selfie.mips1 +$ touch out +$ ./selfie -m 32 selfie.mips1 < linkedlist.c +mem 32MB +[OS] Terminated with 0 + + +To Run: +======= +$ ./selfie -m 32 out +mem 32MB +d,c,b,a +d,b,a +done[OS] Terminated with 6 +$ + +Interpretation and Explanation of what happened +=============================================== +First, linkedlist.c creates some arbitrary elements: + + ... + head = create(0, 'a'); + head = create(head, 'b'); + toberemoved = create(head, 'c'); + ... + +Then, the resulted list is printed to stdout: + + d,c,b,a + +Now, one element is removed: + + ... + remove(head, toberemoved); + ... + +The resulting list is printed again: + d,b,a + + +Some fixed chars are printed (this is an easter-egg styled 'hello world'): + done + +And the application exits with exit code 6: just to clarify this application exits (and not the emulator). + Terminated with 6 + + +Result +====== +[OK] must be implemented in C* +[OK] must compile with selfie +[OK] must run on selfie +[OK alloc() ] the list must be dynamically allocated +[OK, alloc() ] every node must be dynamically allocated +[OK, create() ] inserting nodes to the list and removing nodes from the list +[OK, printll()] list iteration +[NG] Bonus: sort the list. Any way you like +[OK, Oct 14] Deadline: Oct 15, end of day + + diff --git a/linkedlist.c b/linkedlist.c new file mode 100644 index 0000000..3cf897b --- /dev/null +++ b/linkedlist.c @@ -0,0 +1,71 @@ +//erzeugt listenelement +int* create(int* next, int data) { + int* s; + s = (int*)malloc( 2 * 4 ); + + // next == 0 entspricht dem letzten element, ansonsten pointer aufs naechste + *(s+0) = next; + + // payload, daten kommen hier rein, zB 'a' + *(s+1) = data; + + return s; +} + +// entfernt element von liste +// sucht element in liste rekursiv +void remove(int* head, int* element) { + int* next; + next = *(head+0); + + if (next == element) { + // found, remove element. memleak todo: free next element's mem but not impl' yet in c*c + *(head + 0) = *(next + 0); + return; + } + + if ((int)next == 0 ) { + // abbruchbedinung: das letzte element wurde gefunden, der next pointer zeigt auf (int)0 + return; + } + remove(next,element); + + +} + +// gibt liste auf konsole aus, zB a,b,c, +// ruft sich dabei selbst rekursiv fuer alle elemente auf und bricht bei letztem element ab +void printll( int* element) { + int* next; + putchar(*(element + 1)); + + next = *(element+0); + if ((int)next == 0 ) { + // abbruchbedinung: das letzte element wurde gefunden, der next pointer zeigt auf (int)0 + putchar(10); // newline (at least under *nix) + return; + } + putchar(','); + printll(next); +} + +int main() { + int* head; + int* toberemoved; + head = create(0, 'a'); + head = create(head, 'b'); + toberemoved = create(head, 'c'); + head = toberemoved; + head = create(head, 'd'); + printll(head); + + remove(head, toberemoved); + + printll(head); + + putchar('d'); + putchar('o'); + putchar('n'); + putchar('e'); + exit(6); +} From ffb9d1491260f657d605168c5a4176c92e0b7309 Mon Sep 17 00:00:00 2001 From: Armin Langhofer Date: Mon, 26 Oct 2015 23:23:56 +0100 Subject: [PATCH 3/8] assignment1 --- selfie.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 3 deletions(-) diff --git a/selfie.c b/selfie.c index aa50fef..c70d0da 100755 --- a/selfie.c +++ b/selfie.c @@ -72,6 +72,9 @@ // ----------------------------------------------------------------- // ----------------------- LIBRARY FUNCTIONS ----------------------- // ----------------------------------------------------------------- +#include +int NUM_BINARIES; +int *registers_all; void initLibrary(); @@ -3388,11 +3391,37 @@ void loadBinary() { println(); } - if (numberOfReadBytes == 4) - binaryLength = binaryLength + 4; + // armin: bug? + //if (numberOfReadBytes == 4) + // binaryLength = binaryLength + 4; + + binaryLength = binaryLength + numberOfReadBytes; + // armin: why not binaryLength = binaryLength + numberOfReadBytes ?? } } +void duplicateBinary(int num_copies) { + int i; + int j; + int binarywords; + + binarywords = binaryLength / 4; + if (binaryLength %4 != 0 ) + // last 1-3 bytes of binary use another word + binarywords = binarywords + 1; + + + i=0; // current word + j=1; // current instance + while (j <= num_copies) { + while ( i < binarywords) { + *(memory + i + (binarywords * j)) = *(memory + i); + i = i + 1; // word + } + j = j + 1; + } +} + // ----------------------------------------------------------------- // --------------------------- SYSCALLS ---------------------------- // ----------------------------------------------------------------- @@ -4090,13 +4119,82 @@ void execute() { } } +// memory that holds all information about a context except registers +int* contexts; + +// memory that holds registers of each single process +int* registerstable; + + +void saveContext(process) { + *(contexts + process * 5 + 0) = pc; + *(contexts + process * 5 + 2) = reg_hi; + *(contexts + process * 5 + 3) = reg_lo; + *(contexts + process * 5 + 4) = ir; + + // registers dont have to be saved as they're already at correct position +} + +void loadContext(process) { + pc = *(contexts + process * 5 + 0); + reg_hi = *(contexts + process * 5 + 2); + reg_lo = *(contexts + process * 5 + 3); + ir = *(contexts + process * 5 + 4); + + // redirect pointer to process' registers + registers = (registers_all + process * 32 ); +} + + void run() { + + // current instruction within process, e.g. instr 2/3 in process 2 + int m; + + // current process id (0..2 for NUM_BINARIES = 3) + int process; + + // loop variables + int j=0; + int i=0; + + + contexts = malloc(5 * 4 * NUM_BINARIES); + + // like registers except linear memory that is allocated for each process + registers_all = malloc (32*4*NUM_BINARIES); + + // init registers_all (by copying) + for (j=0; j Date: Mon, 26 Oct 2015 23:24:25 +0100 Subject: [PATCH 4/8] demo code and docs --- README_ASSIGNMENT1 | 83 ++++++++++++++++++++++++++++++++++++++++++++++ count.c | 12 +++++++ 2 files changed, 95 insertions(+) create mode 100644 README_ASSIGNMENT1 create mode 100644 count.c diff --git a/README_ASSIGNMENT1 b/README_ASSIGNMENT1 new file mode 100644 index 0000000..ea12f81 --- /dev/null +++ b/README_ASSIGNMENT1 @@ -0,0 +1,83 @@ +############################################################ +## Assignment 1: Loading, scheduling, switching, execution # +############################################################ +https://github.com/cksystemsteaching/AOS-Winter-2015 +Author: armin langhofer + +Please note: in this example _4_ instances of count.c are loaded and executed. + +To Compile, please follow these steps: +====================================== +1. install ubuntu 32bit lts server edition, apt-get install gcc. +2. see https://github.com/mistay/AOS-Winter-2015/tree/selfie-master for how to get selfie up and running +3. git clone repro from https://github.com/mistay/AOS-Winter-2015.git or, if pull request was accepted from + https://github.com/cksystemsteaching/AOS-Winter-2015.git +4. change into cloned directory and do the following: + +$ gcc -o selfie selfie.c +$ touch out +$ ./selfie -c < count.c +$ + +To Run: +======= +# setting NUM_BINARES to 1 ends up in this result: +$ ./selfie -m 32 out +out: memory size 32MB +0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYout: exiting with error code 7 + +# now setting NUM_BINARIES to 4, recomplile and run: +$ ./selfie -m 32 out +out: memory size 32MB +0000111122223333444455556666777788889999::::;;;;<<<<====>>>>????@@@@AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYout: exiting with error code 7 +$ + +Interpretation and Explanation of what happened +=============================================== +NUM_BINARIES is a global 'constant' in selfie.c that can be adjusted to the number of binaries that are loaded into memore as requested by assignment 1. + +setting to 1 and executing a the following demo: + +-- snip demo -- +$ more count.c +int main() { + int i; + i=48; + + // print '0' to 'Z' (in asciitable) + while (i<90) { + putchar(i); + i = i+1; + } + + exit(7); +} +-- /snip demo -- + +leads to the output shown above. + +Some notes on memory usage: +- n times memory is needed (the loadBinary() memory is copied n-1 times) +- the int* registers are copied n-1 times +- pc, reg_hi, reg_lo and ir are copied n-1 times (and organized in int* processes) + +Please note: three commands are executed, then a context switch is performed. The value of three can be adusted in run() and should not affect the number of characters printed when running the demo code. I tested this for m=3 and m=4 successfully. + + + +Result +====== +[OK] Uunderstand how mipster interprets and executes binary instructions. Tipp: add your own comments to the code +[OK] mipster maintains a local state for a process (running executable), e.g., pc, registers, memory +[OK] understand the purpose of each variable and data structure +[OK] duplicate the process state n times +[OK] running mipster like: ./selfie -m 32 yourbinary should generate n instances of yourbinary in a single instance of mipster +[OK] implement preemptive multitasking, i.e., switching between the n instances of yourbinary is determined by mipster +[OK, tested for m=3 and m=4] switch processes every m instructions. 1 <= m <= number of instructions in yourbinary +[OK, see output] implement round-robin scheduling +[OK, see count.c] add some output in yourbinary to demonstrate context switching +[NG, needed more time to work on this assignment] Deadline: Oct 22, end of day + + + + diff --git a/count.c b/count.c new file mode 100644 index 0000000..f219be4 --- /dev/null +++ b/count.c @@ -0,0 +1,12 @@ +int main() { + int i; + i=48; + + // print '0' to 'Z' (in asciitable) + while (i<90) { + putchar(i); + i = i+1; + } + + exit(7); +} From 38cd49e1676187c263c439abfe66ab8eb21dc672 Mon Sep 17 00:00:00 2001 From: Armin Langhofer Date: Mon, 2 Nov 2015 18:01:34 +0100 Subject: [PATCH 5/8] assignment2: implemented segmenttable, sched_yield() and demo for cooperate multitasking --- README_ASSIGNMENT2 | 95 ++++++++++++++++++ count.c | 1 + selfie.c | 244 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 333 insertions(+), 7 deletions(-) create mode 100644 README_ASSIGNMENT2 diff --git a/README_ASSIGNMENT2 b/README_ASSIGNMENT2 new file mode 100644 index 0000000..c879876 --- /dev/null +++ b/README_ASSIGNMENT2 @@ -0,0 +1,95 @@ +########################################################## +## Assignment 2: Memory segmentation, yield system call ## +########################################################## +This assignment deals with cooperative multitasking of n processes in mipster using a single instance of physical memory. + +again, duplicate the process state n times +but, do not duplicate the whole main memory +instead, split the main memory into segments by implementing a segment table in mipster +each process has an entry in the segment table for the segment start address and segment size +design the segment table for constant time access +translate the addresses of read and write operations to memory + +implement cooperative multitasking through a yield system call, i.e., a user process calling sched_yield() will cause the OS to re-schedule + +implement a simple user program that demonstrates yielding, e.g, yield each time after printing a counter to the console +Deadline: Oct 29, end of day + + + + + +To Compile, please follow these steps: +====================================== +1. install ubuntu 32bit lts server edition, apt-get install gcc. +2. see https://github.com/mistay/AOS-Winter-2015/tree/selfie-master for how to get selfie up and running +3. git clone repro from https://github.com/mistay/AOS-Winter-2015.git or, if pull request was accepted from + https://github.com/cksystemsteaching/AOS-Winter-2015.git +4. change into cloned directory and do the following: + + + + +Compile and Run: +================ +$ touch count +$ ./selfie -c count.c -o count +./selfie: this is selfie's cstarc compiling count.c +./selfie: writing code into output file count +$ gcc selfie.c -o selfie && ./selfie -l count -m 32 +./selfie: loading code from input file count +./selfie: this is selfie's mipster executing count with 32MB of memory +000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYcount: exiting with error code 7 +$ + + +now with yield syscall: +$ gcc selfie.c -o selfie && ./selfie -l count -m 32 +./selfie: loading code from input file count +./selfie: this is selfie's mipster executing count with 32MB of memory +0y0y0y1y1y1y2y2y2y3y3y3y4y4y4y5y5y5y6y6y6y7y7y7y8y8y8y9y9y9y:y:y:y;y;y;yy>y>y?y?y?y@y@y@yAyAyAyByByByCyCyCyDyDyDyEyEyEyFyFyFyGyGyGyHyHyHyIyIyIyJyJyJyKyKyKyLyLyLyMyMyMyNyNyNyOyOyOyPyPyPyQyQyQyRyRyRySySySyTyTyTyUyUyUyVyVyVyWyWyWyXyXyXyYyYyYycount: exiting with error code 7 + + +Interpretation and Explanation of what happened +=============================================== +selfie's mipster loads 3 processes into its *memory. +all of the processes get 1Meg of RAM each, segmented into segments in *segmenttable. +first demo output prints result when m=6 in selfie.c (context switch after 12 execute()s). +second output demonstrates yielding (m=120): count yields each time after a char is printed. + + + +-- snip demo -- +$ more count.c +int main() { + int i; + i=48; + + // print '0' to 'Z' (in asciitable) + while (i<90) { + putchar(i); + i = i+1; + sched_yield(); + } + + exit(7); +} +-- /snip demo -- + + +Result +====== +[OK] again, duplicate the process state n times +[OK] but, do not duplicate the whole main memory +[OK] instead, split the main memory into segments by implementing a segment table in mipster +[OK] each process has an entry in the segment table for the segment start address and segment size +[OK] design the segment table for constant time access +[OK] translate the addresses of read and write operations to memory + +[OK] implement cooperative multitasking through a yield system call, i.e., a user process calling sched_yield() will cause the OS to re-schedule + +[OK] implement a simple user program that demonstrates yielding, e.g, yield each time after printing a counter to the console +[NG, neede more time to work on this assignment] Deadline: Oct 29, end of day + + + diff --git a/count.c b/count.c index f219be4..18ba4f3 100644 --- a/count.c +++ b/count.c @@ -6,6 +6,7 @@ int main() { while (i<90) { putchar(i); i = i+1; + sched_yield(); } exit(7); diff --git a/selfie.c b/selfie.c index 46ef229..5d32f52 100644 --- a/selfie.c +++ b/selfie.c @@ -658,6 +658,10 @@ void syscall_open(); void emitMalloc(); void syscall_malloc(); +void emitYield(); +void syscall_sched_yield(); + + void emitPutchar(); // ------------------------ GLOBAL CONSTANTS ----------------------- @@ -668,6 +672,7 @@ int SYSCALL_WRITE = 4004; int SYSCALL_OPEN = 4005; int SYSCALL_MALLOC = 5001; int SYSCALL_GETCHAR = 5002; +int SYSCALL_YIELD = 5003; // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- @@ -693,6 +698,18 @@ int *memory; // ------------------------- INITIALIZATION ------------------------ +// processes: number of processes +int processes; + +// process_id: current running process_id +int process_id; + +// segment table, see comments below how it's structured +int *segmenttable; + +// m: number of execute()s in run() are performed until scheduler switches to next process +int m; + void initMemory(int megabytes) { if (megabytes < 0) megabytes = 64; @@ -701,8 +718,14 @@ void initMemory(int megabytes) { memorySize = megabytes * 1024 * 1024; memory = malloc(memorySize); + + // segmenttable holds up to 100 processes + segmenttable = malloc(100 * 2 * 4); + processes = 0; } + + // ----------------------------------------------------------------- // ------------------------- INSTRUCTIONS -------------------------- // ----------------------------------------------------------------- @@ -810,6 +833,95 @@ void resetInterpreter() { reg_hi = 0; reg_lo = 0; } + + + + +// SEGMENT TABLE +// ------------- +// [process_id + 0] = offset +// [process_id + 1] = length +// e.g. +// [0] = 0 <-- offset process 0 +// [1] = 1M <-- length process 0 +// [2] = 1M (=0 offset + 1M length) <-- offset process 1 +// [3] = 4M <-- length process 1 +// [4] = 5M (=1M offset + 4M length) ... +// [5] = 2M +// [6] = 7M (=5M offset + 2M length) +// [7] = 1M +// ... +int getsegmentoffset(int process_id) { + return *(segmenttable + process_id * 2 + 0); +} +int getsegmentlength(int process_id) { + return *(segmenttable + process_id * 2 + 1); +} +void addsegment() { + if (processes == 0) { + *(segmenttable + 0) = 0; + *(segmenttable + 1) = 1024*1024; // 1Meg + } else { + *(segmenttable + 2 * processes + 0) = *(segmenttable + 2 * (processes - 1) + 0) + + *(segmenttable + 2 * (processes - 1) + 1); + + *(segmenttable + 2 * processes + 1) = 1024*1024; // 1Meg + } +} + +//void debugsegmenttable() { +// //printf("segmenttable\n"); +// //printf("============\n"); +// int i=0; +// for (i=0; i<6; i++) { +// //printf("seg[%d] %d\n",i , *(segmenttable + i)); +// } +//} + +void addprocess() { + int offsetlastsegment; + int i; + addsegment(); +//debugsegmenttable(); + offsetlastsegment = getsegmentoffset(processes); + processes = processes + 1; + process_id = processes - 1; + + copyBinaryToMemory(); + + i = 0; + while (i<32) { + storeMemory(binaryLength + 4*i, 0); + i = i + 1; + } + + + // reg_lo + storeMemory(binaryLength + 4*32, 0); + // reg_hi + storeMemory(binaryLength + 4*33, 0); + // ir: dont care + + // pc + storeMemory(binaryLength + 4*35, 0); + + + storeMemory(binaryLength + 4*REG_SP, offsetlastsegment + getsegmentlength(process_id) - 4); + storeMemory(binaryLength + 4*REG_GP, binaryLength); + storeMemory(binaryLength + 4*REG_K1, binaryLength); + + //ORI + //*(registers+REG_SP) = getsegmentlength(0) - 4; + //*(registers+REG_GP) = binaryLength; + //*(registers+REG_K1) = *(registers+REG_GP); + + +} + + + + + // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- // --------------------- L I B R A R Y --------------------- @@ -3152,6 +3264,7 @@ void compile() { emitOpen(); emitMalloc(); emitPutchar(); + emitYield(); // parser gr_cstar(); @@ -3497,9 +3610,13 @@ void emit() { } void load() { + int fd; + int numberOfReadBytes; + //printf("hello"); + fd = open(binaryName, 0, 0); // 0 = O_RDONLY if (fd < 0) { @@ -3533,8 +3650,14 @@ void load() { println(); } +// //printf("numOfReadBytes: %d", numberOfReadBytes); +// //printf("binaryLength: %d\n", binaryLength); + + if (numberOfReadBytes == 4) binaryLength = binaryLength + 4; + // selfieBUG? was, wenn nur 3 bytes (zB zum schluss) gelsesen werden, dann wird binaryLength nicht um 3 erhoeht..?!? + // vermutl nie ein issue da binary (bei code gemeration) immer %4==0 anz bytes groesze hat } } @@ -3650,7 +3773,8 @@ void syscall_write() { vaddr = *(registers+REG_A1); fd = *(registers+REG_A0); - buffer = memory + tlb(vaddr); + buffer = memory + tlb(vaddr) + getsegmentoffset(process_id); + //buffer = getsegmentoffset(process_id) + memory + tlb(vaddr); size = write(fd, buffer, size); @@ -3660,7 +3784,7 @@ void syscall_write() { print(binaryName); print((int*) ": wrote "); print(itoa(size, string_buffer, 10, 0)); - print((int*) " bytes from buffer at address "); + print((int*) " bytes from buffer at addressSZ "); print(itoa((int) buffer, string_buffer, 16, 8)); print((int*) " into file with descriptor "); print(itoa(fd, string_buffer, 10, 0)); @@ -3766,6 +3890,33 @@ void syscall_malloc() { println(); } } +void emitYield() { + createSymbolTableEntry(GLOBAL_TABLE, (int*) "sched_yield", binaryLength, FUNCTION, INTSTAR_T, 0); + + + emitIFormat(OP_ADDIU, REG_ZR, REG_A3, 0); + emitIFormat(OP_ADDIU, REG_ZR, REG_A2, 0); + emitIFormat(OP_ADDIU, REG_ZR, REG_A1, 0); + emitIFormat(OP_ADDIU, REG_ZR, REG_A0, 0); + + // remove the argument from the stack + emitIFormat(OP_ADDIU, REG_SP, REG_SP, 4); + + + // load the correct syscall number and invoke syscall + emitIFormat(OP_ADDIU, REG_ZR, REG_V0, SYSCALL_YIELD); + emitRFormat(OP_SPECIAL, 0, 0, 0, FCT_SYSCALL); + + // jump back to caller, return value is in REG_V0 + emitRFormat(OP_SPECIAL, REG_RA, 0, 0, FCT_JR); + +} + +void syscall_sched_yield() { + printf("y"); + fflush(stdout); + m = 120; +} void emitPutchar() { createSymbolTableEntry(GLOBAL_TABLE, (int*) "putchar", binaryLength, FUNCTION, INT_T, 0); @@ -3804,11 +3955,24 @@ int tlb(int vaddr) { } int loadMemory(int vaddr) { - return *(memory + tlb(vaddr)); + int offset; + offset = getsegmentoffset(process_id); + +// //printf("load mem: v:%d phy:%d\n", vaddr, tlb(vaddr) + offset ); +// fflush(stdout); + + return *(memory + tlb(vaddr) + offset ); + //return *(memory + tlb(vaddr)); } void storeMemory(int vaddr, int data) { - *(memory + tlb(vaddr)) = data; + int offset; + offset = getsegmentoffset(process_id); + +////printf("mem2write @%d: %d\n", tlb(vaddr) + offset, data); +//fflush(stdout); + *(memory + tlb(vaddr) + offset) = data; + //*(memory + tlb(vaddr) ) = data; } // ----------------------------------------------------------------- @@ -3831,6 +3995,8 @@ void fct_syscall() { syscall_open(); } else if (*(registers+REG_V0) == SYSCALL_MALLOC) { syscall_malloc(); + } else if (*(registers+REG_V0) == SYSCALL_YIELD) { + syscall_sched_yield(); } else { exception_handler(EXCEPTION_UNKNOWNSYSCALL); } @@ -4181,6 +4347,7 @@ void post_debug() { } void fetch() { +// //printf("fetch()"); ir = loadMemory(pc); } @@ -4230,13 +4397,69 @@ void execute() { } } + +void saveContext(int process_id) { + int i; + i=0; + while (i<32) { +// //printf("storing mem (addr: %d)\n", binaryLength + i * 4); +// fflush(stdout); + storeMemory(binaryLength + i * 4, *(registers + i )); + i = i + 1; +// //printf("done storing mem\n"); +// fflush(stdout); + } + storeMemory(binaryLength + 4*32, reg_lo); + storeMemory(binaryLength + 4*33, reg_hi); + storeMemory(binaryLength + 4*34, ir); + storeMemory(binaryLength + 4*35, pc); + +// //printf("done saving\n"); +// fflush(stdout); +} +void loadContext(int process_id) { + int i; + i=0; + while (i<32) { + *(registers + i) = loadMemory(binaryLength + i * 4); + i = i + 1; + } + reg_lo = loadMemory(binaryLength + 4*32); + reg_hi = loadMemory(binaryLength + 4*33); + ir = loadMemory(binaryLength + 4*34); + pc = loadMemory(binaryLength + 4*35); + +// //printf("done loading\n"); +// fflush(stdout); + +} + void run() { + m=0; + process_id = 0; while (1) { +// //printf("pc[%d]: %d\n", process_id, pc); fetch(); decode(); pre_debug(); execute(); post_debug(); + + m = m+1; + if (m > 120) { +// //printf("now switching context\n"); +// fflush(stdout); + m = 0; + saveContext(process_id); + process_id = process_id + 1; + if (process_id == processes ) { +// //printf("-A(%d)-", pc); + process_id = 0; + } + ////printf("pid: %d\n", process_id); + loadContext(process_id); + + } } } @@ -4332,11 +4555,18 @@ void emulate(int argc, int *argv) { print((int*) "MB of memory"); println(); - copyBinaryToMemory(); +//printf("binaryLength: %d\n", binaryLength); +//printf("preparing 3 processes...\n"); + addprocess(); + addprocess(); + addprocess(); - resetInterpreter(); + process_id=0; - *(registers+REG_SP) = memorySize - 4; + resetInterpreter(); +// *(registers+REG_SP) = memorySize - 4; + *(registers+REG_SP) = getsegmentlength(process_id) - 4; +//printf("first stackpointer: %d\n", *(registers+REG_SP)); *(registers+REG_GP) = binaryLength; *(registers+REG_K1) = *(registers+REG_GP); From 7fbc692f3e5f0879c3c39af94b13b131c48eea19 Mon Sep 17 00:00:00 2001 From: Armin Langhofer Date: Thu, 12 Nov 2015 15:41:00 +0100 Subject: [PATCH 6/8] assignment3: implemented kernel process running on top of emulator --- README_ASSIGNMENT3 | 121 ++++++++++++++++++++++++++++++ count.c | 48 ++++++++++-- selfie.c | 181 ++++++++++++++++++++++++++++++++------------- 3 files changed, 290 insertions(+), 60 deletions(-) create mode 100644 README_ASSIGNMENT3 diff --git a/README_ASSIGNMENT3 b/README_ASSIGNMENT3 new file mode 100644 index 0000000..c2f4251 --- /dev/null +++ b/README_ASSIGNMENT3 @@ -0,0 +1,121 @@ +############################################ +## Assignment 3: bootstrapping the kernel ## +############################################ +At the end of this assignment you will have the operating system running on top if mipster along with other processes. + +implement the operating system in selfie.c and use the provided flag (-k) to execute the kernel code. +whenever a trap (e.g. a syscall instruction) or an interrupt (e.g. scheduling timer) happens, the operating is invoked instead of handling the trap or interrupt by the emulator. However, the OS cannot modify the machine state directly, i.e., modifying the memory pointer and registers array is not possible. Therefore: +provide a special system call, e.g., switch(int previous_process, int next_process) in the emulator that is invoked by the operating system only and modifies the machine state. One issue remains: after the OS invokes switch, the OS process must be reset to interrupt-trap handling mode. You can rely on the following convention: mipster starts executing a binay at address 0x0, the main method of selfie.c. Resetting the PC of the OS process to 0x0 after switch will reset the OS but not its heap and globals. The OS stack must be reset as well. Important: selfie -k must start with interrupt/trap handling. If no interrupt or trap is to be handled, the OS switches to the first ready process. If not ready process exists, the OS loads some_program.mips or terminates. + +Deadline: Nov 5, end of day + + + +Compile and Run: +================ +$ touch count +$ gcc selfie.c -o selfie && ./selfie -l count -m 32 +./selfie: loading code from input file count +./selfie: this is selfie's mipster executing count with 32MB of memory +KERNEL syscall_switch_from-to: 0->1 +01234567_NEXTKERNEL syscall_switch_from-to: 0->2 +01234567_NEXTKERNEL syscall_switch_from-to: 0->1 +89:;<=>?@_NEXTKERNEL syscall_switch_from-to: 0->2 +89:;<=>?@_NEXTKERNEL syscall_switch_from-to: 0->1 +ABCDEFGHI_NEXTKERNEL syscall_switch_from-to: 0->2 +ABCDEFGHI_NEXTKERNEL syscall_switch_from-to: 0->1 +JKLMNOPQR_NEXTKERNEL syscall_switch_from-to: 0->2 +JKLMNOPQR_NEXTKERNEL syscall_switch_from-to: 0->1 +STUVWXYcount: exiting with error code 8 +$ + +Interpretation and Explanation of what happened +=============================================== +selfie's mipster loads 3 processes into its *memory. +the 1st process (process_id == 0) could be seen as a "kernel". +the remaining two processes are counters to demonstrate how the kernel works. + +all three processes are in count.c. the switch between kernel and other procesesses is handled by +count.c. a getpid() systemcall is provided for count.c to determine if process should come up as kernel or not. + +please note: as soon as exit syscall is emitted the emulator stops working (second running process is not executed to the end) but resolving this issue seems not to be the goal of this assignment. + +the characters 'K','E','R','N','E','L' are sent to console to indicate that kernel is running. +the kernel then switches to the next process, e.g. when switching from 0 to pid 2 it's indicated +by: 'syscall_switch_from-to: 0->2'. + +please note: a timer interrupt is simulated in run(), see snippet: +-- snip selfie -- + // timer interrupt for o/s. simulates external timer. just interrupts non-os pid + if (process_id != 0) { + m = m+1; + if (m > 200) { + m = 0; + + saveContext(); + process_id = 0; // = O/S + loadContext(); + pc = pc + 4; + } + } + +-- /snip selfie -- +this forces the non-process pids to switch to kernel process. the program counter has to be increased as the syscall from switching from kernel to non-kernel processes (happend before) did not return. + + +please note: 'N','E','X','T' indicates that the kernel code completed. + +last note: as i'm working alone on all the issues and the assignments are sequent it is hard to adhere the deadlines. so i am afraid this issue is late again. + +-- snip demo -- +$ more count.c +int main() { + int i; + int pid; + + int rr_pid; + + pid = (int)getpid(); + + if (pid == 0 ) { + rr_pid = 0; + while (1) { + + // kernel process + putchar('K'); + putchar('E'); + putchar('R'); + putchar('N'); + putchar('E'); + putchar('L'); + + rr_pid = rr_pid + 1; + if (rr_pid > 2) + rr_pid = 1; + sched_switch(0, rr_pid); + putchar('_'); + putchar('N'); + putchar('E'); + putchar('X'); + putchar('T'); + + + //sched_yield(); + } + exit(6); + } else { + // non-kernel processes + i=48; + + // print '0' to 'Z' (in asciitable) + while (i<90) { + putchar(i); + i = i+1; + } + exit(8); + } + +} +-- /snip demo -- + + diff --git a/count.c b/count.c index 18ba4f3..57698c0 100644 --- a/count.c +++ b/count.c @@ -1,13 +1,47 @@ int main() { int i; - i=48; + int pid; - // print '0' to 'Z' (in asciitable) - while (i<90) { - putchar(i); - i = i+1; - sched_yield(); + int rr_pid; + + pid = (int)getpid(); + + if (pid == 0 ) { + rr_pid = 0; + while (1) { + + // kernel process + putchar('K'); + putchar('E'); + putchar('R'); + putchar('N'); + putchar('E'); + putchar('L'); + + rr_pid = rr_pid + 1; + if (rr_pid > 2) + rr_pid = 1; + sched_switch(0, rr_pid); + putchar('_'); + putchar('N'); + putchar('E'); + putchar('X'); + putchar('T'); + + + //sched_yield(); + } + exit(6); + } else { + // non-kernel processes + i=48; + + // print '0' to 'Z' (in asciitable) + while (i<90) { + putchar(i); + i = i+1; + } + exit(8); } - exit(7); } diff --git a/selfie.c b/selfie.c index 5d32f52..b184ea6 100644 --- a/selfie.c +++ b/selfie.c @@ -62,7 +62,7 @@ // Selfie is the result of many years of teaching systems engineering. // The design of the compiler is inspired by the Oberon compiler of // Professor Niklaus Wirth from ETH Zurich. - +#include int *selfieName = (int*) 0; // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ @@ -661,6 +661,11 @@ void syscall_malloc(); void emitYield(); void syscall_sched_yield(); +void emitSwitch(); +void syscall_sched_switch(); + +void emitGetpid(); +void syscall_getpid(); void emitPutchar(); @@ -673,6 +678,8 @@ int SYSCALL_OPEN = 4005; int SYSCALL_MALLOC = 5001; int SYSCALL_GETCHAR = 5002; int SYSCALL_YIELD = 5003; +int SYSCALL_SWITCH = 5004; +int SYSCALL_GETPID = 5005; // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- @@ -3265,6 +3272,8 @@ void compile() { emitMalloc(); emitPutchar(); emitYield(); + emitSwitch(); + emitGetpid(); // parser gr_cstar(); @@ -3661,6 +3670,51 @@ void load() { } } + + + + + + + +void saveContext() { + int i; + i=0; + while (i<32) { +// //printf("storing mem (addr: %d)\n", binaryLength + i * 4); +// fflush(stdout); + storeMemory(binaryLength + i * 4, *(registers + i )); + i = i + 1; +// //printf("done storing mem\n"); +// fflush(stdout); + } + storeMemory(binaryLength + 4*32, reg_lo); + storeMemory(binaryLength + 4*33, reg_hi); + storeMemory(binaryLength + 4*34, ir); + storeMemory(binaryLength + 4*35, pc); +// printf("saveContext. pid: %d, pc: %d, ir: %d, reg_lo: %d, reg_hi: %d\n", process_id, pc, ir, reg_lo, reg_hi); +// fflush(stdout); + +// //printf("done saving\n"); +// fflush(stdout); +} +void loadContext() { + int i; + i=0; + while (i<32) { + *(registers + i) = loadMemory(binaryLength + i * 4); + i = i + 1; + } + reg_lo = loadMemory(binaryLength + 4*32); + reg_hi = loadMemory(binaryLength + 4*33); + ir = loadMemory(binaryLength + 4*34); + pc = loadMemory(binaryLength + 4*35); + +// printf("loadContext. pid: %d, pc: %d, ir: %d, reg_lo: %d, reg_hi: %d\n", process_id, pc, ir, reg_lo, reg_hi); +// fflush(stdout); + +} + // ----------------------------------------------------------------- // --------------------------- SYSCALLS ---------------------------- // ----------------------------------------------------------------- @@ -3893,7 +3947,6 @@ void syscall_malloc() { void emitYield() { createSymbolTableEntry(GLOBAL_TABLE, (int*) "sched_yield", binaryLength, FUNCTION, INTSTAR_T, 0); - emitIFormat(OP_ADDIU, REG_ZR, REG_A3, 0); emitIFormat(OP_ADDIU, REG_ZR, REG_A2, 0); emitIFormat(OP_ADDIU, REG_ZR, REG_A1, 0); @@ -3902,14 +3955,12 @@ void emitYield() { // remove the argument from the stack emitIFormat(OP_ADDIU, REG_SP, REG_SP, 4); - // load the correct syscall number and invoke syscall emitIFormat(OP_ADDIU, REG_ZR, REG_V0, SYSCALL_YIELD); emitRFormat(OP_SPECIAL, 0, 0, 0, FCT_SYSCALL); // jump back to caller, return value is in REG_V0 emitRFormat(OP_SPECIAL, REG_RA, 0, 0, FCT_JR); - } void syscall_sched_yield() { @@ -3918,6 +3969,62 @@ void syscall_sched_yield() { m = 120; } +void emitSwitch() { + createSymbolTableEntry(GLOBAL_TABLE, (int*) "sched_switch", binaryLength, FUNCTION, INT_T, 0); + + + emitIFormat(OP_LW, REG_SP, REG_A1, 0); // to-pid + emitIFormat(OP_ADDIU, REG_SP, REG_SP, 4); + + emitIFormat(OP_LW, REG_SP, REG_A0, 0); // from-pid + emitIFormat(OP_ADDIU, REG_SP, REG_SP, 4); + + + // load the correct syscall number and invoke syscall + emitIFormat(OP_ADDIU, REG_ZR, REG_V0, SYSCALL_SWITCH); + emitRFormat(OP_SPECIAL, 0, 0, 0, FCT_SYSCALL); + + // jump back to caller, return value is in REG_V0 +// emitRFormat(OP_SPECIAL, REG_RA, 0, 0, FCT_JR); +} + +void syscall_sched_switch() { + int old; + int from_pid; + int to_pid; + + from_pid = *(registers+REG_A0); + to_pid = *(registers+REG_A1); + + old = process_id; + process_id = from_pid; // noetig? + //pc = pc + 4; + saveContext(); + printf(" syscall_switch_from-to: %d->%d\n", from_pid, to_pid); + process_id = to_pid; + fflush(stdout); + loadContext(); + +} + + +void emitGetpid() { + createSymbolTableEntry(GLOBAL_TABLE, (int*) "getpid", binaryLength, FUNCTION, VOID_T, 0); + + // load the correct syscall number and invoke syscall + emitIFormat(OP_ADDIU, REG_ZR, REG_V0, SYSCALL_GETPID); + emitRFormat(OP_SPECIAL, 0, 0, 0, FCT_SYSCALL); + + // jump back to caller, return value is in REG_V0 + emitRFormat(OP_SPECIAL, REG_RA, 0, 0, FCT_JR); +} + +void syscall_getpid() { + // return result in REG_V0 + *(registers+REG_V0) = process_id; +} + + void emitPutchar() { createSymbolTableEntry(GLOBAL_TABLE, (int*) "putchar", binaryLength, FUNCTION, INT_T, 0); @@ -3997,10 +4104,16 @@ void fct_syscall() { syscall_malloc(); } else if (*(registers+REG_V0) == SYSCALL_YIELD) { syscall_sched_yield(); + } else if (*(registers+REG_V0) == SYSCALL_SWITCH) { + syscall_sched_switch(); + return; + } else if (*(registers+REG_V0) == SYSCALL_GETPID) { + syscall_getpid(); } else { exception_handler(EXCEPTION_UNKNOWNSYSCALL); } +// printf("nextstep:%d->%d\n", pc, pc+4); pc = pc + 4; } @@ -4398,67 +4511,29 @@ void execute() { } -void saveContext(int process_id) { - int i; - i=0; - while (i<32) { -// //printf("storing mem (addr: %d)\n", binaryLength + i * 4); -// fflush(stdout); - storeMemory(binaryLength + i * 4, *(registers + i )); - i = i + 1; -// //printf("done storing mem\n"); -// fflush(stdout); - } - storeMemory(binaryLength + 4*32, reg_lo); - storeMemory(binaryLength + 4*33, reg_hi); - storeMemory(binaryLength + 4*34, ir); - storeMemory(binaryLength + 4*35, pc); - -// //printf("done saving\n"); -// fflush(stdout); -} -void loadContext(int process_id) { - int i; - i=0; - while (i<32) { - *(registers + i) = loadMemory(binaryLength + i * 4); - i = i + 1; - } - reg_lo = loadMemory(binaryLength + 4*32); - reg_hi = loadMemory(binaryLength + 4*33); - ir = loadMemory(binaryLength + 4*34); - pc = loadMemory(binaryLength + 4*35); - -// //printf("done loading\n"); -// fflush(stdout); - -} void run() { m=0; process_id = 0; while (1) { -// //printf("pc[%d]: %d\n", process_id, pc); fetch(); decode(); pre_debug(); execute(); post_debug(); - m = m+1; - if (m > 120) { -// //printf("now switching context\n"); -// fflush(stdout); - m = 0; - saveContext(process_id); - process_id = process_id + 1; - if (process_id == processes ) { -// //printf("-A(%d)-", pc); - process_id = 0; - } - ////printf("pid: %d\n", process_id); - loadContext(process_id); + // timer interrupt for o/s. simulates external timer. just interrupts non-os pid + if (process_id != 0) { + m = m+1; + if (m > 200) { + m = 0; + + saveContext(); + process_id = 0; // = O/S + loadContext(); + pc = pc + 4; + } } } } From fed97f76e9794c9b6e66147a68cac919fc82767f Mon Sep 17 00:00:00 2001 From: Armin Langhofer Date: Sat, 12 Dec 2015 19:41:52 +0100 Subject: [PATCH 7/8] assignment4: mutex --- README_ASSIGNMENT4 | 143 +++++++++++++++++++++++++++++++++++++++++++++ count.c | 9 +++ selfie.c | 93 +++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 README_ASSIGNMENT4 diff --git a/README_ASSIGNMENT4 b/README_ASSIGNMENT4 new file mode 100644 index 0000000..ff21301 --- /dev/null +++ b/README_ASSIGNMENT4 @@ -0,0 +1,143 @@ +#################################### +## Assignment 4: Mutual Exclusion ## +#################################### +implement a single global lock through mipster syscalls, e.g., a lock() and unlock() call. +implement a simple user program that demonstrates mutual exclusion, e.g, show that one process inside the critical section makes progress, processes not taking the lock make progress, and processes waiting for the lock do not make progress. Hint: you can implement the getpid system call to identify processes. +experiment with and demonstrate different interleavings: using locks, no locks, different time slices +Deadline: Nov 12, end of day + +Bonus: implement basic multi-threading support + +Idea: threads share one address space, processes don't +when duplicating processes, create threads instead, i.e., shared code, heap, globals, but private call stacks, private PC, private registers + + +Compile and Run: +================ +first, compile selfie. then build count (w/ selfie) and afterwards execute count. +$ touch count +$ gcc selfie.c -o selfie +$ ./selfie -c count.c -o count +$ ./selfie -l count -m 32 +$ ./selfie -l count -m 32 +./selfie: loading code from input file count +./selfie: this is selfie's mipster executing count with 32MB of memory +KERNEL syscall_switch_from-to: 0->1 +syscall_lock(): now locking pid 1 +01234567_NEXTKERNEL syscall_switch_from-to: 0->2 + syscall_lock(). switch_from-to: 2->0 +_NEXTKERNEL syscall_switch_from-to: 0->1 +89:;<=>_NEXTKERNEL syscall_switch_from-to: 0->2 + syscall_lock(). switch_from-to: 2->0 +_NEXTKERNEL syscall_switch_from-to: 0->1 +?@ABCDEFGH_NEXTKERNEL syscall_switch_from-to: 0->2 + syscall_lock(). switch_from-to: 2->0 +_NEXTKERNEL syscall_switch_from-to: 0->1 +IJKLMNOPQ_NEXTKERNEL syscall_switch_from-to: 0->2 + syscall_lock(). switch_from-to: 2->0 +_NEXTKERNEL syscall_switch_from-to: 0->1 +RSTUVWXYreleasing lock for pid 1 + syscall_unlock(). switch_from-to: 1->0 +_NEXTKERNEL syscall_switch_from-to: 0->2 +syscall_lock(): now locking pid 2 +_NEXTKERNEL syscall_switch_from-to: 0->1 +could not release lock by pid 1. lock owned by pid 2. +00112233445_NEXTKERNEL syscall_switch_from-to: 0->2 +012345678_NEXTKERNEL syscall_switch_from-to: 0->1 +566778899::;_NEXTKERNEL syscall_switch_from-to: 0->2 +9:;<=>?@A_NEXTKERNEL syscall_switch_from-to: 0->1 +;<<==>>??@@A_NEXTKERNEL syscall_switch_from-to: 0->2 +BCDEFGHIJ_NEXTKERNEL syscall_switch_from-to: 0->1 +ABBCCDDEEFF_NEXTKERNEL syscall_switch_from-to: 0->2 +KLMNOPQRST_NEXTKERNEL syscall_switch_from-to: 0->1 +GGHHIIJJKKLL_NEXTKERNEL syscall_switch_from-to: 0->2 +UVWXYreleasing lock for pid 2 + syscall_unlock(). switch_from-to: 2->0 +_NEXTKERNEL syscall_switch_from-to: 0->1 +MMNNO_NEXTKERNEL syscall_switch_from-to: 0->2 +could not release lock by pid 2. lock owned by pid -1. +00112233445_NEXTKERNEL syscall_switch_from-to: 0->1 +OPPQQRRSSTTU_NEXTKERNEL syscall_switch_from-to: 0->2 +566778899::;_NEXTKERNEL syscall_switch_from-to: 0->1 +UVVWWXXYYcount: exiting with error code 8 +$ + +Interpretation and Explanation of what happened +=============================================== +selfie's mipster loads 3 processes into its *memory. +the 1st process (process_id == 0) could be seen as a "kernel". +the remaining two processes are counters to demonstrate how the kernel works. they first count with just one char per iteration and afterwards use two chars (just to 'wait' for other processes to complete). there's currently no support for mipster to wait until last process exits. + +all three processes are in count.c. the switch between kernel and other procesesses is handled by +count.c. a getpid() systemcall is provided for count.c to determine if process should come up as kernel or not. + +please note: as soon as exit syscall is emitted the emulator stops working (second running process is not executed to the end) but resolving this issue seems not to be the goal of this assignment. + +the characters 'K','E','R','N','E','L' are sent to console to indicate that kernel is running. +the kernel then switches to the next process, e.g. when switching from 0 to pid 2 it's indicated +by: 'syscall_switch_from-to: 0->2'. + +please refer to ASSIGNMENT3 for further details. most of the principles are explained in the correspondending README file. + +-- snip demo -- +$ more count.c +int main() { + int i; + int pid; + + int rr_pid; + + pid = (int)getpid(); + + if (pid == 0 ) { + rr_pid = 0; + while (1) { + + // kernel process + putchar('K'); + putchar('E'); + putchar('R'); + putchar('N'); + putchar('E'); + putchar('L'); + + rr_pid = rr_pid + 1; + if (rr_pid > 2) + rr_pid = 1; + sched_switch(0, rr_pid); + putchar('_'); + putchar('N'); + putchar('E'); + putchar('X'); + putchar('T'); + + + //sched_yield(); + } + exit(6); + } else { + // non-kernel processes + i=48; + + lock(); + // print '0' to 'Z' (in asciitable) + while (i<90) { + putchar(i); + i = i+1; + } + unlock(); + + i=48; + // 'wait' for other processes to exit + while (i<90) { + putchar(i); + putchar(i); + i = i+1; + } + exit(8); + } + +} +-- /snip demo -- + + diff --git a/count.c b/count.c index 57698c0..fda23f9 100644 --- a/count.c +++ b/count.c @@ -36,11 +36,20 @@ int main() { // non-kernel processes i=48; + lock(); // print '0' to 'Z' (in asciitable) while (i<90) { putchar(i); i = i+1; } + unlock(); + + i=48; + while (i<90) { + putchar(i); + putchar(i); + i = i+1; + } exit(8); } diff --git a/selfie.c b/selfie.c index b184ea6..57940e5 100644 --- a/selfie.c +++ b/selfie.c @@ -664,6 +664,12 @@ void syscall_sched_yield(); void emitSwitch(); void syscall_sched_switch(); +void emitLock(); +void syscall_lock(); + +void emitUnlock(); +void syscall_unlock(); + void emitGetpid(); void syscall_getpid(); @@ -680,6 +686,8 @@ int SYSCALL_GETCHAR = 5002; int SYSCALL_YIELD = 5003; int SYSCALL_SWITCH = 5004; int SYSCALL_GETPID = 5005; +int SYSCALL_LOCK = 5006; +int SYSCALL_UNLOCK = 5007; // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- @@ -3274,6 +3282,8 @@ void compile() { emitYield(); emitSwitch(); emitGetpid(); + emitLock(); + emitUnlock(); // parser gr_cstar(); @@ -4024,6 +4034,85 @@ void syscall_getpid() { *(registers+REG_V0) = process_id; } +void emitLock() { + createSymbolTableEntry(GLOBAL_TABLE, (int*) "lock", binaryLength, FUNCTION, VOID_T, 0); + + // load the correct syscall number and invoke syscall + emitIFormat(OP_ADDIU, REG_ZR, REG_V0, SYSCALL_LOCK); + emitRFormat(OP_SPECIAL, 0, 0, 0, FCT_SYSCALL); + + // jump back to caller, return value is in REG_V0 + emitRFormat(OP_SPECIAL, REG_RA, 0, 0, FCT_JR); +} + +// keeps pid. -1 if "free". +int lock = -1; + +void syscall_lock() { + int from_pid; + // return result in REG_V0 + if (lock == -1) { + lock = process_id; + printf("syscall_lock(): now locking pid %d\n", process_id); + } + else { + // context switch + + saveContext(); + + from_pid = process_id; + process_id = 0; + if (process_id == processes) + process_id=1; + printf(" syscall_lock(). switch_from-to: %d->%d\n", from_pid, process_id); + fflush(stdout); + loadContext(); + } + + *(registers+REG_V0) = 66; // todo: void +} + +void emitUnlock() { + createSymbolTableEntry(GLOBAL_TABLE, (int*) "unlock", binaryLength, FUNCTION, VOID_T, 0); + + // load the correct syscall number and invoke syscall + emitIFormat(OP_ADDIU, REG_ZR, REG_V0, SYSCALL_UNLOCK); + emitRFormat(OP_SPECIAL, 0, 0, 0, FCT_SYSCALL); + + // jump back to caller, return value is in REG_V0 + emitRFormat(OP_SPECIAL, REG_RA, 0, 0, FCT_JR); +} + +void syscall_unlock() { + // return result in REG_V0 + int from_pid; + + // security: release lock only if correct pid releases lock + if (lock == process_id) { + printf("releasing lock for pid %d\n", process_id); + lock = -1; + + + // context switch + + saveContext(); + + from_pid = process_id; + process_id = 0; + if (process_id == processes) + process_id=1; + printf(" syscall_unlock(). switch_from-to: %d->%d\n", from_pid, process_id); + fflush(stdout); + loadContext(); + + } else { + printf("could not release lock by pid %d. lock owned by pid %d.\n", process_id, lock); + } + + + *(registers+REG_V0) = 77; //todo: void +} + void emitPutchar() { createSymbolTableEntry(GLOBAL_TABLE, (int*) "putchar", binaryLength, FUNCTION, INT_T, 0); @@ -4109,6 +4198,10 @@ void fct_syscall() { return; } else if (*(registers+REG_V0) == SYSCALL_GETPID) { syscall_getpid(); + } else if (*(registers+REG_V0) == SYSCALL_LOCK) { + syscall_lock(); + } else if (*(registers+REG_V0) == SYSCALL_UNLOCK) { + syscall_unlock(); } else { exception_handler(EXCEPTION_UNKNOWNSYSCALL); } From 64d74e0ec655916c5628361162aac1c40a514d07 Mon Sep 17 00:00:00 2001 From: Armin Langhofer Date: Mon, 4 Jan 2016 17:39:14 +0100 Subject: [PATCH 8/8] assignment5: virtual memory --- README_ASSIGNMENT5 | 151 +++++++++++++++++++++++++++++++++++++++++++++ count.c | 12 +--- selfie.c | 98 ++++++++++++++++++++++++++--- 3 files changed, 245 insertions(+), 16 deletions(-) create mode 100644 README_ASSIGNMENT5 diff --git a/README_ASSIGNMENT5 b/README_ASSIGNMENT5 new file mode 100644 index 0000000..a90e8f6 --- /dev/null +++ b/README_ASSIGNMENT5 @@ -0,0 +1,151 @@ +################################## +## Assignment 5: Virtual Memory ## +################################## +implement on-demand paging instead of memory segmentation in the kernel. +partition the emulated memory (like physical memory on a real machine) into 4KB frames. +each process gets a 4MB virtual address space, i.e., each process may access any address between 0x0 to 0x3FFFFFF. Virtual address space is organised in 4KB pages +whenever a process actually accesses an address, allocate a frame and provide a mapping between pages and frames, i.e., a page table for each process +modify the tlb function accordingly +provide a demo program that allocates the whole virtual memory space but only accesses, for example, 128 different addresses evenly distributed within that region. Make sure that in this case only 128 frames are actually allocated, not the whole 4MB. +page replacement and swapping is not required in this assignment + +Deadline: November 26, end of day + + +Compile and Run: +================ +first, compile selfie. then build count (w/ selfie) and afterwards execute count. +$ gcc selfie.c -o selfie && ./selfie -c count.c -o count && ./selfie -l count -m 32 +./selfie: this is selfie's cstarc compiling count.c +./selfie: writing code into output file count +./selfie: loading code from input file count +./selfie: this is selfie's mipster executing count with 32MB of memory + +DEBUG: syscall_malloc(): pid: 0 . allocated 4 bytes + +DEBUG: syscall_malloc(): pid: 0 . allocated 8 bytes +KERNEL +DEBUG: syscall_malloc(): pid: 1 . allocated 2048000 bytes +0123456_NEXTKERNEL +DEBUG: syscall_malloc(): pid: 2 . allocated 2048000 bytes +0123456_NEXTKERNEL789:;<=>?_NEXTKERNEL789:;<=>?_NEXTKERNEL@ABCDEFGH_NEXTKERNEL@ABCDEFGH_NEXTKERNELIJKLMNOPQ_NEXTKERNELIJKLMNOPQ_NEXTKERNELRSTUVWXY +****************** + usage statistics + ---------------- + pages used: 6 + mem used: 24576 kbytes +****************** +count: exiting with error code 8 +$ + +Interpretation and Explanation of what happened +=============================================== +selfie's mipster loads 3 processes into its *memory (please have a look at the previous assignments). + + +-- snip demo -- +dummy = malloc(500 * 1024 * 4); + +// print '0' to 'Z' (in asciitable) +while (i<90) { + putchar(i); + i = i+1; +} +-- /snip demo -- + +although the demo program "count.c" allocates more space than there's space availible in a 4k page the memory consumption of the emulator is only 24576 kbytes. so, the pages are allocated only when needed (on-the-fly). + + +-- snip selfie.c -- +[...] +int loadMemory(int vaddr) { + int offset; +// if (SEGMENTATION) offset = getsegmentoffset(process_id); +// if (PAGING) + offset = getpageoffset(vaddr); + return *(memory + tlb(vaddr & 0xFFF) + offset ); +} + +void storeMemory(int vaddr, int data) { + int offset; +// if (SEGMENTATION) +// offset = getsegmentoffset(process_id); +// if (PAGING) { + offset = getpageoffset(vaddr); + if (offset == -1) { + offset = newpage(vaddr); + } +// } + + *(memory + tlb(vaddr & 0xFFF) + offset) = data; +} + +int getpageoffset(int vaddr) { + int i; + int offset; + int val; + int found; + + offset = -1; + for (i = 0; i< pages_used; i++) { + val = (process_id << 14) | ((vaddr & 0x3FFFC00) >> 12); + if (*(pagetable + i) == val) { + // found + offset = 4096 * i; // 4096=pagesize + break; + } + } + return offset; +} +[...] +-- /snip selfie.c -- + +pagetables +========== + +each 32 bit address is spitted up into two parts: pagetable and offset, i.e.: + +32 bit virtual address +---------------------- +00000000 00000000 00000000 00000000 (2, binary) + ^--------------^ + pagetable lookup (14bit) + ^-----------^ + offset (12bit) + +the first 6 bits are ignored. + +pagetable +--------- +i.e. 00000000 0000000 00000000 00000000 (2, binary) + ^-----------------^ + pid (18bit) + ^-------------^ + pagetable lookup (14bit) + + +example of calculation of physical address (from pid & virtual address) +----------------------------------------------------------------------- +given: +pid: 1, +virtual address: 000000000 00000000 00100000 000000011 (2) +and pagetable w/ one entry: pagetbable[0]: 00000000 00000000 01000000 00000010 (2) + +lookup process: +1) caluclate pagetableentry from pid & pagetable lookup +00000000 00000000 01000000 00000010 (2) + ^-PID ^--pagetable lookup + +2) loop though all pagetable entries +as only one one entry in pagetable and this entry matches 1) calculated pagetableentry -> index is determined. + +index: 0 (from pagetable[0]): this is the page index. + +offset = index * 4096 (4k) +here: offset = 0 + +physical address = offset + offset from virtual address +here: 0 + 0x11 = 0x11. + + + diff --git a/count.c b/count.c index fda23f9..1515ca8 100644 --- a/count.c +++ b/count.c @@ -1,5 +1,6 @@ int main() { int i; + int *dummy; int pid; int rr_pid; @@ -35,21 +36,14 @@ int main() { } else { // non-kernel processes i=48; + dummy = malloc(500 * 1024 * 4); + - lock(); // print '0' to 'Z' (in asciitable) while (i<90) { putchar(i); i = i+1; } - unlock(); - - i=48; - while (i<90) { - putchar(i); - putchar(i); - i = i+1; - } exit(8); } diff --git a/selfie.c b/selfie.c index 57940e5..e35e8e1 100644 --- a/selfie.c +++ b/selfie.c @@ -722,6 +722,11 @@ int process_id; // segment table, see comments below how it's structured int *segmenttable; +// **** paging **** +int* pagetable; +int pages_used; +// **** /paging **** + // m: number of execute()s in run() are performed until scheduler switches to next process int m; @@ -737,6 +742,10 @@ void initMemory(int megabytes) { // segmenttable holds up to 100 processes segmenttable = malloc(100 * 2 * 4); processes = 0; + + // pagetable holds up to 8192 entries (4kByte pages * 8192 entries = 32MB + pagetable = malloc (8192 * 4); + pages_used = 0; } @@ -904,6 +913,8 @@ void addprocess() { copyBinaryToMemory(); +//printf("binaryLength: %d\n", binaryLength); + i = 0; while (i<32) { storeMemory(binaryLength + 4*i, 0); @@ -3743,6 +3754,17 @@ void emitExit() { emitRFormat(0, 0, 0, 0, FCT_SYSCALL); } + +void print_mem_usage() { + printf("\n"); + printf("******************\n"); + printf(" usage statistics\n"); + printf(" ----------------\n"); + printf(" pages used: %d \n", pages_used); + printf(" mem used: %d kbytes \n", pages_used * 4096); + printf("******************\n"); +} + void syscall_exit() { int exitCode; @@ -3750,6 +3772,9 @@ void syscall_exit() { *(registers+REG_V0) = exitCode; + + print_mem_usage(); + print(binaryName); print((int*) ": exiting with error code "); print(itoa(exitCode, string_buffer, 10, 0)); @@ -3828,16 +3853,22 @@ void emitWrite() { } void syscall_write() { + int size; int vaddr; int fd; int *buffer; + int offset; size = *(registers+REG_A2); vaddr = *(registers+REG_A1); fd = *(registers+REG_A0); - buffer = memory + tlb(vaddr) + getsegmentoffset(process_id); + + offset = getpageoffset(vaddr); + buffer = memory + tlb(vaddr & 0xFFF) + offset; +//printf("buffer: %c size: %d\n", buffer, size); + // was/armin: buffer = memory + tlb(vaddr) + getsegmentoffset(process_id); //buffer = getsegmentoffset(process_id) + memory + tlb(vaddr); size = write(fd, buffer, size); @@ -3937,6 +3968,9 @@ void syscall_malloc() { if (size % 4 != 0) size = size + 4 - size % 4; + + printf("\nDEBUG: syscall_malloc(): pid: %d . allocated %d bytes\n", process_id, size); + bump = *(registers+REG_K1); if (bump + size >= *(registers+REG_SP)) @@ -4010,7 +4044,7 @@ void syscall_sched_switch() { process_id = from_pid; // noetig? //pc = pc + 4; saveContext(); - printf(" syscall_switch_from-to: %d->%d\n", from_pid, to_pid); +// printf(" syscall_switch_from-to: %d->%d\n", from_pid, to_pid); process_id = to_pid; fflush(stdout); loadContext(); @@ -4142,6 +4176,8 @@ void emitPutchar() { // ---------------------------- MEMORY ----------------------------- // ----------------------------------------------------------------- +int* segmenttable; + int tlb(int vaddr) { if (vaddr % 4 != 0) exception_handler(EXCEPTION_ADDRESSERROR); @@ -4150,24 +4186,71 @@ int tlb(int vaddr) { return vaddr / 4; } +int getpageoffset(int vaddr) { + int i; + int offset; + int val; + int found; + + offset = -1; + for (i = 0; i< pages_used; i++) { + val = (process_id << 14) | ((vaddr & 0x3FFFC00) >> 12); +// printf("getpageoffset: uppernipple=%d lowernipple=%d\n", uppernipple, lowernipple); +// printf("getpageoffset: val=%d\n", val); + if (*(pagetable + i) == val) { + // found + offset = 4096 * i; // 4096=pagesize + break; + } + } + return offset; + +} + +// create new page and return offset to mem +int newpage(int vaddr) { + *(pagetable + pages_used) = (process_id << 14) | ((vaddr & 0x3FFFC00) >> 12); + pages_used++; +// printf ("newpage(). pages_used:%d \n", pages_used); +// printf ("newpage(). return offset: %d \n", 4096 * (pages_used -1)); + return 4096 * (pages_used -1); // 4096=pagesize + +} + int loadMemory(int vaddr) { int offset; - offset = getsegmentoffset(process_id); +// if (SEGMENTATION) offset = getsegmentoffset(process_id); +// if (PAGING) + + offset = getpageoffset(vaddr); -// //printf("load mem: v:%d phy:%d\n", vaddr, tlb(vaddr) + offset ); // fflush(stdout); - return *(memory + tlb(vaddr) + offset ); +//printf("loadMemory() vaddr:%d phy:%d data:%d\n", vaddr, tlb(vaddr & 0xFFF) + offset, *(memory + tlb(vaddr & 0xFFF) + offset )); + return *(memory + tlb(vaddr & 0xFFF) + offset ); //return *(memory + tlb(vaddr)); } void storeMemory(int vaddr, int data) { int offset; - offset = getsegmentoffset(process_id); +// printf("storeMemory(). pid: %d. vaddr %d\n", process_id, vaddr); +// if (SEGMENTATION) +// offset = getsegmentoffset(process_id); +// if (PAGING) { + offset = getpageoffset(vaddr); + + + if (offset == -1) { + //printf ("requestednewpage"); + offset = newpage(vaddr); + } +// } ////printf("mem2write @%d: %d\n", tlb(vaddr) + offset, data); //fflush(stdout); - *(memory + tlb(vaddr) + offset) = data; + *(memory + tlb(vaddr & 0xFFF) + offset) = data; +// printf(" --> storeMemory(). vlb(vaddr & 0xFFF): %d, offset: %d, paddr: %d, page: %d data: %d\n", tlb(vaddr & 0xFFF), offset, tlb(vaddr & 0xFFF) + offset, offset/4096, data); + //*(memory + tlb(vaddr) + offset) = data; //*(memory + tlb(vaddr) ) = data; } @@ -4708,6 +4791,7 @@ void copyBinaryToMemory() { a = 0; while (a < binaryLength) { +// printf("copyBinaryToMemory(): pid: %d. storing mem to %d\n", process_id, a); storeMemory(a, loadBinary(a)); a = a + 4;