OBJECTIVE: The goal of this assignment is to understand the implementation of file system caches.
DESCRIPTION: When file data blocks are to be read, the file system cache is first searched for the data. If the desired data blocks are not found in the cache, then the data blocks are read from the disk. A copy of the data blocks are stored in the cache.
All read operations are block oriented. For example, assume that the file system has a block size of 10. When a read operation is issued for bytes 25 to 63 of file1, your program should issue read requests for blocks 3 to 7 of file1. Your program should first look in your simulated cache for blocks 3,4,5, 6, and 7 of file1. If a block is not found in the cache, then your program should load this block from disk into your simulated cache.
The cache is implemented as a linked list of cache packets. Each cache packet contains one block of data and status information (file name, block number, etc.) Initially, the cache is empty and all cache packets are on a free linked list. When there is a cache miss, a cache packet is taken out of the free list, filled with the required information(data and status) and then put in the cache. The cache is searched quickly using a hash table. Hence, each non-empty cache packet will be on the hash linked list.
Eventually, all the empty cache packets will be used up and the free list will be empty. At this point, the cache is full and all subsequent cache misses, will result in replacing an used cache packet with the new data and the new status information. Deciding which packet to replace depends on the cache replacement policy. You will implement the Least Recently Used (LRU) cache replacement policy. The replacement policy is implemented by putting all cache packets on the LRU linked list. Hence, each (empty) cache packet is initially on the free linked list, and later each (non-empty) cache packet is on two linked list, namely, the hash linked list and the LRU linked list.
NOTICE: For the replacement policy, you have a choice between FIFO and LRU.
The Maximum score you can get is 75 points if you implement FIFO.
SUBMIT INSTRUCTIONS: Your assignment is due by
11:30PM March 14, 2008 Friday. Create a make file to build your executable
file.
The name of your executable must be
lru_cache
for LRU policy and
fifo_cache
for FIFO policy.
Submit all your source files (no object
or executable files) and makefile by typing
~cs620/submit 3 file1 file2 file3 ...
where 3 is the number of this assignment.
Again note that if you want to resubmit, then you need to use
3a, 3b, ... for assignment number (not 3).
You can check whether your programs have been successfully submitted by
typing
~cs620/check
DETAILS OF CACHE IMPLEMENTATION:
Header file: cache.h.
This file contains definitions of cache data structures.
#define CACHE_PKT struct cache_pkt
#define HASH_SIZE 50
#define CACHE_SIZE 200
#define FILE_NAME_SIZE 80
#define BLOCK_SIZE 512
CACHE_PKT
{
CACHE_PKT *c_hash_next;
CACHE_PKT *c_hash_prev;
CACHE_PKT *c_free_next;
CACHE_PKT *c_lru_next;
CACHE_PKT *c_lru_prev;
long block_num;
long block_size;
int block_priority;
char file_name[FILE_NAME_SIZE];
char file_data[BLOCK_SIZE];
};
CACHE_PKT *hash_table[HASH_SIZE];
CACHE_PKT *cp_free_list;
CACHE_PKT *cp_lru_head;
CACHE_PKT *cp_lru_tail;
Source file: cache.c A brief description of the routines are given below:
void init_it()- This function allocates the memory space for the cache and links all the cache packets on to the cache packet free list.
Part of the code for init_it():
// initialization function
// creates a single linked list of CACHE_PKT structures
// except for the links, all fields are zeroed
void init_it() {
int i, j;
CACHE_PKT *pCurrent, *pNew;
for (i = 0; i < CACHE_SIZE; i++) {
// allocate memory for a structure
if ((pNew = (CACHE_PKT *) malloc( sizeof(CACHE_PKT) )) == NULL) {
perror( "Error allocating memory" );
exit( 1 );
}
//** Next -- initialize the fields in cache packet.
}
CACHE_PKT *get_cache_free()- If cache free list is not empty, this function unlinks one cache packet from the free list and returns a pointer to the same. If the free list is empty, a null pointer is returned.
int get_block_for_read(char *p_file_name, long block_num, CACHE_PKT **p_p_cache_pkt) - Given a file name and the block number, this function checks to see if the block is in the cache. If the block is not in cache, this function frees (or gets a free cache packet) that is later used by the read operation to store a data block.
void put_block_in_cache(char *p_file_name, long block_num, CACHE_PKT *p_cache_pkt) - This function inserts the packet pointed p_cache_pkt in the hash table and the LRU list.
Hash Table: Use an open hash to maintain blocks in the cache. Base the hash function on the length of the filename (Use realpath()).
// hash function; the hash key is the length of the absolute path
// multiplied with the block number
int hash( char *sFileName, long lBlockNum ) {
size_t iPathLength;
long iHashKey;
iPathLength = strlen( sFileName );
iHashKey = (long)iPathLength * (lBlockNum + 1);
return( iHashKey % HASH_SIZE );
}
DETAILS OF MAIN PROGRAM IMPLEMENTATION FOR A READ-ONLY CACHE: Executable - lru_cache
Source file: xcache.c The program should
continually prompt the user for input
data which includes: file name, seek address (byte offset 0), and number
of bytes (not blocks) to be read.
The pseudo-code for the main program is:
#include < errno.h>
#include < fcntl.h>
#include < sys/stat.h>
#include < sys/types.h>
#include < stdio.h>
#include < stdlib.h>
#include < string.h>
#include < unistd.h>
#include "cache.h"
main() {
CACHE_PKT *pNew;
char sFileName[FILE_NAME_SIZE], sRealPath[FILE_NAME_SIZE];
struct stat stInfo;
long lSeekAddress, lBytes;
long lBlock, lBlockFirst, lBlockLast;
long lStart, lEnd;
int iFile;
size_t iBytes;
//** Initialize pointers to null
//** Create free list
// Prompt user for input
for ( ; ; ) {
//** Get file name
// Find absolute path
if (realpath( sFileName, sRealPath ) == NULL) {
perror( "Absolute path error" );
exit( -1 );
}
// Get status info; needed file length, etc.
if (stat( sRealPath, &stInfo ) == -1) {
perror( "Status error" );
exit( -1 );
}
//** Get seek address
//** Check seek address - if past EOF (stInfo.st_size), print error message
//** Get number of bytes to read
//** Check number of bytes; truncate to file size (stInfo.st_size)
//** Convert to block read: compute start block number and end block number
// Loop from first block to last block
for (lBlock = lBlockFirst; lBlock <= lBlockLast; lBlock++) {
//** If block not in cache
if (get_block_for_read( sRealPath, lBlock, &pNew ) == -1) {
//** Fill in the cache packet file name and block number fields
// Open file and go to the begining of the block
if ((iFile = open( sRealPath, O_RDONLY )) == -1) {
perror( "Open file error" );
exit( -1 );
}
if (lseek( iFile, (off_t)(lBlock * BLOCK_SIZE), SEEK_SET ) == -1) {
perror( "Seek error" );
exit( -1 );
}
// Read up to BLOCK_SIZE bytes and close the file
if ((iBytes = read( iFile, pNew->file_data, BLOCK_SIZE )) == -1) {
perror( "Read from file error" );
exit( -1 );
}
close( iFile );
//** Fill in the cache packet block size field
//** Put the block in cache and display message
}
else //** If block in cache display message
}
}
You can get a makefile from here.
You can get a sample output run from here.
Return to the CS 620 Home Page