From 55e4f8e463933ff290e93285e29c090636f4d5cb Mon Sep 17 00:00:00 2001 From: Jared Chu Date: Sun, 22 Sep 2024 21:02:23 -0700 Subject: [PATCH 1/6] add cat and tests --- user/cat.c | 129 +++++++++++++++++++++++++++++++++++++++++++------- user/test.c | 47 ++++++++++++++++++ user/test.txt | 13 +++++ 3 files changed, 173 insertions(+), 16 deletions(-) create mode 100644 user/test.c create mode 100644 user/test.txt diff --git a/user/cat.c b/user/cat.c index 598f005..8382b18 100644 --- a/user/cat.c +++ b/user/cat.c @@ -4,39 +4,136 @@ char buf[512]; -void -cat(int fd) -{ +//flag variables +uint8 nflag = 0; +uint8 vflag = 0; +uint8 bflag = 0; +uint8 Eflag = 0; + +/** + * @cat Display file content with various flags. + * + * This function reads the content of a file descriptor and displays it + * according to the specified flags. It supports line numbering, showing + * non-printing characters, numbering non-blank lines, and displaying + * end-of-line markers. + * + * @param fd The file descriptor to read from. + * @param line_numbering If non-zero, number all output lines. + * @param show_non_printing If non-zero, show non-printing characters in caret notation. + * @param number_non_blank If non-zero, number only non-blank lines. + * @param show_end_of_line If non-zero, display '$' at the end of each line. + */ + +void cat(int fd, int line_numbering, int show_non_printing, int number_non_blank, int show_end_of_line) { int n; + int line_number = 1; + int i; + int is_blank_line = 1; - while((n = read(fd, buf, sizeof(buf))) > 0) { - if (write(1, buf, n) != n) { - fprintf(2, "cat: write error\n"); - exit(1); + //read the file content in chunks + while ((n = read(fd, buf, sizeof(buf))) > 0) { + for (i = 0; i < n; i++) { + //check if the current character is part of a blank line + if (buf[i] != '\n' && buf[i] != '\r' && buf[i] != '\t' && buf[i] != ' ') { + is_blank_line = 0; + } + + //add line numbers if the -n or -b flag is set + if ((line_numbering || (number_non_blank && !is_blank_line)) && (i == 0 || buf[i-1] == '\n')) { + printf("%6d ", line_number++); + } + + //convert non-printing characters to a visible format if the -v flag is set + //tested externally with test.c amd ran with OS as test (user program) + if (show_non_printing && (buf[i] < 32 || buf[i] == 127)) { + if (buf[i] == 127) { + printf("^?"); + } else { + printf("^%c", buf[i] + 64); + } + } else { + //write the character to the output + if (write(1, &buf[i], 1) != 1) { + fprintf(2, "cat: write error\n"); + exit(1); + } + } + + //display $ at the end of each line if the -E flag is set + if (show_end_of_line && buf[i] == '\n') { + if (!number_non_blank || !is_blank_line) { + printf("$"); + } + } + + //reset blank line flag at the end of each line + if (buf[i] == '\n') { + is_blank_line = 1; + } } } - if(n < 0){ + + //handle read errors (unchanged) + if (n < 0) { fprintf(2, "cat: read error\n"); exit(1); } } -int -main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { int fd, i; + int line_numbering = 0; + int show_non_printing = 0; + int number_non_blank = 0; + int show_end_of_line = 0; + + //parse command-line arguments for flags + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + for (int j = 1; argv[i][j] != '\0'; j++) { //allows handling multiple flags in a single argument + switch (argv[i][j]) { + case 'n': + nflag = 1; + break; + case 'v': + vflag = 1; + break; + case 'b': + bflag = 1; + break; + case 'E': + Eflag = 1; + break; + default: + fprintf(2, "Unrecognized flag: -%c\n", argv[i][j]); + exit(1); + } + } + } else { + break; + } + } + + //set the flag variables + line_numbering = nflag; + show_non_printing = vflag; + number_non_blank = bflag; + show_end_of_line = Eflag; - if(argc <= 1){ - cat(0); + //if no files are specified, read from standard input + if (i == argc) { + cat(0, line_numbering, show_non_printing, number_non_blank, show_end_of_line); exit(0); } - for(i = 1; i < argc; i++){ - if((fd = open(argv[i], 0)) < 0){ + //process each file specified in the command-line arguments (unchanged, except for, for and cat args) + for (; i < argc; i++) { + if ((fd = open(argv[i], 0)) < 0) { fprintf(2, "cat: cannot open %s\n", argv[i]); exit(1); } - cat(fd); + cat(fd, line_numbering, show_non_printing, number_non_blank, show_end_of_line); close(fd); } exit(0); diff --git a/user/test.c b/user/test.c new file mode 100644 index 0000000..fa3b40f --- /dev/null +++ b/user/test.c @@ -0,0 +1,47 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + + +//function to convert non-printing characters to caret notation +//same function as the -v flag, to test +void print_with_caret_notation(const char *str) { + for (int i = 0; i < strlen(str); i++) { + if (str[i] < 32 || str[i] == 127) { + if (str[i] == 127) { + printf("^?"); + } else { + printf("^%c", str[i] + 64); + } + } else { + printf("%c", str[i]); + } + } + printf("\n"); +} + +int main() { + //test cases with non-printing characters + const char *test1 = "Hello\001World"; // SOH (ASCII 1) + const char *test2 = "Tab\tCharacter"; // TAB (ASCII 9) + const char *test3 = "New\nLine"; // LF (ASCII 10) + const char *test4 = "Carriage\rReturn"; // CR (ASCII 13) + const char *test5 = "Delete\177Char"; // DEL (ASCII 127) + + printf("Test 1: "); + print_with_caret_notation(test1); + + printf("Test 2: "); + print_with_caret_notation(test2); + + printf("Test 3: "); + print_with_caret_notation(test3); + + printf("Test 4: "); + print_with_caret_notation(test4); + + printf("Test 5: "); + print_with_caret_notation(test5); + + return 0; +} diff --git a/user/test.txt b/user/test.txt new file mode 100644 index 0000000..4ebbef5 --- /dev/null +++ b/user/test.txt @@ -0,0 +1,13 @@ +Hello, World! +This is a test file. + +It contains some non-printing characters. +Here is a tab: <- tab character +Here is a newline: + +Here is a carriage return: +<- carriage return character +Here is a bell character:<- bell character +Here is a delete character:<- delete character +Here is a form feed character:<- form feed character +Here is a vertical tab character:<- vertical tab character From 8fedd98b3d1fd22146e6542af1930a501b7e5249 Mon Sep 17 00:00:00 2001 From: Jared Chu Date: Sun, 22 Sep 2024 21:02:51 -0700 Subject: [PATCH 2/6] update Makefile for test --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 2584e4a..08849c6 100644 --- a/Makefile +++ b/Makefile @@ -116,6 +116,7 @@ mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h .PRECIOUS: %.o UPROGS=\ + $U/_test\ $U/_cat\ $U/_echo\ $U/_forktest\ From a913d82e2f9ea89832267bc0ee8d5ed2e154dea9 Mon Sep 17 00:00:00 2001 From: jcchu0 <123777900+jcchu0@users.noreply.github.com> Date: Mon, 9 Dec 2024 01:26:19 -0800 Subject: [PATCH 3/6] Update umalloc.c --- user/umalloc.c | 196 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 132 insertions(+), 64 deletions(-) diff --git a/user/umalloc.c b/user/umalloc.c index 2092a32..f7c1790 100644 --- a/user/umalloc.c +++ b/user/umalloc.c @@ -1,90 +1,158 @@ +//TO start P3: +// - make reuse function +// - calls FSM function (deafult it to first fit) +// - implement malloc_print +//use memtest to test your code + #include "kernel/types.h" #include "kernel/stat.h" #include "user/user.h" #include "kernel/param.h" -// Memory allocator by Kernighan and Ritchie, -// The C programming Language, 2nd ed. Section 8.7. +/* If we haven't passed -DDEBUG=1 to gcc, then this will be set to 0: */ +#ifndef DEBUG +#define DEBUG 1 +#endif + +#define LOGP(str) \ + do { if (DEBUG) fprintf(2, "%s:%d:%s(): %s", __FILE__, \ + __LINE__, __func__, str); } while (0) -typedef long Align; +#define LOG(fmt, ...) \ + do { if (DEBUG) fprintf(2, "%s:%d:%s(): " fmt, __FILE__, \ + __LINE__, __func__, __VA_ARGS__); } while (0) -union header { - struct { - union header *ptr; - uint size; - } s; - Align x; +struct __attribute__((__packed__)) mem_block { + uint size; + struct mem_block *next; + int free; }; -typedef union header Header; +struct __attribute__((__packed__)) free_block { + struct mem_block *block_header; + struct free_block *next_free; + struct free_block *prev_free; +}; + +//Bitwise AND? +// 111111111100 +// 101110100101 +void set_used(struct mem_block *block); +{ + block->size = block->size & (~0x01); +} + +void set_free(struct mem_block *block); +{ + block->size = block->size | 0x01; +} + +bool ist_free(struct mem_block *block); +{ + return block->size & 0x01; +} -static Header base; -static Header *freep; +//useful for implementing print_mem +uint get_size(struct mem_block *block); +{ + return block->size & (~0x01); +} + +struct mem_block *head; void free(void *ap) { - Header *bp, *p; - - bp = (Header*)ap - 1; - for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) - if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) - break; - if(bp + bp->s.size == p->s.ptr){ - bp->s.size += p->s.ptr->s.size; - bp->s.ptr = p->s.ptr->s.ptr; - } else - bp->s.ptr = p->s.ptr; - if(p + p->s.size == bp){ - p->s.size += bp->s.size; - p->s.ptr = bp->s.ptr; - } else - p->s.ptr = bp; - freep = p; + struct mem_block *block = ((struct mem_block *) ap) - 1; + LOG("Free request: %p; size: %d\n", ap, block->size); + + block->free = 1; + if (block == head) { + head = head->next; + //we can un-sbrk this memory + uint total_sz = block->size + sizeof(struct mem_block); + char *ret = sbrk(-total_sz); + //... + LOG("De-allocating %d bytes! (new brk: %p)\n", total_sz, ret); + } } -static Header* -morecore(uint nu) +struct mem_block *split(struct mem_block *block, uint size) { - char *p; - Header *hp; - - if(nu < 4096) - nu = 4096; - p = sbrk(nu * sizeof(Header)); - if(p == (char*)-1) - return 0; - hp = (Header*)p; - hp->s.size = nu; - free((void*)(hp + 1)); - return freep; + //Things we need to check to determine if we can split: + // - the block is not null + // - the block is actually free + // - the split size is greater than our minimum size + // - the block has enought free space + // (subtract out whatever the requested new size is and make sure we still have enough) + // + // HINT: user char * to do pointer arithmetic + return NULL; } void* malloc(uint nbytes) { - Header *p, *prevp; - uint nunits; + //TODO: make sure we also align the allocations here! (16 bytes) + // get this right... + uint total_sz = nbytes + sizeof(struct mem_block); + LOG("Allocaton request: %d; total size: %d\n", nbytes, total_sz); + + //TODO: try to reuse an existing block! + // (blocks set to free) + // reuse(size) -> a block, or NULL if we couldn't find one to reuse + // FSM algorithms + // if we can reuse, we SPLIT IT first (if possible), return it, and stop here - nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; - if((prevp = freep) == 0){ - base.s.ptr = freep = prevp = &base; - base.s.size = 0; + //TODO: if we couldn't reuse a block, proceed! + // + // If we need to sbrk more data, we do it on multiples of the page size (4096) + //... this means we have to *split* blocks! + //Splitting: + // - use the first part of the sbrk'd data for the block the user asked for + // - use the remainder for a new free block that can be reused later + // + //one char = 1 byte, so sbrk returns a char * + //this is actually the only guarentee in the C spec about data type sizes + struct mem_block *header = (struct mem_block *) sbrk(total_sz); + + header->size = nbytes; + header->free = 0; + +//this should be a FUNCTION (and all other linked list stuff) + if (head == NULL) { + head = block; + } else { + block->next = head; + head = block; } - for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ - if(p->s.size >= nunits){ - if(p->s.size == nunits) - prevp->s.ptr = p->s.ptr; - else { - p->s.size -= nunits; - p += p->s.size; - p->s.size = nunits; - } - freep = prevp; - return (void*)(p + 1); - } - if(p == freep) - if((p = morecore(nunits)) == 0) - return 0; + + LOG("Allocation successful @ %p\n", block); + return block + 1; +} + + + + + + + + + +//this function is perfect and never needs to change +void * +calloc(uint nmemb, uint size) +{ + void *memory = malloc(nmemb * size); + if (memory == NULL){ + return NULL; } + memset(memory, 0, nmemb * size); + return memory; +} + +void * +realloc(void *ptr, uint size) +{ + return NULL; } From c4d122dd74a6dc42f9af409931df1b2482c59e04 Mon Sep 17 00:00:00 2001 From: jcchu0 <123777900+jcchu0@users.noreply.github.com> Date: Mon, 9 Dec 2024 01:27:32 -0800 Subject: [PATCH 4/6] Create mt80.c --- user/mt80.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 user/mt80.c diff --git a/user/mt80.c b/user/mt80.c new file mode 100644 index 0000000..4776483 --- /dev/null +++ b/user/mt80.c @@ -0,0 +1,49 @@ +#include "kernel/types.h" +#include "user/user.h" + +#pragma GCC diagnostic ignored "-Wunused-variable" + +int main(void) +{ +// malloc_setfsm(FSM_FIRST_FIT); + + char *a = malloc(132); + char *b = malloc(42); /* Will be deleted */ + char *c = malloc(132); + char *d = malloc(132); /* Will be deleted */ + char *e = malloc(132); + char *f = malloc(3132); + + /* These allocations total 4096 bytes after alignment is included. */ + + malloc_name(a, "A"); + malloc_name(b, "B"); + malloc_name(c, "C"); + malloc_name(d, "D"); + malloc_name(e, "E"); + malloc_name(f, "F"); + + free(b); + free(d); + + /** + * The following allocation will choose: + * - First fit: free space at the end of the page (unnamed, no variable) + * - Best fit: B + * - Worst fit: D + */ + char *g = malloc(42); + + malloc_name(g, "G"); + + malloc_print(); + + /** + * Things to check: + * - correct alignment (evenly divisible by 16) + * - free block sizes are correct (make sure not off by one) + * - end of last block's memory address - first address = 4096 + */ + + return 0; +} From 239647f6f56fe92b7a5b5098d038db9bc52c96ba Mon Sep 17 00:00:00 2001 From: jcchu0 <123777900+jcchu0@users.noreply.github.com> Date: Mon, 9 Dec 2024 01:31:59 -0800 Subject: [PATCH 5/6] Delete user/mt80.c --- user/mt80.c | 49 ------------------------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 user/mt80.c diff --git a/user/mt80.c b/user/mt80.c deleted file mode 100644 index 4776483..0000000 --- a/user/mt80.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "kernel/types.h" -#include "user/user.h" - -#pragma GCC diagnostic ignored "-Wunused-variable" - -int main(void) -{ -// malloc_setfsm(FSM_FIRST_FIT); - - char *a = malloc(132); - char *b = malloc(42); /* Will be deleted */ - char *c = malloc(132); - char *d = malloc(132); /* Will be deleted */ - char *e = malloc(132); - char *f = malloc(3132); - - /* These allocations total 4096 bytes after alignment is included. */ - - malloc_name(a, "A"); - malloc_name(b, "B"); - malloc_name(c, "C"); - malloc_name(d, "D"); - malloc_name(e, "E"); - malloc_name(f, "F"); - - free(b); - free(d); - - /** - * The following allocation will choose: - * - First fit: free space at the end of the page (unnamed, no variable) - * - Best fit: B - * - Worst fit: D - */ - char *g = malloc(42); - - malloc_name(g, "G"); - - malloc_print(); - - /** - * Things to check: - * - correct alignment (evenly divisible by 16) - * - free block sizes are correct (make sure not off by one) - * - end of last block's memory address - first address = 4096 - */ - - return 0; -} From 936343e98fae9879ee55b8490388237b6ef3cfb4 Mon Sep 17 00:00:00 2001 From: jcchu0 <123777900+jcchu0@users.noreply.github.com> Date: Mon, 9 Dec 2024 01:32:22 -0800 Subject: [PATCH 6/6] Update umalloc.c --- user/umalloc.c | 196 ++++++++++++++++--------------------------------- 1 file changed, 64 insertions(+), 132 deletions(-) diff --git a/user/umalloc.c b/user/umalloc.c index f7c1790..2092a32 100644 --- a/user/umalloc.c +++ b/user/umalloc.c @@ -1,158 +1,90 @@ -//TO start P3: -// - make reuse function -// - calls FSM function (deafult it to first fit) -// - implement malloc_print -//use memtest to test your code - #include "kernel/types.h" #include "kernel/stat.h" #include "user/user.h" #include "kernel/param.h" -/* If we haven't passed -DDEBUG=1 to gcc, then this will be set to 0: */ -#ifndef DEBUG -#define DEBUG 1 -#endif - -#define LOGP(str) \ - do { if (DEBUG) fprintf(2, "%s:%d:%s(): %s", __FILE__, \ - __LINE__, __func__, str); } while (0) +// Memory allocator by Kernighan and Ritchie, +// The C programming Language, 2nd ed. Section 8.7. -#define LOG(fmt, ...) \ - do { if (DEBUG) fprintf(2, "%s:%d:%s(): " fmt, __FILE__, \ - __LINE__, __func__, __VA_ARGS__); } while (0) +typedef long Align; -struct __attribute__((__packed__)) mem_block { - uint size; - struct mem_block *next; - int free; +union header { + struct { + union header *ptr; + uint size; + } s; + Align x; }; -struct __attribute__((__packed__)) free_block { - struct mem_block *block_header; - struct free_block *next_free; - struct free_block *prev_free; -}; - -//Bitwise AND? -// 111111111100 -// 101110100101 -void set_used(struct mem_block *block); -{ - block->size = block->size & (~0x01); -} - -void set_free(struct mem_block *block); -{ - block->size = block->size | 0x01; -} - -bool ist_free(struct mem_block *block); -{ - return block->size & 0x01; -} +typedef union header Header; -//useful for implementing print_mem -uint get_size(struct mem_block *block); -{ - return block->size & (~0x01); -} - -struct mem_block *head; +static Header base; +static Header *freep; void free(void *ap) { - struct mem_block *block = ((struct mem_block *) ap) - 1; - LOG("Free request: %p; size: %d\n", ap, block->size); - - block->free = 1; - if (block == head) { - head = head->next; - //we can un-sbrk this memory - uint total_sz = block->size + sizeof(struct mem_block); - char *ret = sbrk(-total_sz); - //... - LOG("De-allocating %d bytes! (new brk: %p)\n", total_sz, ret); - } + Header *bp, *p; + + bp = (Header*)ap - 1; + for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) + if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) + break; + if(bp + bp->s.size == p->s.ptr){ + bp->s.size += p->s.ptr->s.size; + bp->s.ptr = p->s.ptr->s.ptr; + } else + bp->s.ptr = p->s.ptr; + if(p + p->s.size == bp){ + p->s.size += bp->s.size; + p->s.ptr = bp->s.ptr; + } else + p->s.ptr = bp; + freep = p; } -struct mem_block *split(struct mem_block *block, uint size) +static Header* +morecore(uint nu) { - //Things we need to check to determine if we can split: - // - the block is not null - // - the block is actually free - // - the split size is greater than our minimum size - // - the block has enought free space - // (subtract out whatever the requested new size is and make sure we still have enough) - // - // HINT: user char * to do pointer arithmetic - return NULL; + char *p; + Header *hp; + + if(nu < 4096) + nu = 4096; + p = sbrk(nu * sizeof(Header)); + if(p == (char*)-1) + return 0; + hp = (Header*)p; + hp->s.size = nu; + free((void*)(hp + 1)); + return freep; } void* malloc(uint nbytes) { - //TODO: make sure we also align the allocations here! (16 bytes) - // get this right... - uint total_sz = nbytes + sizeof(struct mem_block); - LOG("Allocaton request: %d; total size: %d\n", nbytes, total_sz); - - //TODO: try to reuse an existing block! - // (blocks set to free) - // reuse(size) -> a block, or NULL if we couldn't find one to reuse - // FSM algorithms - // if we can reuse, we SPLIT IT first (if possible), return it, and stop here + Header *p, *prevp; + uint nunits; - //TODO: if we couldn't reuse a block, proceed! - // - // If we need to sbrk more data, we do it on multiples of the page size (4096) - //... this means we have to *split* blocks! - //Splitting: - // - use the first part of the sbrk'd data for the block the user asked for - // - use the remainder for a new free block that can be reused later - // - //one char = 1 byte, so sbrk returns a char * - //this is actually the only guarentee in the C spec about data type sizes - struct mem_block *header = (struct mem_block *) sbrk(total_sz); - - header->size = nbytes; - header->free = 0; - -//this should be a FUNCTION (and all other linked list stuff) - if (head == NULL) { - head = block; - } else { - block->next = head; - head = block; + nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; + if((prevp = freep) == 0){ + base.s.ptr = freep = prevp = &base; + base.s.size = 0; } - - LOG("Allocation successful @ %p\n", block); - return block + 1; -} - - - - - - - - - -//this function is perfect and never needs to change -void * -calloc(uint nmemb, uint size) -{ - void *memory = malloc(nmemb * size); - if (memory == NULL){ - return NULL; + for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ + if(p->s.size >= nunits){ + if(p->s.size == nunits) + prevp->s.ptr = p->s.ptr; + else { + p->s.size -= nunits; + p += p->s.size; + p->s.size = nunits; + } + freep = prevp; + return (void*)(p + 1); + } + if(p == freep) + if((p = morecore(nunits)) == 0) + return 0; } - memset(memory, 0, nmemb * size); - return memory; -} - -void * -realloc(void *ptr, uint size) -{ - return NULL; }