Commit 351290fb authored by Henry K. Sun's avatar Henry K. Sun
Browse files

Initial commit

parents
No related merge requests found
Showing with 742 additions and 0 deletions
+742 -0
*.o
test_matrix
CC = gcc
CFLAGS = -Wall -Werror -g -O0
VMEM_OBJS = virtualmem.o vmalloc.o matrix.o test_matrix.o
# So that the binary programs can be listed in fewer places.
# You will want to add to this variable as you implement various policies.
BINARIES = test_matrix
all: $(BINARIES)
# Compile this file with optimizations so that it accesses memory in
# reasonable ways.
matrix.o: CFLAGS += -O2
test_matrix: $(VMEM_OBJS) vmpolicy_random.o
$(CC) $(CPPFLAGS) $(CFLAGS) $^ -o $@ $(LDFLAGS)
clean:
rm -f *.o *~ $(BINARIES)
.PHONY: all clean
/*============================================================================
* Implementation of a simple 2D matrix type, along with a few operations that
* can be performed on matrices. Matrices are allocated from the virtual
* memory pool using the simple allocator declared in vmalloc.h.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "matrix.h"
#include "vmalloc.h"
/* Allocate a new matrix object of size rows x cols, from the virtual memory
* pool. The elements themselves are uninitialized.
*/
matrix_t * vmalloc_matrix(int rows, int cols) {
matrix_t *m;
m = vmem_alloc(sizeof(matrix_t) + rows * cols * sizeof(int));
m->rows = rows;
m->cols = cols;
return m;
}
/* Allocate a new matrix object of size rows x cols, using malloc(). The
* elements themselves are uninitialized. This function allows us to have a
* copy of the test matrices outside of the virtual memory system, so that we
* can verify that no contents are lost or corrupted by bugs in the virtual
* memory system.
*/
matrix_t * malloc_matrix(int rows, int cols) {
matrix_t *m;
m = malloc(sizeof(matrix_t) + rows * cols * sizeof(int));
m->rows = rows;
m->cols = cols;
return m;
}
/* Generate random values in the range [-1000, 1000] for the specified
* matrix.
*/
void generate_matrix_values(matrix_t *m) {
int i;
assert(m != NULL);
for (i = 0; i < m->rows * m->cols; i++)
m->elems[i] = rand() % 2001 - 1000;
}
/* Returns the element at the specified row and column in the matrix. */
int get_elem(const matrix_t *m, int r, int c) {
int index;
assert(m != NULL);
assert(r >= 0 && r < m->rows);
assert(c >= 0 && c < m->cols);
index = r * m->cols + c;
assert(index >= 0 && index < m->rows * m->cols);
return m->elems[index];
}
/* Sets the element at the specified row and column in the matrix. */
void set_elem(matrix_t *m, int r, int c, int value) {
int index;
assert(m != NULL);
assert(r >= 0 && r < m->rows);
assert(c >= 0 && c < m->cols);
index = r * m->cols + c;
assert(index >= 0 && index < m->rows * m->cols);
m->elems[index] = value;
}
/* Multiplies the two matrices m1 and m2, storing the results into the result
* matrix.
*/
void multiply_matrices(const matrix_t *m1, const matrix_t *m2,
matrix_t *result) {
int r, c, i, val;
assert(m1 != NULL);
assert(m2 != NULL);
assert(result != NULL);
assert(m1->cols == m2->rows);
assert(m1->rows == result->rows);
assert(m2->cols == result->cols);
for (r = 0; r < result->rows; r++) {
printf(".");
fflush(stdout);
for (c = 0; c < result->cols; c++) {
val = 0;
for (i = 0; i < m1->cols; i++)
val += get_elem(m1, r, i) * get_elem(m2, i, c);
set_elem(result, r, c, val);
}
}
printf("\n");
}
/* Given two matrices of the same dimensions, copies the elements from the
* source matrix into the destination matrix.
*/
void copy_matrix(const matrix_t *src, matrix_t *dst) {
int i;
assert(src != NULL);
assert(dst != NULL);
assert(src->rows == dst->rows);
assert(src->cols == dst->cols);
for (i = 0; i < src->rows * src->cols; i++)
dst->elems[i] = src->elems[i];
}
/* Compare two matrices for equality; returns nonzero if the matrices have the
* same values, or zero if the matrices are different.
*/
int compare_matrices(const matrix_t *m1, const matrix_t *m2) {
int i;
assert(m1 != NULL);
assert(m2 != NULL);
if (m1->rows != m2->rows || m1->cols != m2->cols)
return 0;
for (i = 0; i < m1->rows * m1->cols; i++) {
if (m1->elems[i] != m2->elems[i])
return 0;
}
return 1;
}
/*============================================================================
* Declarations of a simple 2D matrix type, along with a few operations that
* can be performed on matrices. Matrices can be allocated either from the
* virtual memory pool using the simple allocator declared in vmalloc.h, or
* using malloc().
*/
#ifndef MATRIX_H
#define MATRIX_H
/* A simple 2D matrix type. */
typedef struct matrix_t {
int rows;
int cols;
int elems[];
} matrix_t;
matrix_t * malloc_matrix(int rows, int cols);
matrix_t * vmalloc_matrix(int rows, int cols);
void generate_matrix_values(matrix_t *m);
int get_elem(const matrix_t *m, int r, int c);
void set_elem(matrix_t *m, int r, int c, int value);
void multiply_matrices(const matrix_t *m1, const matrix_t *m2,
matrix_t *result);
void copy_matrix(const matrix_t *src, matrix_t *dst);
int compare_matrices(const matrix_t *m1, const matrix_t *m2);
#endif /* MATRIX_H */
Answers to HW8 Questions
========================
a) How to detect when a page is accessed?
b) How to detect when a page becomes dirty?
f) Page-load rate of "test_matrix -m 1024 1000" using RANDOM policy:
g) Page Replacement Policy #1 (fill in the information below)
Name of policy you chose to implement:
Below, give the command-line for invoking the matrix-test program for a
1000x1000 matrix, with a maximum of 1024 resident pages. (If multiple
steps are required, list them all here.)
>>>
Give the resulting page-load rate of the above command:
If you have any additional information to share about your policy, please
share it here. Please keep your comments brief; you don't need to repeat
what the assignment says about the policy here.
h) Page Replacement Policy #2 (if you implemented two policies)
Name of policy you chose to implement:
Below, give the command-line for invoking the matrix-test program for a
1000x1000 matrix, with a maximum of 1024 resident pages. (If multiple
steps are required, list them all here.)
>>>
Give the resulting page-load rate of the above command:
If you have any additional information to share about your policy, please
share it here. Please keep your comments brief; you don't need to repeat
what the assignment says about the policy here.
/*============================================================================
* A test program for exercising the virtual memory system by generating a
* couple of matrices and then performing matrix multiplication into a third
* matrix.
*/
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include "virtualmem.h"
#include "vmalloc.h"
#include "matrix.h"
#define DEFAULT_MAX_RESIDENT 64
static long seed = 0;
static unsigned int max_resident = DEFAULT_MAX_RESIDENT;
static int size;
/* Prints the test program's usage, and then exit the program. */
void usage(const char *prog) {
printf("usage: %s [--seed num] [--max_resident num] size\n", prog);
printf("\tRuns the test program, generating two square matrices with size\n");
printf("\trows and columns, and then multiplying them into a third matrix.\n\n");
printf("\t--seed | -s num optionally specifies the seed for the random\n");
printf("\tnumber generator. The system time is used otherwise.\n\n");
printf("\t--max_resident | -m num specifies the maximum number of pages\n");
printf("\tthat may be resident in the virtual memory system.\n");
exit(1);
}
/* Parse the command-line arguments passed to the program. */
void parse_args(int argc, char **argv) {
int c;
while (1) {
static struct option long_options[] = {
/* These options set a flag.
{"verbose", no_argument, &verbose_flag, 1},
*/
/* These options don’t set a flag.
* We distinguish them by their indices. */
{"seed", required_argument, 0, 's'},
{"max_resident", required_argument, 0, 'm'},
{0, 0, 0, 0}
};
/* getopt_long stores the option index here. */
int option_index = 0;
c = getopt_long(argc, argv, "s:m:", long_options, &option_index);
/* Detect the end of the options. */
if (c == -1)
break;
switch (c) {
case 0:
/* If this option set a flag, do nothing else now. */
if (long_options[option_index].flag != 0)
break;
printf("option %s", long_options[option_index].name);
if (optarg)
printf(" with arg %s", optarg);
printf("\n");
break;
case 's':
seed = atol(optarg);
printf("Setting seed to %ld\n", seed);
break;
case 'm':
max_resident = atoi(optarg);
printf("Max resident pages = %d\n", max_resident);
break;
case '?':
/* getopt_long already printed an error message. */
usage(argv[0]);
/* usage() will exit the program. */
break;
default:
abort();
}
}
if (optind + 1 != argc)
usage(argv[0]);
size = atoi(argv[optind]);
}
int main(int argc, char **argv) {
matrix_t *m1, *m2, *result; /* Allocated from virtual memory pool */
matrix_t *m1v, *m2v, *resultv; /* Allocated with malloc(), to verify */
/* Parse arguments */
parse_args(argc, argv);
/* Configure the test. */
if (seed == 0)
seed = time(NULL);
printf("Options:\n");
printf(" * Random seed = %ld\n", seed);
printf(" * Max resident pages = %u\n", max_resident);
printf(" * Using %d x %d matrices\n", size, size);
printf("\n");
srand(seed);
/* Initialize the virtual memory system. */
vmem_init(max_resident);
vmem_alloc_init();
/* Perform the test. */
printf("Generating two matrices\n\n");
/* Allocate matrices from the two memory sources. Generate values into
* the malloc()'d matrix since we can trust it. Then copy the values into
* the vmalloc()'d matrix.
*/
m1v = malloc_matrix(size, size);
m1 = vmalloc_matrix(size, size);
generate_matrix_values(m1v);
copy_matrix(m1v, m1);
m2v = malloc_matrix(size, size);
m2 = vmalloc_matrix(size, size);
generate_matrix_values(m2v);
copy_matrix(m2v, m2);
resultv = malloc_matrix(size, size);
result = vmalloc_matrix(size, size);
printf("Multiplying the matrices together\n");
printf(" * Printing one dot per row in result matrix.\n\n");
/* Multiply the vmalloc()'d matrices and the malloc()'d matrices
* separately, so that we can compare the results.
*/
multiply_matrices(m1, m2, result);
multiply_matrices(m1v, m2v, resultv);
printf("Verifying source and result matrix contents\n");
if (compare_matrices(m1, m1v))
printf(" * Matrix m1 is correct\n");
else
printf(" * ERROR: Matrix m1 doesn't contain correct values!\n");
if (compare_matrices(m2, m2v))
printf(" * Matrix m2 is correct\n");
else
printf(" * ERROR: Matrix m2 doesn't contain correct values!\n");
if (compare_matrices(result, resultv))
printf(" * Result matrix is correct\n");
else
printf(" * ERROR: Result matrix doesn't contain correct values!\n");
printf("\nDone!\n\n");
printf("Total page loads: %u\n", get_num_loads());
return 0;
}
This diff is collapsed.
/*============================================================================
* This file contains declarations for the simple user-space virtual memory
* system.
*/
#ifndef VIRTUALMEM_H
#define VIRTUALMEM_H
#include <stdint.h>
/* When debugging virtual memory, setting this to 1 will result in a lot of
* details being output to standard error.
*/
#define VERBOSE 1
/* Type for representing a page number. Since we have a limit of 4K
* pages, we can use a 16-bit unsigned integer for this value.
*/
typedef uint16_t page_t;
/* Type for representing an entry in the page table. On IA32, Page Table
* Entries are 32 bits each, but in our simple virtual memory system, they
* are only 8 bits because there is no address translation; we only have to
* store permissions.
*/
typedef unsigned char pte_t;
/* An individual page is 4KiB. This is a hardware constraint, so we can't
* change this value.
*/
#define PAGE_SIZE 4096
/* The total number of pages that we will have in our virtual address space.
* If virtual-memory allocations exceed NUM_PAGES * PAGE_SIZE, the program
* will crash.
*/
#define NUM_PAGES 4096
/*===========================================================================
* Page table entry values and helper functions
*/
#define PAGE_RESIDENT 0x01 /* Is the page resident in memory? */
#define PAGE_ACCESSED 0x02 /* Has the page been accessed? */
#define PAGE_DIRTY 0x04 /* Has the page been modified? */
#define PAGEPERM_MASK 0xF0 /* A mask for extracting the permission value. */
#define PAGEPERM_NONE 0x10 /* No access is permitted. */
#define PAGEPERM_READ 0x20 /* Read-only access is permitted. */
#define PAGEPERM_RDWR 0x40 /* Read/write access is permitted. */
/* Functions for manipulating entries in the page table. */
void clear_page_entry(page_t page);
void set_page_resident(page_t page);
int is_page_resident(page_t page);
void set_page_accessed(page_t page);
void clear_page_accessed(page_t page);
int is_page_accessed(page_t page);
void set_page_dirty(page_t page);
void clear_page_dirty(page_t page);
int is_page_dirty(page_t page);
int get_page_permission(page_t page);
void set_page_permission(page_t page, int perm);
/* This function translates permission values from page-table entries into the
* corresponding permissions for mmap() and mprotect() to use.
*/
int pageperm_to_mmap(int perm);
/*============================================================================
* Functions for the virtual memory system
*/
/* Start the virtual memory manager. This returns the base virtual address. */
void * vmem_init(unsigned int max_resident);
/* Functions to determine the start and end of the virtual memory area, and
* to map between addresses and pages.
*/
void * get_vmem_start();
void * get_vmem_end();
void * page_to_addr(page_t page);
page_t addr_to_page(void *addr);
/* Return statistics about the virtual memory system. */
unsigned int get_num_faults();
unsigned int get_num_loads();
#endif /* VIRTUALMEM_H */
/*============================================================================
* Implementation of the simple allocator that sits on top of the virtual
* memory system.
*/
#include <stdio.h>
#include "virtualmem.h"
#include "vmalloc.h"
/* Just like the "unacceptable allocator" in HW3, we start our pointer at
* the start of virtual memory, and just move it forward by the requested
* allocation size, as long as the request will fit in the virtual memory
* area. When we run out of space, we start returning NULL.
*
* Oh, and we never deallocate...
*/
static void *nextptr;
/* Initialize the simple memory allocator. */
void vmem_alloc_init() {
nextptr = get_vmem_start();
}
/* Request an allocation of the specified size. This will return NULL if
* no more memory is available.
*/
void * vmem_alloc(unsigned int size) {
void *p;
if (nextptr + size > get_vmem_end()) {
fprintf(stderr, "vmem_alloc(%u): ran out of heap space\n", size);
return NULL;
}
p = nextptr;
nextptr += size;
return p;
}
/*============================================================================
* Declarations for the simple allocator that sits on top of the virtual
* memory system.
*/
#ifndef VMALLOC_H
#define VMALLOC_H
void vmem_alloc_init();
void * vmem_alloc(unsigned int size);
#endif /* VMALLOC_H */
/*============================================================================
* This file defines an interface that can be implemented to provide a page
* replacement policy in the virtual memory system.
*
* We don't mind if policies use malloc() and free(), just because it keeps
* things simpler.
*/
#ifndef VMPOLICY_H
#define VMPOLICY_H
#include "virtualmem.h"
/* Called by vmem_init() to initialize the page replacement policy. The
* function should return nonzero for successful initialization, 0 otherwise.
*/
int policy_init(int max_resident);
/* Called by vmem_cleanup() to release any resources used by the policy. */
void policy_cleanup(void);
/* Called by the virtual memory system to inform the policy that a page has been
* mapped into virtual memory.
*/
void policy_page_mapped(page_t page);
/* Called by the virtual memory system when a SIGALRM signal is fired, so
* that timer-based policies can update their internal state.
*/
void policy_timer_tick(void);
/* Called by map_page() when space needs to be made for another virtual page
* to be mapped, by evicting a currently mapped page. Note that this function
* both chooses a page to evict, and records in the paging policy that the page
* is now evicted. The virtual memory system will also carry through its own
* tasks for evicting the page.
*/
page_t choose_and_evict_victim_page(void);
#endif /* VMPOLICY_H */
/*============================================================================
* Implementation of the RANDOM page replacement policy.
*
* We don't mind if paging policies use malloc() and free(), just because it
* keeps things simpler. In real life, the pager would use the kernel memory
* allocator to manage data structures, and it would endeavor to allocate and
* release as little memory as possible to avoid making the kernel slow and
* prone to memory fragmentation issues.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "vmpolicy.h"
/*============================================================================
* "Loaded Pages" Data Structure
*
* This data structure records all pages that are currently loaded in the
* virtual memory, so that we can choose a random page to evict very easily.
*/
typedef struct loaded_pages_t {
/* The maximum number of pages that can be resident in memory at once. */
int max_resident;
/* The number of pages that are currently loaded. This can initially be
* less than max_resident.
*/
int num_loaded;
/* This is the array of pages that are actually loaded. Note that only the
* first "num_loaded" entries are actually valid.
*/
page_t pages[];
} loaded_pages_t;
/*============================================================================
* Policy Implementation
*/
/* The list of pages that are currently resident. */
static loaded_pages_t *loaded;
/* Initialize the policy. Return nonzero for success, 0 for failure. */
int policy_init(int max_resident) {
fprintf(stderr, "Using RANDOM eviction policy.\n\n");
loaded = malloc(sizeof(loaded_pages_t) + max_resident * sizeof(page_t));
if (loaded) {
loaded->max_resident = max_resident;
loaded->num_loaded = 0;
}
/* Return nonzero if initialization succeeded. */
return (loaded != NULL);
}
/* Clean up the data used by the page replacement policy. */
void policy_cleanup(void) {
free(loaded);
loaded = NULL;
}
/* This function is called when the virtual memory system maps a page into the
* virtual address space. Record that the page is now resident.
*/
void policy_page_mapped(page_t page) {
assert(loaded->num_loaded < loaded->max_resident);
loaded->pages[loaded->num_loaded] = page;
loaded->num_loaded++;
}
/* This function is called when the virtual memory system has a timer tick. */
void policy_timer_tick(void) {
/* Do nothing! */
}
/* Choose a page to evict from the collection of mapped pages. Then, record
* that it is evicted. This is very simple since we are implementing a random
* page-replacement policy.
*/
page_t choose_and_evict_victim_page(void) {
int i_victim;
page_t victim;
/* Figure out which page to evict. */
i_victim = rand() % loaded->num_loaded;
victim = loaded->pages[i_victim];
/* Shrink the collection of loaded pages now, by moving the last page in the
* collection into the spot that the victim just occupied.
*/
loaded->num_loaded--;
loaded->pages[i_victim] = loaded->pages[loaded->num_loaded];
#if VERBOSE
fprintf(stderr, "Choosing victim page %u to evict.\n", victim);
#endif
return victim;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment