diff --git a/README-demo.gif b/README-demo.gif index 49a1c12..4f2d03d 100644 Binary files a/README-demo.gif and b/README-demo.gif differ diff --git a/README.md b/README.md index b3db0d6..4cf02f1 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,7 @@ Hux - An x86 32-bit single-CPU toy operating system kernel built from scratch, f ## Tutorial / Development Doc -I document the whole development process of Hux - its skeleton, related theories, practice pitfalls, plus everything I reckon important as a complete set of tutorials. They can be found at: - -- The [**WIKI pages 📝**](https://github.com/josehu07/hux-kernel/wiki) of this repo ✭ -- The Hux kernel dev doc PDF (identical, WIP) +I document the whole development process of Hux as a complete set of tutorials. They can be found at the [**WIKI pages 📝**](https://github.com/josehu07/hux-kernel/wiki) of this GitHub repository ✭. If there are any typos / mistakes / errors, please raise an issue! @@ -30,14 +27,13 @@ Requires a Linux host development environment. Tested on Ubuntu Xenial, Bionic, Clone the repo, set up a development cross-compilation toolchain following [this wiki page](https://github.com/josehu07/hux-kernel/wiki/02.-The-Very-First-Skeleton), then build Hux by: ```bash +$ make clean $ make ``` -Or, if you just want to try out Hux without a development toolchain, download both the released kernel image `hux.iso` and the initial file system image `vsfs.img` to the folder. - -(The images have bot been uploaded yet. The first release of Hux is coming soon.) +Or, if you just want to try out Hux without a development toolchain, download both the released kernel image `hux.iso` and the initial file system image `vsfs.img` to the folder. (The images have bot been uploaded yet. The first release of Hux is coming soon.) -To run Hux in QEMU, do: +To run Hux in QEMU (currently only supports GUI VGA mode, "nographics" mode is coming soon), do: ```bash $ make qemu @@ -45,8 +41,6 @@ $ make qemu You will see the QEMU GUI popping up with GRUB loaded. Choose the "`Hux`" option with Enter to boot into Hux. -A random screenshot of the system at current progress: -

