CSE 451 Project 3 FAQ

CSE 451

Introduction to Operating Systems

Autumn 1997


CSE 451 Project 3 FAQ

Q?

Ans.

What do I do if I run out of swap space?

If your user program uses all of swap space and then tries to allocate a new page, then there is no way the program can terminate cleanly and correctly (at least none I can think of). So mt-os can print an error message and exit, since it only executes one program at a time.

Note that memhog, vm_test, and vm_test.2 should not run out of swap space.

My VM system seems to be working perfectly (all my asserts succeed), but yet my user programs still crash after a while. What in the world is going on?

Make sure you're not forgetting to inform the TLB when you evict a page from memory.

I have an error that happens very far along into the execution of my user program -- after millions of instructions have been executed. How can i set a breakpoint that only fires right before the error happens?

If you create a global cycle count variable to keep track of which user instruction is executing, I think you can figure out the approximate cycle (in run()) at which the problem happens, and you can add some code near the begging of the loop in run() that says something like:

  cycle++;
  if (cycle == PROBLEM_CYCLE) {
        printf("reached problem cycle\n");
  }
Then i think if you put a breakpoint on the printf() you can start tracing through the code at the beginning of the cycle in which the problem occurs.

This may not be very useful for threaded programs, since interrupts inside the mt-os kernel are nondeterministic.

I'm getting something printed out from my program but i don't know where it's coming from. How can i figure this out?

One way is to grep through every source file in your project:

orcas% find . -name "*.[ch]" | xargs grep "Unknown opcode"
./mipsi-orig-src/print.c:           error(Benign, "Unknown opcode field of inst 0x%x at 0x%x\n", inst, pc);
./mipsi-orig-src/run.c:     error(Fatal, "Unknown opcode field of inst 0x%x at 0x%x\n", inst, pc);
./mipsi-src/print.c:        error(Benign, "Unknown opcode field of inst 0x%x at 0x%x\n", inst, pc);
./mipsi-src/run.c:          error(Fatal, "Unknown opcode field of inst 0x%x at 0x%x\n", inst, pc);

Can i use C++ in implementing my OS?

Sure. Just make sure that i can still "make" the components of the OS in the same way.

To be able to call C from C++ and vice versa you have to make sure you tell the compiler to use the right linkage conventions. C++ "mangles" (that's the technical term) function names by tacking on info about the types of the parameters, so that linking among C++ .o files is type safe. See /cse/courses/cse451/97au/support/c++/ for examples of what to do.

You have to explicitly specify C linkage in two cases:

  1. The function you're calling from C++ is a C function (like aa()) and so when you call it from C++ the compiler should make a call to a non-mangled name, OR
  2. The C++ function you're defining (like b()) will be called from C, and thus should not have its name mangled.

I'm having linking problems. What's going on?

To help diagnose these problems or problems like this, check out "nm" and "dis", which allow you to see what's inside object files and libraries. These are really cool programs. Check them out.

A few things:

Can we get extra credit?

By popular demand, I have decided to offer extra credit for those brave souls who decide to implement a more realistic VM system in the following fashion:

  1. multi-level paging (something like pp. 277-279), so that new pages of page tables are allocated only as needed, AND
  2. all "large" VM data structures (eg, records per virtual page, per physical page, and per swap space) are stored in simulated physical memory (right alongside code and data pages from the program), AND
  3. the OS keeps a free page list so that it rarely, if ever, has to page something out before it can page something in (as discussed in class)
If you're VM system conforms to the spirit of these three conditions, you may get up to 10% extra credit for project 3. Keep in mind that you may still want to try something simple first, and then move to something more complex when the simple scheme works.

Do we have to keep our virtual memory data structures in the simulated physical memory and swap space disk?

No. It will be most convenient for you to keep all code and data for mt-os (including page tables) outside of the simulated memory and disk. For example, this means you can use the (real, Alpha) malloc() call to allocate memory for all OS data structures. This will make life much easier for you. This is, of course, unrealistic, since in a real machine the OS has to keep data structures either in RAM or on disk. But it comes out of your beauty sleep time if you want to try to be more realistic...

What's up with minithread_die()? Why does it have an assert(0) in it? What are we supposed to put there?

You don't need to implement this function. I don't think it's in any .h files accessible to user programs.

The virtual page number in the mipsi VM system is 20 bits, which implies 1M entries in the page table, each (say) 4-bytes large. So we'll have a page table that is about 4MB. This seems fairly large. Is it OK to use a single-level page table?

In a real OS, you would almost certainly not allocate 4 MB in RAM for the page table for each process.

However, one gigantic page table is ok for this project, though you should think through the alternatives (mainly multi-level page tables).

How does malloc() work?

In UNIX (or at least in DEC Ultrix), malloc() uses the brk() and/or sbrk() UNIX system calls (as well as the getpagesize() system call) to request that the OS map more memory into the process's address space to grow the heap upward. In order to avoid implementing these system calls, mipsi and mt-os just say "OK" when a user program makes an s/brk() system call; they do nothing until the pages are actually touched, at which point your VM code will be invoked to allocate the pages.

How do we deal with dynamically allocating storage for the heap and stack?

To allocate memory to the process on demand, your code should treat all virtual pages in the ranges given to you by mt_vm_initialize() as legal. If a reference is made to a virtual page in this legal range and you haven't allocated memory for it (because it is new heap or stack space), you need to allocate swap space (and then phyical memory) for it. This is how real operating systems allow stacks to grow dynamically.

My user program gets "Unimplimented system call" errors. Why?

Mipsi handles some ULTRIX system calls, including write()s to stdio, and all the system calls needed by malloc(). However, there are many standard C library functions that use system calls that mipsi does not emulate, including printf, gettimeofday, and exit().

To allow you to output text to the screen, "mtio.h" and "mtio.c" in USER/ define some work-arounds to allow you to continue to use printf. So in your user programs you should include "mtio.h" and use printf as normal. Then link with "mtio.o"

Instead of calling the standard C library funtion exit() at the end of a program, you should call the system call _exit() directly.

If you get some other mysterious "Unimplimented system call" error message, check the system call list at: mipsi-include/MIPS/syscall-ultrix.h to see which system call mipsi is choking on.

What do i do if i detect an error inside a system call?

Since the mt-os API lacks return values for most of the system calls, there's not much you can do when a user passes in an invalid parameter (or any other error condition occurs). When you detect an error you can return to the user immediately (effectively ignoring the system call request), or you can termiante the thread, or you can terminate the user program. Whatever you think is most reasonable.

Is it a problem that the code in minithreads is turning interrupts on?

No, this is OK. Note that interrupts are turned on as we enter the mt-os "kernel" in mt_syscall(). Note also that this means any code in your mt-os that uses shared read/write data structures (handle tables, page talbes...) needs to be carefully synchronized with semaphores or mutexes.

My VM system crashes and i have no idea why. What can i do to figure out where things are going wrong?

I recommend 1) lots of debugging print statements and 2) assertions everywhere.

  1. To use debugging print statements, you probably want to use a macro like:
    #ifdef DEBUGGING_VM
    #define DEBUGVM(p)   printf p
    #else
    #define DEBUGVM(p)   
    #endif
    
    Then you can use this macro like this:
    DEBUGVM(("Page fault for VPN %d", vpage));
    
    Using a macro similar to this you can easily control which of your debugging print statements fire in a particular build of your code. You could even make the macro use the value of a command line argument to control printing.

    You should consider putting at least one debug print statement inside every function, so you can tell what functions are being executed, and what their parameters are.

    One valuable technique is piping the output of your program through grep to grep for all actions involving a giving virtual page, physical page, or swap space page.

  2. To use assertions, include the following standard library include:
    #include < assert.h >
    
    Then use it like this:
    assert(my_page_table[vpn].in_memory == TRUE);
    
    At run time, the value of the expression inside the assertion is evaluated. If it is true, nothing happens. If the expression is false, assert() will halt your program, print the expression that is false, and the line number in the code where the assertion failed. This is an amazingly powerful way to have your program debug itself.

    When you are using assertions, be sure not to place anything inside the assert() call that has a side effect (ie, an assigment or function call that makes an assignment). In non-debug builds of your program (ie, if you define NDEBUG), assert statements compile into no-ops. (see /usr/include/assert.h). As always, be careful about not mixing up = and ==.

    Asserts are your friend. Use them everywhere you think you know something about the state of your program (eg, variable foo should equal 3 here). Because you will find you're often not doing everything you need to do to make these statements true. In something like a VM system, which is essentially just bookkeeping about the location of pages, you shuold be able to make about as many assertions as regular program statements. You will be happy if you do this, since assert() will catch your error immediately, rather than (say) two thousand instructions later when the program tries to use a page you just faulted in.


cse451-webmaster@cs.washington.edu