A: A good way to test is to create a few traces that are small enough to verify manually. For example, try the following trace with the "debug" turned on:
0010000 R 0020000 R 0030000 R 0040000 RWith four events, you can turn on the "debug" mode and manually verify that each algorithm behaves properly on such an input: the random algorithm should replace pages arbitrarily, while the lru algorithm should replace the first page, and so forth. Of course, one little trace like this doesn't prove the whole system, so you should construct similar small traces to test other boundary conditions.
A: Use the >> (shift right) operator to extract the page number:
pagenum = addr >> 12;
A: If you have a regular page table, then, yes, you need 2^20 entries. On each memory reference, you can easily look in the table to see if the referenced page is in memory. Of course, you will have to have some way to make sure that you have no more than "nframes" of memory in use at one time. This will be a large, inefficient table, but it will work well enough for a simulation.
Another way to do it is to have an inverted page table. Then, you only need one entry in the table for each frame of memory. But, you will have to search the table on each memory reference to see if the page is actually in memory.
Either way is acceptable.
A: In order to keep several pieces of information per page, create a little structure:
struct pageinfo {
int referenced;
int modified;
int lastused;
...
};
And then allocate an array of structures:
struct pageinfo *table;
table = malloc( sizeof(struct pageinfo) * N );
Where N is the number of pages (if you have a regular page table)
or N is the number of frames (if you have an inverted page table).
Now, you have an array of pageinfo structures that you can
use like so:
table[5].referenced = x;
table[23].lastused = y;
A: If there is an empty frame available, the random algorithm should choose it. Only when there are no frames available should the algorithm choose one at random.