For development setup & instructions, please check out the wiki pages (recommended). I have every single detail documented there. @@ -99,11 +93,12 @@ Check the "References" section [here](https://github.com/josehu07/hux-kernel/wik - [x] Essential system calls - [x] Time-sharing scheduler - [x] Basic IDE disk driver -- [ ] Very simple file system +- [x] Very simple file system +- [ ] More user-level utilities +- [ ] Thorough user-level tests - [ ] Caching, swapping, & logging -- [ ] Multi-threading concurrency - [x] Synchronization primitives +- [ ] Multi-threading concurrency - [ ] Move to APIC & IOAPIC - [ ] Direct memory access -- [ ] Summary of current flaws - [ ] Extend Hux to Rux with Rust diff --git a/src/common/printf.c b/src/common/printf.c index 08fcdb5..ddf56e2 100644 --- a/src/common/printf.c +++ b/src/common/printf.c @@ -896,6 +896,8 @@ _vsnprintf(char *buf, size_t count, const char *fmt, va_list va) seg_end++; fmt++; } + + *buf = '\0'; } diff --git a/src/filesys/exec.c b/src/filesys/exec.c index 94e3159..fcc6f9d 100644 --- a/src/filesys/exec.c +++ b/src/filesys/exec.c @@ -175,18 +175,19 @@ exec_program(mem_inode_t *inode, char *filename, char **argv) goto fail; sp = sp - (strlen(argv[argc]) + 1); sp &= 0xFFFFFFFC; /** Align to 32-bit words. */ - memcpy((char *) (paddr_top - (USER_MAX - sp)), argv[argc], + memcpy((char *) (paddr_top + PAGE_SIZE - (USER_MAX - sp)), argv[argc], strlen(argv[argc]) + 1); ustack[3 + argc] = sp; } ustack[3 + argc] = 0; /** End of argv list. */ ustack[2] = sp - (argc + 1) * 4; /** `argv` */ - ustack[1] = argc; /** `argv` */ + ustack[1] = argc; /** `argc` */ ustack[0] = 0x0000DEAD; /** Fake return address. */ sp -= (3 + argc + 1) * 4; - memcpy((char *) (paddr_top - (USER_MAX - sp)), ustack, (3 + argc + 1) * 4); + memcpy((char *) (paddr_top + PAGE_SIZE - (USER_MAX - sp)), ustack, + (3 + argc + 1) * 4); /** Change process name. */ strncpy(proc->name, filename, strlen(filename)); diff --git a/src/filesys/file.c b/src/filesys/file.c index 46a83df..9400d95 100644 --- a/src/filesys/file.c +++ b/src/filesys/file.c @@ -10,6 +10,7 @@ #include "file.h" #include "block.h" #include "vsfs.h" +#include "sysfile.h" #include "../common/debug.h" #include "../common/string.h" @@ -521,3 +522,17 @@ file_put(file_t *file) /** Actually closing, put inode. */ inode_put(inode); } + + +/** Get metadata information of a file. */ +void +file_stat(file_t *file, file_stat_t *stat) +{ + inode_lock(file->inode); + + stat->inumber = file->inode->inumber; + stat->type = file->inode->d_inode.type; + stat->size = file->inode->d_inode.size; + + inode_unlock(file->inode); +} diff --git a/src/filesys/file.h b/src/filesys/file.h index 0a499d1..2f52241 100644 --- a/src/filesys/file.h +++ b/src/filesys/file.h @@ -12,6 +12,7 @@ #include #include "vsfs.h" +#include "sysfile.h" #include "../common/spinlock.h" #include "../common/parklock.h" @@ -73,5 +74,7 @@ file_t *file_get(); void file_ref(file_t *file); void file_put(file_t *file); +void file_stat(file_t *file, file_stat_t *stat); + #endif diff --git a/src/filesys/sysfile.c b/src/filesys/sysfile.c index 9e7b683..998da9f 100644 --- a/src/filesys/sysfile.c +++ b/src/filesys/sysfile.c @@ -193,3 +193,20 @@ syscall_exec(void) } return SYS_FAIL_RC; } + +/** int32_t fstat(int32_t fd, file_stat_t *stat); */ +int32_t +syscall_fstat(void) +{ + int32_t fd; + file_stat_t *stat; + + if (!sysarg_get_int(0, &fd)) + return SYS_FAIL_RC; + if (!sysarg_get_mem(1, (char **) &stat, sizeof(file_stat_t))) + return SYS_FAIL_RC; + + if (!filesys_fstat(fd, stat)) + return SYS_FAIL_RC; + return 0; +} diff --git a/src/filesys/sysfile.h b/src/filesys/sysfile.h index f712ace..971c0aa 100644 --- a/src/filesys/sysfile.h +++ b/src/filesys/sysfile.h @@ -19,6 +19,15 @@ #define CREATE_DIR 0x2 +/** For the `fstat()` syscall. */ +struct file_stat { + uint32_t inumber; + uint32_t type; + uint32_t size; +}; +typedef struct file_stat file_stat_t; + + int32_t syscall_open(); int32_t syscall_close(); int32_t syscall_create(); @@ -28,6 +37,7 @@ int32_t syscall_write(); int32_t syscall_chdir(); int32_t syscall_getcwd(); int32_t syscall_exec(); +int32_t syscall_fstat(); #endif diff --git a/src/filesys/vsfs.c b/src/filesys/vsfs.c index 36ff24c..84ddc23 100644 --- a/src/filesys/vsfs.c +++ b/src/filesys/vsfs.c @@ -664,6 +664,21 @@ filesys_exec(char *path, char **argv) } +/** Get metadata information about an open file. */ +bool +filesys_fstat(int8_t fd, file_stat_t *stat) +{ + file_t *file = _find_process_file(fd); + if (file == NULL) { + warn("fstat: cannot find file for fd %d", fd); + return false; + } + + file_stat(file, stat); + return true; +} + + /** Flush the in-memory modified bitmap block to disk. */ bool inode_bitmap_update(uint32_t slot_no) diff --git a/src/filesys/vsfs.h b/src/filesys/vsfs.h index 014f2ae..3d2fb5e 100644 --- a/src/filesys/vsfs.h +++ b/src/filesys/vsfs.h @@ -13,6 +13,8 @@ #include #include +#include "sysfile.h" + #include "../common/bitmap.h" @@ -126,6 +128,8 @@ bool filesys_getcwd(char *buf, size_t limit); bool filesys_exec(char *path, char **argv); +bool filesys_fstat(int8_t fd, file_stat_t *stat); + bool inode_bitmap_update(uint32_t slot_no); bool data_bitmap_update(uint32_t slot_no); diff --git a/src/interrupt/syscall.c b/src/interrupt/syscall.c index ad9b146..8b70385 100644 --- a/src/interrupt/syscall.c +++ b/src/interrupt/syscall.c @@ -46,7 +46,8 @@ static syscall_t syscall_handlers[] = { [SYSCALL_WRITE] syscall_write, [SYSCALL_CHDIR] syscall_chdir, [SYSCALL_GETCWD] syscall_getcwd, - [SYSCALL_EXEC] syscall_exec + [SYSCALL_EXEC] syscall_exec, + [SYSCALL_FSTAT] syscall_fstat }; #define NUM_SYSCALLS ((int32_t) (sizeof(syscall_handlers) / sizeof(syscall_t))) diff --git a/src/interrupt/syscall.h b/src/interrupt/syscall.h index 763fa53..27a7968 100644 --- a/src/interrupt/syscall.h +++ b/src/interrupt/syscall.h @@ -37,6 +37,7 @@ #define SYSCALL_CHDIR 17 #define SYSCALL_GETCWD 18 #define SYSCALL_EXEC 19 +#define SYSCALL_FSTAT 20 /** diff --git a/user/init.c b/user/init.c index 71a20be..8180990 100644 --- a/user/init.c +++ b/user/init.c @@ -4,6 +4,7 @@ #include +#include #include "lib/syscall.h" #include "lib/debug.h" @@ -22,8 +23,9 @@ main(void) if (shell_pid == 0) { /** Child: exec the command line shell. */ - char *argv[1]; - argv[0] = 0; + char *argv[2]; + argv[0] = "shell"; + argv[1] = NULL; exec("shell", argv); error("init: failed to exec the shell program"); diff --git a/user/lib/printf.c b/user/lib/printf.c index cdd552e..d7e7c07 100644 --- a/user/lib/printf.c +++ b/user/lib/printf.c @@ -632,6 +632,8 @@ _vsnprintf(char *buf, size_t count, const char *fmt, va_list va) seg_end++; fmt++; } + + *buf = '\0'; } /** Wrapper over string printing for `printf()` and `cprintf()`. */ diff --git a/user/lib/syscall.h b/user/lib/syscall.h index c25bf57..893b25c 100644 --- a/user/lib/syscall.h +++ b/user/lib/syscall.h @@ -24,6 +24,29 @@ #define CREATE_FILE 0x1 #define CREATE_DIR 0x2 +/** Struct & type code for `fstat()`. */ +#define INODE_TYPE_EMPTY 0 +#define INODE_TYPE_FILE 1 +#define INODE_TYPE_DIR 2 + +struct file_stat { + uint32_t inumber; + uint32_t type; + uint32_t size; +}; +typedef struct file_stat file_stat_t; + +/** Struct of a directory entry, see `vsfs.h`. */ +#define DENTRY_SIZE 128 +#define MAX_FILENAME 100 + +struct dentry { + uint32_t valid; + uint32_t inumber; + char filename[DENTRY_SIZE - 8]; +} __attribute__((packed)); +typedef struct dentry dentry_t; + /** * Externed from ASM `syscall.s`. @@ -50,6 +73,7 @@ extern int32_t write(int32_t fd, char *src, uint32_t len); extern int32_t chdir(char *path); extern int32_t getcwd(char *buf, uint32_t limit); extern int32_t exec(char *path, char **argv); +extern int32_t fstat(int32_t fd, file_stat_t *stat); #endif diff --git a/user/lib/syscall.s b/user/lib/syscall.s index 4ec9595..fcbddf1 100644 --- a/user/lib/syscall.s +++ b/user/lib/syscall.s @@ -39,3 +39,4 @@ SYSCALL_LIBGEN write, SYSCALL_WRITE SYSCALL_LIBGEN chdir, SYSCALL_CHDIR SYSCALL_LIBGEN getcwd, SYSCALL_GETCWD SYSCALL_LIBGEN exec, SYSCALL_EXEC +SYSCALL_LIBGEN fstat, SYSCALL_FSTAT diff --git a/user/lib/syslist.s b/user/lib/syslist.s index 50d5e4b..d8319ee 100644 --- a/user/lib/syslist.s +++ b/user/lib/syslist.s @@ -30,3 +30,4 @@ SYSCALL_WRITE = 16 SYSCALL_CHDIR = 17 SYSCALL_GETCWD = 18 SYSCALL_EXEC = 19 +SYSCALL_FSTAT = 20 diff --git a/user/ls.c b/user/ls.c new file mode 100644 index 0000000..31c637b --- /dev/null +++ b/user/ls.c @@ -0,0 +1,128 @@ +/** + * Command line utility - list directory. + */ + + +#include +#include +#include + +#include "lib/syscall.h" +#include "lib/printf.h" +#include "lib/debug.h" +#include "lib/string.h" + + +#define CONCAT_BUT_SIZE 300 + + +static char * +_get_filename(char *path) +{ + int32_t idx; + for (idx = strlen(path) - 1; idx >= 0; --idx) { + if (path[idx] == '/') + return &path[idx + 1]; + } + return path; +} + +static void +_print_file_stat(char *filename, file_stat_t *stat) +{ + bool is_dir = stat->type == INODE_TYPE_DIR; + printf("%5u %s %8u ", stat->inumber, is_dir ? "D" : "F", stat->size); + cprintf(is_dir ? VGA_COLOR_LIGHT_BLUE : VGA_COLOR_LIGHT_GREY, + "%s\n", filename); +} + + +static void +_list_directory(char *path) +{ + int8_t fd = open(path, OPEN_RD); + if (fd < 0) { + warn("ls: cannot open path '%s'", path); + return; + } + + file_stat_t stat; + if (fstat(fd, &stat) != 0) { + warn("ls: cannot get stat of '%s'", path); + close(fd); + return; + } + + /** Listing on a regular file. */ + if (stat.type == INODE_TYPE_FILE) { + _print_file_stat(_get_filename(path), &stat); + close(fd); + return; + } + + /** + * Listing a directory, then read out its content, interpret as an + * array of directory entries. + */ + char concat_buf[CONCAT_BUT_SIZE]; + strncpy(concat_buf, path, CONCAT_BUT_SIZE - 2); + if (CONCAT_BUT_SIZE - 1 - strlen(concat_buf) < MAX_FILENAME) { + warn("ls: path '%s' too long", path); + close(fd); + return; + } + char *name_buf = &concat_buf[strlen(concat_buf)]; + *(name_buf++) = '/'; + *name_buf = '\0'; + + dentry_t dentry; + while (read(fd, (char *) &dentry, sizeof(dentry_t)) == sizeof(dentry_t)) { + if (dentry.valid != 1) + continue; + + strncpy(name_buf, dentry.filename, MAX_FILENAME); + int8_t inner_fd = open(concat_buf, OPEN_RD); + if (inner_fd < 0) { + warn("ls: cannot open path '%s'", concat_buf); + close(fd); + return; + } + + file_stat_t inner_stat; + if (fstat(inner_fd, &inner_stat) != 0) { + warn("ls: cannot get stat of '%s'", concat_buf); + close(inner_fd); + close(fd); + return; + } + + _print_file_stat(dentry.filename, &inner_stat); + close(inner_fd); + } + + close(fd); +} + + +void +main(int argc, char *argv[]) +{ + if (argc < 2) { + _list_directory("."); + exit(); + } + + if (argc == 2) { + _list_directory(argv[1]); + exit(); + } + + /** If multiple directories, display as multiple sections. */ + for (int i = 1; i < argc; ++i) { + printf("%s:\n", argv[i]); + _list_directory(argv[i]); + if (i < argc - 1) + printf("\n"); + } + exit(); +} diff --git a/user/shell.c b/user/shell.c index a9194f5..2aeb6cb 100644 --- a/user/shell.c +++ b/user/shell.c @@ -11,7 +11,14 @@ #include "lib/printf.h" #include "lib/debug.h" #include "lib/string.h" -#include "lib/malloc.h" +#include "lib/types.h" + + +#define CWD_BUF_SIZE 256 +#define LINE_BUF_SIZE 256 +#define MAX_EXEC_ARGS 32 + +#define ENV_PATH "/" /** A fancy welcome logo in ASCII. */ @@ -29,172 +36,149 @@ _shell_welcome_logo(void) "\n"); } +/** Compose & print the prompt. */ +static void +_print_prompt(void) +{ + char username[5] = "hush"; + cprintf(VGA_COLOR_GREEN, "%s", username); + + cprintf(VGA_COLOR_DARK_GREY, ":"); + + char cwd[CWD_BUF_SIZE]; + memset(cwd, 0, CWD_BUF_SIZE); + if (getcwd(cwd, CWD_BUF_SIZE - MAX_FILENAME - 1) != 0) + error("shell: failed to get cwd"); + size_t ret_len = strlen(cwd); + if (cwd[ret_len - 1] != '/') { + cwd[ret_len] = '/'; + cwd[ret_len + 1] = '\0'; + } + cprintf(VGA_COLOR_CYAN, "%s", cwd); + + cprintf(VGA_COLOR_DARK_GREY, "$ "); +} + + +/** Built-in cmd `cd`: change directory. */ +static void +_change_cwd(char *path) +{ + if (path == NULL) /** empty path, go to "/". */ + path = "/"; + + if (chdir(path) != 0) + warn("shell: cd to path '%s' failed", path); +} + +/** Fork + exec external command executable. */ +static void +_fork_exec(char *path, char **argv) +{ + int8_t pid = fork(0); + if (pid < 0) { + warn("shell: failed to fork child process"); + return; + } + + if (pid == 0) { /** Child. */ + /** + * Try the executable under current working directory if found. + * Otherwise, fall through to try the one under ENV_PATH (which + * is the root directory "/"). + */ + if (open(path, OPEN_RD) < 0) { /** Not found. */ + char full[CWD_BUF_SIZE]; + snprintf(full, CWD_BUF_SIZE, "%s/%s", ENV_PATH, path); + exec(full, argv); + } else + exec(path, argv); + + warn("shell: failed to exec '%s'", path); + exit(); + + } else { /** Parent shell. */ + int8_t pid_w = wait(); + if (pid_w != pid) + warn("shell: waited pid %d does not equal %d", pid_w, pid); + } +} + -__attribute__((unused)) +/** + * Parse a command line. Parse the tokens according to whitespaces, + * interpret the first one as the executable filename, and together + * with the rest as the argument list. + */ +static int +_parse_tokens(char *line, char **argv) +{ + size_t argi; + size_t pos = 0; + + for (argi = 0; argi < MAX_EXEC_ARGS - 1; ++argi) { + while (isspace(line[pos])) + pos++; + if (line[pos] == '\0') { + argv[argi] = NULL; + return argi; + } + + argv[argi] = &line[pos]; + + while(!isspace(line[pos])) + pos++; + if (line[pos] != '\0') + line[pos++] = '\0'; + } + + argv[argi] = NULL; + return argi; +} + +/** + * Handle a command line. + * - If is a built-in command, handle directly; + * - Else, fork a child process to run the command executable. + */ static void -_test_child_workload(void) +_handle_cmdline(char *line) { - int32_t res = 0; - for (int32_t i = 12345; i < 57896; ++i) - for (int32_t j = i; j > 12345; --j) - res += (i * j) % 567; - printf("res %d: %d\n", getpid(), res); + char *argv[MAX_EXEC_ARGS]; + int argc = _parse_tokens(line, argv); + if (argc <= 0) + return; + else if (argc >= MAX_EXEC_ARGS) + warn("shell: line exceeds max num of args %d", MAX_EXEC_ARGS); + + if (strncmp(argv[0], "cd", 2) == 0) + _change_cwd(argv[1]); /** Could be NULL. */ + else + _fork_exec(argv[0], argv); } void -main(void) +main(int argc, char *argv[]) { - _shell_welcome_logo(); + (void) argc; // Unused. + (void) argv; - printf("Hello from the exec'ed shell program!\n"); + _shell_welcome_logo(); - char cmd_buf[256]; - memset(cmd_buf, 0, 256); + char cmd_buf[LINE_BUF_SIZE]; + memset(cmd_buf, 0, LINE_BUF_SIZE); while (1) { - printf("temp shell $ "); + _print_prompt(); - if (kbdstr(cmd_buf, 256) < 0) - warn("shell: failed to get keyboard string"); + if (kbdstr(cmd_buf, LINE_BUF_SIZE) < 0) + error("shell: failed to get keyboard string"); else - printf("%s", cmd_buf); + _handle_cmdline(cmd_buf); - memset(cmd_buf, 0, 256); + memset(cmd_buf, 0, LINE_BUF_SIZE); } - // char dirname[128] = "temp"; - // char filepath[128] = "temp/test.txt"; - // char filename[128] = "test.txt"; - - // printf("[P] Created dir '%s' -> %d\n", dirname, create(dirname, CREATE_DIR)); - // printf("[P] Created file '%s' -> %d\n", filepath, create(filepath, CREATE_FILE)); - // printf("[P] Changed cwd to '%s' -> %d\n", dirname, chdir(dirname)); - - // int8_t pid = fork(0); - // assert(pid >= 0); - - // if (pid == 0) { - // // Child. - // char cwd[100]; - // printf("[C] Called getcwd -> %d\n", getcwd(cwd, 100)); - // printf(" cwd: %s\n", cwd); - // int8_t fd = open(filename, OPEN_WR); - // printf("[C] Opened file '%s' -> %d\n", filename, fd); - // printf("[C] Written to fd %d -> %d\n", fd, write(fd, "AAAAA", 5)); - // printf(" src: %s\n", "AAAAA"); - // exit(); - - // } else { - // // Parent. - // assert(wait() == pid); - // printf("[P] Changed cwd to '%s' -> %d\n", "./..", chdir("./..")); - // int8_t fd = open(filepath, OPEN_RD); - // printf("[P] Opened file '%s' -> %d\n", filepath, fd); - // char buf[6] = {0}; - // printf("[P] Read from fd %d -> %d\n", fd, read(fd, buf, 5)); - // printf(" dst: %s\n", buf); - // printf("[P] Closing fd %d -> %d\n", fd, close(fd)); - // printf("[P] Removing file '%s' -> %d\n", filepath, remove(filepath)); - // printf("[P] Removing dir '%s' -> %d\n", dirname, remove(dirname)); - // } - - // printf("Files done!\n"); - - // int8_t i; - - // printf("parent: forking...\n"); - // for (i = 1; i <= 3; ++i) { - // int8_t pid = fork(i*4); - // if (pid < 0) - // error("parent: forking child i=%d failed", i); - // if (pid == 0) { - // // Child. - // _test_child_workload(); - // exit(); - // } else - // printf("parent: forked child pid=%d, timeslice=%d\n", pid, i*4); - // } - - // printf("parent: waiting...\n"); - // for (i = 1; i <= 3; ++i) { - // int8_t pid = wait(); - // printf("parent: waited child pid=%d\n", pid); - // } - - // printf("On-stack buffer of size 8200...\n"); - // char buf[8200]; - // buf[0] = 'A'; - // buf[1] = '\0'; - // printf("Variable buf @ %p: %s\n", buf, buf); - - // printf("\nOn-heap allocations & frees...\n"); - // char *buf1 = (char *) malloc(200); - // printf("Buf1: %p\n", buf1); - // char *buf2 = (char *) malloc(4777); - // printf("Buf2: %p\n", buf2); - // mfree(buf1); - // char *buf3 = (char *) malloc(8); - // printf("Buf3: %p\n", buf3); - // mfree(buf3); - // mfree(buf2); - - // char kbd_buf[100]; - // printf("Please enter an input str: "); - // kbdstr(kbd_buf, 100); - // printf("Fetched str from keyboard: %s\n", kbd_buf); - - // int32_t mypid = getpid(); - // printf(" Parent: parent gets pid - %d\n", mypid); - // sleep(2000); - - // cprintf(VGA_COLOR_LIGHT_GREEN, "\n Round 1 --\n"); - // printf(" Parent: forking child 1\n"); - // int32_t pid1 = fork(0); - // if (pid1 < 0) { - // cprintf(VGA_COLOR_RED, " Parent: fork failed\n"); - // _fake_halt(); - // } - // if (pid1 == 0) { - // // Child. - // printf(" Child1: entering an infinite loop\n"); - // _fake_halt(); - // } else { - // // Parent. - // printf(" Parent: child 1 has pid - %d\n", pid1); - // sleep(1500); - // printf(" Parent: slept 1.5 secs, going to kill child 1\n"); - // kill(pid1); - // wait(); - // printf(" Parent: killed child 1\n"); - // } - - // cprintf(VGA_COLOR_LIGHT_GREEN, "\n Round 2 --\n"); - // printf(" Current uptime: %d ms\n", uptime()); - // printf(" Going to sleep for 2000 ms...\n"); - // sleep(2000); - // printf(" Current uptime: %d ms\n", uptime()); - - // cprintf(VGA_COLOR_LIGHT_GREEN, "\n Round 3 --\n"); - // printf(" Parent: forking child 2\n"); - // int32_t pid2 = fork(0); - // if (pid2 < 0) { - // cprintf(VGA_COLOR_RED, " Parent: fork failed\n"); - // _fake_halt(); - // } - // if (pid2 == 0) { - // // Child. - // printf(" Child2: going to sleep 2 secs\n"); - // sleep(2000); - // exit(); - // } else { - // // Parent. - // printf(" Parent: child 2 has pid - %d\n", pid2); - // wait(); - // printf(" Parent: waited child 2\n"); - // } - - // cprintf(VGA_COLOR_GREEN, "\n Cases done!\n"); - exit(); } diff --git a/user/tests/filetest.c b/user/tests/filetest.c new file mode 100644 index 0000000..1d9e169 --- /dev/null +++ b/user/tests/filetest.c @@ -0,0 +1,58 @@ +/** + * User test program - file system operations. + */ + + +#include +#include +#include + +#include "../lib/debug.h" +#include "../lib/printf.h" +#include "../lib/syscall.h" + + +void +main(int argc, char *argv[]) +{ + (void) argc; // Unused. + (void) argv; + + char dirname[128] = "temp"; + char filepath[128] = "temp/test.txt"; + char filename[128] = "test.txt"; + + printf("[P] Created dir '%s' -> %d\n", dirname, create(dirname, CREATE_DIR)); + printf("[P] Created file '%s' -> %d\n", filepath, create(filepath, CREATE_FILE)); + printf("[P] Changed cwd to '%s' -> %d\n", dirname, chdir(dirname)); + + int8_t pid = fork(0); + assert(pid >= 0); + + if (pid == 0) { + // Child. + char cwd[100]; + printf("[C] Called getcwd -> %d\n", getcwd(cwd, 100)); + printf(" cwd: %s\n", cwd); + int8_t fd = open(filename, OPEN_WR); + printf("[C] Opened file '%s' -> %d\n", filename, fd); + printf("[C] Written to fd %d -> %d\n", fd, write(fd, "AAAAA", 5)); + printf(" src: %s\n", "AAAAA"); + exit(); + + } else { + // Parent. + assert(wait() == pid); + printf("[P] Changed cwd to '%s' -> %d\n", "./..", chdir("./..")); + int8_t fd = open(filepath, OPEN_RD); + printf("[P] Opened file '%s' -> %d\n", filepath, fd); + char buf[6] = {0}; + printf("[P] Read from fd %d -> %d\n", fd, read(fd, buf, 5)); + printf(" dst: %s\n", buf); + printf("[P] Closing fd %d -> %d\n", fd, close(fd)); + printf("[P] Removing file '%s' -> %d\n", filepath, remove(filepath)); + printf("[P] Removing dir '%s' -> %d\n", dirname, remove(dirname)); + } + + exit(); +} diff --git a/user/tests/memtest.c b/user/tests/memtest.c new file mode 100644 index 0000000..60dbd0b --- /dev/null +++ b/user/tests/memtest.c @@ -0,0 +1,40 @@ +/** + * User test program - file system operations. + */ + + +#include +#include +#include + +#include "../lib/debug.h" +#include "../lib/printf.h" +#include "../lib/syscall.h" +#include "../lib/malloc.h" + + +void +main(int argc, char *argv[]) +{ + (void) argc; // Unused. + (void) argv; + + printf("On-stack buffer of size 8200...\n"); + char buf[8200]; + buf[0] = 'A'; + buf[1] = '\0'; + printf("Variable buf @ %p: %s\n", buf, buf); + + printf("\nOn-heap allocations & frees...\n"); + char *buf1 = (char *) malloc(200); + printf("Buf1: %p\n", buf1); + char *buf2 = (char *) malloc(4777); + printf("Buf2: %p\n", buf2); + mfree(buf1); + char *buf3 = (char *) malloc(8); + printf("Buf3: %p\n", buf3); + mfree(buf3); + mfree(buf2); + + exit(); +} diff --git a/user/tests/proctest.c b/user/tests/proctest.c new file mode 100644 index 0000000..61fd3b4 --- /dev/null +++ b/user/tests/proctest.c @@ -0,0 +1,75 @@ +/** + * User test program - file system operations. + */ + + +#include +#include +#include + +#include "../lib/debug.h" +#include "../lib/printf.h" +#include "../lib/syscall.h" + + +void +main(int argc, char *argv[]) +{ + (void) argc; // Unused. + (void) argv; + + int32_t mypid = getpid(); + printf(" Parent: parent gets pid - %d\n", mypid); + sleep(2000); + + cprintf(VGA_COLOR_LIGHT_GREEN, "\n Round 1 --\n"); + printf(" Parent: forking child 1\n"); + int32_t pid1 = fork(0); + if (pid1 < 0) { + cprintf(VGA_COLOR_RED, " Parent: fork failed\n"); + exit(); + } + if (pid1 == 0) { + // Child. + printf(" Child1: entering an infinite loop\n"); + while (true) + sleep(5000); + } else { + // Parent. + printf(" Parent: child 1 has pid - %d\n", pid1); + sleep(1500); + printf(" Parent: slept 1.5 secs, going to kill child 1\n"); + kill(pid1); + wait(); + printf(" Parent: killed child 1\n"); + } + + cprintf(VGA_COLOR_LIGHT_GREEN, "\n Round 2 --\n"); + printf(" Current uptime: %d ms\n", uptime()); + printf(" Going to sleep for 2000 ms...\n"); + sleep(2000); + printf(" Current uptime: %d ms\n", uptime()); + + cprintf(VGA_COLOR_LIGHT_GREEN, "\n Round 3 --\n"); + printf(" Parent: forking child 2\n"); + int32_t pid2 = fork(0); + if (pid2 < 0) { + cprintf(VGA_COLOR_RED, " Parent: fork failed\n"); + exit(); + } + if (pid2 == 0) { + // Child. + printf(" Child2: going to sleep 2 secs\n"); + sleep(2000); + exit(); + } else { + // Parent. + printf(" Parent: child 2 has pid - %d\n", pid2); + wait(); + printf(" Parent: waited child 2\n"); + } + + cprintf(VGA_COLOR_GREEN, "\n Cases done!\n"); + + exit(); +} diff --git a/user/tests/schedtest.c b/user/tests/schedtest.c new file mode 100644 index 0000000..d6aea62 --- /dev/null +++ b/user/tests/schedtest.c @@ -0,0 +1,54 @@ +/** + * User test program - weighted scheduling. + */ + + +#include +#include +#include + +#include "../lib/debug.h" +#include "../lib/printf.h" +#include "../lib/syscall.h" + + +static void +_test_child_workload(void) +{ + int32_t res = 0; + for (int32_t i = 12345; i < 57896; ++i) + for (int32_t j = i; j > 12345; --j) + res += (i * j) % 567; + printf("res %d: %d\n", getpid(), res); +} + + +void +main(int argc, char *argv[]) +{ + (void) argc; // Unused. + (void) argv; + + int8_t i; + + printf("parent: forking...\n"); + for (i = 1; i <= 3; ++i) { + int8_t pid = fork(i*4); + if (pid < 0) + error("parent: forking child i=%d failed", i); + if (pid == 0) { + // Child. + _test_child_workload(); + exit(); + } else + printf("parent: forked child pid=%d, timeslice=%d\n", pid, i*4); + } + + printf("parent: waiting...\n"); + for (i = 1; i <= 3; ++i) { + int8_t pid = wait(); + printf("parent: waited child pid=%d\n", pid); + } + + exit(); +} diff --git a/wiki b/wiki index 2b48c13..a1c932e 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 2b48c13242a74af964b2a23186ff4a18698f02c6 +Subproject commit a1c932ec867e9cc10abf74851ccadf6b19476dd2