From 1c273f96f86ea0d9058db57a5fa16eb2f3034711 Mon Sep 17 00:00:00 2001 From: Kailash Turimella Date: Mon, 6 Oct 2025 14:46:50 -0700 Subject: [PATCH 1/5] completed --- Makefile | 2 + docs/compress.md | 50 ++++++++++++ mkfs/mkfs.c | 103 +++++++++++++++++++++++-- user/compress.c | 178 +++++++++++++++++++++++++++++++++++++++++++ user/compress_test.c | 130 +++++++++++++++++++++++++++++++ 5 files changed, 455 insertions(+), 8 deletions(-) create mode 100644 docs/compress.md create mode 100644 user/compress.c create mode 100644 user/compress_test.c diff --git a/Makefile b/Makefile index cf031b0..477d25f 100644 --- a/Makefile +++ b/Makefile @@ -124,6 +124,8 @@ mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h UPROGS=\ $U/_cat\ + $U/_compress\ + $U/_compress_test\ $U/_echo\ $U/_forktest\ $U/_grep\ diff --git a/docs/compress.md b/docs/compress.md new file mode 100644 index 0000000..c2cae4d --- /dev/null +++ b/docs/compress.md @@ -0,0 +1,50 @@ +# compress — Run-Length Encode / Decode files + +## Usage + +`compress` provides simple Run-Length Encoding (RLE) for any file. It can read from **stdin** and write to **stdout** if file paths are omitted. + +* `-c` : **Compress** input → RLE output +* `-d` : **Decompress** RLE input → original bytes +* `-h` : Show help/usage + +> RLE format: a stream of 2-byte pairs `[count][value]` with `count` in 1–255. Long runs are split. Random data may grow in size. + +### Examples + +```sh +# File → file +$ compress -c README.md README.rle +$ compress -d README.rle README.out + +# Stream mode (stdin/stdout + pipes/redirection) +$ cat README.md | compress -c > foo.rle +$ cat foo.rle | compress -d > out.txt +``` + +--- + +## Testing + +An automated test runner `/compress_test` is included. It creates sample files, runs compress/decompress, and checks round-trip equality. + +### To run tests + +1. Boot the OS and open the shell. +2. Run: + + ``` + /compress_test + ``` +3. You should see `PASS`/`FAIL` lines and a summary, e.g.: + + ``` + [PASS] small text + [PASS] empty file + [PASS] long run 1000x'A' + [PASS] binary 0x00..0xFF + + All tests PASSED + ``` + +If a test fails, the message indicates which stage (compress/decompress/compare) had an issue. diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index 5f080bc..5e3873e 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -4,12 +4,18 @@ #include #include #include +#include +#include +#include +#define dirent xv6_dirent #define stat xv6_stat // avoid clash with host struct stat #include "kernel/types.h" #include "kernel/fs.h" #include "kernel/stat.h" #include "kernel/param.h" +#undef dirent +#undef stat #ifndef static_assert #define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0) @@ -20,9 +26,9 @@ // Disk layout: // [ boot block | sb block | log | inode blocks | free bit map | data blocks ] -int nbitmap = FSSIZE/BPB + 1; +int nbitmap = FSSIZE/(BSIZE*8) + 1; int ninodeblocks = NINODES / IPB + 1; -int nlog = LOGBLOCKS+1; // Header followed by LOGBLOCKS data blocks. +int nlog = LOGBLOCKS; int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap) int nblocks; // Number of data blocks @@ -65,12 +71,88 @@ xint(uint x) return y; } +void +add_file(int parent_fd, uint parentino, char *filename) +{ + uint inum; + int fd; + char buf[BSIZE]; + struct xv6_dirent de; + + if((fd = openat(parent_fd, filename, 0)) < 0) + die(filename); + + inum = ialloc(T_FILE); + + bzero(&de, sizeof(de)); + de.inum = xshort(inum); + strncpy(de.name, filename, DIRSIZ); + iappend(parentino, &de, sizeof(de)); + + ssize_t cc; + while((cc = read(fd, buf, sizeof(buf))) > 0) + iappend(inum, buf, cc); + + close(fd); +} + +uint +add_dir(int level, int parent_fd, uint parentino, char *dirname) +{ + struct xv6_dirent de; + uint dino = ialloc(T_DIR); + bzero(&de, sizeof(de)); + de.inum = xshort(dino); + strcpy(de.name, dirname); + iappend(parentino, &de, sizeof(de)); + + bzero(&de, sizeof(de)); + de.inum = xshort(dino); + strcpy(de.name, "."); + iappend(dino, &de, sizeof(de)); + + bzero(&de, sizeof(de)); + de.inum = xshort(parentino); + strcpy(de.name, ".."); + iappend(dino, &de, sizeof(de)); + + int dir_fd = -1; + if ((dir_fd = openat(parent_fd, dirname, O_RDONLY)) == -1) { + perror("open"); + return dino; + } + + DIR *d = fdopendir(dir_fd); + if (d == NULL) { + perror("fdopendir"); + return dino; + } + + struct dirent *e; + while ((e = readdir(d)) != NULL) { + if (e->d_name[0] == '.') { + continue; + } + + if (e->d_type == DT_REG) { + printf("%*s+ %s\n", level * 2, "", e->d_name); + add_file(dir_fd, dino, e->d_name); + } else if (e->d_type == DT_DIR) { + printf("%*s+ /%s\n", level * 2, "", e->d_name); + add_dir(level + 1, dir_fd, dino, e->d_name); + } + } + close(dir_fd); + + return dino; +} + int main(int argc, char *argv[]) { int i, cc, fd; uint rootino, inum, off; - struct dirent de; + struct xv6_dirent de; char buf[BSIZE]; struct dinode din; @@ -83,7 +165,7 @@ main(int argc, char *argv[]) } assert((BSIZE % sizeof(struct dinode)) == 0); - assert((BSIZE % sizeof(struct dirent)) == 0); + assert((BSIZE % sizeof(struct xv6_dirent)) == 0); fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); if(fsfd < 0) @@ -102,7 +184,7 @@ main(int argc, char *argv[]) sb.inodestart = xint(2+nlog); sb.bmapstart = xint(2+nlog+ninodeblocks); - printf("nmeta %d (boot, super, log blocks %u, inode blocks %u, bitmap blocks %u) blocks %d total %d\n", + printf("nmeta %d (boot, super, log blocks %u inode blocks %u, bitmap blocks %u) blocks %d total %d\n", nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE); freeblock = nmeta; // the first free block that we can allocate @@ -128,6 +210,13 @@ main(int argc, char *argv[]) iappend(rootino, &de, sizeof(de)); for(i = 2; i < argc; i++){ + struct stat sb; + stat(argv[i], &sb); + if (S_ISDIR(sb.st_mode)) { + add_dir(0, AT_FDCWD, rootino, argv[i]); + continue; + } + // get rid of "user/" char *shortname; if(strncmp(argv[i], "user/", 5) == 0) @@ -147,8 +236,6 @@ main(int argc, char *argv[]) if(shortname[0] == '_') shortname += 1; - assert(strlen(shortname) <= DIRSIZ); - inum = ialloc(T_FILE); bzero(&de, sizeof(de)); @@ -240,7 +327,7 @@ balloc(int used) int i; printf("balloc: first %d blocks have been allocated\n", used); - assert(used < BPB); + assert(used < BSIZE*8); bzero(buf, BSIZE); for(i = 0; i < used; i++){ buf[i/8] = buf[i/8] | (0x1 << (i%8)); diff --git a/user/compress.c b/user/compress.c new file mode 100644 index 0000000..ee64323 --- /dev/null +++ b/user/compress.c @@ -0,0 +1,178 @@ +// user/compress.c — Run-Length Encoding (RLE) compressor/decompressor for FogOSv2 +// Format: stream of pairs [count:1 byte][value:1 byte], repeated to EOF. +// Runs >255 are split into multiple pairs. + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "kernel/fcntl.h" +#include "user/user.h" + +#define INBUF 512 +#define OUTBUF 128 + +static void usage(void) { + printf( + "usage:\n" + " compress -c [in] [out] # compress\n" + " compress -d [in] [out] # decompress\n" + "If in/out omitted, stdin/stdout are used.\n" + ); +} + +static int open_in(const char *p) { + if (!p) return 0; // stdin + int fd = open(p, O_RDONLY); + if (fd < 0) { + printf("compress: cannot open %s\n", p); + } + return fd; +} + +static int open_out(const char *p) { + if (!p) return 1; // stdout + int fd = open(p, O_CREATE | O_WRONLY); + if (fd < 0) { + printf("compress: cannot create %s\n", p); + } + return fd; +} + +/* -------------------- RLE compress -------------------- */ +static int rle_compress_fd(int in, int out) { + uchar buf[INBUF]; + int have_prev = 0; + uchar prev = 0; + int cnt = 0; + + for (;;) { + int n = read(in, buf, sizeof(buf)); + if (n < 0) { printf("compress: read error\n"); return -1; } + if (n == 0) break; + + for (int i = 0; i < n; i++) { + uchar b = buf[i]; + if (!have_prev) { + prev = b; cnt = 1; have_prev = 1; + } else if (b == prev && cnt < 255) { + cnt++; + } else { + uchar pair[2] = { (uchar)cnt, prev }; + if (write(out, pair, 2) != 2) { printf("compress: write error\n"); return -1; } + prev = b; cnt = 1; + } + } + } + + if (have_prev) { + uchar pair[2] = { (uchar)cnt, prev }; + if (write(out, pair, 2) != 2) { printf("compress: write error\n"); return -1; } + } + return 0; +} + +/* ------------------ RLE decompress -------------------- */ +static int rle_decompress_fd(int in, int out) { + uchar buf[INBUF]; + int carry = -1; // -1 = no carry; otherwise holds a pending count byte + uchar outbuf[OUTBUF]; + + for (int k = 0; k < OUTBUF; k++) outbuf[k] = 0; // init once + + for (;;) { + int n = read(in, buf, sizeof(buf)); + if (n < 0) { printf("compress: read error\n"); return -1; } + if (n == 0) break; + + int i = 0; + + // If we had an odd byte (count) from the previous read, pair it now. + if (carry != -1) { + if (n < 1) { printf("compress: corrupt input (missing value)\n"); return -1; } + int cnt = carry; + uchar val = buf[0]; + if (cnt <= 0) { printf("compress: corrupt input (zero/neg count)\n"); return -1; } + + // emit cnt copies of val + for (int j = 0; j < OUTBUF; j++) outbuf[j] = val; + int left = cnt; + while (left > 0) { + int chunk = left < OUTBUF ? left : OUTBUF; + if (write(out, outbuf, chunk) != chunk) { printf("compress: write error\n"); return -1; } + left -= chunk; + } + i = 1; // consumed one byte (value) + carry = -1; // cleared + } + + // Process full pairs from this buffer + for (; i + 1 < n; i += 2) { + int cnt = buf[i]; + uchar val = buf[i+1]; + if (cnt <= 0) { printf("compress: corrupt input (zero/neg count)\n"); return -1; } + + for (int j = 0; j < OUTBUF; j++) outbuf[j] = val; + int left = cnt; + while (left > 0) { + int chunk = left < OUTBUF ? left : OUTBUF; + if (write(out, outbuf, chunk) != chunk) { printf("compress: write error\n"); return -1; } + left -= chunk; + } + } + + // If there's one byte left, it's a dangling count; carry it to next read + if (i < n) { + carry = buf[n-1]; + } + } + + if (carry != -1) { // ended with odd number of bytes → corrupt + printf("compress: corrupt input (odd byte count)\n"); + return -1; + } + return 0; +} + +/* ------------------------ main ------------------------ */ +int main(int argc, char *argv[]) { + if (argc < 2) { usage(); exit(1); } + + int mode = 0; // 1 = compress, 2 = decompress + const char *inpath = 0; // NULL -> stdin + const char *outpath = 0; // NULL -> stdout + + // Parse: first flag (-c/-d), then up to 2 positional args + for (int i = 1; i < argc; i++) { + char *a = argv[i]; + if (strcmp(a, "-c") == 0) { + mode = 1; + } else if (strcmp(a, "-d") == 0) { + mode = 2; + } else if (!inpath) { + inpath = a; + } else if (!outpath) { + outpath = a; + } else { + printf("compress: too many arguments\n"); + usage(); + exit(1); + } + } + + if (mode == 0) { + printf("compress: must specify -c or -d\n"); + usage(); + exit(1); + } + + int in = open_in(inpath); + if (in < 0) exit(1); + int out = open_out(outpath); + if (out < 0) { if (in > 1) close(in); exit(1); } + + int rc = (mode == 1) ? rle_compress_fd(in, out) : rle_decompress_fd(in, out); + + if (in > 1) close(in); + if (out > 1) close(out); + if (rc < 0) exit(1); + exit(0); +} diff --git a/user/compress_test.c b/user/compress_test.c new file mode 100644 index 0000000..f4b4b6f --- /dev/null +++ b/user/compress_test.c @@ -0,0 +1,130 @@ +// It creates small input files, runs `compress -c` then `compress -d` via +// fork/exec, and verifies the round-trip output matches the original. + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "kernel/fcntl.h" +#include "user/user.h" + +#define IN_CHUNK 512 + +static int write_file(const char *path, const void *buf, int len) { + int fd = open(path, O_CREATE | O_WRONLY); + if (fd < 0) { printf("compress_test: open(O_CREATE) %s failed\n", path); return -1; } + const char *p = (const char*)buf; + int left = len; + while (left > 0) { + int n = write(fd, p, left); + if (n < 0) { printf("compress_test: write %s failed\n", path); close(fd); return -1; } + left -= n; p += n; + } + close(fd); + return 0; +} + +static int files_equal(const char *a, const char *b) { + int fa = open(a, O_RDONLY); + int fb = open(b, O_RDONLY); + if (fa < 0 || fb < 0) { if (fa>=0) close(fa); if (fb>=0) close(fb); return 0; } + + char ba[IN_CHUNK], bb[IN_CHUNK]; + for (;;) { + int na = read(fa, ba, sizeof(ba)); + int nb = read(fb, bb, sizeof(bb)); + if (na < 0 || nb < 0) { close(fa); close(fb); return 0; } + if (na != nb) { close(fa); close(fb); return 0; } + if (na == 0) { close(fa); close(fb); return 1; } // both EOF + for (int i = 0; i < na; i++) if (ba[i] != bb[i]) { close(fa); close(fb); return 0; } + } +} + +static int run_prog(char *prog, char *argv[]) { + int pid = fork(); + if (pid < 0) { printf("compress_test: fork failed\n"); return -1; } + if (pid == 0) { + // child + exec(prog, argv); + printf("compress_test: exec %s failed\n", prog); + exit(127); + } + int status = 0; + if (wait(&status) < 0) { + // Some FogOS variants support wait(&status); others allow wait(0). + // If your toolchain complains, change to: wait(0); + } + return status; +} + +static int do_roundtrip(const char *label, const char *infile, + const char *rlefile, const char *outfile) +{ + // compress -c infile rlefile + char *argv_c[] = { "compress", "-c", (char*)infile, (char*)rlefile, 0 }; + int st1 = run_prog("compress", argv_c); + if (st1 != 0) { printf("[FAIL] %s: compress step failed (status=%d)\n", label, st1); return -1; } + + // decompress -d rlefile outfile + char *argv_d[] = { "compress", "-d", (char*)rlefile, (char*)outfile, 0 }; + int st2 = run_prog("compress", argv_d); + if (st2 != 0) { printf("[FAIL] %s: decompress step failed (status=%d)\n", label, st2); return -1; } + + // compare + if (!files_equal(infile, outfile)) { + printf("[FAIL] %s: round-trip mismatch\n", label); + return -1; + } + + printf("[PASS] %s\n", label); + return 0; +} + +static void make_repeating(char *buf, int len, char byte) { + for (int i = 0; i < len; i++) buf[i] = byte; +} + +int +main(void) +{ + int rc = 0; + + // --- Test 1: small text --- + const char *t1_in = "t1_in.txt"; + const char *t1_rle = "t1.rle"; + const char *t1_out = "t1_out.txt"; + const char *msg = "Hello RLE!\nHello RLE!\nAAABBBCCCDDD\n"; + if (write_file(t1_in, msg, strlen(msg)) < 0) rc = -1; + if (rc == 0 && do_roundtrip("small text", t1_in, t1_rle, t1_out) < 0) rc = -1; + + // --- Test 2: empty file --- + const char *t2_in = "t2_in.bin"; + const char *t2_rle = "t2.rle"; + const char *t2_out = "t2_out.bin"; + if (rc == 0 && write_file(t2_in, "", 0) < 0) rc = -1; + if (rc == 0 && do_roundtrip("empty file", t2_in, t2_rle, t2_out) < 0) rc = -1; + + // --- Test 3: long run (>255) --- + const char *t3_in = "t3_in.bin"; + const char *t3_rle = "t3.rle"; + const char *t3_out = "t3_out.bin"; + char big[1000]; + make_repeating(big, sizeof(big), 'A'); + if (rc == 0 && write_file(t3_in, big, sizeof(big)) < 0) rc = -1; + if (rc == 0 && do_roundtrip("long run 1000x'A'", t3_in, t3_rle, t3_out) < 0) rc = -1; + + // --- Test 4: binary with zero bytes --- + const char *t4_in = "t4_in.bin"; + const char *t4_rle = "t4.rle"; + const char *t4_out = "t4_out.bin"; + char bin[256]; + for (int i = 0; i < 256; i++) bin[i] = (char)i; // 0x00..0xFF once + if (rc == 0 && write_file(t4_in, bin, sizeof(bin)) < 0) rc = -1; + if (rc == 0 && do_roundtrip("binary 0x00..0xFF", t4_in, t4_rle, t4_out) < 0) rc = -1; + + if (rc == 0) { + printf("\nAll tests PASSED\n"); + exit(0); + } else { + printf("\nSome tests FAILED\n"); + exit(1); + } +} From 7e31681dd90e93ad4d1af7d89b181658c7012472 Mon Sep 17 00:00:00 2001 From: Kailash Turimella Date: Tue, 30 Dec 2025 08:32:50 +0530 Subject: [PATCH 2/5] p --- 1.sh | 33 ++++ 2.sh | 433 +++++++++++++++++++++++++++++++++++++++++++++ 3.sh | 430 ++++++++++++++++++++++++++++++++++++++++++++ 4.sh | 432 ++++++++++++++++++++++++++++++++++++++++++++ Makefile | 34 +++- kernel/exec.c | 35 ++++ kernel/fs.c | 93 ++++++++++ kernel/memlayout.h | 6 + kernel/proc.c | 183 +++++++++++++++---- kernel/proc.h | 8 + kernel/strace.h | 121 +++++++++++++ kernel/syscall.c | 28 ++- kernel/syscall.h | 9 + kernel/sysfile.c | 28 +++ kernel/sysproc.c | 104 ++++++++++- mkfs/mkfs.c | 105 +---------- user/sh.c | 34 +++- user/user.h | 47 +++-- user/usys.pl | 9 + 19 files changed, 2016 insertions(+), 156 deletions(-) create mode 100644 1.sh create mode 100644 2.sh create mode 100644 3.sh create mode 100644 4.sh create mode 100644 kernel/strace.h diff --git a/1.sh b/1.sh new file mode 100644 index 0000000..3c124d2 --- /dev/null +++ b/1.sh @@ -0,0 +1,33 @@ +# Start of file. This line should not cause errors +echo Command 000 +echo Command 001 +echo Command 002 +ehchhho Command 003 +echo Command 004 +echo Command 005 +echo Command 006 # Look, it's command 6. This shouldn't print +echo Command 007 +echo Command 008 +# Empty commands (like this one) and blank lines should not go in history + + + + + +/ls / +mkdir a +mkdir a/b +mkdir a/b/c +mkdir a/b/c/hello +cd a +cd b/c/hello +/ls +/wc /README.md +cd / +/wc README.md +echo --- +history +echo --- +history -t +exit +echo If you see this line, exit is not working. diff --git a/2.sh b/2.sh new file mode 100644 index 0000000..d07fb12 --- /dev/null +++ b/2.sh @@ -0,0 +1,433 @@ +# Make sure empty lines (including this one) are handled +# properly to pass this test. +# +# Bad/unknown commands **do** go into the history. + +thisisacommandthatprobablydoesntexistbutletstryitanywayhuh +echo Command 000 +echo Command 001 +echo Command 002 +echo Command 003 +echo Command 004 +echo Command 005 + +echo Command 006 + +echo Command 007 + +echo Command 008 + +echo Command 009 +echo Command 010 +echo Command 011 +echo Command 012 +echo Command 013 +echo Command 014 +echo Command 015 +echo Command 016 +echo Command 017 +echo Command 018 +echo Command 019 +echo Command 020 +echo Command 021 +echo Command 022 +echo Command 023 +echo Command 024 +echo Command 025 +echo Command 026 +echo Command 027 +echo Command 028 +echo Command 029 +echo Command 030 +echo Command 031 +echo Command 032 +echo Command 033 +echo Command 034 +echo Command 035 +echo Command 036 +echo Command 037 +echo Command 038 +echo Command 039 +echo Command 040 +echo Command 041 +echo Command 042 +echo Command 043 +echo Command 044 +echo Command 045 +echo Command 046 +echo Command 047 +echo Command 048 +echo Command 049 +echo Command 050 +echo Command 051 +echo Command 052 +echo Command 053 +echo Command 054 +echo Command 055 +echo Command 056 +echo Command 057 +echo Command 058 +echo Command 059 +echo Command 060 +echo Command 061 +echo Command 062 +echo Command 063 +echo Command 064 +echo Command 065 +echo Command 066 +echo Command 067 +echo Command 068 +echo Command 069 +echo Command 070 +echo Command 071 +echo Command 072 +echo Command 073 +echo Command 074 +echo Command 075 +echo Command 076 +echo Command 077 +echo Command 078 +echo Command 079 +echo Command 080 +echo Command 081 +echo Command 082 +echo Command 083 +echo Command 084 +echo Command 085 +echo Command 086 +echo Command 087 +echo Command 088 +echo Command 089 +echo Command 090 +echo Command 091 +echo Command 092 +echo Command 093 +echo Command 094 +echo Command 095 +echo Command 096 +echo Command 097 +echo Command 098 +echo Command 099 +echo Command 100 +echo Command 101 +echo Command 102 +echo Command 103 +echo Command 104 +echo Command 105 +echo Command 106 +echo Command 107 +echo Command 108 +echo Command 109 +echo Command 110 +echo Command 111 +echo Command 112 +echo Command 113 +echo Command 114 +echo Command 115 +echo Command 116 +echo Command 117 +echo Command 118 +echo Command 119 +echo Command 120 +echo Command 121 +echo Command 122 +echo Command 123 +echo Command 124 +echo Command 125 +echo Command 126 +echo Command 127 +echo Command 128 +echo Command 129 +echo Command 130 +echo Command 131 +echo Command 132 +echo Command 133 +echo Command 134 +echo Command 135 +echo Command 136 +echo Command 137 +echo Command 138 +echo Command 139 +echo Command 140 +echo Command 141 +echo Command 142 +echo Command 143 +echo Command 144 +echo Command 145 +echo Command 146 +echo Command 147 +echo Command 148 +echo Command 149 +echo Command 150 +echo Command 151 +echo Command 152 +echo Command 153 +echo Command 154 +echo Command 155 +echo Command 156 +echo Command 157 +echo Command 158 +echo Command 159 +echo Command 160 +echo Command 161 +echo Command 162 +echo Command 163 +echo Command 164 +echo Command 165 +echo Command 166 +echo Command 167 +echo Command 168 +echo Command 169 +echo Command 170 +echo Command 171 +echo Command 172 +echo Command 173 +echo Command 174 +echo Command 175 +echo Command 176 +echo Command 177 +echo Command 178 +echo Command 179 +echo Command 180 +echo Command 181 +echo Command 182 +echo Command 183 +echo Command 184 +echo Command 185 +echo Command 186 +echo Command 187 +echo Command 188 +echo Command 189 +echo Command 190 +echo Command 191 +echo Command 192 +echo Command 193 +echo Command 194 +echo Command 195 +echo Command 196 +echo Command 197 +echo Command 198 +echo Command 199 +echo Command 200 +echo Command 201 +echo Command 202 +echo Command 203 +echo Command 204 +echo Command 205 +echo Command 206 +echo Command 207 +echo Command 208 +echo Command 209 +echo Command 210 +echo Command 211 +echo Command 212 +echo Command 213 +echo Command 214 +echo Command 215 +echo Command 216 +echo Command 217 +echo Command 218 +echo Command 219 +echo Command 220 +echo Command 221 +echo Command 222 +echo Command 223 +echo Command 224 +echo Command 225 +echo Command 226 +echo Command 227 +echo Command 228 +echo Command 229 +echo Command 230 +echo Command 231 +echo Command 232 +echo Command 233 +echo Command 234 +echo Command 235 +echo Command 236 +echo Command 237 +echo Command 238 +echo Command 239 +echo Command 240 +echo Command 241 +echo Command 242 +echo Command 243 +echo Command 244 +echo Command 245 +echo Command 246 +echo Command 247 +echo Command 248 +echo Command 249 +echo Command 250 +echo Command 251 +echo Command 252 +echo Command 253 +echo Command 254 +echo Command 255 +echo Command 256 +echo Command 257 +echo Command 258 +echo Command 259 +echo Command 260 +echo Command 261 +echo Command 262 +echo Command 263 +echo Command 264 +echo Command 265 +echo Command 266 +echo Command 267 +echo Command 268 +echo Command 269 +echo Command 270 +echo Command 271 +echo Command 272 +echo Command 273 +echo Command 274 +echo Command 275 +echo Command 276 +echo Command 277 +echo Command 278 +echo Command 279 +echo Command 280 +echo Command 281 +echo Command 282 +echo Command 283 +echo Command 284 +echo Command 285 +echo Command 286 +echo Command 287 +echo Command 288 +echo Command 289 +echo Command 290 +echo Command 291 +echo Command 292 +echo Command 293 +echo Command 294 +echo Command 295 +echo Command 296 +echo Command 297 +echo Command 298 +echo Command 299 +echo Command 300 +echo Command 301 +echo Command 302 +echo Command 303 +echo Command 304 +echo Command 305 +echo Command 306 +echo Command 307 +echo Command 308 +echo Command 309 +echo Command 310 +echo Command 311 +echo Command 312 +echo Command 313 +echo Command 314 +echo Command 315 +echo Command 316 +echo Command 317 +echo Command 318 +echo Command 319 +echo Command 320 +echo Command 321 +echo Command 322 +echo Command 323 +echo Command 324 +echo Command 325 +echo Command 326 +echo Command 327 +echo Command 328 +echo Command 329 +echo Command 330 +echo Command 331 +echo Command 332 +echo Command 333 +echo Command 334 +echo Command 335 +echo Command 336 +echo Command 337 +echo Command 338 +echo Command 339 +echo Command 340 +echo Command 341 +echo Command 342 +echo Command 343 +echo Command 344 +echo Command 345 +echo Command 346 +echo Command 347 +echo Command 348 +echo Command 349 +echo Command 350 +echo Command 351 +echo Command 352 +echo Command 353 +echo Command 354 +echo Command 355 +echo Command 356 +echo Command 357 +echo Command 358 +echo Command 359 +echo Command 360 +echo Command 361 +echo Command 362 +echo Command 363 +echo Command 364 +echo Command 365 +echo Command 366 +echo Command 367 +echo Command 368 +echo Command 369 +echo Command 370 +echo Command 371 +echo Command 372 +echo Command 373 +echo Command 374 +echo Command 375 +echo Command 376 +echo Command 377 +echo Command 378 +echo Command 379 +echo Command 380 +echo Command 381 +echo Command 382 +echo Command 383 +echo Command 384 +echo Command 385 +echo Command 386 +echo Command 387 +echo Command 388 +echo Command 389 +echo Command 390 +echo Command 391 +echo Command 392 +echo Command 393 +echo Command 394 +echo Command 395 +echo Command 396 +echo Command 397 +echo Command 398 +echo Command 399 +echo Command 400 +echo Command 401 +echo Command 402 +echo Command 403 +echo Command 404 +echo Command 405 +echo Command 406 +pwd # May or may not exist... +echo Command 407 +echo Command 408 +echo Command 409 +echo Command 410 +echo Command 411 +echo Command 412 +echo Command 413 +echo Command 414 +echo Command 415 +echo Command 416 +echo Command 417 +echo Command 418 +echo Command 419 +echo ----- +history diff --git a/3.sh b/3.sh new file mode 100644 index 0000000..6c4e8d8 --- /dev/null +++ b/3.sh @@ -0,0 +1,430 @@ +!542 # Doesn't actually exist :-) +history +echo Command 002 +echo Command 003 +echo Command 004 +echo Command 005 +echo Command 006 +echo Command 007 +echo Command 008 +echo Command 009 +echo Command 010 +echo Command 011 +echo Command 012 +echo Command 013 +echo Command 014 +echo Command 015 +echo Command 016 +echo Command 017 +echo Command 018 +echo Command 019 +echo Command 020 +echo Command 021 +echo Command 022 +echo Command 023 +echo Command 024 +echo Command 025 +echo Command 026 +echo Command 027 +echo Command 028 +echo Command 029 +echo Command 030 +echo Command 031 +echo Command 032 +echo Command 033 +echo Command 034 +echo Command 035 +echo Command 036 +echo Command 037 +echo Command 038 +echo Command 039 +echo Command 040 +echo Command 041 +echo Command 042 +echo Command 043 +echo Command 044 +echo Command 045 +echo Command 046 +echo Command 047 +echo Command 048 +echo Command 049 +echo Command 050 +echo Command 051 +echo Command 052 +echo Command 053 +echo Command 054 +echo Command 055 +echo Command 056 +echo Command 057 +echo Command 058 +echo Command 059 +echo Command 060 +echo Command 061 +echo Command 062 +echo Command 063 +echo Command 064 +echo Command 065 +echo Command 066 +echo Command 067 +echo Command 068 +echo Command 069 +echo Command 070 +echo Command 071 +echo Command 072 +echo Command 073 +echo Command 074 +echo Command 075 +echo Command 076 +echo Command 077 +echo Command 078 +echo Command 079 +echo Command 080 +echo Command 081 +echo Command 082 +echo Command 083 +echo Command 084 +echo Command 085 +echo Command 086 +echo Command 087 +echo Command 088 +echo Command 089 +echo Command 090 +echo Command 091 +echo Command 092 +echo Command 093 +echo Command 094 +echo Command 095 +echo Command 096 +echo Command 097 +echo Command 098 +echo Command 099 +echo Command 100 +echo Command 101 +echo Command 102 +echo Command 103 +echo Command 104 +echo Command 105 +echo Command 106 +echo Command 107 +echo Command 108 +echo Command 109 +echo Command 110 +echo Command 111 +echo Command 112 +echo Command 113 +echo Command 114 +echo Command 115 +echo Command 116 +echo Command 117 +echo Command 118 +echo Command 119 +echo Command 120 +echo Command 121 +echo Command 122 +echo Command 123 +echo Command 124 +echo Command 125 +echo Command 126 +echo Command 127 +echo Command 128 +echo Command 129 +echo Command 130 +echo Command 131 +echo Command 132 +echo Command 133 +echo Command 134 +echo Command 135 +echo Command 136 +echo Command 137 +echo Command 138 +echo Command 139 +echo Command 140 +echo Command 141 +echo Command 142 +echo Command 143 +echo Command 144 +echo Command 145 +echo Command 146 +echo Command 147 +echo Command 148 +echo Command 149 +echo Command 150 +echo Command 151 +echo Command 152 +echo Command 153 +echo Command 154 +echo Command 155 +echo Command 156 +echo Command 157 +echo Command 158 +echo Command 159 +echo Command 160 +echo Command 161 +echo Command 162 +echo Command 163 +echo Command 164 +echo Command 165 +echo Command 166 +echo Command 167 +echo Command 168 +echo Command 169 +echo Command 170 +echo Command 171 +echo Command 172 +echo Command 173 +echo Command 174 +echo Command 175 +echo Command 176 +echo Command 177 +echo Command 178 +echo Command 179 +echo Command 180 +echo Command 181 +echo Command 182 +echo Command 183 +echo Command 184 +echo Command 185 +echo Command 186 +echo Command 187 +echo Command 188 +echo Command 189 +echo Command 190 +echo Command 191 +echo Command 192 +echo Command 193 +echo Command 194 +echo Command 195 +echo Command 196 +echo Command 197 +echo Command 198 +echo Command 199 +echo Command 200 +echo Command 201 +echo Command 202 +echo Command 203 +echo Command 204 +echo Command 205 +echo Command 206 +echo Command 207 +echo Command 208 +echo Command 209 +echo Command 210 +echo Command 211 +echo Command 212 +echo Command 213 +echo Command 214 +echo Command 215 +echo Command 216 +echo Command 217 +echo Command 218 +echo Command 219 +echo Command 220 +echo Command 221 +echo Command 222 +echo Command 223 +echo Command 224 +echo Command 225 +echo Command 226 +echo Command 227 +echo Command 228 +echo Command 229 +echo Command 230 +echo Command 231 +echo Command 232 +echo Command 233 +echo Command 234 +echo Command 235 +echo Command 236 +echo Command 237 +echo Command 238 +echo Command 239 +echo Command 240 +echo Command 241 +echo Command 242 +echo Command 243 +echo Command 244 +echo Command 245 +echo Command 246 +echo Command 247 +echo Command 248 +echo Command 249 +echo Command 250 +echo Command 251 +echo Command 252 +echo Command 253 +echo Command 254 +echo Command 255 +echo Command 256 +echo Command 257 +echo Command 258 +echo Command 259 +echo Command 260 +echo Command 261 +echo Command 262 +echo Command 263 +echo Command 264 +echo Command 265 +echo Command 266 +echo Command 267 +echo Command 268 +echo Command 269 +echo Command 270 +echo Command 271 +echo Command 272 +echo Command 273 +echo Command 274 +echo Command 275 +echo Command 276 +echo Command 277 +echo Command 278 +echo Command 279 +echo Command 280 +echo Command 281 +echo Command 282 +echo Command 283 +echo Command 284 +echo Command 285 +echo Command 286 +echo Command 287 +echo Command 288 +echo Command 289 +echo Command 290 +echo Command 291 +echo Command 292 +echo Command 293 +echo Command 294 +echo Command 295 +echo Command 296 +echo Command 297 +echo Command 298 +echo Command 299 +echo Command 300 +echo Command 301 +echo Command 302 +echo Command 303 +echo Command 304 +echo Command 305 +echo Command 306 +echo Command 307 +echo Command 308 +echo Command 309 +echo Command 310 +echo Command 311 +echo Command 312 +echo Command 313 +echo Command 314 +echo Command 315 +echo Command 316 +echo Command 317 +echo Command 318 +echo Command 319 +echo Command 320 +echo Command 321 +echo Command 322 +echo Command 323 +echo Command 324 +echo Command 325 +echo Command 326 +echo Command 327 +echo Command 328 +echo Command 329 +echo Command 330 +echo Command 331 +echo Command 332 +echo Command 333 +echo Command 334 +echo Command 335 +echo Command 336 +echo Command 337 +echo Command 338 +echo Command 339 +echo Command 340 +echo Command 341 +echo Command 342 +echo Command 343 +echo Command 344 +echo Command 345 +echo Command 346 +echo Command 347 +echo Command 348 +echo Command 349 +echo Command 350 +echo Command 351 +echo Command 352 +echo Command 353 +echo Command 354 +echo Command 355 +echo Command 356 +echo Command 357 +echo Command 358 +echo Command 359 +echo Command 360 +echo Command 361 +echo Command 362 +echo Command 363 +echo Command 364 +echo Command 365 +echo Command 366 +echo Command 367 +echo Command 368 +echo Command 369 +echo Command 370 +echo Command 371 +echo Command 372 +echo Command 373 +echo Command 374 +echo Command 375 +echo Command 376 +echo Command 377 +echo Command 378 +echo Command 379 +echo Command 380 +echo Command 381 +echo Command 382 +echo Command 383 +echo Command 384 +echo Command 385 +echo Command 386 +echo Command 387 +echo Command 388 +echo Command 389 +echo Command 390 +echo Command 391 +echo Command 392 +echo Command 393 +echo Command 394 +echo Command 395 +echo Command 396 +echo Command 397 +echo Command 398 +echo Command 399 +echo Command 400 +echo Command 401 +echo Command 402 +echo Command 403 +echo Command 404 +echo Command 405 +echo Command 406 +echo Command 407 +echo Command 408 +echo Command 409 +echo Command 410 +echo Command 411 +echo Command 412 +echo Command 413 +echo Command 414 +echo Command 415 +echo Command 416 +echo Command 417 +echo Command 418 +echo Command 419 +echo Command 420 +echo ----- +history +echo ----- +!420 # Works! +!325 # Works! +!325 # Shouldn't work the 2nd time -- gets popped off history. +!401 # Works! +!23 # Does not exist anymore +!999 # Hasn't happened yet diff --git a/4.sh b/4.sh new file mode 100644 index 0000000..22e012c --- /dev/null +++ b/4.sh @@ -0,0 +1,432 @@ +echo Command 000 +echo Command 001 +cat /README.md +echo Command 003 +echo Command 004 +echo Command 005 +echo Command 006 +echo Command 007 +echo Command 008 +echo Command 009 +echo Command 010 +echo Command 011 +echo Command 012 +echo Command 013 +echo Command 014 +echo Command 015 +echo Command 016 +echo Command 017 +echo Command 018 +echo Command 019 +echo Command 020 +echo Command 021 +echo Command 022 +echo Command 023 +echo Command 024 +echo Command 025 +echo Command 026 +echo Command 027 +echo Command 028 +echo Command 029 +echo Command 030 +echo Command 031 +echo Command 032 +echo Command 033 +echo Command 034 +echo Command 035 +echo Command 036 +echo Command 037 +echo Command 038 +echo Command 039 +echo Command 040 +echo Command 041 +echo Command 042 +echo Command 043 +echo Command 044 +echo Command 045 +echo Command 046 +echo Command 047 +echo Command 048 +echo Command 049 +echo Command 050 +echo Command 051 +echo Command 052 +echo Command 053 +echo Command 054 +echo Command 055 +echo Command 056 +echo Command 057 +echo Command 058 +echo Command 059 +echo Command 060 +echo Command 061 +echo Command 062 +echo Command 063 +echo Command 064 +echo Command 065 +echo Command 066 +echo Command 067 +echo Command 068 +echo Command 069 +echo Command 070 +echo Command 071 +echo Command 072 +echo Command 073 +echo Command 074 +echo Command 075 +echo Command 076 +echo Command 077 +echo Command 078 +echo Command 079 +echo Command 080 +echo Command 081 +echo Command 082 +echo Command 083 +echo Command 084 +echo Command 085 +echo Command 086 +echo Command 087 +echo Command 088 +echo Command 089 +echo Command 090 +echo Command 091 +echo Command 092 +echo Command 093 +echo Command 094 +echo Command 095 +echo Command 096 +echo Command 097 +echo Command 098 +echo Command 099 +echo Command 100 +echo Command 101 +echo Command 102 +echo Command 103 +echo Command 104 +echo Command 105 +echo Command 106 +echo Command 107 +echo Command 108 +echo Command 109 +echo Command 110 +echo Command 111 +echo Command 112 +echo Command 113 +echo Command 114 +echo Command 115 +echo Command 116 +echo Command 117 +echo Command 118 +echo Command 119 +echo Command 120 +echo Command 121 +echo Command 122 +echo Command 123 +echo Command 124 +echo Command 125 +echo Command 126 +echo Command 127 +echo Command 128 +echo Command 129 +echo Command 130 +# Potato +echo Command 131 +echo Command 132 +echo Command 133 +echo Command 134 +echo Command 135 +echo Command 136 +echo Command 137 +echo Command 138 +echo Command 139 +echo Command 140 +echo Command 141 +echo Command 142 +echo Command 143 +echo Command 144 +echo Command 145 +echo Command 146 +echo Command 147 +echo Command 148 +echo Command 149 +echo Command 150 +echo Command 151 +echo Command 152 +echo Command 153 +echo Command 154 +echo Command 155 +echo Command 156 +echo Command 157 +echo Command 158 +echo Command 159 +echo Command 160 +echo Command 161 +echo Command 162 +echo Command 163 +echo Command 164 +echo Command 165 +echo Command 166 +echo Command 167 +echo Command 168 +echo Command 169 +echo Command 170 +echo Command 171 +echo Command 172 +echo Command 173 +echo Command 174 +echo Command 175 +echo Command 176 +echo Command 177 +echo Command 178 +echo Command 179 +echo Command 180 +echo Command 181 +echo Command 182 +echo Command 183 +echo Command 184 +echo Command 185 +echo Command 186 +echo Command 187 +echo Command 188 +echo Command 189 +echo Command 190 +echo Command 191 +echo Command 192 +echo Command 193 +echo Command 194 +echo Command 195 +echo Command 196 +echo Command 197 +echo Command 198 +echo Command 199 +echo Command 200 +echo Command 201 +echo Command 202 +echo Command 203 +echo Command 204 +echo Command 205 +echo Command 206 +echo Command 207 +echo Command 208 +echo Command 209 +echo Command 210 +echo Command 211 +echo Command 212 +echo Command 213 +echo Command 214 +echo Command 215 +echo Command 216 +echo Command 217 +echo Command 218 +echo Command 219 +echo Command 220 +echo Command 221 +echo Command 222 +echo Command 223 +echo Command 224 +echo Command 225 +echo Command 226 +echo Command 227 +echo Command 228 +echo Command 229 +echo Command 230 +echo Command 231 +echo Command 232 +echo Command 233 +echo Command 234 +echo Command 235 +echo Command 236 +echo Command 237 +echo Command 238 +echo Command 239 +echo Command 240 +echo Command 241 +echo Command 242 +echo Command 243 +echo Command 244 +echo Command 245 +echo Command 246 +echo Command 247 +echo Command 248 +echo Command 249 +echo Command 250 +echo Command 251 +echo Command 252 +echo Command 253 +echo Command 254 +echo Command 255 +echo Command 256 +echo Command 257 +echo Command 258 +echo Command 259 +echo Command 260 +echo Command 261 +echo Command 262 +echo Command 263 +echo Command 264 +echo Command 265 +echo Command 266 +echo Command 267 +echo Command 268 +echo Command 269 +echo Command 270 +echo Command 271 +echo Command 272 +echo Command 273 +echo Command 274 +echo Command 275 +echo Command 276 +echo Command 277 +echo Command 278 +echo Command 279 +echo Command 280 +echo Command 281 +echo Command 282 +echo Command 283 +echo Command 284 +echo Command 285 +echo Command 286 +echo Command 287 +echo Command 288 +echo Command 289 +echo Command 290 +echo Command 291 +echo Command 292 +echo Command 293 +echo Command 294 +echo Command 295 +echo Command 296 +echo Command 297 +echo Command 298 +echo Command 299 +echo Command 300 +echo Command 301 +echo Command 302 +echo Command 303 +echo Command 304 +echo Command 305 +echo Command 306 +echo Command 307 +echo Command 308 +echo Command 309 +echo Command 310 +echo Command 311 +echo Command 312 +echo Command 313 +echo Command 314 +echo Command 315 +echo Command 316 +echo Command 317 +echo Command 318 +echo Command 319 +echo Command 320 +echo Command 321 +echo Command 322 +echo Command 323 +echo Command 324 +echo Command 325 +echo Command 326 +echo Command 327 +echo Command 328 +echo Command 329 +echo Command 330 +echo Command 331 +echo Command 332 +echo Command 333 +echo Command 334 +echo Command 335 +echo Command 336 +echo Command 337 +echo Command 338 +echo Command 339 +echo Command 340 +echo Command 341 +echo Command 342 +echo Command 343 +echo Command 344 +echo Command 345 +echo Command 346 +echo Command 347 +echo Command 348 +echo Command 349 +echo Command 350 +echo Command 351 +echo Command 352 +echo Command 353 +echo Command 354 +echo Command 355 +echo Command 356 +echo Command 357 +echo Command 358 +echo Command 359 +echo Command 360 +echo Command 361 +echo Command 362 +echo Command 363 +echo Command 364 +echo Command 365 +echo Command 366 +echo Command 367 +echo Command 368 +echo Command 369 +echo Command 370 +echo Command 371 +ls / +echo Command 373 +echo Command 374 +echo Command 375 +echo Command 376 +echo Command 377 +echo Command 378 +echo Command 379 +echo Command 380 +echo Command 381 +echo Command 383 +echo Command 384 +mkdir /a +ls /a +echo Command 385 +echo Command 386 +echo Command 387 +echo Command 388 +mysterycommand +echo Command 389 +echo Command 390 +echo Command 391 +echo Command 392 +grep Potato$ /README.md +grep Potato$ /4.sh +echo Command 394 +echo Command 395 +echo Command 396 +echo Command 397 +echo Command 398 +echo Command 399 +echo Command 400 +echo Command 401 +echo Command 402 +echo Command 403 +echo Command 404 +echo Command 405 +wc /README.md +echo Command 407 +echo Command 408 +echo Command 409 +echo Command 410 +echo Command 411 +echo Command 412 +echo Command 413 +echo Command 414 +echo Command 415 +echo Command 416 +echo Command 417 +echo Command 418 +echo Command 419 +echo ----- +!ls # ls an empty dir +!m # run mysterycommand and fail since it doesn't exist +!grepfzw # no command had this prefix +!grep # find 'Potato' in this script (*not* in README.md) +!w # word count README.md +!gr # find 'Potato' again +!e # last echo command (-----) diff --git a/Makefile b/Makefile index 477d25f..9496185 100644 --- a/Makefile +++ b/Makefile @@ -83,11 +83,14 @@ endif LDFLAGS = -z max-page-size=4096 -$K/kernel: $(OBJS) $K/kernel.ld +$K/kernel: $(OBJS) $K/kernel.ld $K/strace.h $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $(OBJDUMP) -S $K/kernel > $K/kernel.asm $(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym +$K/strace.h: generate-traces.sh $U/user.h + ./generate-traces.sh > $K/strace.h + $K/%.o: $K/%.S $(CC) -g -c -o $@ $< @@ -110,7 +113,7 @@ $U/usys.o : $U/usys.S $U/_forktest: $U/forktest.o $(ULIB) # forktest has less library code linked in - needs to be small # in order to be able to max out the proc table. - $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o + $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o $U/umalloc.o $U/printf.o $(OBJDUMP) -S $U/_forktest > $U/forktest.asm mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h @@ -123,20 +126,43 @@ mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h .PRECIOUS: %.o UPROGS=\ + $U/_about\ + $U/_barca\ + $U/_benchmark\ + $U/_broken\ $U/_cat\ + $U/_catlines\ $U/_compress\ $U/_compress_test\ + $U/_clear\ + $U/_clock\ $U/_echo\ + $U/_fnr\ $U/_forktest\ + $U/_freemem\ $U/_grep\ $U/_init\ $U/_kill\ + $U/_leetify\ $U/_ln\ $U/_ls\ $U/_mkdir\ + $U/_mmap\ + $U/_mmaptest\ + $U/_mt70\ + $U/_mt80\ + $U/_mt90\ + $U/_nice\ + $U/_opt_cat\ + $U/_pwd\ $U/_rm\ + $U/_reboot\ $U/_sh\ + $U/_shutdown\ + $U/_spinner\ + $U/_strace\ $U/_stressfs\ + $U/_tolower\ $U/_usertests\ $U/_grind\ $U/_wc\ @@ -145,8 +171,8 @@ UPROGS=\ $U/_forphan\ $U/_dorphan\ -fs.img: mkfs/mkfs README.md $(UPROGS) - mkfs/mkfs fs.img README.md $(UPROGS) +fs.img: mkfs/mkfs README.md tm.txt input.txt $(UPROGS) script.sh spin1.sh spin2.sh 1.sh 2.sh + mkfs/mkfs fs.img README.md tm.txt input.txt $(UPROGS) script.sh spin1.sh spin2.sh 1.sh 2.sh -include kernel/*.d user/*.d diff --git a/kernel/exec.c b/kernel/exec.c index 7cb6fe5..061e574 100644 --- a/kernel/exec.c +++ b/kernel/exec.c @@ -44,6 +44,37 @@ kexec(char *path, char **argv) } ilock(ip); + // check for shebang (#!) + char shebang[2]; + if(readi(ip, 0, (uint64)shebang, 0, 2) == 2 && shebang[0] == '#' && shebang[1] == '!') { + // now read the interpreter + char interpreter[MAXPATH]; + int n = readi(ip, 0, (uint64)interpreter, 2, MAXPATH); + if (n <= 0) + goto bad; + + // find \n and remove it from the interpreter + for (i = 0; i < n; i++) { + if(interpreter[i] == '\n') { + interpreter[i] = '\0'; + break; + } + } + + // build new argv array to pass to kexec + char *new_argv[MAXARG]; + new_argv[0] = interpreter; + new_argv[1] = path; + for (i = 1; argv[1] && i < MAXARG - 2; i++) + new_argv[i + 1] = argv[i]; + new_argv[i + 1] = 0; + + // unlock and end any file system log operations + iunlockput(ip); + end_op(); + return kexec(interpreter, new_argv); + } + // Read the ELF header. if(readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf)) goto bad; @@ -133,6 +164,10 @@ kexec(char *path, char **argv) p->sz = sz; p->trapframe->epc = elf.entry; // initial program counter = main p->trapframe->sp = sp; // initial stack pointer + + p->mmap = TRAPFRAME - (USERSTACK * PGSIZE); + p->mmap_pages = 0; + proc_freepagetable(oldpagetable, oldsz); return argc; // this ends up in a0, the first argument to main(argc, argv) diff --git a/kernel/fs.c b/kernel/fs.c index 9274678..19ecc66 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -718,3 +718,96 @@ nameiparent(char *path, char *name) { return namex(path, 1, name); } + +// Get the name of a particular inode in a directory. +int +diriname(struct inode *dp, ushort inum, char name[DIRSIZ], ushort *iparent) +{ + ilock(dp); + + if (dp->type != T_DIR) + panic("diriname not DIR"); + + struct dirent de; + if (iparent) { + // Read second entry ('..', aka parent) + readi(dp, 0, (uint64) &de, sizeof(struct dirent), sizeof(struct dirent)); + *iparent = de.inum; + } + + for (uint off = 0; off < dp->size; off += sizeof(de)){ + if (readi(dp, 0, (uint64) &de, off, sizeof(de)) != sizeof(de)) + panic("diriname read"); + if (de.inum == 0) + continue; + if (de.inum == inum && name) { + // Found the inode + strncpy(name, de.name, DIRSIZ); + iunlockput(dp); + return strlen(name); + } + } + + iunlockput(dp); + return -1; +} + +int +getcwd(char *buf, uint size) +{ + struct inode *ip = myproc()->cwd; + ilock(ip); + + if(ip->type != T_DIR) + panic("getcwd not DIR"); + + if (!buf || size <= 1) { + iunlock(ip); + return -1; + } + + if (ip->inum == 1 && size > 1) { + buf[0] = '/'; + buf[1] = '\0'; + iunlock(ip); + return 0; + } + + // We copy the path to the *end* of the buffer instead of the beginning, then + // work backwards. Start by null terminating the string. + buf[size - 1] = '\0'; + uint bufp = size - 2; + + struct inode *i = iget(ip->dev, ip->inum); + + ushort iparent; + ushort last_parent = ip->inum; + ushort icurrent = ip->inum; + char name[DIRSIZ]; + iunlock(ip); + + // Starting with the CWD inode, go up one level (..), read that directory, and + // search for the CWD inode in it. Repeat the process until we reach /. + while (icurrent != 1 && diriname(i, icurrent, name, &iparent) != -1) { + if (icurrent != last_parent) { + // We found another path element. Add it to the string. + int len = strlen(name); + if (len < bufp) { + bufp -= len; + memmove(buf + bufp, name, len); + buf[--bufp] = '/'; + } else { + // No more space to store the path. + break; + } + } + i = iget(ip->dev, iparent); + icurrent = last_parent; + last_parent = iparent; + } + + // If we didn't use the entire buffer, slide the string over to the beginning. + memmove(buf, buf + bufp, size - bufp); + + return 0; +} diff --git a/kernel/memlayout.h b/kernel/memlayout.h index 9bc9424..3848ff4 100644 --- a/kernel/memlayout.h +++ b/kernel/memlayout.h @@ -25,6 +25,12 @@ #define VIRTIO0 0x10001000 #define VIRTIO0_IRQ 1 +// goldfish_rtc clock +#define GOLDFISH 0x101000 + +// qemu "test" device for signaling exit/status +#define VIRT_TEST 0x100000 + // qemu puts platform-level interrupt controller (PLIC) here. #define PLIC 0x0c000000L #define PLIC_PRIORITY (PLIC + 0x0) diff --git a/kernel/proc.c b/kernel/proc.c index 9d6cf3f..f1bf4fa 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -125,6 +125,12 @@ allocproc(void) p->pid = allocpid(); p->state = USED; + p->nice = 1; + p->priority = 3 - p->nice; + + p->mmap = 0; + p->mmap_pages = 0; + // Allocate a trapframe page. if((p->trapframe = (struct trapframe *)kalloc()) == 0){ freeproc(p); @@ -158,8 +164,15 @@ freeproc(struct proc *p) if(p->trapframe) kfree((void*)p->trapframe); p->trapframe = 0; - if(p->pagetable) - proc_freepagetable(p->pagetable, p->sz); + if (p->pagetable) { + if (p->mmap_pages > 0) { + uvmunmap(p->pagetable, p->mmap, p->mmap_pages, 1); + p->mmap_pages = 0; + p->mmap = 0; + } + proc_freepagetable(p->pagetable, p->sz); + } + p->pagetable = 0; p->sz = 0; p->pid = 0; @@ -168,6 +181,7 @@ freeproc(struct proc *p) p->chan = 0; p->killed = 0; p->xstate = 0; + p->tracing = 0; p->state = UNUSED; } @@ -279,6 +293,25 @@ kfork(void) // Cause fork to return 0 in the child. np->trapframe->a0 = 0; + np->mmap = p->mmap; + np->mmap_pages = p->mmap_pages; + + for (i = 0; i < p->mmap_pages; i++) { + uint64 va = p->mmap + (uint64)i * PGSIZE; + + uint64 pa = walkaddr(p->pagetable, va); + if (pa == 0) + panic("kfork: mmap walkaddr failed"); + + + if(mappages(np->pagetable, va, PGSIZE, pa, + PTE_R | PTE_W | PTE_U) < 0) + panic("kfrok: mmap mappages failed"); + + + incref(pa); + } + // increment reference counts on open file descriptors. for(i = 0; i < NOFILE; i++) if(p->ofile[i]) @@ -287,6 +320,9 @@ kfork(void) safestrcpy(np->name, p->name, sizeof(p->name)); + np->nice = p->nice; // copy parent's nicenessœ + np->priority = p->priority; + pid = np->pid; release(&np->lock); @@ -411,6 +447,64 @@ kwait(uint64 addr) } } +// Wait for a child process to exit and return its pid. +// Return -1 if this process has no children. +int +kwait2(uint64 addr, uint64 addr2) +{ + struct proc *pp; + int havekids, pid; + struct proc *p = myproc(); + + acquire(&wait_lock); + + for(;;){ + // Scan through table looking for exited children. + havekids = 0; + for(pp = proc; pp < &proc[NPROC]; pp++){ + if(pp->parent == p){ + // make sure the child isn't still in exit() or swtch(). + acquire(&pp->lock); + + havekids = 1; + if(pp->state == ZOMBIE){ + // Found one. + pid = pp->pid; + if(addr != 0 && copyout(p->pagetable, addr, (char *)&pp->xstate, + sizeof(pp->xstate)) < 0) { + release(&pp->lock); + release(&wait_lock); + return -1; + } + + // copy syscall count to user + if(addr2 != 0 && copyout(p->pagetable, addr2, (char *)&pp->counter, + sizeof(pp->counter)) < 0) { + release(&pp->lock); + release(&wait_lock); + return -1; + } + + freeproc(pp); + release(&pp->lock); + release(&wait_lock); + return pid; + } + release(&pp->lock); + } + } + + // No point waiting if we don't have any children. + if(!havekids || killed(p)){ + release(&wait_lock); + return -1; + } + + // Wait for a child to exit. + sleep(p, &wait_lock); //DOC: wait-sleep + } +} + // Per-CPU process scheduler. // Each CPU calls scheduler() after setting itself up. // Scheduler never returns. It loops, doing: @@ -426,39 +520,64 @@ scheduler(void) c->proc = 0; for(;;){ - // The most recent process to run may have had interrupts - // turned off; enable them to avoid a deadlock if all - // processes are waiting. Then turn them back off - // to avoid a possible race between an interrupt - // and wfi. - intr_on(); - intr_off(); - - int found = 0; - for(p = proc; p < &proc[NPROC]; p++) { - acquire(&p->lock); - if(p->state == RUNNABLE) { - // Switch to chosen process. It is the process's job - // to release its lock and then reacquire it - // before jumping back to us. - p->state = RUNNING; - c->proc = p; - swtch(&c->context, &p->context); - - // Process is done running for now. - // It should have changed its p->state before coming back. - c->proc = 0; - found = 1; + // The most recent process to run may have had interrupts + // turned off; enable them to avoid a deadlock if all + // processes are waiting. Then turn them back off + // to avoid a possible race between an interrupt + // and wfi. + intr_on(); + intr_off(); + + int found = 0; + + // Find the highest priority among RUNNABLE processes + // so we can start scanning from there + int max_priority = -1; + for(p = proc; p < &proc[NPROC]; p++){ + acquire(&p->lock); + if(p->state == RUNNABLE && p->priority > max_priority) + max_priority = p->priority; + release(&p->lock); + } + + // Iterate priority "levels" from highest we observed down to 0. + // At each level, run any RUNNABLE process whose priority is >= level. + if(max_priority >= 0){ + for(int level = max_priority; level >= 0; level--){ + int found_runnable = 0; + + for(p = proc; p < &proc[NPROC]; p++){ + acquire(&p->lock); + if(p->state == RUNNABLE && p->priority >= level){ + found_runnable = 1; + + // Switch to chosen process. It is the process's job + // to release its lock and then reacquire it + // before jumping back to us. + p->state = RUNNING; + c->proc = p; + swtch(&c->context, &p->context); + + // Process is done running for now. + // It should have changed its p->state before coming back. + c->proc = 0; + found = 1; + } + release(&p->lock); } - release(&p->lock); - } - if(found == 0) { - // nothing to run; stop running on this core until an interrupt. - asm volatile("wfi"); + + // If no process qualified at this level, just drop to the next one. + if(!found_runnable) + continue; } } -} + if(found == 0){ + // nothing to run; stop running on this core until an interrupt. + asm volatile("wfi"); + } + } +} // Switch to scheduler. Must hold only p->lock // and have changed proc->state. Saves and restores // intena because intena is a property of this @@ -681,7 +800,7 @@ procdump(void) state = states[p->state]; else state = "???"; - printf("%d %s %s", p->pid, state, p->name); + printf("%d %s %s priority=%d nice=%d\n", p->pid, state, p->name, p->priority, p->nice); printf("\n"); } } diff --git a/kernel/proc.h b/kernel/proc.h index d021857..d591306 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -91,6 +91,10 @@ struct proc { int killed; // If non-zero, have been killed int xstate; // Exit status to be returned to parent's wait int pid; // Process ID + int tracing; // Trace Flag + int counter; // Counter for tracking SYS calls + int nice; // (0 = highest priority) + int priority; // (derived: prio = 3 - nice; higher number = higher priority) // wait_lock must be held when using this: struct proc *parent; // Parent process @@ -98,6 +102,10 @@ struct proc { // these are private to the process, so p->lock need not be held. uint64 kstack; // Virtual address of kernel stack uint64 sz; // Size of process memory (bytes) + + uint64 mmap; // lowest virtual address used by mmap region + int mmap_pages; // number of mmap pages currently mapped + pagetable_t pagetable; // User page table struct trapframe *trapframe; // data page for trampoline.S struct context context; // swtch() here to run process diff --git a/kernel/strace.h b/kernel/strace.h new file mode 100644 index 0000000..3fb96c2 --- /dev/null +++ b/kernel/strace.h @@ -0,0 +1,121 @@ +#include "defs.h" +#include "syscall.h" +#pragma GCC diagnostic ignored "-Wunused-function" + +static uint64 +read_int(int n) +{ + int i; + argint(n, &i); + return i; +} + +static void * +read_ptr(int n) +{ + // TODO: read an argument as a pointer. wait() in sysproc.c + // is a good example of doing this. + uint64 addr; + argaddr(n, &addr); + return (void *)addr; +} + +static char * +read_str(int n) +{ + // TODO: read an argument as a string. open() or mkdir() in sysfile.c + // are good examples of doing this. + static char buf[128]; + if (argstr(n, buf, sizeof(buf)) < 0) + return "(null)"; + return buf; +} + +void +strace(struct proc *p, int syscall_num, uint64 ret_val) +{ + // printf("[strace] syscall: %d\n", syscall_num); + // This will drop the raw syscall output here, so it's commented out for + // now... we need to update this function so that it prints system call + // tracing information (based on the syscall_num passed in). + switch(syscall_num){ + case SYS_fork: + printf("[%d|%s] fork() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_exit: + printf("[%d|%s] exit(int = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_wait: + printf("[%d|%s] wait(status = %p) = %ld\n", p->pid, p->name, read_ptr(0), ret_val); + break; +case SYS_pipe: + printf("[%d|%s] pipe(pipefd = %p) = %ld\n", p->pid, p->name, read_ptr(0), ret_val); + break; +case SYS_read: + printf("[%d|%s] read(fd = %ld, buf = %p, count = %ld) = %ld\n", p->pid, p->name, read_int(0), read_ptr(1), read_int(2), ret_val); + break; +case SYS_write: + printf("[%d|%s] write(fd = %ld, buf = %p, count = %ld) = %ld\n", p->pid, p->name, read_int(0), read_ptr(1), read_int(2), ret_val); + break; +case SYS_close: + printf("[%d|%s] close(fd = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_kill: + printf("[%d|%s] kill(pid = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_exec: + printf("[%d|%s] exec(path = %p, argv = %p) = %ld\n", p->pid, p->name, read_ptr(0), read_ptr(1), ret_val); + break; +case SYS_open: + printf("[%d|%s] open(path = %s, mode = %ld) = %ld\n", p->pid, p->name, read_str(0), read_int(1), ret_val); + break; +case SYS_mknod: + printf("[%d|%s] mknod(path = %s, major = %ld, minor = %ld) = %ld\n", p->pid, p->name, read_str(0), read_int(1), read_int(2), ret_val); + break; +case SYS_unlink: + printf("[%d|%s] unlink(path = %s) = %ld\n", p->pid, p->name, read_str(0), ret_val); + break; +case SYS_fstat: + printf("[%d|%s] fstat(fd = %ld, st = %p) = %ld\n", p->pid, p->name, read_int(0), read_ptr(1), ret_val); + break; +case SYS_link: + printf("[%d|%s] link(old_path = %s, new_path = %s) = %ld\n", p->pid, p->name, read_str(0), read_str(1), ret_val); + break; +case SYS_mkdir: + printf("[%d|%s] mkdir(path = %s) = %ld\n", p->pid, p->name, read_str(0), ret_val); + break; +case SYS_chdir: + printf("[%d|%s] chdir(path = %s) = %ld\n", p->pid, p->name, read_str(0), ret_val); + break; +case SYS_dup: + printf("[%d|%s] dup(fd = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_getpid: + printf("[%d|%s] getpid() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_sbrk: + printf("[%d|%s] sbrk(int = %ld) = %p\n", p->pid, p->name, read_int(0), (void *) ret_val); + break; +case SYS_pause: + printf("[%d|%s] pause(int = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_uptime: + printf("[%d|%s] uptime() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_shutdown: + printf("[%d|%s] shutdown() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_reboot: + printf("[%d|%s] reboot() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_clock: + printf("[%d|%s] clock() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_strace_on: + printf("[%d|%s] strace_on() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_wait2: + printf("[%d|%s] wait2(status = %p, counter = %p) = %ld\n", p->pid, p->name, read_ptr(0), read_ptr(1), ret_val); + break; + } +} diff --git a/kernel/syscall.c b/kernel/syscall.c index 076d965..fc2075c 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -6,6 +6,7 @@ #include "proc.h" #include "syscall.h" #include "defs.h" +#include "strace.h" // Fetch the uint64 at addr from the current process. int @@ -101,6 +102,15 @@ extern uint64 sys_unlink(void); extern uint64 sys_link(void); extern uint64 sys_mkdir(void); extern uint64 sys_close(void); +extern uint64 sys_shutdown(void); +extern uint64 sys_reboot(void); +extern uint64 sys_clock(void); +extern uint64 sys_strace_on(void); +extern uint64 sys_wait2(void); +extern uint64 sys_nice(void); +extern uint64 sys_getcwd(void); +extern uint64 sys_freemem(void); +extern uint64 sys_mmap(void); // An array mapping syscall numbers from syscall.h // to the function that handles the system call. @@ -126,6 +136,15 @@ static uint64 (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, +[SYS_shutdown] sys_shutdown, +[SYS_reboot] sys_reboot, +[SYS_clock] sys_clock, +[SYS_strace_on] sys_strace_on, +[SYS_wait2] sys_wait2, +[SYS_nice] sys_nice, +[SYS_getcwd] sys_getcwd, +[SYS_freemem] sys_freemem, +[SYS_mmap] sys_mmap, }; void @@ -138,7 +157,14 @@ syscall(void) if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { // Use num to lookup the system call function for num, call it, // and store its return value in p->trapframe->a0 - p->trapframe->a0 = syscalls[num](); + p->counter++; // increase sys call counter + + uint64 ptr = syscalls[num](); + + if (p->tracing) { + strace(p, num, ptr); + } + p->trapframe->a0 = ptr; } else { printf("%d %s: unknown sys call %d\n", p->pid, p->name, num); diff --git a/kernel/syscall.h b/kernel/syscall.h index 3dd926d..5e11c4c 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -20,3 +20,12 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 +#define SYS_shutdown 22 +#define SYS_reboot 23 +#define SYS_clock 24 +#define SYS_strace_on 25 +#define SYS_wait2 26 +#define SYS_nice 27 +#define SYS_getcwd 28 +#define SYS_freemem 29 +#define SYS_mmap 30 diff --git a/kernel/sysfile.c b/kernel/sysfile.c index d8234ce..e6ca5ba 100644 --- a/kernel/sysfile.c +++ b/kernel/sysfile.c @@ -406,6 +406,34 @@ sys_mknod(void) return 0; } +uint64 +sys_getcwd(void) +{ + uint64 ubuf; + int sz; + + begin_op(); + argaddr(0, &ubuf); + argint(1, &sz); + + if (sz > PGSIZE) { + end_op(); + return -1; + } + + char *buf = kalloc(); + getcwd(buf, sz); + + if (copyout(myproc()->pagetable, ubuf, buf, sz) < 0) { + end_op(); + return -1; + } + + kfree(buf); + end_op(); + return 0; +} + uint64 sys_chdir(void) { diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 3044d00..58038e7 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -33,7 +33,16 @@ sys_wait(void) { uint64 p; argaddr(0, &p); - return kwait(p); + return kwait2(p, 0); +} + +uint64 +sys_wait2(void) +{ + uint64 p, p2; + argaddr(0, &p); + argaddr(1, &p2); + return kwait2(p, p2); } uint64 @@ -105,3 +114,96 @@ sys_uptime(void) release(&tickslock); return xticks; } + +// shutdown +uint64 +sys_shutdown(void) +{ + volatile uint32 *test_dev = (uint32 *) VIRT_TEST; + *test_dev = 0x5555; + + return 0; +} + +// reboot +uint64 +sys_reboot(void) +{ + volatile uint32 *test_dev = (uint32 *) VIRT_TEST; + *test_dev = 0x7777; + + return 0; +} + +uint64 +sys_clock(void) +{ + volatile uint32 *base = (uint32*)GOLDFISH; + + uint32 first = base[0]; // low 32 bits + uint32 second = base[1]; // high 32 + + return ((uint64)second << 32) | first; +} + +uint64 +sys_strace_on(void) +{ + struct proc *p = myproc(); + p->tracing = 1; + return 0; +} + +uint64 +sys_nice(void) +{ + int n; + argint(0, &n); + + if (n < 0) n = 0; + if (n > 3) n = 3; + + struct proc *p = myproc(); + acquire(&p->lock); + + p->nice = n; + p->priority = 3 - n; + release(&p->lock); + + return p->priority; +} + +uint64 +sys_freemem(void) +{ + uint64 pages = kfreepages(); + return pages * (PGSIZE / 1024); +} + +uint64 +sys_mmap(void) +{ + struct proc *p = myproc(); + + void *pa = kalloc(); + if(pa == 0) + return (uint64)-1; + + memset(pa, 0, PGSIZE); + + uint64 va = p->mmap - PGSIZE; + + if(mappages(p->pagetable, va, PGSIZE, (uint64)pa, + PTE_R | PTE_W | PTE_U) < 0){ + kfree(pa); + return (uint64)-1; + } + + if(walkaddr(p->pagetable, va) != (uint64)pa) + panic("sys_mmap: mapping error"); + + p->mmap = va; + p->mmap_pages++; + + return va; +} diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index 5e3873e..67eb79d 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -4,18 +4,12 @@ #include #include #include -#include -#include -#include -#define dirent xv6_dirent #define stat xv6_stat // avoid clash with host struct stat #include "kernel/types.h" #include "kernel/fs.h" #include "kernel/stat.h" #include "kernel/param.h" -#undef dirent -#undef stat #ifndef static_assert #define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0) @@ -26,9 +20,9 @@ // Disk layout: // [ boot block | sb block | log | inode blocks | free bit map | data blocks ] -int nbitmap = FSSIZE/(BSIZE*8) + 1; +int nbitmap = FSSIZE/BPB + 1; int ninodeblocks = NINODES / IPB + 1; -int nlog = LOGBLOCKS; +int nlog = LOGBLOCKS+1; // Header followed by LOGBLOCKS data blocks. int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap) int nblocks; // Number of data blocks @@ -71,88 +65,12 @@ xint(uint x) return y; } -void -add_file(int parent_fd, uint parentino, char *filename) -{ - uint inum; - int fd; - char buf[BSIZE]; - struct xv6_dirent de; - - if((fd = openat(parent_fd, filename, 0)) < 0) - die(filename); - - inum = ialloc(T_FILE); - - bzero(&de, sizeof(de)); - de.inum = xshort(inum); - strncpy(de.name, filename, DIRSIZ); - iappend(parentino, &de, sizeof(de)); - - ssize_t cc; - while((cc = read(fd, buf, sizeof(buf))) > 0) - iappend(inum, buf, cc); - - close(fd); -} - -uint -add_dir(int level, int parent_fd, uint parentino, char *dirname) -{ - struct xv6_dirent de; - uint dino = ialloc(T_DIR); - bzero(&de, sizeof(de)); - de.inum = xshort(dino); - strcpy(de.name, dirname); - iappend(parentino, &de, sizeof(de)); - - bzero(&de, sizeof(de)); - de.inum = xshort(dino); - strcpy(de.name, "."); - iappend(dino, &de, sizeof(de)); - - bzero(&de, sizeof(de)); - de.inum = xshort(parentino); - strcpy(de.name, ".."); - iappend(dino, &de, sizeof(de)); - - int dir_fd = -1; - if ((dir_fd = openat(parent_fd, dirname, O_RDONLY)) == -1) { - perror("open"); - return dino; - } - - DIR *d = fdopendir(dir_fd); - if (d == NULL) { - perror("fdopendir"); - return dino; - } - - struct dirent *e; - while ((e = readdir(d)) != NULL) { - if (e->d_name[0] == '.') { - continue; - } - - if (e->d_type == DT_REG) { - printf("%*s+ %s\n", level * 2, "", e->d_name); - add_file(dir_fd, dino, e->d_name); - } else if (e->d_type == DT_DIR) { - printf("%*s+ /%s\n", level * 2, "", e->d_name); - add_dir(level + 1, dir_fd, dino, e->d_name); - } - } - close(dir_fd); - - return dino; -} - int main(int argc, char *argv[]) { int i, cc, fd; uint rootino, inum, off; - struct xv6_dirent de; + struct dirent de; char buf[BSIZE]; struct dinode din; @@ -165,9 +83,9 @@ main(int argc, char *argv[]) } assert((BSIZE % sizeof(struct dinode)) == 0); - assert((BSIZE % sizeof(struct xv6_dirent)) == 0); + assert((BSIZE % sizeof(struct dirent)) == 0); - fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); + fsfd = open(argv[1], O_RDWR|O_CREATE|O_TRUNC, 0666); if(fsfd < 0) die(argv[1]); @@ -184,7 +102,7 @@ main(int argc, char *argv[]) sb.inodestart = xint(2+nlog); sb.bmapstart = xint(2+nlog+ninodeblocks); - printf("nmeta %d (boot, super, log blocks %u inode blocks %u, bitmap blocks %u) blocks %d total %d\n", + printf("nmeta %d (boot, super, log blocks %u, inode blocks %u, bitmap blocks %u) blocks %d total %d\n", nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE); freeblock = nmeta; // the first free block that we can allocate @@ -210,13 +128,6 @@ main(int argc, char *argv[]) iappend(rootino, &de, sizeof(de)); for(i = 2; i < argc; i++){ - struct stat sb; - stat(argv[i], &sb); - if (S_ISDIR(sb.st_mode)) { - add_dir(0, AT_FDCWD, rootino, argv[i]); - continue; - } - // get rid of "user/" char *shortname; if(strncmp(argv[i], "user/", 5) == 0) @@ -236,6 +147,8 @@ main(int argc, char *argv[]) if(shortname[0] == '_') shortname += 1; + assert(strlen(shortname) <= DIRSIZ); + inum = ialloc(T_FILE); bzero(&de, sizeof(de)); @@ -327,7 +240,7 @@ balloc(int used) int i; printf("balloc: first %d blocks have been allocated\n", used); - assert(used < BSIZE*8); + assert(used < BPB); bzero(buf, BSIZE); for(i = 0; i < used; i++){ buf[i/8] = buf[i/8] | (0x1 << (i%8)); diff --git a/user/sh.c b/user/sh.c index 278fb37..f5454c6 100644 --- a/user/sh.c +++ b/user/sh.c @@ -77,6 +77,14 @@ runcmd(struct cmd *cmd) if(ecmd->argv[0] == 0) exit(1); exec(ecmd->argv[0], ecmd->argv); + + if (strchr(ecmd->argv[0], '/') == 0) { + char buf[128]; + buf[0] = '/'; + strcpy(buf+1, ecmd->argv[0]); + exec(buf, ecmd->argv); + } + fprintf(2, "exec %s failed\n", ecmd->argv[0]); break; @@ -132,9 +140,12 @@ runcmd(struct cmd *cmd) } int -getcmd(char *buf, int nbuf) +getcmd(char *buf, int nbuf, int is_script) { - write(2, "$ ", 2); + if (!is_script) { + write(2, "$ ", 2); + } + memset(buf, 0, nbuf); gets(buf, nbuf); if(buf[0] == 0) // EOF @@ -143,10 +154,11 @@ getcmd(char *buf, int nbuf) } int -main(void) +main(int argc, char *argv[]) { static char buf[100]; int fd; + int is_script = 0; // flag to check if we are running a script // Ensure that three file descriptors are open. while((fd = open("console", O_RDWR)) >= 0){ @@ -156,12 +168,22 @@ main(void) } } + if (argc > 1) { + is_script = 1; + close(0); // we close stdin + if (open(argv[1], O_RDONLY) < 0) { + fprintf(2, "sh: cannot open %s\n", argv[1]); + exit(1); + } + } + + // Read and run input commands. - while(getcmd(buf, sizeof(buf)) >= 0){ + while(getcmd(buf, sizeof(buf), is_script) >= 0){ char *cmd = buf; while (*cmd == ' ' || *cmd == '\t') - cmd++; - if (*cmd == '\n') // is a blank command + cmd++; + if (*cmd == '\n' || *cmd == '#') // is a blank command continue; if(cmd[0] == 'c' && cmd[1] == 'd' && cmd[2] == ' '){ // Chdir must be called by the parent, not the child. diff --git a/user/user.h b/user/user.h index ac84de9..718b656 100644 --- a/user/user.h +++ b/user/user.h @@ -5,25 +5,34 @@ struct stat; // system calls int fork(void); int exit(int) __attribute__((noreturn)); -int wait(int*); -int pipe(int*); -int write(int, const void*, int); -int read(int, void*, int); -int close(int); -int kill(int); -int exec(const char*, char**); -int open(const char*, int); -int mknod(const char*, short, short); -int unlink(const char*); -int fstat(int fd, struct stat*); -int link(const char*, const char*); -int mkdir(const char*); -int chdir(const char*); -int dup(int); +int wait(int *status); +int pipe(int *pipefd); +int write(int fd, const void *buf, int count); +int read(int fd, void *buf, int count); +int close(int fd); +int kill(int pid); +int exec(const char* path, char **argv); +int open(const char* path, int mode); +int mknod(const char* path, short major, short minor); +int unlink(const char* path); +int fstat(int fd, struct stat* st); +int link(const char* old_path, const char* new_path); +int mkdir(const char* path); +int chdir(const char* path); +int dup(int fd); int getpid(void); -char* sys_sbrk(int,int); +char* sys_sbrk(int n, int incr); int pause(int); int uptime(void); +int shutdown(void); +int reboot(void); +int clock(void); +int strace_on(void); +int wait2(int *status, int *counter); +int nice(int n); +int getcwd(char *, int); +int freemem(void); +void* mmap(void); // ulib.c int stat(const char*, struct stat*); @@ -39,6 +48,7 @@ int memcmp(const void *, const void *, uint); void *memcpy(void *, const void *, uint); char* sbrk(int); char* sbrklazy(int); +int getline(char **buf, int sizep, int fd); // printf.c void fprintf(int, const char*, ...) __attribute__ ((format (printf, 2, 3))); @@ -47,3 +57,8 @@ void printf(const char*, ...) __attribute__ ((format (printf, 1, 2))); // umalloc.c void* malloc(uint); void free(void*); +void* calloc(uint, uint); +void* realloc(void*, uint); +void malloc_name(void *ptr, const char *name); +void malloc_print(void); +void malloc_setfsm(int); diff --git a/user/usys.pl b/user/usys.pl index c5d4c3a..983c857 100755 --- a/user/usys.pl +++ b/user/usys.pl @@ -42,3 +42,12 @@ sub entry { entry("sbrk"); entry("pause"); entry("uptime"); +entry("shutdown"); +entry("reboot"); +entry("clock"); +entry("strace_on"); +entry("wait2"); +entry("nice"); +entry("getcwd"); +entry("freemem"); +entry("mmap"); From db273526ccd6c51a6142088ecc051208a7b5f11f Mon Sep 17 00:00:00 2001 From: Kailash Turimella Date: Tue, 30 Dec 2025 08:34:43 +0530 Subject: [PATCH 3/5] . --- kernel/kalloc.c | 68 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/kernel/kalloc.c b/kernel/kalloc.c index 0699e7e..da58887 100644 --- a/kernel/kalloc.c +++ b/kernel/kalloc.c @@ -9,6 +9,16 @@ #include "riscv.h" #include "defs.h" +// Store a reference count for each physical page +static int ref_count[(PHYSTOP - KERNBASE) / PGSIZE]; + +// Convert physical address to index in reference count array +static inline int +pa2idx(uint64 pa) +{ + return (pa - KERNBASE) / PGSIZE; +} + void freerange(void *pa_start, void *pa_end); extern char end[]; // first address after kernel. @@ -23,6 +33,15 @@ struct { struct run *freelist; } kmem; +void +incref(uint64 pa) +{ + int idx = pa2idx(pa); + acquire(&kmem.lock); + ref_count[idx]++; + release(&kmem.lock); +} + void kinit() { @@ -34,7 +53,7 @@ void freerange(void *pa_start, void *pa_end) { char *p; - p = (char*)PGROUNDUP((uint64)pa_start); + p = (char*)PGROUNDUP((uint64)pa_start); for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) kfree(p); } @@ -47,18 +66,35 @@ void kfree(void *pa) { struct run *r; + uint64 addr = (uint64)pa; - if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) + if((addr % PGSIZE) != 0 || addr < (uint64)end || addr >= PHYSTOP) panic("kfree"); + int idx = pa2idx(addr); + + acquire(&kmem.lock); + + if(ref_count[idx] < 0) + panic("kfree: ref_count underflow"); + + if(ref_count[idx] > 0){ + // normal case: page was allocated by kalloc + ref_count[idx]--; + if(ref_count[idx] > 0){ + // still in use somewhere else – don't free yet + release(&kmem.lock); + return; + } + } + // Fill with junk to catch dangling refs. memset(pa, 1, PGSIZE); r = (struct run*)pa; - - acquire(&kmem.lock); r->next = kmem.freelist; kmem.freelist = r; + release(&kmem.lock); } @@ -72,11 +108,31 @@ kalloc(void) acquire(&kmem.lock); r = kmem.freelist; - if(r) + if(r){ kmem.freelist = r->next; + ref_count[pa2idx((uint64)r)] = 1; // << MUST be inside lock + } release(&kmem.lock); if(r) - memset((char*)r, 5, PGSIZE); // fill with junk + memset((char*)r, 5, PGSIZE); + return (void*)r; +} + +uint64 +kfreepages(void) +{ + uint64 num_pages = 0; + struct run *r; + + acquire(&kmem.lock); + r = kmem.freelist; + while (r) { + num_pages++; + r = r->next; + } + release(&kmem.lock); + + return num_pages; } From 20c1fdc0dced0d328e2fcc95fc228c72cbb6b351 Mon Sep 17 00:00:00 2001 From: Kailash Turimella Date: Tue, 30 Dec 2025 08:39:32 +0530 Subject: [PATCH 4/5] . --- kernel/defs.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/defs.h b/kernel/defs.h index fdec535..81d57ba 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -53,12 +53,15 @@ int readi(struct inode*, int, uint64, uint, uint); void stati(struct inode*, struct stat*); int writei(struct inode*, int, uint64, uint, uint); void itrunc(struct inode*); +int getcwd(char *, uint); void ireclaim(int); // kalloc.c void* kalloc(void); void kfree(void *); void kinit(void); +uint64 kfreepages(void); +void incref(uint64 pa); // log.c void initlog(int, struct superblock*); @@ -97,6 +100,7 @@ void sched(void); void sleep(void*, struct spinlock*); void userinit(void); int kwait(uint64); +int kwait2(uint64, uint64); void wakeup(void*); void yield(void); int either_copyout(int user_dst, uint64 dst, void *src, uint64 len); From c0059dc8d7b4b5b7094af72c6dae08087cdb0728 Mon Sep 17 00:00:00 2001 From: Kailash Turimella Date: Wed, 31 Dec 2025 05:47:42 -0800 Subject: [PATCH 5/5] completed --- Makefile | 6 +- generate-traces.sh | 147 +++++++++++++ kernel/param.h | 2 +- mkfs/mkfs.c | 2 +- test.sh | 9 + tm.txt | 500 +++++++++++++++++++++++++++++++++++++++++++++ user/about.c | 13 ++ user/benchmark.c | 40 ++++ user/broken.c | 324 +++++++++++++++++++++++++++++ user/catlines.c | 40 ++++ user/clear.c | 8 + user/clock.c | 13 ++ user/fnr.c | 29 +++ user/freemem.c | 11 + user/leetify.c | 154 ++++++++++++++ user/mmap.c | 17 ++ user/mmaptest.c | 53 +++++ user/mt70.c | 49 +++++ user/mt80.c | 49 +++++ user/mt90.c | 90 ++++++++ user/nice.c | 32 +++ user/opt_cat.c | 59 ++++++ user/pwd.c | 11 + user/reboot.c | 14 ++ user/shell.c | 389 +++++++++++++++++++++++++++++++++++ user/shutdown.c | 14 ++ user/spinner.c | 28 +++ user/strace.c | 21 ++ user/tolower.c | 12 ++ user/ulib.c | 66 ++++++ user/umalloc.c | 485 ++++++++++++++++++++++++++++++++++++------- user/user.h | 12 +- user/usys.pl | 0 33 files changed, 2613 insertions(+), 86 deletions(-) create mode 100755 generate-traces.sh create mode 100644 test.sh create mode 100644 tm.txt create mode 100644 user/about.c create mode 100644 user/benchmark.c create mode 100644 user/broken.c create mode 100644 user/catlines.c create mode 100644 user/clear.c create mode 100644 user/clock.c create mode 100644 user/fnr.c create mode 100644 user/freemem.c create mode 100644 user/leetify.c create mode 100644 user/mmap.c create mode 100644 user/mmaptest.c create mode 100644 user/mt70.c create mode 100644 user/mt80.c create mode 100644 user/mt90.c create mode 100644 user/nice.c create mode 100644 user/opt_cat.c create mode 100644 user/pwd.c create mode 100644 user/reboot.c create mode 100644 user/shell.c create mode 100644 user/shutdown.c create mode 100644 user/spinner.c create mode 100644 user/strace.c create mode 100644 user/tolower.c mode change 100755 => 100644 user/usys.pl diff --git a/Makefile b/Makefile index 9496185..430d995 100644 --- a/Makefile +++ b/Makefile @@ -127,7 +127,6 @@ mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h UPROGS=\ $U/_about\ - $U/_barca\ $U/_benchmark\ $U/_broken\ $U/_cat\ @@ -158,6 +157,7 @@ UPROGS=\ $U/_rm\ $U/_reboot\ $U/_sh\ + $U/_shell\ $U/_shutdown\ $U/_spinner\ $U/_strace\ @@ -171,8 +171,8 @@ UPROGS=\ $U/_forphan\ $U/_dorphan\ -fs.img: mkfs/mkfs README.md tm.txt input.txt $(UPROGS) script.sh spin1.sh spin2.sh 1.sh 2.sh - mkfs/mkfs fs.img README.md tm.txt input.txt $(UPROGS) script.sh spin1.sh spin2.sh 1.sh 2.sh +fs.img: mkfs/mkfs README.md tm.txt $(UPROGS) 1.sh 2.sh 3.sh 4.sh test.sh + mkfs/mkfs fs.img README.md tm.txt $(UPROGS) 1.sh 2.sh 3.sh 4.sh test.sh -include kernel/*.d user/*.d diff --git a/generate-traces.sh b/generate-traces.sh new file mode 100755 index 0000000..2207f29 --- /dev/null +++ b/generate-traces.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# This script is designed to be run on a Linux host (i.e., not in xv6) +# You should probably run this script to see what it does first. To do that, you +# may need to make it executable: +# +# chmod +x generate-traces.sh +# +# And then run it from the *root* of your OS directory. +# +# +# For each user space system call stub, this script will print out information +# about its name, arguments, and return values. +# +# You can add information to the stubs that this script will extract. For +# example, instead of: +# +# int write(int, const void *, int); +# +# You can add argument names: +# +# int write(int fd, const void *buf, int count); +# +# And this script will extract them. By default, pointers are assumed to be +# addresses, but if you want the script to treat them like strings you can add a +# bit of metadata with a C comment: +# +# int mkdir(const char* /*str*/ path); +# +# Here's a few examples from a user/user.h file: +# int exit(int status) __attribute__((noreturn)); +# int wait(int *status); +# int pipe(int *pipefd); +# int mkdir(const char* /*str*/ path); + + +# The following code extracts function information from user/user.h based on the +# system calls found in user/usys.pl. + +# Retrieves the type of the argument provided as a printf format specifier +get_type() { + case "${1}" in + *'/*str*/'*) echo "%s" ;; + *"*"*) echo "%p" ;; + *) echo "%ld" ;; # default to int + esac +} + +# Determines which function to call for a given format specifier. +get_conversion() { + case "${1}" in + '%p') echo "read_ptr(${2})" ;; + '%s') echo "read_str(${2})" ;; + '%ld') echo "read_int(${2})" ;; + esac +} + +# Inspects the user/user.h file to extract system call information +syscall_output=$( +for i in $(sed -n 's/entry("\(.*\)");/\1/p' user/usys.pl); do + return_value=$(sed -nE \ + "s:\s*([A-Za-z_][A-Za-z0-9_\* ]+)\s+${i}.*:\1:p;" user/user.h) + args=$(sed 's/const//g; s/void//g; s/struct//g; s/((noreturn))//g' user/user.h \ + | sed -nE "s:.*\s${i}\((.*)\).*:\1:p") + + # Extract argument names and types for printing + arg_list="" + arg_convs="" + arg_count=0 + export IFS="," + for arg in $args; do + arg_name=$(echo "${arg}" | sed -nE 's:.*\b([A-Za-z0-9_]+)\s*:\1:p') + arg_type=$(get_type "${arg}") + arg_conv=$(get_conversion "${arg_type}" "${arg_count}") + arg_list="${arg_list}${arg_name} = ${arg_type}, " + arg_convs="${arg_convs}${arg_conv}, " + (( arg_count++ )) + done + + # Remove extra ', ' + [[ "${arg_count}" -gt 0 ]] && arg_list="${arg_list::-2}" + + # cast the return type, if necessary + return_type="$(get_type "${return_value}")" + cast_ret="" + if [[ "${return_type}" == "%p" ]]; then + cast_ret="(void *)" + fi + + # Print out the C code to display system call information. You will probably + # need to modify this so it can be used to conditionally print out the + # appropriate info. + echo "case SYS_${i}:" + echo " printf(\"[%d|%s] ${i}(${arg_list}) = $(get_type "${return_value}")\\n\", p->pid, p->name, ${arg_convs} ${cast_ret} ret_val);" + echo " break;" +done +) + +# The information we want has been extracted and stored in $syscall_output. +# Using this information, we'll generate C code below. When we run 'cat < [args...]\n", argv[0]); + exit(1); + } + + uint64 start = clock(); + + int pid = fork(); + if (pid < 0) { + fprintf(2, "benchmark: fork failed\n"); + exit(1); + } + if (pid == 0) { + exec(argv[1], &argv[1]); + fprintf(2, "benchmark: exec %s failed\n", argv[1]); + exit(1); + } + + int status; + int counter; + + if (wait2(&status, &counter) < 0) { + fprintf(2, "benchmark: wait2 failed\n"); + exit(1); + } + + uint64 end = clock(); + uint64 time_taken = end - start; + uint64 time_taken_ms = time_taken / 1000000ULL; + + printf("Benchmark Complete\n"); + printf("Time Elapsed: %d ms\n", (int)time_taken_ms); + printf("System Calls: %d\n", (int)counter); + exit(0); +} diff --git a/user/broken.c b/user/broken.c new file mode 100644 index 0000000..779f9ed --- /dev/null +++ b/user/broken.c @@ -0,0 +1,324 @@ +/** + * @file broken.c + * @author mmalensek + * + * This program contains a series of buggy, broken, or strange C functions for + * you to ponder. Your job is to analyze each function, fix whatever bugs the + * functions might have, and then explain what went wrong. Sometimes the + * compiler will give you a hint. + * + * ____________ + * < Good luck! > + * ------------ + * \ ^__^ + * \ (oo)\_______ + * (__)\ )\/\ + * ||----w | + * || || + */ + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + + +static int func_counter = 1; +#define FUNC_START() printf("\n\n%d.) %s\n", func_counter++, __func__); + +#pragma GCC diagnostic ignored "-Waggressive-loop-optimizations" +#pragma GCC diagnostic ignored "-Wdangling-pointer" +#pragma GCC diagnostic ignored "-Wformat-overflow" + +/** + * This code example was taken from the book 'Mastering C Pointers,' one of the + * not so good ways to learn pointers. It was trying to demonstrate how to print + * 'BNGULAR'... with pointers...? Maybe? + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * String literals are stored in read only memory + * '66' in this example is 'B'. What we can do is set a[0] = 'B' to replace the first char in the string. + */ +void +angular(void) +{ + FUNC_START(); + + char a[] = "ANGULAR"; + a[0] = 'B'; + printf("%s\n", a); +} + +/** + * This function is the next step after 'Hello world' -- it takes user input and + * prints it back out! (Wow). + * + * But, unfortunately, it doesn't work. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * The main problem for this one is that we have to allocate space + for the string. I tried to change gets to fgets but I don't think + that works because using stdio.h didn't work. + + */ +void +greeter(void) +{ + FUNC_START(); + + char name[128]; + + printf("Please enter your name: "); + gets(name, 128); + + // Remove newline character + char *p = name; + for ( ; *p != '\n' && *p != 0; p++) { } + *p = '\0'; + + printf("Hello, %s!\n", name); +} + +/** + * This code isn't so much broken, but more of an exercise in understanding how + * C data types are represented in memory. + * + * (1) Fill in your guesses below to see if you can get all 6 correct on the + * first try. + * (2) Explain *WHY* you get the sizes you do for each of the 6 entries. + * sizeof(int) and sizeof(float) are fairly straightforward. + + A char is always 1. Both ints and floats are 4. + things is 48 because it is an array of 12 ints. so 12 x 4 = 48 + 'A' is an int so thats why its 4 bytes. + "A" is a string literal so it is 2 bytes. The null terminator "\0" is included in there. + * + */ +#define SIZE_CHECK(sz, guess) (sz), sz == guess ? "Right!" : "Wrong!" +void +sizer(void) +{ + FUNC_START(); + + int guesses[] = {1, 4, 4, 48, 4, 2 }; // fill in 6 guesses here + if (sizeof(guesses) / sizeof(int) != 6) { + printf("Please enter a guess for each of the sizes below.\n"); + printf("Example: guesses[] = { 1, 4, 0, 0, 0, 0 }\n"); + printf("would mean sizeof(char) == 1, sizeof(int) == 4, and so on"); + return; + } + + int things[12] = { 0 }; + printf("sizeof(char) = %ld\t[%s]\n", SIZE_CHECK(sizeof(char), guesses[0])); + printf("sizeof(int) = %ld\t[%s]\n", SIZE_CHECK(sizeof(int), guesses[1])); + printf("sizeof(float) = %ld\t[%s]\n", SIZE_CHECK(sizeof(float), guesses[2])); + printf("sizeof(things) = %ld\t[%s]\n", SIZE_CHECK(sizeof(things), guesses[3])); + printf("sizeof('A') = %ld\t[%s]\n", SIZE_CHECK(sizeof('A'), guesses[4])); + printf("sizeof(\"A\") = %ld\t[%s]\n", SIZE_CHECK(sizeof("A"), guesses[5])); +} + +/** + * This 'useful' function prints out an array of integers with their indexes, or + * at least tries to. It even has a special feature where it adds '12' to the + * array. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * One problem I found is that the loop is until < 1000, which exceeds the size we set for + the stuff array which is 100. + Another problem or inconvinience is the 14[stuff + 1] = 12. At first I didn't understand it, + but after searching online what it means. It basically just sets 12 to index 15. + */ +void +displayer(void) +{ + FUNC_START(); + + int stuff[100] = { 0 }; + + /* Can you guess what the following does without running the program? */ + /* Rewrite it so it's easier to read. */ + stuff[15] = 12; + + for (int i = 0; i < 100; ++i) { + printf("%d: %d\n", i, stuff[i]); + } +} + +/** + * Adds up the contents of an array and prints the total. Unfortunately the + * total is wrong! See main() for the arguments that were passed in. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * The problem is that sizeof(arr) doesn't work as intended. It wouldn't get to the + end of the loop. To fix it I changed the function call so we can get the correct size of + array is use that to loop through. + */ +void +adder(int *arr, int n) +{ + FUNC_START(); + + int total = 0; + + for (int i = 0; i < n; ++i) { + total += arr[i]; + } + + printf("Total is: %d\n", total); +} + +/** + * This function is supposed to be somewhat like strcat, but it doesn't work. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * The problem is that it returns a pointer to a local array. When the function returns, it finishes. + To fix we use malloc to allocate it on the heap. + * + */ +char * +suffixer(char *a, char *b) +{ + FUNC_START(); + + char *buf = malloc(strlen(a) + strlen(b) + 1); + if (!buf) return 0; + + char *buf_start = buf; + + strcpy(buf, a); + strcpy(buf + strlen(a), b); + return buf_start; + +} + +/** + * This is an excerpt of Elon Musk's Full Self Driving code. Unfortunately, it + * keeps telling people to take the wrong turn. Figure out how to fix it, and + * explain what's going on so Elon can get back to work on leaving Earth for + * good. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * One problem is that in the if statement we aren't checking if its actually + true or not. So we have to add == 0 for it to work. The whole string comparison + part is also not needed. + + There is also 2 overflow issues. + */ +void +driver(void) +{ + FUNC_START(); + + char street1[10] = { "fulton" }; + char street2[10] = { "gomery" }; + char street3[10] = { "piedmont" }; + char street4[10] = { "baker" }; + char street5[10] = { "haight" }; + + if (strcmp(street1, street2) == 0) { + char *new_name = "saint agnew "; + memcpy(street4, new_name, strlen(new_name)); + } + + printf("Welcome to TeslaOS 0.1!\n"); + printf("Calculating route...\n"); + printf("Turn left at the intersection of %s and %s.\n", street5, street3); +} + +/** + * This function tokenizes a string by space, sort of like a basic strtok or + * strsep. It has two subtle memory bugs for you to find. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * One problem was is a size bug. the malloc allocates a byte too few + so strcpy(line, str) overflows. + Once the scan hits '\0', if it advances it would go past the buffer. + + */ +void +tokenizer(void) +{ + FUNC_START(); + + char *str = "Hope was a letter I never could send"; + char *line = malloc(strlen(str) + 1); + char *base = line; + char *c = line; + + strcpy(line, str); + + while (*c != '\0') { + + for ( ; *c != ' ' && *c != '\0'; c++) { + // find the next space (or end of string) + } + + if (*c == '\0') { + printf("%s\n", line); + break; + } + + *c = '\0'; + printf("%s\n", line); + + line = c + 1; + c = line; + } + + free(base); +} + +/** +* This function should print one thing you like about C, and one thing you +* dislike about it. Assuming you don't mess up, there will be no bugs to find +* here! +*/ +void +finisher(void) +{ + FUNC_START(); + + // TODO + printf("Something I like about C is how you can control much more about how everything works.\n"); + printf("Something I don't like is that having a lot more control means more stuff can go wrong :( "); +} + +int +main(void) +{ + printf("Starting up!"); + + + angular(); + + greeter(); + + sizer(); + + displayer(); + + int nums[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 }; + adder(nums, sizeof(nums) / sizeof(nums[0])); + + char *result = suffixer("kayak", "ing"); + printf("Suffixed: %s\n", result); + + driver(); + + tokenizer(); + + finisher(); + + return 0; +} diff --git a/user/catlines.c b/user/catlines.c new file mode 100644 index 0000000..9de4315 --- /dev/null +++ b/user/catlines.c @@ -0,0 +1,40 @@ +#include "kernel/fcntl.h" +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +fgets7(char *buf, int max, int fd) +{ + int i, cc; + char c; + + for (i = 0; i + 1 < max; ) { + cc = read(fd, &c, 1); + if(cc < 1) + break; + buf[i++] = c; + if(c == '\n' || c == '\r') + break; + } + buf[i] = '\0'; + return i; +} + +int +main(int argc, char *argv[]) +{ + if (argc <= 1) { + fprintf(2, "Usage: %s filename\n", argv[0]); + return 1; + } + + int fd = open(argv[1], O_RDONLY); + char buf[128]; + int line_count = 0; + while (fgets7(buf, 128, fd) > 0 ) { + printf("Line %d: %s", line_count++, buf); + } + + return 0; +} diff --git a/user/clear.c b/user/clear.c new file mode 100644 index 0000000..b54b651 --- /dev/null +++ b/user/clear.c @@ -0,0 +1,8 @@ +#include "kernel/types.h" +#include "user/user.h" + +int +main(void) { + write(1, "\x1b[2J\x1b[H", 7); + exit(0); +} diff --git a/user/clock.c b/user/clock.c new file mode 100644 index 0000000..627a700 --- /dev/null +++ b/user/clock.c @@ -0,0 +1,13 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(void) +{ + uint64 ns = clock(); // call the clock function + uint64 sec64 = ns / 1000000000ULL; // convert to seconds + printf("%d\n", (int)sec64); + exit(0); + +} diff --git a/user/fnr.c b/user/fnr.c new file mode 100644 index 0000000..140ecb2 --- /dev/null +++ b/user/fnr.c @@ -0,0 +1,29 @@ +#include "kernel/types.h" +#include "user/user.h" + +int main(int argc, char *argv[]) { + char *line = malloc(512); + while (getline(&line, 512, 0) > 0) { + for (int i = 1; i < argc; i += 2) { + char *find = argv[i]; + char *repl = argv[i + 1]; + uint find_len = strlen(find); + if (strlen(line) < find_len) { + continue; + } + for (int j = 0; j < strlen(line) - find_len; ++j) { + char *p = line + j; + char *q = find; + int k; + for (k = 0; *p && *p == *q && k < find_len; ++k) { + p++, q++; + } + if (k == find_len) { + memcpy(line + j, repl, find_len); + } + } + } + printf("%s", line); + } + return 0; +} diff --git a/user/freemem.c b/user/freemem.c new file mode 100644 index 0000000..0c87fb5 --- /dev/null +++ b/user/freemem.c @@ -0,0 +1,11 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(void) +{ + int k = freemem(); + printf("%d KiB\n", k); + exit(0); +} diff --git a/user/leetify.c b/user/leetify.c new file mode 100644 index 0000000..b18fc2b --- /dev/null +++ b/user/leetify.c @@ -0,0 +1,154 @@ +/** + * @file leetify.c + * + * Scaffolding to create an amazing l337speak generator using only external + * commands combined with pipelines. + */ + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fcntl.h" + +/** + * Represents a command in a pipeline. + * Note that EITHER stdout_pipe can be true, or a stdout_file can be set. + * + * - tokens Array of strings that describe the command to run + * - stdout_pipe set to true if this command's output should be written to pipe + * - stdout_file set to a file name if this command's output should be written + * to a file + */ +struct command { + char **tokens; + int stdout_pipe; + char *stdout_file; +}; + +void +execute_pipeline(struct command *cmds, int n) +{ + + // go through all the commands + for (int i = 0; i < n - 1; i++) { + + int p[2]; // p[0] stdin, p[1] stdout + if (pipe(p) < 0) { + fprintf(2, "pipe failed\n"); + exit(1); + } + + int pid = fork(); + if (pid < 0) { + fprintf(2, "fork failed\n"); + exit(1); + } + + if (pid == 0) { + // child + + // redirect stdout to write end of pipe + close(p[0]); // close read + close(1); // redirect stdout -> pipe write end + dup(p[1]); + close(p[1]); + + // exec + exec(cmds[i].tokens[0], cmds[i].tokens); + + fprintf(2, "exec %s failed\n", cmds[i].tokens[0]); + exit(1); + } else { + // parent + + // close write end of pipe + close(p[1]); + + // redirect stdin + close(0); + dup(p[0]); + + // close copy of the read end + close(p[0]); + + } + } + struct command *final_command = &cmds[n-1]; + if (final_command->stdout_file) { + int fd = open(final_command->stdout_file, O_CREATE | O_WRONLY | O_TRUNC); + if (fd < 0) { + fprintf(2, "open failed for %s\n", final_command->tokens[0]); + exit(1); + } + close(1); + dup(fd); + close(fd); + } + + exec(final_command->tokens[0], final_command->tokens); + fprintf(2, "exec failed: %s\n", final_command->tokens[0]); + exit(1); + +} + +int +main(int argc, char *argv[]) +{ + char *input_file = (char *)0; + char *output_file = (char *) 0; + + if (argc < 2 || argc > 3) { + printf("Usage: %s file-to-leetify [output-file]\n", argv[0]); + return 1; + } + + input_file = argv[1]; + + if (argc == 3) { + output_file = argv[2]; + } + + printf("Input file: %s\n", input_file); + if (output_file) { + printf("Writing to output file: %s\n", output_file); + } + + // cat input file + char *command1[] = { "cat", input_file, (char *)0 }; + // tolower + char *command2[] = { "tolower", (char *)0 }; + // fnr + char *command3[] = { "fnr", "the", "teh", "a", "4", "e", "3", "i", "!", (char *)0 }; + // fnr 2 + char *command4[] = { "fnr", "l", "1", "o", "0", "s", "5", (char *)0 }; + + struct command cmds[4] = { 0 }; + cmds[0].tokens = command1; /* What this command runs */ + cmds[0].stdout_pipe = 1; + cmds[0].stdout_file = (char *)0; /* This command is not writing to a file. */ + + cmds[1].tokens = command2; + cmds[1].stdout_pipe = 1; + cmds[1].stdout_file = (char *)0; /* This command's output goes to a pipe. */ + + cmds[2].tokens = command3; + cmds[2].stdout_pipe = 1; + cmds[2].stdout_file = (char *)0; + + cmds[3].tokens = command4; + cmds[3].stdout_pipe = 0; /* Last command so set stdout_pipe = false */ + cmds[3].stdout_file = output_file; + + int pid = fork(); + if (pid == -1) { + fprintf(2, "Fork failed\n"); + return 1; + } else if (pid == 0) { + execute_pipeline(cmds, 4); + } else { + int status; + wait(&status); + } + + return 0; +} diff --git a/user/mmap.c b/user/mmap.c new file mode 100644 index 0000000..d3f8523 --- /dev/null +++ b/user/mmap.c @@ -0,0 +1,17 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(void) +{ + char *p = (char*)mmap(); + if (!p) { + printf("mmap failed\n"); + exit(1); + } + + strcpy(p, "hello from mmap"); + printf("%s\n", p); + exit(0); +} diff --git a/user/mmaptest.c b/user/mmaptest.c new file mode 100644 index 0000000..f3477d9 --- /dev/null +++ b/user/mmaptest.c @@ -0,0 +1,53 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(void) +{ + char *a = mmap(); + char *b = mmap(); + char *c = mmap(); + + strcpy(a, "a: hello"); + strcpy(b, "b: world"); + strcpy(c, "c: original"); + + printf("parent original:\n%s\n%s\n%s\n\n", a, b, c); + + + int pid = fork(); + + if(pid == 0){ + + printf("child sees after fork:\n%s\n%s\n%s\n\n", a, b, c); + + // child modifies first two pages + strcpy(a, "a: spurs"); + strcpy(b, "b: child changed world"); + + // forks and makes another child (grandchild) + int pid2 = fork(); + if(pid2 == 0){ + printf("grandchild sees:\n%s\n%s\n%s\n\n", a, b, c); + + // grandchild modifies the 3rd page + strcpy(c, "C: grandchild updated this"); + + printf("grandchild done.\n"); + exit(0); + } + + // child waits for grandchild to finish updating page C + wait(0); + + printf("child after grandchild:\n%s\n%s\n%s\n\n", a, b, c); + exit(0); + } + + wait(0); + + printf("parent final:\n%s\n%s\n%s\n\n", a, b, c); + + exit(0); +} diff --git a/user/mt70.c b/user/mt70.c new file mode 100644 index 0000000..4776483 --- /dev/null +++ b/user/mt70.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; +} 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; +} diff --git a/user/mt90.c b/user/mt90.c new file mode 100644 index 0000000..c126462 --- /dev/null +++ b/user/mt90.c @@ -0,0 +1,90 @@ +#include "kernel/types.h" +#include "user/user.h" + +#pragma GCC diagnostic ignored "-Wunused-variable" + +void +merge_test(void) +{ + char *start = sbrk(0); + printf("Starting address: %p\n\n", start); + + void *a = malloc(532); malloc_name(a, "A"); + void *b = malloc(326); malloc_name(b, "B"); + void *c = malloc(282); malloc_name(c, "C"); + void *d = malloc(1032); malloc_name(d, "D"); + void *e = malloc(432); malloc_name(e, "E"); + + free(b); + free(c); + free(a); + free(d); + free(e); + + malloc_print(); + + printf("\n\n"); + + void *f = malloc(632); malloc_name(f, "F"); + void *g = malloc(182); malloc_name(f, "G"); + void *h = malloc(9928); malloc_name(f, "H"); + + free(f); + free(g); + free(h); + + malloc_print(); + + char *end = sbrk(0); + printf("\n\nEnding address: %p\n", end); + + if (start == end) { + printf("Success! start == end!\n"); + } +} + +void +realloc_test(void) +{ + char *a = malloc(532); malloc_name(a, "A"); + void *b = malloc(326); malloc_name(b, "B"); + void *c = malloc(282); malloc_name(c, "C"); + void *d = malloc(1032); malloc_name(d, "D"); + void *e = malloc(432); malloc_name(e, "E"); + + /* Does not actually need to be resized */ + a = realloc(a, 542); malloc_name(a, "R1"); + + /* No room, must free + allocate new */ + c = realloc(c, 9632); malloc_name(c, "R2"); + + /* Now that 'c' is free, we can expand 'b' into it */ + b = realloc(b, 632); malloc_name(b, "R3"); + + /* Expand into free space at the end of the region. This will produce a new + * free block out of the remaining space */ + e = realloc(e, 1312); malloc_name(e, "R4"); + + /* Shrink 'd' down to make a new free block */ + d = realloc(d, 832); malloc_name(d, "R5"); + + /* Shrink 'd' one more time to make sure that the two neighboring free + * blocks get merged back together */ + d = realloc(d, 632); malloc_name(d, "R6"); + + malloc_print(); +} + +int +main(void) +{ + printf("---- MERGE TEST ----\n"); + merge_test(); + printf("\n\n"); + + printf("---- REALLOC TEST ----\n"); + realloc_test(); + printf("\n\n"); + + return 0; +} diff --git a/user/nice.c b/user/nice.c new file mode 100644 index 0000000..fe0b3e6 --- /dev/null +++ b/user/nice.c @@ -0,0 +1,32 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int main(int argc, char *argv[]) { + if (argc < 3) { + fprintf(2, "usage: nice <0..3> cmd [args...]\n"); + exit(1); + } + int n = atoi(argv[1]); + if (n < 0) + n = 0; + if (n > 3) + n = 3; + + int pid = fork(); + + if (pid < 0) { + fprintf(2, "nice: fork failed\n"); + exit(1); + } + + if (pid == 0) { + nice(n); + exec(argv[2], &argv[2]); + fprintf(2, "nice: exec %s failed\n", argv[2]); + exit(1); + } + + wait(0); + exit(0); +} diff --git a/user/opt_cat.c b/user/opt_cat.c new file mode 100644 index 0000000..ed4b763 --- /dev/null +++ b/user/opt_cat.c @@ -0,0 +1,59 @@ +#include "kernel/fcntl.h" +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +static int +fgets3(char *out, int max, int fd) +{ + enum { CAP = 8192 }; + static char storage[CAP]; + static int r = 0, w = 0; + int outi = 0; + + for (;;) { + if (r >= w) { + r = w = 0; + int n = read(fd, storage, CAP); + if (n <= 0) { + if (outi == 0) return 0; + out[outi] = '\0'; + return outi; + } + w = n; + } + + while (r < w && outi + 1 < max) { + char c = storage[r++]; + out[outi++] = c; + if (c == '\n' || c == '\r') { + out[outi] = '\0'; + return outi; + } + } + + if (outi + 1 >= max) { + out[outi] = '\0'; + return outi; + } + } +} + + +int +main(int argc, char *argv[]) +{ + if (argc <= 1) { + fprintf(2, "Usage: %s filename\n", argv[0]); + return 1; + } + + int fd = open(argv[1], O_RDONLY); + char buf[128]; + int line_count = 0; + while (fgets3(buf, 128, fd) > 0 ) { + printf("Line %d: %s", line_count++, buf); + } + + return 0; +} diff --git a/user/pwd.c b/user/pwd.c new file mode 100644 index 0000000..165ab8b --- /dev/null +++ b/user/pwd.c @@ -0,0 +1,11 @@ +#include "kernel/types.h" +#include "user/user.h" + +int +main(void) +{ + char buf[128]; + getcwd(buf, 128); + printf("%s\n", buf); + return 0; +} diff --git a/user/reboot.c b/user/reboot.c new file mode 100644 index 0000000..a44da64 --- /dev/null +++ b/user/reboot.c @@ -0,0 +1,14 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char **argv) +{ + printf("Rebooting...\n"); + reboot(); + + // error message just in case + fprintf(2, "reboot failed!\n"); + exit(1); +} diff --git a/user/shell.c b/user/shell.c new file mode 100644 index 0000000..f315f55 --- /dev/null +++ b/user/shell.c @@ -0,0 +1,389 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fcntl.h" + +int fgets(char *buf, unsigned int max, int fd); + +uint +strspn(const char *str, const char *chars) +{ + uint i, j; + for (i = 0; str[i] != '\0'; i++) { + for (j = 0; chars[j] != str[i]; j++) { + if (chars[j] == '\0') + return i; + } + } + return i; +} + +uint +strcspn(const char *str, const char *chars) +{ + const char *p, *sp; + char c, sc; + for (p = str;;) { + c = *p++; + sp = chars; + do { + if ((sc = *sp++) == c) { + return (p - 1 - str); + } + } while (sc != 0); + } +} + +char +*next_token(char **str_ptr, const char *delim) +{ + if (*str_ptr == 0) { + return 0; + } + + uint tok_start = strspn(*str_ptr, delim); + uint tok_end = strcspn(*str_ptr + tok_start, delim); + + /* Zero length token. We must be finished. */ + if (tok_end == 0) { + *str_ptr = 0; + return 0; + } + + /* Take note of the start of the current token. We'll return it later. */ + char *current_ptr = *str_ptr + tok_start; + + /* Shift pointer forward (to the end of the current token) */ + *str_ptr += tok_start + tok_end; + + if (**str_ptr == '\0') { + /* If the end of the current token is also the end of the string, we + * must be at the last token. */ + *str_ptr = 0; + } else { + /* Replace the matching delimiter with a NUL character to terminate the + * token string. */ + **str_ptr = '\0'; + + /* Shift forward one character over the newly-placed NUL so that + * next_pointer now points at the first character of the next token. */ + (*str_ptr)++; + } + + return current_ptr; +} + +// helper func to just print the basic prompt +static void print_prompt(int last_status, int cmd_num) { + char path[128]; + int n = getcwd(path, sizeof(path)); + if (n < 0) { + path[0] = '/'; path[1] = 0; + } + printf("[%d]-[%d]-[%s]$ ", last_status, cmd_num, path); +} + +static int is_blank(const char *s) { + for (; *s; s++) { + if (*s!= ' ' && *s!= '\t' && *s!= '\n' && *s!= '\r') return 0; + } + return 1; +} + +// helper fun to remove remove newliens and return characters +static void strip_trailing_newline(char *buf, int *len_io) { + int len = *len_io; + while (len > 0 && (buf[len-1] == '\n' || buf[len-1] == '\r')) { + buf[--len] = 0; + } + *len_io = len; +} + +// helper to check for comments and remove them +static void remove_comment(char *buf) { + for (int i = 0; buf[i]; i++) { + if (buf[i] == '#') { + buf[i] = 0; + break; + } + } +} + +#define MAX_HIST 100 +struct history_entry { + int num; + char line[256]; + int duration_ms; +}; + +static struct history_entry hist[MAX_HIST]; + +// method to add to the history +static void add_history(const char *line, int cmd_num) { + if (!line || !*line) return; + int slot = (cmd_num - 1) % MAX_HIST; + hist[slot].num = cmd_num; + + int i = 0; + for (; i < (int)sizeof(hist[slot].line) - 1 && line[i]; i++) { + hist[slot].line[i] = line[i]; + } + hist[slot].line[i] = 0; +} + +// actual history command +static void history_command(int current_cmd_num, int show_time) { + int last_done = current_cmd_num - 1; // last completed command + if (last_done < 1) return; + + int first = last_done - MAX_HIST + 1; + if (first < 1) first = 1; + + for (int n = first; n <= last_done; n++) { + int slot = (n - 1) % MAX_HIST; + if (hist[slot].num == n && hist[slot].line[0] != 0) { + if (show_time) { + printf("[%d|%dms] %s\n", hist[slot].num, hist[slot].duration_ms, hist[slot].line); + } else { + printf("%d %s\n", hist[slot].num, hist[slot].line); + } + } + } +} + +// lookup in history with number arg +static const char* hist_num(int num) { + if (num < 1) return 0; + int slot = (num - 1) % MAX_HIST; + + // make sure that everything matches and line isn't empty + if (hist[slot].num == num && hist[slot].line[0] != 0) return hist[slot].line; + + return 0; +} + +// lookup in history with prefix arg +static const char* hist_prefix(const char *prefix, int last_done) { + int prefix_len = 0; + while(prefix[prefix_len]) + prefix_len++; + + // go through all the commands + // from newest to oldest + for (int n = last_done; n >= 1; n--) { + int slot = (n - 1) % MAX_HIST; + + if (hist[slot].num != n || hist[slot].line[0] == 0) + continue; + + const char *s = hist[slot].line; + + // compare prefix against start of the command + int i = 0; + while (i < prefix_len && s[i] == prefix[i]) + i++; + + // if they match return the string + if (i == prefix_len) + return s; + } + + return 0; +} + +int +main(int argc, char *argv[]) +{ + + // -1. print a prompt + // 0. get the user's command (stdin) + // 1. fork + // 2. exec + + + int last_status = 0; // 0 = success, 1 = fail + int cmd_num = 1; + int scripting = 0; + int script_fd = -1; + + if (argc > 1) { + script_fd = open(argv[1], O_RDONLY); + if (script_fd < 0) { + fprintf(2, "shell: cannot open %s", argv[1]); + exit(1); + } + scripting = 1; + } + + + while (1) { + if (!scripting) { + print_prompt(last_status, cmd_num); + } + + char buf[256]; + + int r; + if (scripting) { + int i = 0; + while (i + 1 < sizeof(buf)) { + char c; + r = read(script_fd, &c, 1); + if (r < 1) { + break; + } + buf[i++] = c; + if (c == '\n') + break; + } + buf[i] = 0; + + if (i == 0) { + //fprintf(2, "shell: script EOF, exiting\n"); + exit(0); + } + } else { + if (gets(buf, sizeof(buf)) == 0) { + printf("\n"); + exit(0); + } + } + + + int got = strlen(buf); + + // remove "\n" and comments + strip_trailing_newline(buf, &got); + remove_comment(buf); + + // skip blank lines + if (is_blank(buf)) { + continue; + } + + /* + Extra history commands: + 1. "!" -> last command + 2. "!5" -> command number 5 + 3. "!prefix" -> most recent command with the prefix + */ + + if (buf[0] == '!') { + int last_done = cmd_num - 1; + const char *expanded = 0; + + // the prev command + if (buf[1] == '!' && buf[2] == 0) { + expanded = hist_num(last_done); + } else if (buf[1] >= '0' && buf[1] <= '9') { + int n = 0; + for (int i = 1; buf[i] >= '0' && buf[i] <= '9'; i++) { + n = n * 10 + (buf[i] - '0'); + } + expanded = hist_num(n); + } else { + // !prefix + expanded = hist_prefix(buf + 1, last_done); + } + + if (!expanded) { + fprintf(2, "history: no match for '%s'\n", buf); + last_status = 1; + cmd_num++; + continue; + } + + + int i = 0; + while (i < (int)sizeof(buf) - 1 && expanded[i]) { + buf[i] = expanded[i]; + i++; + } + buf[i] = 0; + + printf("%s\n", buf); + } + + // record this command before executing so numbers align + add_history(buf, cmd_num); + + + char *args[32]; + int tokens = 0; + char *next_tok = buf; + char *curr_tok; + /* Tokenize. */ + while ((curr_tok = next_token(&next_tok, " \n\t\r")) != 0 && tokens < 31) { + // printf("Token: '%s'\n", curr_tok); + args[tokens++] = curr_tok; + } + args[tokens] = 0; + if (tokens == 0) { + continue; + } + + + // built in commands + if (strcmp(args[0], "exit") == 0) { + exit(0); + } + + // history command + if (strcmp(args[0], "history") == 0) { + int show_time = 0; + if (tokens > 1 && strcmp(args[1], "-t") == 0) + show_time = 1; + history_command(cmd_num, show_time); + last_status = 0; + cmd_num++; + continue; + } + + + if (strcmp(args[0], "cd") == 0) { + if (tokens < 2) { + fprintf(2, "chdir: missing arg\n"); + last_status = 1; + } else if (chdir(args[1]) < 0) { + fprintf(2, "chdir: no file or directory found: %s\n", args[1]); + last_status = 1; + } else { + last_status = 0; + } + cmd_num++; + continue; + } + + uint64 start = clock(); + + int pid = fork(); + if (pid < 0 ) { + fprintf(2, "Fork failed"); + last_status = 1; + cmd_num++; + continue; + + } else if (pid == 0) { + // child + // execute the command + exec(args[0], args); + + fprintf(2, "exec failed: %s\n", args[0]); + exit(1); + + } else { + wait(0); + + uint64 end = clock(); + uint64 diff = end - start; + int diff_ms = (int)(diff / 1000000ULL); + + int slot = (cmd_num - 1) % MAX_HIST; + hist[slot].duration_ms = diff_ms; + + cmd_num++; + } + } + + return 0; +} diff --git a/user/shutdown.c b/user/shutdown.c new file mode 100644 index 0000000..418275a --- /dev/null +++ b/user/shutdown.c @@ -0,0 +1,14 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char **argv) +{ + printf("Shutting down...\n"); + shutdown(); + + // error message just in case + fprintf(2, "shutdown failed!\n"); + exit(1); +} diff --git a/user/spinner.c b/user/spinner.c new file mode 100644 index 0000000..0c70fd8 --- /dev/null +++ b/user/spinner.c @@ -0,0 +1,28 @@ +#include "kernel/types.h" +#include "user/user.h" + +int +main(int argc, char *argv[]) +{ + if (argc <= 2) { + fprintf(2, "Usage: spinner name amount\n"); + exit(1); + } + + char *name = argv[1]; + int amount = atoi(argv[2]); + + int start = uptime(); + for (int i = 0; i < amount; ++i) { + printf("%s", name); + for (int j = 0; j < 10000000; ++j) { + // wheee :-) + } + } + int end = uptime(); + + printf("\n[%s] has finished, %d work in %d ticks.\n", + name, amount, end - start); + + exit(0); +} diff --git a/user/strace.c b/user/strace.c new file mode 100644 index 0000000..f7a6cb8 --- /dev/null +++ b/user/strace.c @@ -0,0 +1,21 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char *argv[]) { + if(argc < 2) { + fprintf(2, "Usage: tracer command...\n"); + exit(1); + } + + if (fork() == 0) { + strace_on(); + exec(argv[1], argv + 1); + fprintf(2, "tracer: exec %s failed\n", argv[1]); + exit(1); + } + wait(0); + + exit(0); +} diff --git a/user/tolower.c b/user/tolower.c new file mode 100644 index 0000000..55eb0a5 --- /dev/null +++ b/user/tolower.c @@ -0,0 +1,12 @@ +#include "kernel/types.h" +#include "user/user.h" + +int main() { + for (int c = 0; read(0, &c, 1) > 0; ) { + if (c >= 'A' && c <= 'Z') { + c = c - 'A' + 'a'; + } + write(1, &c, 1); + } + return 0; +} diff --git a/user/ulib.c b/user/ulib.c index a2f5a20..82b1b3a 100644 --- a/user/ulib.c +++ b/user/ulib.c @@ -158,3 +158,69 @@ sbrklazy(int n) { return sys_sbrk(n, SBRK_LAZY); } +// Reads contents of given file ('fd') to 'buf' of size 'max'. +// Returns character's read, excluding null terminator. +int +fgets(char *buf, unsigned int max, int fd) +{ + int i, cc; + char c; + + for(i=0; i+1 < max; ){ + cc = read(fd, &c, 1); + if(cc < 1) { + return -1; + } + buf[i++] = c; + if(c == '\n' || c == '\r'){ + break; + } + } + + buf[i] = '\0'; + return i; +} + +// Like fgets, but dynamically allocates correct memory +// to store the string. +// Caller provides pointer to a malloced 'buf' and capacity ('size'). +// 'buf' points to NULL, a new buffer is allocated. +// Returns characters read, excluding null terminator. +int +getline(char **buf, int size, int fd) +{ + int chars_read = 0; + int curr_read; + + // initilize buffer if not already initilized (size = 2, malloc space for a char + /0) + if (*buf == 0 || size < 2) { + free(*buf); // in case buf was 1 (if null, does nothing) + *buf = malloc(2); + size = 2; + } + + while ((curr_read = fgets(*buf + chars_read, size - chars_read, fd))) { + + if (curr_read < 0) { // error in fgets + return - 1; + } + + chars_read += curr_read; + + //resize + char *temp = malloc(size * 2); + memcpy(temp, *buf, chars_read + 1); // include null term + free(*buf); + *buf = temp; + size = size * 2; + + // break if end of line/eof + char *last_char = *buf + (chars_read - 1); + if (strcmp(last_char, "\n") == 0 || strcmp(last_char, "\r") == 0) { + break; + } + } + + return chars_read; +} + diff --git a/user/umalloc.c b/user/umalloc.c index 2ac6894..16c4bf3 100644 --- a/user/umalloc.c +++ b/user/umalloc.c @@ -3,88 +3,423 @@ #include "user/user.h" #include "kernel/param.h" -// Memory allocator by Kernighan and Ritchie, -// The C programming Language, 2nd ed. Section 8.7. +#define NULL 0 +#define PAGE_SIZE 4096 +#define MIN_SPLIT_BLOCK_SIZE (sizeof(struct mem_block) + 16) -typedef long Align; -union header { - struct { - union header *ptr; - uint size; - } s; - Align x; +#define NAME_LEN 16 + +#define BLOCK_HEADER_SIZE sizeof(struct mem_block) + + +/* If we haven't passed -DDEBUG=1 to gcc, then this will be set to 0: */ +#ifndef DEBUG +#define DEBUG 1 +#endif + + +// Real allocator block metadata +struct mem_block { + char name[8]; + uint64 size; + struct mem_block *next; + struct mem_block *prev; }; -typedef union header Header; -static Header base; -static Header *freep; +struct mem_block *head; +struct mem_block *tail; + +// Free space management algorithms +enum { + FSM_FIRST_FIT = 0, + FSM_BEST_FIT = 1, + FSM_WORST_FIT = 2, +}; + +static int current_fsm = FSM_FIRST_FIT; + +// Our memory: +// +----------+ <--- base +// | | +// | | +// | | <--- bound +// | | +// | | +// | | +// | | +// | | +// | | +// | | +// +----------+ +// +// malloc -> ask kernel for more memory +// free -> tell kernel to shrink our memory +// +// "Hello Kernel, can I have 32 bytes of space please?" (sbrk) +// -> allocate 1 page of memory +// -> hand us 32 bytes of it +// "Hello Kernel, can I have 32 bytes of space please?" (sbrk) +// -> it would be bad to allocate another page here +// maybe the previous thing is not used anymore +// maybe we want to make use of the 4096 - 32 bytes left from the first +// page +// +// +// So, our allocator is going to allocate *pages* of memory at a time. +// One page can hold multiple blocks of memory: +// +-------------------------------------------+ +// | | | | | | | 1 page (4096 bytes) +// +-------------------------------------------+ +// ^ ^ ^ ^ ^ + +// The 'size' of a block: +// 1101 .... 0010 0000 0001 <- free +// 1101 .... 1110 0000 0000 <- used +// +// Why we can do this: +// - We are going to round everything up so that sizes are multiples of 16 +// - That means the smallest size we can have is 16 (0001 0000) +// - Note how the last 4 bits are never used... so we store the free bit there! + +// Get the block header from a data pointer +struct mem_block* get_header(void* ptr) { + return (struct mem_block*)((char*)ptr - BLOCK_HEADER_SIZE); +} + +static uint +align16(uint size) +{ + return (size + 15) & ~0x0F; +} + +// +void set_used(struct mem_block *block) +{ + // turn off the least significant bit + block->size = block->size & (~0x01); +} + +void set_free(struct mem_block *block) +{ + // turn on the least significant bit + block->size = block->size | 0x01; +} + +int is_free(struct mem_block *block) +{ + // check whether the last bit is set or not + return block->size & 0x01; +} + +uint get_size(struct mem_block *block) +{ + // ignore the free bit when retrieving the size + return block->size & (~0x01); +} + +/* Given an existing block, this function should split it in two */ +// How this works: +// Maybe we allocate 1 byte +// That gets turned into 16 (that's our minimum size) +// But then that gets turned into 4096 (our page size) +struct mem_block * +split(struct mem_block *block, uint total_sz) +{ + if (block == NULL) { + return NULL; + } + + if (!is_free(block)) { + return NULL; + } + + uint old_size = get_size(block); + + // Don't split if remainder would be too small + if (old_size <= total_sz + MIN_SPLIT_BLOCK_SIZE) { + return NULL; + } + + char *base = (char *) block; + struct mem_block *new_block = (struct mem_block *) (base + total_sz); + + uint remainder_size = old_size - total_sz; + + new_block->size = remainder_size; + set_free(new_block); + + new_block->next = block->next; + new_block->prev = block; + + if (block->next != NULL) { + block->next->prev = new_block; + } else { + // if block was tail, new_block becomes new tail + tail = new_block; + } + + block->size = total_sz; + set_used(block); + block->next = new_block; + + return new_block; +} + +// We have some space like this: +// +-----------------------------+ +// | mem_block | data | +// +-----------------------------+ +// ^ ^ +// | | +// | +--- actual data pointer (for user space programs) +// | +// +----- header +// + +// find first free whose size is big enough +static struct mem_block* +find_free_first_fit(uint total_sz) +{ + struct mem_block *curr = head; + + while (curr != NULL) { + if (is_free(curr) && get_size(curr) >= total_sz) { + return curr; + } + curr = curr->next; + } + return NULL; +} + +// find free block with smallest size >= total_sz +static struct mem_block* +find_free_best_fit(uint total_sz) +{ + struct mem_block *curr = head; + struct mem_block *best = NULL; + + while (curr != NULL) { + if (is_free(curr) && get_size(curr) >= total_sz) { + if (best == NULL || get_size(curr) < get_size(best)) { + best = curr; + } + } + curr = curr->next; + } + + return best; +} + +static struct mem_block* +find_free_worst_fit(uint total_sz) +{ + struct mem_block *curr = head; + struct mem_block *worst = NULL; + + while (curr != NULL) { + if (is_free(curr) && get_size(curr) >= total_sz) { + if (worst == NULL || get_size(curr) > get_size(worst)) { + worst = curr; + } + } + curr = curr->next; + } + return worst; +} + +static struct mem_block * +find_free_block(uint total_sz) +{ + switch (current_fsm) { + case FSM_BEST_FIT: + return find_free_best_fit(total_sz); + case FSM_WORST_FIT: + return find_free_worst_fit(total_sz); + case FSM_FIRST_FIT: + default: + return find_free_first_fit(total_sz); + } +} + + +void * +malloc(uint size) +{ + if (size == 0) { + return NULL; + } + + uint aligned = align16(size); + uint total_sz = BLOCK_HEADER_SIZE + aligned; + + // try to find a suitable free block first + struct mem_block *block = find_free_block(total_sz); + if (block != NULL) { + // take space from this free block + split(block, total_sz); + set_used(block); + return (void *)(block + 1); + } + + // no suitable free block, request a new chunk from the OS + uint request = total_sz; + if (request < PAGE_SIZE) { + request = PAGE_SIZE; + } + + block = (struct mem_block *) sbrk(request); + if ((long)block == -1) { + return NULL; + } + + // treat the whole new region as a single free block + block->size = request; + set_free(block); + block->next = NULL; + block->prev = tail; + + if (head == NULL) { + head = block; + tail = block; + } else { + tail->next = block; + tail = block; + } + + // now carve out the requested block from this new free block + struct mem_block *alloc_block = block; + split(alloc_block, total_sz); + set_used(alloc_block); + + return (void *)(alloc_block + 1); +} +// linked list helpers: +// * add a new block +// * remove a block +// * AND make sure you can handle both the (1) block list, and (2) free list 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; -} - -static Header* -morecore(uint nu) -{ - char *p; - Header *hp; - - if(nu < 4096) - nu = 4096; - p = sbrk(nu * sizeof(Header)); - if(p == SBRK_ERROR) - return 0; - hp = (Header*)p; - hp->s.size = nu; - free((void*)(hp + 1)); - return freep; -} - -void* -malloc(uint nbytes) -{ - Header *p, *prevp; - uint nunits; - - nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; - if((prevp = freep) == 0){ - base.s.ptr = freep = prevp = &base; - base.s.size = 0; +free(void *ptr) +{ + + if (ptr == NULL) { + return; } - 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; + + // ptr points to the user data; header is right before that + struct mem_block *block = ((struct mem_block *)ptr) -1; + + if (is_free(block)) { + return; } + + set_free(block); + +} + +// This is great because we can allocate arrays of things easily +// And they're zeroed out +// This function is perfect and never needs to be changed +void * +calloc(uint nmemb, uint size) +{ + char *mem = malloc(nmemb * size); + if (mem == NULL) { + return NULL; + } + memset(mem, 0, nmemb * size); + return mem; +} + +// takes a pointer to a previous allocation and resizes it (shrink / grow) +void * +realloc(void *ptr, uint size) +{ + if (ptr == NULL) { + return malloc(size); + } + + if (size == 0) { + free(ptr); + return NULL; + } + + struct mem_block *old_block = ((struct mem_block *) ptr) - 1; + uint old_total_size = old_block->size; + uint old_payload_size = old_total_size - sizeof(struct mem_block); + + void *new_ptr = malloc(size); + if (new_ptr == NULL) { + return NULL; + } + + uint copy_size = old_payload_size < size ? old_payload_size : size; + memmove(new_ptr, ptr, copy_size); + + free(ptr); + + return new_ptr; +} + +void +malloc_print(void) +{ + struct mem_block *curr; + + printf("--Current Memory State --\n"); + + curr = head; + while (curr != NULL) { + char *start = (char *)curr; + char *end = start + get_size(curr); + const char *status = is_free(curr) ? "FREE" : "USED"; + + printf("[BLOCK %p-%p] %d\t[%s]\n", + start, + end, + get_size(curr), + status); + + curr = curr->next; + } + + printf("\n-- Free List --\n"); + + curr = head; + int first = 1; + while (curr != NULL) { + if (is_free(curr)) { + if (!first) { + printf(" -> "); + } + printf("[%p]", curr); + first = 0; + } + curr = curr->next; + } + if (!first) { + printf(" -> "); + } + printf("NULL\n"); +} + +void +malloc_setfsm(int algo) +{ + if (algo == FSM_FIRST_FIT || + algo == FSM_BEST_FIT || + algo == FSM_WORST_FIT) { + current_fsm = algo; + } +} + +void malloc_name(void *ptr, const char *name) { + struct mem_block *block = get_header(ptr); + int i; + for (i = 0; i < 7 && name[i] != 0; i++) { + block->name[i] = name[i]; + } + block->name[i] = 0; } diff --git a/user/user.h b/user/user.h index 718b656..2a2b379 100644 --- a/user/user.h +++ b/user/user.h @@ -12,13 +12,13 @@ int read(int fd, void *buf, int count); int close(int fd); int kill(int pid); int exec(const char* path, char **argv); -int open(const char* path, int mode); -int mknod(const char* path, short major, short minor); -int unlink(const char* path); +int open(const char*/*str*/ path, int mode); +int mknod(const char* /*str*/ path, short major, short minor); +int unlink(const char* /*str*/ path); int fstat(int fd, struct stat* st); -int link(const char* old_path, const char* new_path); -int mkdir(const char* path); -int chdir(const char* path); +int link(const char* /*str*/old_path, const char* /*str*/ new_path); +int mkdir(const char* /*str*/ path); +int chdir(const char* /*str*/ path); int dup(int fd); int getpid(void); char* sys_sbrk(int n, int incr); diff --git a/user/usys.pl b/user/usys.pl old mode 100755 new mode 100644