[-]*\\d+)");
96 |
97 | MatchCollection matchcol = r.Matches(rawInstruction);
98 | foreach(Match m in matchcol)
99 | {
100 | GroupCollection g = m.Groups;
101 |
102 | for (int i = 1; i < g.Count ; i++)
103 | {
104 | if (g[i].Value.Length != 0 )
105 | {
106 | if (r.GroupNameFromNumber(i) == "opcode")
107 | {
108 | this.OpCode = (InstructionType)byte.Parse(g[i].Value);
109 | }
110 |
111 | if (r.GroupNameFromNumber(i) == "param" || r.GroupNameFromNumber(i) == "const")
112 | {
113 | //Yank them as ints (to preserve signed-ness)
114 | // Treat them as uints for storage
115 | // This will only affect negative numbers, and
116 | // VERY large unsigned numbers
117 | if (uint.MaxValue == this.Param1)
118 | this.Param1 = uint.Parse(g[i].Value);
119 | else if (uint.MaxValue == this.Param2)
120 | {
121 | if (g[i].Value[0] == '-')
122 | this.Param2 = (uint)int.Parse(g[i].Value);
123 | else
124 | this.Param2 = uint.Parse(g[i].Value);
125 |
126 | }
127 | }
128 |
129 | }
130 | }
131 | }
132 | }
133 | }
134 |
135 | ///
136 | /// This enum provides an easy conversion between numerical opCodes like "2" and text
137 | /// and easy to remember consts like "Addi"
138 | ///
139 | public enum InstructionType
140 | {
141 | ///
142 | /// No op
143 | ///
144 | Noop = 0,
145 |
146 | ///
147 | /// Increments register
148 | ///
149 | /// 1 r1
150 | ///
151 | ///
152 | Incr,
153 |
154 | ///
155 | /// Adds constant 1 to register 1
156 | ///
157 | /// 2 r1, $1
158 | ///
159 | ///
160 | Addi,
161 |
162 | ///
163 | /// Adds r2 to r1 and stores the value in r1
164 | ///
165 | /// 3 r1, r2
166 | ///
167 | ///
168 | Addr,
169 |
170 | ///
171 | /// Pushes contents of register 1 onto stack
172 | ///
173 | /// 4 r1
174 | ///
175 | ///
176 | Pushr,
177 |
178 | ///
179 | /// Pushes constant 1 onto stack
180 | ///
181 | /// 5 $1
182 | ///
183 | ///
184 | Pushi,
185 |
186 | ///
187 | /// Moves constant 1 into register 1
188 | ///
189 | /// 6 r1, $1
190 | ///
191 | ///
192 | Movi,
193 |
194 | ///
195 | /// Moves contents of register2 into register 1
196 | ///
197 | /// 7 r1, r2
198 | ///
199 | ///
200 | Movr,
201 |
202 | ///
203 | /// Moves contents of memory pointed to register 2 into register 1
204 | ///
205 | /// 8 r1, r2
206 | ///
207 | ///
208 | Movmr,
209 |
210 | ///
211 | /// Moves contents of register 2 into memory pointed to by register 1
212 | ///
213 | /// 9 r1, r2
214 | ///
215 | ///
216 | Movrm,
217 |
218 | ///
219 | /// Moves contents of memory pointed to by register 2 into memory pointed to by register 1
220 | ///
221 | /// 10 r1, r2
222 | ///
223 | ///
224 | Movmm,
225 |
226 | ///
227 | /// Prints out contents of register 1
228 | ///
229 | /// 11 r1
230 | ///
231 | ///
232 | Printr,
233 |
234 | ///
235 | /// Prints out contents of memory pointed to by register 1
236 | ///
237 | /// 12 r1
238 | ///
239 | ///
240 | Printm,
241 |
242 | ///
243 | /// Control transfers to the instruction whose address is r1 bytes relative to the current instruction.
244 | /// r1 may be negative.
245 | ///
246 | /// 13 r1
247 | ///
248 | ///
249 | Jmp,
250 |
251 | ///
252 | /// Compare contents of r1 with 1. If r1 < 9 set sign flag. If r1 > 9 clear sign flag.
253 | /// If r1 == 9 set zero flag.
254 | ///
255 | /// 14 r1, $9
256 | ///
257 | ///
258 | Cmpi,
259 |
260 |
261 | ///
262 | /// Compare contents of r1 with r2. If r1 < r2 set sign flag. If r1 > r2 clear sign flag.
263 | /// If r1 == r2 set zero flag.
264 | ///
265 | /// 15 r1, r2
266 | ///
267 | ///
268 | Cmpr,
269 |
270 |
271 | ///
272 | /// If the sign flag is set, jump to the instruction that is offset r1 bytes from the current instruction
273 | ///
274 | /// 16 r1
275 | ///
276 | ///
277 | Jlt,
278 |
279 | ///
280 | /// If the sign flag is clear, jump to the instruction that is offset r1 bytes from the current instruction
281 | ///
282 | /// 17 r1
283 | ///
284 | ///
285 | Jgt,
286 |
287 | ///
288 | /// If the zero flag is set, jump to the instruction that is offset r1 bytes from the current instruction
289 | ///
290 | /// 18 r1
291 | ///
292 | ///
293 | Je,
294 |
295 | ///
296 | /// Call the procedure at offset r1 bytes from the current instrucion.
297 | /// The address of the next instruction to excetute after a return is pushed on the stack
298 | ///
299 | /// 19 r1
300 | ///
301 | ///
302 | Call,
303 |
304 | ///
305 | /// Call the procedure at offset of the bytes in memory pointed by r1 from the current instrucion.
306 | /// The address of the next instruction to excetute after a return is pushed on the stack
307 | ///
308 | /// 20 r1
309 | ///
310 | ///
311 | Callm,
312 |
313 | ///
314 | /// Pop the return address from the stack and transfer control to this instruction
315 | ///
316 | /// 21
317 | ///
318 | ///
319 | Ret,
320 |
321 | ///
322 | /// Allocate memory of the size equal to r1 bytes and return the address of the new memory in r2.
323 | /// If failed, r2 is cleared to 0.
324 | ///
325 | /// 22 r1, r2
326 | ///
327 | ///
328 | Alloc,
329 |
330 | ///
331 | /// Acquire the OS lock whose # is provided in register r1.
332 | /// Icf the lock is not held by the current process
333 | /// the operation is a no-op
334 | ///
335 | /// 23 r1
336 | ///
337 | ///
338 | AcquireLock,
339 |
340 | ///
341 | /// Release the OS lock whose # is provided in register r1.
342 | /// Another process or the idle process
343 | /// must be scheduled at this point.
344 | /// if the lock is not held by the current process,
345 | /// the instruction is a no-op
346 | ///
347 | /// 24 r1
348 | ///
349 | ///
350 | ReleaseLock,
351 |
352 | ///
353 | /// Sleep the # of clock cycles as indicated in r1.
354 | /// Another process or the idle process
355 | /// must be scheduled at this point.
356 | /// If the time to sleep is 0, the process sleeps infinitely
357 | ///
358 | /// 25 r1
359 | ///
360 | ///
361 | Sleep,
362 |
363 | ///
364 | /// Set the priority of the current process to the value
365 | /// in register r1
366 | ///
367 | /// 26 r1
368 | ///
369 | ///
370 | SetPriority,
371 |
372 | ///
373 | /// This opcode causes an exit and the process's memory to be unloaded.
374 | /// Another process or the idle process must now be scheduled
375 | ///
376 | /// 27
377 | ///
378 | ///
379 | Exit,
380 |
381 | ///
382 | /// Free the memory allocated whose address is in r1
383 | ///
384 | /// 28 r1
385 | ///
386 | ///
387 | FreeMemory,
388 |
389 | ///
390 | /// Map the shared memory region identified by r1 and return the start address in r2
391 | ///
392 | /// 29 r1, r2
393 | ///
394 | ///
395 | MapSharedMem,
396 |
397 | ///
398 | /// Signal the event indicated by the value in register r1
399 | ///
400 | /// 30 r1
401 | ///
402 | ///
403 | SignalEvent,
404 |
405 | ///
406 | /// Wait for the event in register r1 to be triggered resulting in a context-switch
407 | ///
408 | /// 31 r1
409 | ///
410 | ///
411 | WaitEvent,
412 |
413 | ///
414 | /// Read the next 32-bit value into register r1
415 | ///
416 | /// 32 r1
417 | ///
418 | ///
419 | Input,
420 |
421 | ///
422 | /// set the bytes starting at address r1 of length r2 to zero
423 | ///
424 | /// 33 r1, r2
425 | ///
426 | ///
427 | MemoryClear,
428 |
429 | ///
430 | /// Terminate the process whose id is in the register r1
431 | ///
432 | /// 34 r1
433 | ///
434 | ///
435 | TerminateProcess,
436 |
437 | ///
438 | /// Pop the contents at the top of the stack into register r1
439 | ///
440 | /// 35 r1
441 | ///
442 | ///
443 | Popr,
444 |
445 | ///
446 | /// Pop the contents at the top of the stack into the memory pointed to by register r1
447 | ///
448 | /// 36 r1
449 | ///
450 | ///
451 | Popm
452 | };
453 |
454 | }
455 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Scott Hanselman's Tiny Virtual Operating System and Abstract Machine in C#
2 |
3 | [](https://ci.appveyor.com/project/ScottHanselman/tinyos)
4 |
5 | ## What is this?
6 |
7 | This was the final project for my CST352-Operating Systems class at [OIT](http://www.oit.edu) (Oregon Institute of Technology). The requirements, exactly as they were given to me by the teacher, are in [originalassignment.md](originalassignment.md). The goal of this project was to write a small _virtual_ operating system for an _abstract_ machine that provides a number of basic OS-like services like:
8 |
9 | - Virtual Memory, Demand Paging
10 | - Input/Output
11 | - Memory Protection, Shared Memory
12 | - Registers, Stack, Data, Heap, etc
13 | - Jump instructions for calling "Functions"
14 | - And so on…
15 |
16 | This is a cute, fun, interesting and _completely useless thing_. So, don't get your hopes up that you'll get actual work done with it.
17 |
18 | It's neat however, as it is a study on how I solved a particular problem (this assignment) given a 10 week semester. I was the only student to use C#, and I finished it in 4 weeks, leaving 6 weeks to chill and watch the other students using Java and C++ do their thing.
19 |
20 | It's also ironic because I used a high-level OO language like C# to deal with a minute concept like an "Operating System" the might have 256 bytes (bytes, not Kbytes) of memory.
21 |
22 | During this process I exercised a good and interesting chunk of the C# language and the .NET Framework. There are some very silly things like the OS's implementation of virtual memory swapping out memory pages as XML. I might take an array of 4 bytes and make an XML file to help them. I hope the irony isn't lost on you. I also commented all the C# with XML and built an [ndoc](http://ndoc.sf.net) MSDN style help file. So, you might just use this as a learning tool and a pile of sample code.
23 |
24 | One Note: I'm an ok programmer, but understand that I whipped this out in several 3am coding sessions, as well as during lectures in class. This is NOT fabulous code, and should be looked upon as interesting, not gospel. I'm sure I've done some very clever things in it, and for each clever thing, there's _n_ stupid things. I'll leave the repair of these stupid things as an exercise to the reader.
25 |
26 | Of course, you'll need the .NET Framework to run this, but to really have fun debugging it and stepping through the code you'll need VS.NET. Go read the code and final\_project.doc and enjoy!
27 |
28 | Scott Hanselman
29 |
30 | [scott@hanselman.com](mailto:scott@hanselman.com)
31 |
32 | May 2002
33 |
34 | ## What's the jist?
35 |
36 | I started out with some basic OO constructs, a static CPU object, an OS object, objects for Processes, Instructions, etc. A lot of this information is in the Help (CHM) File. Basically it's setup like this:
37 |
38 | - *CPU Object* – responsible for physical memory (which may be smaller than addressable memory) holding CPU registers and fetching program opcodes and translating them into SystemCalls in the OS, etc.
39 | - *OS Object* – Most of the work is here. It has a MemoryManager object who is responsible for the translations between Addressable (Virtual) memory, Process memory and Physical Memory. All Processes access memory from _their_ point of view…they never talk to "physical memory" (Which is just an array of bytes in the CPU object)
40 | The scheduler is in the OS object as well as locks, events, and all that good stuff. Each of the 36 opcodes are implemented here and treated as C# delegates…well, now I'm telling you all the good stuff…read the code!
41 | - *Program Object* - represents a collection of instructions. Responsible for parsing the files off disk and holding them.
42 | - *Process Object* – different than Program as it's an actual running process in the TinyOS. There can be more than one instance of a Process for each Program. (You can run the same program twice, etc) It has a ProcessControlBlock containing Process specific registers, etc.
43 | - *EntryPoint Class* – Contains main() and "bootstraps" the whole thing.
44 |
45 | The OS is made up, the CPU is made up, the opCodes are made up. This is an exercise to learn about Operating System concepts, and it's not meant to look or act like any specific OS or system.
46 |
47 | ## What does a Program look like?
48 |
49 | Here's an example program, specifically the "idle" loop. The idle loop is a process that never ends, it just prints out "20" over and over. I use it to keep the clock running when all the other processes are sleeping.
50 | ```
51 | 6 r4, $0 ;move 0 into r4
52 | 26 r4 ;lower our priority TO 0
53 | 6 r1, $20 ;move 20 into r1
54 | 11 r1 ;print the number 20
55 | 6 r2, $-19 ;back up the ip 19 bytes
56 | 13 r2 ;loop forever (jump back 19)
57 | ```
58 | Programs consist of:
59 |
60 | ```
61 | Opcode, [optional param], [other optional param] ;optional comment
62 | ```
63 |
64 | So, if we look at:
65 |
66 | ```
67 | 6 r4, $0 ;move 0 into r4
68 | ```
69 |
70 | We see that it's operation 6, which is Movi or "Move Immediate." It moves a constant value into one of our 10 registers. The first param is r4, indicating register 4. The second param is $0. The "$" indicates a constant. So we are the value 0 into register #4. Just like x86 assembly – only not at all.
71 |
72 | So if you look at the comments for this app you can see that it loops forever.
73 |
74 | ## How do run Program(s)
75 |
76 | Usage:
77 | ```
78 | OS membytes [files]
79 | ```
80 | For example:
81 | ```
82 | OS 1568 prog1.txt prog2.txt prog3.txt prog1.txt idle.txt
83 | ```
84 | This command line would run the OS with 1568 bytes of virtual (addressable) memory and start up 5 processes. Note that Prog1.txt is specified twice. That means it will have two separate and independent running processes. We also specified the forever running idle.txt. Use CTRL-C to break. The OS has operations for shared memory, locks, and events, so you can setup rudimentary inter-module communication.
85 |
86 | ## Why should I care?
87 |
88 | There are 13 other sample apps you can play with. The OS is most interesting when you run it with multiple apps. You can mess with OS.config to setup the amount of memory available to each process, the whole system's physical memory, memory page size etc.
89 |
90 | It's interesting from an OS theory point of view if you think about the kinds of experiments you can do like lowering physical memory to something ridiculous like 32 bytes. That will increase page faults and virtual memory swapping. So, the OS can be tuned with the config file.
91 |
92 | ## What are the options in OS.config?
93 |
94 | It is certainly possible to give the OS a bad config, like more physical memory than addressable, or a page size that is the same size as physical memory. But, use common sense and play. I've setup it up with reasonable defaults.
95 |
96 | Here's a sample OS.config file. The Config has to be in the same directory as the OS.exe. Take a look at the included OS.config, as it has additional XML comments explaining each option.
97 | ```
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | ```
116 | Of note are the Dumpxxx options. With all of these options set to false, the OS only outputs the bare minimum debug information. With them on (I like to have the all on) you'll be able to see the results of each instruction and how they affect registers, physical memory, etc. You'll see physical memory pages swap to disk, memory fragmentation, the instruction pointer increment, and processes take turns on the CPU.
117 |
118 | Be sure to turn up the size of screen buffer (usually under "Layout" in the Properties Dialog of your command prompt) to something like 3000 or 9999. This way you'll be able to scroll back and look at the details of your run.
119 | ```
120 | OS 512 prog1.txt > output.txt
121 | ```
122 | You might also try something like this to create a log file, or modify the source to include logging!
123 |
124 | ## What are the available OpCodes?
125 |
126 | These are also printed and explain in the Help File as well as Final\_Project.doc. When writing a program you use the value, not the text name of the opcode. So, you'd say `2 r1, $1` and **NOT** `addi r1, $1`.
127 |
128 | | Opcode | Value(decimal) | Format |
129 | | --- | --- | --- |
130 | | Incr | 1 | incr r1(increment value of register 1 by 1 ). |
131 | | Addi | 2 | addi r1,$1 is the same as incr r1 |
132 | | addr | 3 | Addr r1, r2( r1 <= r1 + r2 ). |
133 | | Pushr | 4 | Pushr rx (pushes contents of register x onto stack. Decrements sp by 4 ). |
134 | | Pushi | 5 | Pushi $x . pushes the constant x onto stack. Sp is decremented by 4 after push. |
135 | | Movi | 6 | Movi rx, $y. rx <= y |
136 | | Movr | 7 | Movr rx, ry ; rx <= ry |
137 | | Movmr | 8 | Movmr rx, ry ; rx <= [ry] |
138 | | Movrm | 9 | Movrm rx,ry; [rx] <= ry |
139 | | Movmm | 10 | Movmm rx, ry [rx] <= [ry] |
140 | | Printr | 11 | Printr r1 ; displays contents of register 1 |
141 | | Printm | 12 | Printm r1; displays contents of memory whose address is in register 1. |
142 | | Jmp | 13 | Jmp r1; control transfers to the instruction whose address is r1 bytes relative to the current instruction. R1 may be negative. |
143 | | Cmpi | 14 | Cmpi rx, $y; subtract y from register rx. If rx < y, set sign flag. If rx > y, clear sign flag. If rx == y , set zero flag. |
144 | | Cmpr | 15 | The same as cmpi except now both operands are registers. |
145 | | Jlt | 16 | Jlt rx; if the sign flag is set, jump to the instruction whose offset is rx bytes from the current instruction. |
146 | | Jgt | 17 | Jgt rx; if the sign flag is clear, jump to the instruction whose offset is rx bytes from the current instruction |
147 | | Je | 18 | Je rx; if the zero flag is clear, jump to the instruction whose offset is rx bytes from the current instruction. |
148 | | Call | 19 | Call r1; call the procedure at offset r1 bytes from the current instruction. The address of the next instruction to execute after a return is pushed on the stack. |
149 | | Callm | 20 | Callm r1; call the procedure at offset [r1] bytes from the current instruction. The address of the next instruction to execute after a return is pushed on the stack. |
150 | | Ret | 21 | Pop the return address from the stack and transfer control to this instruction. |
151 | | Alloc | 22 | Alloc r1, r2; allocate memory of size equal to r1 bytes and return the address of the new memory in r2. If failed, r2 is cleared to 0. |
152 | | AcquireLock | 23 | AcquireLock r1; Acquire the operating system lock whose # is provided in the register r1. If the lock is invalid, the instruction is a no-op. |
153 | | ReleaseLock | 24 | Releaselock r1; release the operating system lock whose # is provided in the register r1; if the lock is not held by the current process, the instruction is a no-op. |
154 | | Sleep | 25 | Sleep r1; Sleep the # of clock cycles as indicated in r1. Another process or the idle process must be scheduled at this point. If the time to sleep is 0, the process sleeps infinitely. |
155 | | SetPriority | 26 | SetPriority r1; Set the priority of the current process to the value in register r1; See priorities discussion in Operating system design |
156 | | Exit | 27 | Exit. This opcode is executed by a process to exit and be unloaded. Another process or the idle process must now be scheduled. |
157 | | FreeMemory | 28 | FreeMemory r1; Free the memory allocated whose address is in r1. |
158 | | MapSharedMem | 29 | MapSharedMem r1, r2; Map the shared memory region identified by r1 and return the start address in r2. |
159 | | SignalEvent | 30 | SignalEvent r1; Signal the event indicated by the value in register r1. |
160 | | WaitEvent | 31 | WaitEvent r1; Wait for the event in register r1 to be triggered. This results in context-switches happening. |
161 | | Input | 32 | Input r1; read the next 32-bit value into register r1. |
162 | | MemoryClear | 33 | MemoryClear r1, r2; set the bytes starting at address r1 of length r2 bytes to zero. |
163 | | TerminateProcess | 34 | TerminateProcess r1; terminate the process whose id is in the register r1. |
164 | | Popr | 35 | pop the contents at the top of the stack into register rx which is the operand. Stack pointer is decremented by 4. |
165 | | popm | 36 | pop the contents at the top of the stack into the memory operand whose address is in the register which is the operand. Stack pointer is decremented by 4. |
166 |
167 | ## Known Issues/Things to Know
168 |
169 | - The OS will look in the current directory for files if no directory is specified.
170 | - Processes will be loaded and executed in the order they are specified on the command line.
171 | - There are two idle processes included. idle.txt will run with lowest priority and will print out 20. It will run forever. idle-n.txt will run for n loops (not cycles), specified in the code. As shipped, idle-n will run for 100 loops.
172 | - Redirecting OS output to a file is convenient: OS 1024 prog1.txt prog2.txt prog3.txt > output.txt
173 | - OS.config contains many debug flags that can be included to provide insight into non-debug program execution.
174 | - OS.config contains many options for configuration of memory. It IS possible to feed invalid values into this config file. For example, it's not valid to have a physical memory size larger than addressable memory, or a page size larger than addressable memory, etc. This behavior is appropriate, and is by design.
175 | - Setting DumpInstruction=true in OS.config will output the a debug print of the executing instruction and the current process id.
176 | - If an idle process (idle.txt, etc) isn't included, and all running processes sleep or block on a lock, the OS will spin forever looking for an eligible process to run, and will find none. CTRL-C will exit at this point. This behavior is per spec and by design.
177 | - The OS will create XML swap files in the current for each memory page swapped. These are cleaned up at startup, rather than shutdown, for purposes of exploration.
178 | - Batch files for testing purposes are included in this directory.
179 |
--------------------------------------------------------------------------------
/originalassignment.md:
--------------------------------------------------------------------------------
1 | # CST 352 Operating systems final project spec
2 |
3 | The goal of this project is to write a virtual operating system for an abstract machine to provide the following services. The details of the abstract machine will be provided below.
4 |
5 | - Load and unload programs.
6 | - Allocate and deallocate virtual memory.
7 | - Provide I/O services.
8 | - These will just be standard I/o input.
9 | - Provide services for scheduling processes.
10 | - Based on priority.
11 | - Based on time cycles expiring.
12 | - Based on resources.
13 | - Provide services for mutual exclusion and synchronization.
14 | - Semaphores.
15 | - Locks( mutexes ).
16 | - Events.
17 |
18 | ## Changes made on 4/8/2002
19 |
20 | The following features are mandatory. Mandatory implies these features must be implemented and fully functional. If some or all of the features are not working as specified, the grade is left to the instructor's subjective decisions. This feature set is called the BASE FEATURE SET in this document.
21 |
22 | - Process scheduling based on priority.
23 | - Process scheduling based on time quantum expiration is optional.
24 | - This implies that the current running process continues to run until it exits or tries to acquire a resource that is currently held by another process.
25 | - The functionality for Sleep is mandatory.
26 | - Provide output services. (Print. This is used to verify the functionality of the program ).
27 | - Provide mutual exclusion facilities. Minimally locks must be implemented.
28 | - An idle process is required. This process continues to run until time elapses for another process to wake up.
29 | - On a process exit.
30 | - All the memory that still belongs to the process MUST be clean deallocated and MUST be made available for other processes.
31 | - All internal OS structures used for the process(PCB..) must be released as well.
32 | - If the process holds locks when it exits, the lock MUST be marked as not acquired.
33 | - This implies any process waiting for this lock MUST now be made eligible to run.
34 | - These features, if implemented completely, account for 65% of the total grade for the class.
35 |
36 | ## Changes made on 05/06/2002
37 |
38 | The following features may be implemented, in addition to the above, for an additional 35% grade. This implies that if the above mandatory features and these features are implemented completely and verified to be functionally complete, the student SHALL receive the full grade of 100% for the class. The mandatory features are the minimum set of features needed. Students are strongly advised to get their mandatory features working before even venturing into these features. Students MUST clearly indicate at the time of submission what features they implemented. This feature set is KNOWN AS THE EXTENDED FEATURE SET in this document.
39 |
40 | - Implement dynamic memory allocation features.
41 | - Alloc and FreeMemory MUST be implemented.
42 | - Implement virtual memory using dynamic paging.
43 | - Each process's memory is now classified as virtual memory.
44 | - Each process has its own set of page tables that map its virtual memory to physical memory.
45 | - A process may incur page faults when it tries to access memory it currently does not own or does not have in memory.
46 | - Page size for the page tables can be set to 4 bytes or taken as an input to the OS command line arguments.
47 | - Normal data files may be used to store the unused state of a process.
48 | - Each process may have its own file for paging or may share a file.
49 | - OS code must service page faults and handle them properly.
50 | - Page tables can either grow in size or be restricted to a fixed size.
51 | - The former is preferable although both will be considered as good.
52 |
53 | The following are the list of opcodes that are minimally needed for the above features. The instructor SHALL use only these opcodes in his sample programs in order to test students' projects. It is left up to the students to choose additional opcodes to implement if they find them interesting.
54 |
55 | ## Opcodes
56 |
57 | | Opcode | BASE FEATURE SET | EXTENDED FEATURE SET |
58 | | --- | --- | --- |
59 | | Incr | x | x |
60 | | Addi | x | x |
61 | | movmr | x | x |
62 | | movmm | x | x |
63 | | movrm | x | x |
64 | | printr | x | x |
65 | | printm | x | x |
66 | | AcquireLock | x | x |
67 | | ReleaseLock | x | x |
68 | | Alloc | | x |
69 | | FreeMemory | | x |
70 | | Sleep | x | x |
71 | | MemoryClear | | x |
72 | | Exit | x | x |
73 | | movi | x | x |
74 | | movr | x | x |
75 |
76 | The project will implement a virtual operating system. The term virtual in this context implies an operating system that will run on top of a standard platform.
77 |
78 | - Either a standard PC with Windows or Linux platform is sufficient.
79 | - Any standard C/C++/Java compiler may be used to implement the operating system.
80 | - Since there is no real hardware here, the operating system provides the above services + executes the programs themselves.
81 | - In this sense, the operating system is both the system and the hardware , all in one. It interprets the programs by itself( similar to the Java virtual machine).
82 |
83 | ## Definition of the abstract machine.
84 |
85 | The machine used for this project will be an abstract one. Abstract implies there is no real hardware (CPU+ chipset + hardwired logic) needed to implement this. Instead, it will be emulated in software. The machine has the following details. The abstract machine is a 32-bit machine which implies all addresses are 32-bits and registers are 32-bits as well.
86 |
87 | - It has 10 general purpose registers.
88 | - Each register is 32-bits wide and can store any information.
89 | - They are addressed as 1 through 10 in the opcodes for this machine.
90 | - Register 10 is also the stack pointer register.
91 | - Always contains the top of the stack.
92 | - Grows towards lower addresses.
93 | - Pushing decrements stack pointer. Popping increments it.
94 | - Only 32-bit quantities may be pushed or popped.
95 | - Only the full-registers may be addressed.
96 | - This is unlike x86 where a 32-bit register may be addressed by its full 32-bit , 16-bit or 8-bit portions.
97 | - The abstract machine has 2 bit flag registers.
98 | - SF is the sign flag. This is set when any comparison of 2 quantities results in a sign extension.
99 | - ZF is the sign flag. This is set when any comparison of 2 quantities results in a zero( both quantities are equal ).
100 | - The instruction pointer register is special and is 32-bits wide.
101 | - Not accessible to the programmer.
102 | - Modified when the program executes.
103 | - Must be saved by the operating system on function calls and context switches.
104 | - Also known as eip in this document.
105 | - The abstract machine has the following opcodes. Since the intent of this project is to teach Operating system design and implementation, the machine is not complete in the sense that it does not provide a full repertoire of instructions (similar to x86 or other architectures). Each opcode is exactly 1 byte long and is of the following format.
106 | - <opcode> <argument 1> <argument 2> ..
107 | - All instructions are multiples of bytes.
108 | - All instructions take exactly one clock cycle to execute, independent of the actual operation of the instruction.
109 | - Context-switches only happen on instruction boundaries.
110 | - Constants are indicated by the operator $ in front of them.
111 | - Registers are indicated by r1 … r10.
112 | - The stack pointer (register 10) has another name for it which is sp.
113 |
114 | | Opcode | Value(decimal) | Format |
115 | | --- | --- | --- |
116 | | Incr | 1 | incr r1(increment value of register 1 by 1 ). |
117 | | Addi | 2 | addi r1,$1 is the same as incr r1 |
118 | | addr | 3 | Addr r1, r2( r1 <= r1 + r2 ). |
119 | | Pushr | 4 | Pushr rx (pushes contents of register x onto stack. Decrements sp by 4 ). |
120 | | Pushi | 5 | Pushi $x . pushes the constant x onto stack. Sp is decremented by 4 after push. |
121 | | Movi | 6 | Movi rx, $y. rx <= y |
122 | | Movr | 7 | Movr rx, ry ; rx <= ry |
123 | | Movmr | 8 | Movmr rx, ry ; rx <= [ry] |
124 | | Movrm | 9 | Movrm rx,ry; [rx] <= ry |
125 | | Movmm | 10 | Movmm rx, ry [rx] <= [ry] |
126 | | Printr | 11 | Printr r1 ; displays contents of register 1 |
127 | | Printm | 12 | Printm r1; displays contents of memory whose address is in register 1. |
128 | | Jmp | 13 | Jmp r1; control transfers to the instruction whose address is r1 bytes relative to the current instruction. R1 may be negative. |
129 | | Cmpi | 14 | Cmpi rx, $y; subtract y from register rx. If rx < y, set sign flag. If rx > y, clear sign flag. If rx == y , set zero flag. |
130 | | Cmpr | 15 | The same as cmpi except now both operands are registers. |
131 | | Jlt | 16 | Jlt rx; if the sign flag is set, jump to the instruction whose offset is rx bytes from the current instruction. |
132 | | Jgt | 17 | Jgt rx; if the sign flag is clear, jump to the instruction whose offset is rx bytes from the current instruction |
133 | | Je | 18 | Je rx; if the zero flag is clear, jump to the instruction whose offset is rx bytes from the current instruction. |
134 | | Call | 19 | Call r1; call the procedure at offset r1 bytes from the current instruction. The address of the next instruction to execute after a return is pushed on the stack. |
135 | | Callm | 20 | Callm r1; call the procedure at offset [r1] bytes from the current instruction. The address of the next instruction to execute after a return is pushed on the stack. |
136 | | Ret | 21 | Pop the return address from the stack and transfer control to this instruction. |
137 | | Alloc | 22 | Alloc r1, r2; allocate memory of size equal to r1 bytes and return the address of the new memory in r2. If failed, r2 is cleared to 0. |
138 | | AcquireLock | 23 | AcquireLock r1; Acquire the operating system lock whose # is provided in the register r1. If the lock is invalid, the instruction is a no-op. |
139 | | ReleaseLock | 24 | Releaselock r1; release the operating system lock whose # is provided in the register r1; if the lock is not held by the current process, the instruction is a no-op. |
140 | | Sleep | 25 | Sleep r1; Sleep the # of clock cycles as indicated in r1. Another process or the idle process must be scheduled at this point. If the time to sleep is 0, the process sleeps infinitely. |
141 | | SetPriority | 26 | SetPriority r1; Set the priority of the current process to the value in register r1; See priorities discussion in Operating system design |
142 | | Exit | 27 | Exit. This opcode is executed by a process to exit and be unloaded. Another process or the idle process must now be scheduled. |
143 | | FreeMemory | 28 | FreeMemory r1; Free the memory allocated whose address is in r1. |
144 | | MapSharedMem | 29 | MapSharedMem r1, r2; Map the shared memory region identified by r1 and return the start address in r2. |
145 | | SignalEvent | 30 | SignalEvent r1; Signal the event indicated by the value in register r1. |
146 | | WaitEvent | 31 | WaitEvent r1; Wait for the event in register r1 to be triggered. This results in context-switches happening. |
147 | | Input | 32 | Input r1; read the next 32-bit value into register r1. |
148 | | MemoryClear | 33 | MemoryClear r1, r2; set the bytes starting at address r1 of length r2 bytes to zero. |
149 | | TerminateProcess | 34 | TerminateProcess r1; terminate the process whose id is in the register r1. |
150 | | Popr | 35 | pop the contents at the top of the stack into register rx which is the operand. Stack pointer is decremented by 4. |
151 | | popm | 36 | pop the contents at the top of the stack into the memory operand whose address is in the register which is the operand. Stack pointer is decremented by 4. |
152 |
153 | ## Operating system design goals
154 |
155 | The operating system designed in this project must provide the following services to programs written for this operating system.
156 |
157 | - Provide virtual memory services.
158 | - All processes will have page tables allocated by the operating system.
159 | - OS will detect invalid memory accesses and terminate the processes.
160 | - Terminating processes means the operating system will display an error message on the console and choose the next process to execute.
161 | - Processes can allocate any amount of memory subject to the resources available.
162 | - Processes only manipulate virtual addresses.
163 | - Provide inter-process synchronization mechanisms.
164 | - OS provides mutexes for a process to lock out other processes while manipulating critical shared structures.
165 | - For this project, the OS comes with (or is preconfigured with) 10 locks, each identified by the numbers 1 through 10.
166 | - The OS does not provide any services for processes to allocate their own critical sections.
167 | - The instructions AcquireLock and ReleaseLock allow a process to acquire and release the OS provided locks.
168 | - The locks provided by the OS are meant for use by application processes to protect themselves while manipulating shared-critical structures.
169 | - Provide scheduling services.
170 | - Each process is scheduled independently.
171 | - Processes are single-threaded.
172 | - Only one process is running at a time.
173 | - A process once scheduled(means it starts running) continues to run until the following happens.
174 | - It exits( by executing opcode Exit ).
175 | - It sleeps for some time( by executing opcode Sleep ).
176 | - It acquires a lock already acquired by someone else.
177 | - It accesses an address which requires a page fault and there is not enough memory available. Once another process frees up some memory, this process is scheduled again.
178 | - Waits on an event to be signaled by another process.
179 | - Its time quantum runs out.
180 | - Each process has a time quantum. A time quantum is the # of clock cycles it is allowed to run before it is scheduled out.
181 | - Provide memory-mapping services.
182 | - This OS provides built-in or preconfigured shared memory regions of size 10000 bytes each for a total of 10 memory regions, each indexed by the number 1 thru 10 respectively.
183 | - Processes may map any of them into their address spaces by using the opcode MapSharedMem and passing the # of the shared memory region.
184 | - Processes may use this facility to share memory and perform inter-process communication.
185 | - Provide I/O services.
186 | - Processes may use these I/O services to print out values to the console.
187 | - Processes may use input services to read values from the console as well.
188 |
189 | ## Project requirements
190 |
191 | Where the word MUST is used, the features are required in order for the student to receive full grade for the project. The word OS is used to describe the implementation of this operating system. Where the word MAY is used, the feature is optional.
192 |
193 | - Students MAY use C/C++ or Java to write the operating system.
194 | - If C/C++, students MUST use Visual C++ 5.0 or 6.0.
195 | - If Java, the code must be compilable using Sun's JDK 2.0 or greater.
196 | - The OS must be compilable without any errors.
197 | - Any errors in compilation results in 0 grade.
198 | - The students are responsible for providing any readme or any other documentation to the instruction in order to evaluate the project.
199 | - The program must be compilable on windows platforms.
200 | - Warnings MUST be minimized.
201 | - If the operating system code crashes, the grade provided is based on the instructor's subjective decisions.
202 | - The instructor will evaluate the project using sample files that he will provide later in the class.
203 | - The team ( of 2 students ) is graded as a whole for the project.
204 | - Any differences between the team members( if any ) must be resolved by the members themselves.
205 | - It is strongly advised that students team up for this project.
206 |
207 | ## Project features
208 |
209 | - The OS must contain an interpreter that understands the above opcodes.
210 | - The OS must be invoked as follows.
211 | - OS <# of bytes> <program1.txt> <program2.txt> …..
212 | - Each programx.txt contains the code for the programs to be loaded and executed.
213 | - The number of bytes for all programs that can be loaded(including code, data, heap and stack) must be provided in the first parameter.
214 | - This # does not include the # of bytes the OS will itself need to keep track of information about each process such as process blocks.
215 | - The OS will provide the following services.
216 | - Loader to load programs of the above type.
217 | - The students may assume that the instructor will provide programs with correct opcodes.
218 | - This is the not the same as rogue programs.
219 | - Scheduler for scheduling programs.
220 | - Maintain process context(using process context blocks or otherwise ).
221 | - The time quantum a process is limited to running before it is scheduled out is 10 clock cycles.
222 | - All process context (including all registers, page tables) MUST be stored in the process context block.
223 | - An idle process must be provided that runs if no other process is eligible to run.
224 | - The idle process will be provided in a separate file called idle.txt.
225 | - The idle process just sits in a tight loop printing out the value 20.
226 | - The idle process only has a quantum of 5 clock cycles.
227 | - The idle process never exits.
228 | - The scheduler always schedules the highest-priority process eligible to run.
229 | - There are 32-priorities in the OS.
230 | - Processes with higher priorities run until they give up control, exit or are terminated.
231 | - The bigger the priority #, the higher the priority for the process.
232 | - A process may adjust its own priority by invoking the opcode SetPriority.
233 | - In addition to these queues, the scheduler MUST implement queues for blocking & other types of processes.
234 | - Virtual memory manager.
235 | - Each process MUST only access virtual addresses.
236 | - When a process is loaded, the memory manager MUST construct its page tables in the appropriate fashion.
237 | - Every memory access by each process goes through page tables and accesses the real memory.
238 | - This is true for all addresses(code, data, stack and heap).
239 | - The details of breaking down a virtual address to a physical address using page tables will be provided in the class.
240 | - If a process accesses an address it does not own, the OS must print an error message with the values of the registers of the process at the time the problem occurred, terminate the process and schedule another one.
241 | - Processes sharing memory.
242 | - A process may map the OS provided shared memory region into its own address space using the MapSharedMem opcode.
243 | - This allows 2 or more processes to share memory.
244 | - The exact usage of these memory regions is left to the processes themselves.
245 | - Process virtual memory.
246 | - A process's memory map is divided into 4 regions.
247 | - Code. This is the region containing the instructions with some combination of the above opcodes.
248 | - Stack. This is the region where the program may store temporary variables. When a function is called, the interpreter stores the return address here as well.
249 | - When a program is loaded, it is provided a stack of 4k bytes.
250 | - The stack grows and shrinks depending on usage.
251 | - Global data.
252 | - When a program is loaded, it is provided a memory region called global data of size 4k bytes. The contents are initialized to 0. Please see "process initial state" for more information.
253 | - Heap.
254 | - Heap is the portion of the address space that the process can allocate dynamically using the Alloc and FreeMemory opcodes.
255 | - Process initial state.
256 | - A program becomes a process when it is loaded by the operating system.
257 | - When a process is created, its registers have the following state.
258 | - It is the responsibility of the operating system to set it up this way.
259 | - R1 – r7 are undefined.
260 | - Register r8 contains the id of the process.
261 | - R9 contains the virtual address of the global data region( of length 4k bytes ).
262 | - R10 (sp) is set to the top of the stack.
263 | - Growing the stack implies it grows towards lower addresses.
264 | - The instruction pointer(eip) contains the value 0 essentially translating(via page tables) to the first instruction in the program to be executed.
265 | - Process exiting.
266 | - A process may exit due to any of the following reasons.
267 | - It invokes the Exit opcode.
268 | - It performs an illegal operation(it access a memory location it does not own).
269 | - Another process kills this process using the TerminateProcess opcode.
270 | - It is left to the individual processes to find the id of the other processes.
271 | - Admittedly, the security is very weak(allowing any process to terminate any other process).
272 | - When a process exits for whatever reason, your OS must perform the following.
273 | - Display the # of page faults, the # of context switches it went through, the # of clock cycles it consumed.
274 | - Process synchronization.
275 | - The OS provides 10 mutexes or locks.
276 | - The locks are numbered through 1 to 10.
277 | - A process acquires the lock using AcquireLock.
278 | - If another process already owns the lock, the current process is put to sleep on the wait queue.
279 | - If a process X tries to acquire the same lock Y two or more times, it is a no-operation. In short, the process MUST not deadlock against itself.
280 | - A processes releases a lock it is currently holding via ReleaseLock.
281 | - This MUST make only one process(the highest priority process currently waiting) eligible to be scheduled.
282 | - This implies that other processes also blocking for this lock will not be scheduled. Only the highest priority process will be scheduled(if any).
283 | - The process releasing the lock might be scheduled out because the process now eligible to run may have a higher priority than the one releasing the lock.
284 | - The OS must take into account priority inversion.
285 | - It is possible that a higher-priority process is blocked for a resource that is currently owned by a lower-priority process.
286 | - In this case, the OS must bump up the priority of the lower priority process to that of the blocked process and keep it this way until the lower-priority process releases the resources.
287 | - If a process holding a lock exits or is terminated for whatever reason,
288 | - The lock MUST be considered not held and another process waiting(if any) should be made eligible for scheduling.
289 | - Events.
290 | - Events, unlike locks, are provided for process synchronization.
291 | - They allow one process to notify another process the occurrence of an event.
292 | - The OS provides built-in events for a total of 10. They are numbered 1 to 10.
293 | - Any event is in one of 2 states.
294 | - Signaled. This implies any process waiting on it is eligible to run now.
295 | - An event is set to signaled when a process invokes the SignalEvent opcode.
296 | - Non-signaled.
297 | - This is the initial state of all events when the OS boots up(so to speak).
298 | - When a process that is waiting on it becomes eligible to run, the event becomes non-signaled allowing another process to wait on it.
299 | - There is no concept of ownership of an event.
300 |
--------------------------------------------------------------------------------
/src/TinyOSCore/MemoryManager.cs:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------
2 | //
3 | // Copyright (c) Scott Hanselman. All Rights Reserved.
4 | //
5 | // ------------------------------------------------------------------------------
6 | //
7 | // Scott Hanselman's Tiny Academic Virtual CPU and OS
8 | // Copyright (c) 2002, Scott Hanselman (scott@hanselman.com)
9 | // All rights reserved.
10 | //
11 | // A BSD License
12 | // Redistribution and use in source and binary forms, with or without modification,
13 | // are permitted provided that the following conditions are met:
14 | //
15 | // Redistributions of source code must retain the above copyright notice,
16 | // this list of conditions and the following disclaimer.
17 | // Redistributions in binary form must reproduce the above copyright notice,
18 | // this list of conditions and the following disclaimer in the documentation
19 | // and/or other materials provided with the distribution.
20 | // Neither the name of Scott Hanselman nor the names of its contributors
21 | // may be used to endorse or promote products derived from this software without
22 | // specific prior written permission.
23 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
27 | // BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
31 | // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 | // THE POSSIBILITY OF SUCH DAMAGE.
33 | //
34 | using System;
35 | using System.Collections;
36 | using System.IO;
37 | using System.Runtime.Serialization;
38 | using System.Xml;
39 | using System.Xml.Serialization;
40 | using System.Diagnostics;
41 | using System.Threading;
42 | using System.Text;
43 | using System.Configuration;
44 | using System.Runtime.Serialization.Formatters.Binary;
45 |
46 | namespace Hanselman.CST352
47 | {
48 | ///
49 | /// The MemoryManager for the . All memory accesses by a
50 | /// go through this class.
51 | ///
52 | ///
53 | /// theOS.memoryMgr[processId, 5]; //accesses memory at address 5
54 | ///
55 | public class MemoryManager
56 | {
57 | private ArrayList _pageTable;
58 |
59 | //BitArray freePhysicalPages = new BitArray((int)(CPU.physicalMemory.Length/CPU.pageSize), true);
60 | private bool[] freePhysicalPages = new bool[(int)(CPU.physicalMemory.Length/CPU.pageSize)];
61 |
62 | ///
63 | /// Total ammount of addressable memory. This is set in the Constructor.
64 | /// Once set, it is readonly
65 | ///
66 | public readonly uint virtualMemSize = 0;
67 |
68 | ///
69 | ///
70 | ///
71 | ///
72 | public MemoryManager(uint virtualMemSizeIn)
73 | {
74 | //
75 | // Find a size for addressableMemory that is on a page boundary
76 | //
77 | virtualMemSize = CPU.UtilRoundToBoundary(virtualMemSizeIn, CPU.pageSize);
78 |
79 | //
80 | // Size of memory must be a factor of CPU.pageSize
81 | // This was asserted when the CPU initialized memory
82 | //
83 | uint physicalpages = (uint)(CPU.physicalMemory.Length/CPU.pageSize);
84 | uint addressablepages = (uint)(virtualMemSize/CPU.pageSize);
85 |
86 | _pageTable = new ArrayList((int)addressablepages);
87 |
88 | // Delete all our Swap Files
89 | foreach (string f in Directory.GetFiles(".","*.xml"))
90 | File.Delete(f);
91 |
92 | // For all off addressable memory...
93 | // Make the pages in physical and the pages that aren't in physical
94 | for (uint i = 0;i < virtualMemSize; i+=CPU.pageSize)
95 | {
96 | // Mark the Pages that are in physical memory as "false" or "not free"
97 | MemoryPage p;
98 | if (i < CPU.physicalMemory.Length)
99 | {
100 | p = new MemoryPage(i, true);
101 | freePhysicalPages[(int)(i/CPU.pageSize)] = false;
102 | }
103 | else p = new MemoryPage(i, false);
104 |
105 | _pageTable.Add(p);
106 | }
107 |
108 | //
109 | // Cordon off some shared memory regions...these are setting the AppSettings
110 | //
111 | uint SharedRegionsSize = uint.Parse(EntryPoint.Configuration["SharedMemoryRegionSize"]);
112 | uint SharedRegions = uint.Parse(EntryPoint.Configuration["NumOfSharedMemoryRegions"]);
113 | if (SharedRegions > 0 && SharedRegionsSize > 0)
114 | {
115 | uint TotalPagesNeeded = (uint)(SharedRegions*SharedRegionsSize/CPU.pageSize);
116 | uint pagesPerRegion = TotalPagesNeeded/SharedRegions;
117 |
118 | // ForExample:
119 | // I need 2 regions
120 | // 64 bytes needed for each
121 | // 4 pages each
122 | // 8 total pages needed
123 |
124 | // Because I pre-allocate shared memory I'll have the luxury of contigous pages of memory.
125 | // I'll exploit this hack in MapSharedMemoryToProcess
126 | foreach (MemoryPage page in _pageTable)
127 | {
128 | // Do we still need pages?
129 | if (TotalPagesNeeded > 0)
130 | {
131 | // If this page is assigned to the OS, take it
132 | if (page.SharedMemoryRegion == 0)
133 | {
134 | // Now assign it to us
135 | page.SharedMemoryRegion = SharedRegions;
136 | TotalPagesNeeded--;
137 | if (TotalPagesNeeded % pagesPerRegion == 0)
138 | SharedRegions--;
139 | }
140 | }
141 | else //We have all we need
142 | break;
143 | }
144 | }
145 | }
146 |
147 |
148 | ///
149 | ///
150 | ///
151 | /// The Process
152 | /// The number of bytes requested. Will be rounded up to the nearest page
153 | /// The Start Address of the Alloc'ed memory
154 | public uint ProcessHeapAlloc(Process p, uint bytesRequested)
155 | {
156 | // Round up to the nearest page boundary
157 | uint pagesRequested = MemoryManager.BytesToPages(bytesRequested);
158 | uint addrStart = 0;
159 |
160 | //
161 | // Finds n *Contiguous* Pages
162 | //
163 |
164 | // Start with a list of potentialPages...
165 | ArrayList potentialPages = new ArrayList();
166 |
167 | // Look through all the pages in our heap
168 | for (int i = 0; i < p.PCB.heapPageTable.Count; i++)
169 | {
170 | // The pages must be contiguous
171 | bool bContiguous = true;
172 |
173 | //From this start page, check for contiguous free pages nearby
174 | MemoryPage startPage = (MemoryPage)p.PCB.heapPageTable[i];
175 |
176 | //Is this page, and x ahead of it free?
177 | if (startPage.heapAllocationAddr == 0)
178 | {
179 | potentialPages.Clear();
180 | potentialPages.Add(startPage);
181 |
182 | //Is this page, and x ahead of it free?
183 | for (int j = 1; j < pagesRequested;j++)
184 | {
185 | // Have we walked past the end of the heap?
186 | if ((i+j) >= p.PCB.heapPageTable.Count)
187 | throw new HeapException(p.PCB.pid, pagesRequested*CPU.pageSize);
188 |
189 | MemoryPage nextPage = (MemoryPage)p.PCB.heapPageTable[i+j];
190 | if (nextPage.heapAllocationAddr == 0)
191 | potentialPages.Add(nextPage);
192 | else
193 | bContiguous = false;
194 | }
195 | // If we make it here, we've found enough contiguous pages, break and continue
196 | if (bContiguous == true)
197 | break;
198 | }
199 | }
200 |
201 | // Did we not find enough pages?
202 | if (potentialPages.Count != pagesRequested)
203 | throw new HeapException(p.PCB.pid, pagesRequested*CPU.pageSize);
204 |
205 | // Mark each page with the address of the original alloc
206 | // so we can Free them later
207 | addrStart = ((MemoryPage)potentialPages[0]).addrProcessIndex;
208 | foreach (MemoryPage page in potentialPages)
209 | page.heapAllocationAddr = addrStart;
210 |
211 | return addrStart;
212 | }
213 |
214 |
215 | ///
216 | /// For debugging only. The value used to "zero out" memory when doing a FreeMemory.
217 | ///
218 | private static int memoryClearInt;
219 |
220 | ///
221 | /// Releases pages that were Alloc'ed from the Process's Heap
222 | ///
223 | /// The Processes
224 | /// The Process address that the allocation began at
225 | ///
226 | public uint ProcessHeapFree(Process p, uint startAddr)
227 | {
228 | uint pageCount = 0;
229 | foreach (MemoryPage page in p.PCB.heapPageTable)
230 | {
231 | if (page.heapAllocationAddr == startAddr)
232 | {
233 | page.heapAllocationAddr = 0;
234 | pageCount++;
235 | }
236 | }
237 |
238 | //
239 | // For Heap Debugging, uncomment this line,
240 | // this incrementing value will be used to
241 | // clear memory out when releasing heap blocks
242 | //
243 | //memoryClearInt++;
244 |
245 | memoryClearInt = 0;
246 | SetMemoryOfProcess(p.PCB.pid, startAddr, pageCount*CPU.pageSize, (byte)memoryClearInt);
247 | return 0;
248 |
249 | }
250 |
251 | ///
252 | /// Adds all the pages allocated to a Process's heap to a PCB specific table of memory pages
253 | ///
254 | /// The Process
255 | public void CreateHeapTableForProcess(Process p)
256 | {
257 | foreach (MemoryPage page in _pageTable)
258 | {
259 | if (page.pidOwner == p.PCB.pid)
260 | {
261 | if (page.addrProcessIndex >= p.PCB.heapAddrStart && page.addrProcessIndex < p.PCB.heapAddrEnd)
262 | {
263 | p.PCB.heapPageTable.Add(page);
264 | }
265 | }
266 | }
267 | }
268 |
269 |
270 | ///
271 | /// Gets a 4 byte unsigned integer (typically an opCode param) from memory
272 | ///
273 | /// The calling processid
274 | /// The address in memory from the Process's point of view
275 | ///
276 | public uint getUIntFrom(uint processid, uint processIndex)
277 | {
278 | return CPU.BytesToUInt(getBytesFrom(processid, processIndex, 4));
279 | }
280 |
281 | ///
282 | /// Sets a 4 byte unsigned integer (typically an opCode param) to memory
283 | ///
284 | /// The calling processid
285 | /// The address in memory from the Process's point of view
286 | /// The new value
287 | public void setUIntAt(uint processid, uint processIndex, uint avalue)
288 | {
289 | setBytesAt(processid, processIndex, CPU.UIntToBytes(avalue));
290 | }
291 |
292 | ///
293 | /// Gets an array of "length" bytes from a specific process's memory address
294 | ///
295 | /// The calling process's id
296 | /// The address in memory from the Process's point of view
297 | /// how many bytes
298 | /// an initialized byte array containing the contents of memory
299 | public byte[] getBytesFrom(uint processid, uint processIndex, uint length)
300 | {
301 | byte[] bytes = new byte[length];
302 | for (uint i = 0; i < length; i++)
303 | {
304 | bytes[i] = this[processid, processIndex + i];
305 | }
306 | return bytes;
307 | }
308 |
309 | ///
310 | /// Sets an array of bytes to a specific process's memory address
311 | ///
312 | /// The calling processid
313 | /// The address in memory from the Process's point of view
314 | /// The source array of bytes
315 | public void setBytesAt(uint processid, uint processIndex, byte[] pageValue)
316 | {
317 | for (uint i = 0; i < pageValue.Length; i++)
318 | {
319 | this[processid, processIndex+i] = pageValue[i];
320 | }
321 | }
322 |
323 |
324 |
325 | ///
326 | /// Translates a Process's address space into physical address space
327 | ///
328 | /// The calling process's id
329 | /// The address in memory from the Process's point of view
330 | /// Whether we mark this as dirty or not
331 | /// The physical address of the memory we requested
332 | /// This process has accessed memory outside it's Process address space
333 | public uint ProcessAddrToPhysicalAddr(uint processid, uint processMemoryIndex, bool dirtyFlag)
334 | {
335 | foreach(MemoryPage page in _pageTable)
336 | {
337 | // If this process owns this page
338 | if (page.pidOwner == processid)
339 | {
340 | // If this page is responsible for the memory addresses we are interested in
341 | if((processMemoryIndex >= page.addrProcessIndex) && (processMemoryIndex < page.addrProcessIndex+CPU.pageSize))
342 | {
343 | // Get the page offset
344 | uint pageOffset = processMemoryIndex - page.addrProcessIndex;
345 | return ProcessAddrToPhysicalAddrHelper(page, dirtyFlag, pageOffset);
346 | }
347 | }
348 |
349 | // Maybe this is a shared region?
350 | if (page.SharedMemoryRegion != 0)
351 | {
352 | // Go through the list of owners and see if we are one...
353 | for (int i = 0; i <= page.pidSharedOwnerList.Count-1; i++)
354 | {
355 | // Do we own this page?
356 | if ((uint)page.pidSharedOwnerList[i] == processid)
357 | {
358 | // Does this page handle this address?
359 | if (processMemoryIndex >= (uint)page.pidSharedProcessIndex[i] && processMemoryIndex < (uint)page.pidSharedProcessIndex[i]+CPU.pageSize)
360 | {
361 | uint pageOffset = processMemoryIndex - (uint)page.pidSharedProcessIndex[i];
362 | return ProcessAddrToPhysicalAddrHelper(page, dirtyFlag, pageOffset);
363 | }
364 | }
365 | }
366 | }
367 | }
368 |
369 | // If we make it here, this process has accessed memory that doesn't exist in the page table
370 | // We'll catch this exception and terminate the process for accessing memory that it doesn't own
371 | throw(new MemoryException(processid, processMemoryIndex));
372 | }
373 |
374 | private uint ProcessAddrToPhysicalAddrHelper(MemoryPage page, bool dirtyFlag, uint pageOffset)
375 | {
376 | // Get the page offset
377 | uint virtualIndex = page.addrVirtual+pageOffset;
378 |
379 | // Update Flags for this process
380 | page.isDirty = dirtyFlag || page.isDirty;
381 | page.accessCount++;
382 | page.lastAccessed = DateTime.Now;
383 |
384 | // Take this new "virtual" address (relative to all addressable memory)
385 | // and translate it to physical ram. Page Faults may occur inside this next call.
386 | uint physicalIndex = VirtualAddrToPhysical(page,virtualIndex);
387 | return physicalIndex;
388 | }
389 |
390 |
391 | ///
392 | /// Resets a memory page to defaults, deletes that page's swap file and
393 | /// may mark the page as free in physical memory
394 | ///
395 | /// The to reset
396 | public void ResetPage(MemoryPage page)
397 | {
398 | if (page.isValid == true)
399 | {
400 | // Make this page as availble in physical memory
401 | uint i = page.addrPhysical / CPU.pageSize;
402 | Debug.Assert(i < freePhysicalPages.Length); //has to be
403 | freePhysicalPages[(int)i] = true;
404 | }
405 |
406 | //Reset to reasonable defaults
407 | page.isDirty = false;
408 | page.addrPhysical = 0;
409 | page.pidOwner = 0;
410 | page.pageFaults = 0;
411 | page.accessCount = 0;
412 | page.lastAccessed = DateTime.Now;
413 | page.addrProcessIndex = 0;
414 | page.heapAllocationAddr = 0;
415 |
416 | // Delete this page's swap file
417 | string filename = System.Environment.CurrentDirectory + "/page" + page.pageNumber + "." + page.addrVirtual + ".xml";
418 | File.Delete(filename);
419 | }
420 |
421 | ///
422 | ///
423 | ///
424 | ///
425 | ///
426 | ///
427 | public uint VirtualAddrToPhysical(MemoryPage page, uint virtualIndex)
428 | {
429 | if (page.isValid == false)
430 | {
431 | int i = 0;
432 | for (; i < freePhysicalPages.Length; i++)
433 | {
434 | if (freePhysicalPages[i] == true)
435 | {
436 | // Found a free physical page!
437 | freePhysicalPages[i] = false;
438 | break;
439 | }
440 | }
441 |
442 | // If we have reach the end of the freePhysicalPages
443 | // without finding a free page - we are out of physical memory, therefore
444 | // we PageFault and start looking for victim pages to swap out
445 | if (i == freePhysicalPages.Length)
446 | {
447 | MemoryPage currentVictim = null;
448 | foreach (MemoryPage possibleVictim in _pageTable)
449 | {
450 | if (!page.Equals(possibleVictim) && possibleVictim.isValid == true)
451 | {
452 | if (currentVictim == null) currentVictim = possibleVictim;
453 |
454 | // If this is the least accessed Memory Page we've found so far
455 | if (possibleVictim.lastAccessed < currentVictim.lastAccessed)
456 | currentVictim = possibleVictim;
457 | }
458 | }
459 | // Did we find no victims? That's a HUGE problem, and shouldn't ever happen
460 | Debug.Assert(currentVictim != null);
461 |
462 | SwapOut(currentVictim);
463 |
464 | // Take the physical address of this page
465 | page.addrPhysical = currentVictim.addrPhysical;
466 |
467 | SwapIn(page);
468 | }
469 | else // no page fault
470 | {
471 | // Map this page to free physical page "i"
472 | page.addrPhysical = (uint)(i*CPU.pageSize);
473 | SwapIn(page);
474 | }
475 |
476 | }
477 |
478 | // Adjust the physical address with pageOffset from a page boundary
479 | uint pageOffset = virtualIndex % CPU.pageSize;
480 | uint physicalIndex = page.addrPhysical+pageOffset;
481 | return physicalIndex;
482 | }
483 |
484 | ///
485 | /// Public accessor method to make Virtual Memory look like an array
486 | ///
487 | ///
488 | /// theOS.memoryMgr[processId, 5]; //accesses memory at address 5
489 | ///
490 | public byte this[uint processid, uint processMemoryIndex]
491 | {
492 | get
493 | {
494 | uint physicalIndex = ProcessAddrToPhysicalAddr(processid, processMemoryIndex, false);
495 | return CPU.physicalMemory[physicalIndex];
496 | }
497 | set
498 | {
499 | uint physicalIndex = ProcessAddrToPhysicalAddr(processid, processMemoryIndex, true);
500 | CPU.physicalMemory[physicalIndex] = value;
501 | }
502 | }
503 |
504 | ///
505 | /// Helper method to translate # of bytes to # of Memory Pages
506 | ///
507 | /// bytes to translate
508 | /// number of pages
509 | public static uint BytesToPages(uint bytes)
510 | {
511 | return (CPU.UtilRoundToBoundary(bytes, CPU.pageSize)/CPU.pageSize);
512 | //return ((uint)(bytes / CPU.pageSize) + (uint)(bytes % CPU.pageSize));
513 | }
514 |
515 | ///
516 | /// Takes a Process's ID and releases all MemoryPages assigned to it, zeroing and reseting them
517 | ///
518 | /// Process ID
519 | public void ReleaseMemoryOfProcess(uint pid)
520 | {
521 | foreach (MemoryPage page in _pageTable)
522 | {
523 | if (page.pidOwner == pid)
524 | {
525 | if (page.isValid == true)
526 | {
527 | SetMemoryOfProcess(pid,page.addrProcessIndex,CPU.pageSize,0);
528 | }
529 | ResetPage(page);
530 | }
531 |
532 | if (page.SharedMemoryRegion != 0)
533 | {
534 | for (int i = 0; i <= page.pidSharedOwnerList.Count-1; i++)
535 | {
536 | // Do we own this page?
537 | if ((uint)page.pidSharedOwnerList[i] == pid)
538 | {
539 | page.pidSharedOwnerList.RemoveAt(i);
540 | page.pidSharedProcessIndex.RemoveAt(i);
541 | break;
542 | }
543 | }
544 | }
545 | }
546 | }
547 |
548 | ///
549 | /// Zeros out memory belonging to a Process from start until length
550 | ///
551 | /// Process ID
552 | /// start memory address
553 | /// length in bytes
554 | /// the new value of the byte
555 | public void SetMemoryOfProcess(uint pid, uint start, uint length, byte newvalue)
556 | {
557 | for (uint i = start; i < (start+length);i++)
558 | this[pid,i] = newvalue;
559 | }
560 |
561 | ///
562 | /// Maps the shared memory region to the process passed in
563 | ///
564 | /// the number of the shared region to map
565 | /// Process ID
566 | /// the index in process memory of the shared region
567 | public uint MapSharedMemoryToProcess(uint memoryRegion, uint pid)
568 | {
569 | uint SharedRegionsSize = uint.Parse(EntryPoint.Configuration["SharedMemoryRegionSize"]);
570 | uint PagesNeeded = (uint)(SharedRegionsSize/CPU.pageSize);
571 |
572 | uint startAddrProcessIndex;
573 | uint addrProcessIndex = 0;
574 |
575 | //Find the max address used by this process (a free place to map this memory to)
576 | foreach (MemoryPage page in _pageTable)
577 | {
578 | if (page.pidOwner == pid)
579 | addrProcessIndex = Math.Max(page.addrProcessIndex,addrProcessIndex);
580 | }
581 | //Add one more page, to get the address of where to map the Shared Memory Region
582 | addrProcessIndex += CPU.pageSize;
583 | startAddrProcessIndex = addrProcessIndex;
584 |
585 | // Very inefficient:
586 | // Now, find the Shared Memory pages and and map them to this process
587 | foreach (MemoryPage page in _pageTable)
588 | {
589 | if (PagesNeeded > 0)
590 | {
591 | if (page.SharedMemoryRegion == memoryRegion)
592 | {
593 | page.pidSharedOwnerList.Add(pid);
594 | page.pidSharedProcessIndex.Add(addrProcessIndex);
595 | addrProcessIndex += CPU.pageSize;
596 | PagesNeeded--;
597 | }
598 | }
599 | else
600 | // We've got enough pages...
601 | break;
602 | }
603 | return startAddrProcessIndex;
604 | }
605 |
606 |
607 | ///
608 | /// Takes a number of bytes and a process id and assigns MemoryPages in the pageTable to the Process
609 | ///
610 | /// # of bytes to assign
611 | /// Process ID
612 | public void MapMemoryToProcess(uint bytes, uint pid)
613 | {
614 | uint pagesNeeded = BytesToPages(bytes);
615 | uint addrProcessIndex = 0;
616 |
617 | foreach (MemoryPage page in _pageTable)
618 | {
619 | if (pagesNeeded > 0)
620 | {
621 | // If this page is assigned to the OS,
622 | // and not a SharedMemoryRegion and take it
623 | if (page.pidOwner == 0 && page.SharedMemoryRegion == 0)
624 | {
625 | // Now assign it to us
626 | page.pidOwner = pid;
627 | page.addrProcessIndex = addrProcessIndex;
628 | addrProcessIndex += CPU.pageSize;
629 | pagesNeeded--;
630 | }
631 | }
632 | else
633 | // We've got enough pages...
634 | break;
635 | }
636 |
637 | // Did we go through the whole pageTable and not have enough memory?
638 | if (pagesNeeded > 0)
639 | {
640 | Console.WriteLine("OUT OF MEMORY: Process {0} requested {1} more bytes than were available!",pid,pagesNeeded*CPU.pageSize);
641 | System.Environment.Exit(1);
642 | }
643 | }
644 |
645 | ///
646 | /// Represents an entry in the Page Table. MemoryPages (or "Page Table Entries")
647 | /// are created once and never destroyed, their values are just reassigned
648 | ///
649 | public class MemoryPage
650 | {
651 | ///
652 | /// The number of the shared memory region this MemoryPage is mapped to
653 | ///
654 | public uint SharedMemoryRegion = 0;
655 |
656 | ///
657 | /// One of two parallel arrays, one of shared owners of this page, one of shared process indexes of this page
658 | ///
659 | public ArrayList pidSharedOwnerList = new ArrayList();
660 |
661 | ///
662 | /// One of two parallel arrayz, one of shared owners of this page, one of shared process indexes of this page
663 | ///
664 | public ArrayList pidSharedProcessIndex = new ArrayList();
665 |
666 | ///
667 | /// The number this page is in addressable Memory. Set once and immutable
668 | ///
669 | public readonly uint pageNumber = 0;
670 |
671 | ///
672 | /// The address in addressable space this page is responsbile for
673 | ///
674 | public readonly uint addrVirtual = 0;
675 |
676 | ///
677 | /// The address in Process space this page is responsible for
678 | ///
679 | public uint addrProcessIndex = 0;
680 |
681 | ///
682 | /// The process address that originally allocated this page. Kept so we can free that page(s) later.
683 | ///
684 | public uint heapAllocationAddr = 0;
685 |
686 | ///
687 | /// The process that is currently using this apge
688 | ///
689 | public uint pidOwner = 0;
690 |
691 | ///
692 | /// This is only valid when
693 | /// pidOwner != 0 and isValid == true
694 | /// meaning the page is actually mapped and present
695 | ///
696 | public uint addrPhysical = 0;
697 |
698 | ///
699 | /// Is the page in memory now?
700 | ///
701 | public bool isValid;
702 |
703 | ///
704 | /// Has the page been changes since it was last swapped in from Disk?
705 | ///
706 | public bool isDirty = false;
707 |
708 | ///
709 | /// For statistics: How many times has this page been involved in a pageFault?
710 | ///
711 | public uint pageFaults = 0;
712 |
713 | ///
714 | /// For aging and swapping: How many times has this page's address range been accessed?
715 | ///
716 | public uint accessCount = 0;
717 |
718 | ///
719 | /// For aging and swapping: When was this page last accessed?
720 | ///
721 | public DateTime lastAccessed = DateTime.Now;
722 |
723 | ///
724 | /// Only public constructor for a Memory Page and is only called once
725 | /// in the constructor
726 | ///
727 | /// The address in addressable memory this page is responsible for
728 | /// Is this page in memory right now?
729 | public MemoryPage(uint initAddrVirtual, bool isValidFlag)
730 | {
731 | isValid = isValidFlag;
732 | if (isValid)
733 | addrPhysical = initAddrVirtual;
734 | addrVirtual = initAddrVirtual;
735 | pageNumber = (addrVirtual)/CPU.pageSize;
736 | }
737 | }
738 |
739 | ///
740 | /// Represents the actual values in memory that a MemoryPage points to.
741 | /// MemoryPageValue is serialized to disk, currently as XML, in .
742 | ///
743 | [Serializable] public class MemoryPageValue
744 | {
745 | ///
746 | /// The array of bytes holding the value of memory for this page
747 | ///
748 | [XmlArray(ElementName = "byte", Namespace = "http://www.hanselman.com")]
749 | public byte[] memory = new byte[CPU.pageSize];
750 |
751 | ///
752 | /// For aging and swapping: How many times has this page's address range been accessed?
753 | ///
754 | public uint accessCount = 0;
755 |
756 | ///
757 | /// For aging and swapping: When was this page last accessed?
758 | ///
759 | public DateTime lastAccessed = DateTime.Now;
760 | }
761 |
762 | ///
763 | /// Swaps the specified to disk. Currently implemented as XML for fun.
764 | ///
765 | /// The to be swapped
766 | public void SwapOut(MemoryPage victim)
767 | {
768 | if (victim.isDirty)
769 | {
770 |
771 | // Generate a filename based on address and page number
772 | string filename = System.Environment.CurrentDirectory + "/page" + victim.pageNumber + "-" + victim.addrVirtual + ".xml";
773 |
774 | // IFormatter ser = new BinaryFormatter();
775 | // Stream writer = new FileStream(filename, FileMode.Create);
776 |
777 | XmlSerializer ser = new XmlSerializer(typeof(MemoryPageValue));
778 | Stream fs = new FileStream(filename, FileMode.Create);
779 | XmlWriter writer = new XmlTextWriter(fs, new UTF8Encoding());
780 |
781 | MemoryPageValue pageValue = new MemoryPageValue();
782 |
783 | // Copy the bytes from Physical Memory so we don't pageFault in a Fault Hander
784 | byte[] bytes = new byte[CPU.pageSize];
785 | for (int i = 0; i < CPU.pageSize; i++)
786 | bytes[i] = CPU.physicalMemory[victim.addrPhysical+i];
787 |
788 | // Copy details from the MemoryPage to the MemoryPageValue
789 | pageValue.memory = bytes;
790 | pageValue.accessCount = victim.accessCount;
791 | pageValue.lastAccessed = victim.lastAccessed;
792 |
793 | //Console.WriteLine("Swapping out page {0} at physical memory {1}",victim.pageNumber, victim.addrPhysical);
794 |
795 | // Write the MemoryPageValue to disk!
796 | ser.Serialize(writer,pageValue);
797 |
798 | //writer.Flush();
799 | //writer.Close();
800 | fs.Close();
801 | }
802 | victim.isValid = false;
803 | }
804 |
805 | ///
806 | /// Swaps in the specified from disk. Currently implemented as XML for fun.
807 | ///
808 | /// The that is being swapped in
809 | public void SwapIn(MemoryPage winner)
810 | {
811 | // Generate a filename based on address and page number
812 | string filename = System.Environment.CurrentDirectory + "/page" + winner.pageNumber + "-" + winner.addrVirtual + ".xml";
813 | if (File.Exists(filename) && winner.isValid == false)
814 | {
815 | //BinaryFormatter ser = new BinaryFormatter();
816 | //Stream reader = new FileStream(filename, FileMode.Open);
817 |
818 | XmlSerializer ser = new XmlSerializer(typeof(MemoryPageValue));
819 | Stream fs = new FileStream(filename, FileMode.Open);
820 | XmlReader reader = new XmlTextReader(fs);
821 |
822 | // Load the MemoryPageValue in from Disk!
823 | MemoryPageValue pageValue = (MemoryPageValue)ser.Deserialize(reader);
824 |
825 | // Copy the bytes from Physical Memory so we don't pageFault in a Fault Hander
826 | for (int i = 0; i < CPU.pageSize; i++)
827 | CPU.physicalMemory[winner.addrPhysical+i] = pageValue.memory[i];
828 |
829 | //Console.WriteLine("Swapping in page {0} at physical memory {1}",winner.pageNumber, winner.addrPhysical);
830 |
831 | winner.accessCount = pageValue.accessCount;
832 | winner.lastAccessed = pageValue.lastAccessed;
833 |
834 | pageValue = null;
835 |
836 | reader.Close();
837 | fs.Close();
838 | File.Delete(filename);
839 | }
840 | else //no swap file, do nothing
841 | {
842 | //Console.WriteLine(filename + " doesn't exist");
843 | }
844 |
845 | // We are now in memory and we were involved in Page Fault
846 | winner.isValid = true;
847 | winner.pageFaults++;
848 | }
849 |
850 | ///
851 | /// For statistical purposes only.
852 | /// Total up how many times this Process has been involved in a Page Fault
853 | ///
854 | /// The Process to total
855 | /// number of Page Faults
856 | public uint PageFaultsForProcess(Process p)
857 | {
858 | uint totalPageFaults = 0;
859 | foreach (MemoryPage page in _pageTable)
860 | {
861 | if (page.pidOwner == p.PCB.pid)
862 | {
863 | totalPageFaults += page.pageFaults;
864 | }
865 | }
866 | return totalPageFaults;
867 | }
868 | }
869 |
870 | ///
871 | /// Memory Protection: MemoryExceptions are constructed and thrown
872 | /// when a accessed memory that doesn't belong to it.
873 | ///
874 | public class MemoryException : Exception
875 | {
876 | ///
877 | /// Process ID
878 | ///
879 | public uint pid = 0;
880 | ///
881 | /// Process address in question
882 | ///
883 | public uint processAddress = 0;
884 |
885 | ///
886 | /// Public Constructor for a Memory Exception
887 | ///
888 | /// Process ID
889 | /// Process address
890 | public MemoryException(uint pidIn, uint addrIn)
891 | {
892 | pid = pidIn;
893 | processAddress = addrIn;
894 | }
895 |
896 | ///
897 | /// Pretty printing for MemoryExceptions
898 | ///
899 | /// Formatted string about the MemoryException
900 | public override string ToString()
901 | {
902 | return String.Format("Process {0} tried to access memory at address {1} and will be terminated! ",pid, processAddress);
903 | }
904 | }
905 |
906 | ///
907 | /// Memory Protection: MemoryExceptions are constructed and thrown
908 | /// when a accessed memory that doesn't belong to it.
909 | ///
910 | public class StackException : Exception
911 | {
912 | ///
913 | /// Process ID
914 | ///
915 | public uint pid = 0;
916 | ///
917 | /// Num of Bytes more than the stack could handle
918 | ///
919 | public uint tooManyBytes = 0;
920 |
921 | ///
922 | /// Public Constructor for a Memory Exception
923 | ///
924 | /// Process ID
925 | /// Process address
926 | public StackException(uint pidIn, uint tooManyBytesIn)
927 | {
928 | pid = pidIn;
929 | tooManyBytes = tooManyBytesIn;
930 | }
931 |
932 | ///
933 | /// Pretty printing for MemoryExceptions
934 | ///
935 | /// Formatted string about the MemoryException
936 | public override string ToString()
937 | {
938 | return String.Format("Process {0} tried to push {1} too many bytes on to the stack and will be terminated! ",pid, tooManyBytes);
939 | }
940 | }
941 |
942 | ///
943 | /// Memory Protection: MemoryExceptions are constructed and thrown
944 | /// when a accessed memory that doesn't belong to it.
945 | ///
946 | public class HeapException : Exception
947 | {
948 | ///
949 | /// Process ID
950 | ///
951 | public uint pid = 0;
952 | ///
953 | /// Num of Bytes more than the stack could handle
954 | ///
955 | public uint tooManyBytes = 0;
956 |
957 | ///
958 | /// Public Constructor for a Memory Exception
959 | ///
960 | /// Process ID
961 | /// Process address
962 | public HeapException(uint pidIn, uint tooManyBytesIn)
963 | {
964 | pid = pidIn;
965 | tooManyBytes = tooManyBytesIn;
966 | }
967 |
968 | ///
969 | /// Pretty printing for MemoryExceptions
970 | ///
971 | /// Formatted string about the MemoryException
972 | public override string ToString()
973 | {
974 | return String.Format("Process {0} tried to alloc {1} bytes more from the heap than were free and will be terminated! ",pid, tooManyBytes);
975 | }
976 | }
977 |
978 |
979 | }
980 |
--------------------------------------------------------------------------------