From 119b665972d9eca4a0feec05cb130ca8929047d2 Mon Sep 17 00:00:00 2001 From: Ivan Zinchenko Date: Sun, 18 Jan 2026 19:00:45 +0500 Subject: [PATCH 1/9] wip --- .github/workflows/vtkm.yml | 46 +++--- .github/workflows/vtpc.yml | 90 ++++++------ .github/workflows/vtsh.yml | 1 + lab/vtld/.vscode/settings.json | 5 + lab/vtld/cpu_dedup | Bin 0 -> 16736 bytes lab/vtld/cpu_dedup.c | 77 ++++++++++ lab/vtld/io_replace | Bin 0 -> 17216 bytes lab/vtld/io_replace.c | 203 ++++++++++++++++++++++++++ lab/vtld/iobench | Bin 0 -> 17224 bytes lab/vtld/iobench.c | 232 ++++++++++++++++++++++++++++++ lab/vtld/makefile | 16 +++ lab/vtsh/.vscode/tasks.json | 28 ++++ lab/vtsh/bin/main.c | 4 +- lab/vtsh/lib/CMakeLists.txt | 8 +- lab/vtsh/lib/exec.c | 112 +++++++++++++++ lab/vtsh/lib/exec.h | 4 + lab/vtsh/lib/parser.c | 161 +++++++++++++++++++++ lab/vtsh/lib/parser.h | 32 +++++ lab/vtsh/lib/tokenizer.c | 63 ++++++++ lab/vtsh/lib/tokenizer.h | 4 + lab/vtsh/lib/utils.c | 52 +++++++ lab/vtsh/lib/utils.h | 17 +++ lab/vtsh/lib/vtsh.c | 53 ++++++- lab/vtsh/lib/vtsh.h | 3 +- lab/vtsh/test/loltU0D5`4S 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 literal 0 HcmV?d00001 diff --git a/lab/vtld/cpu_dedup.c b/lab/vtld/cpu_dedup.c new file mode 100644 index 0000000..a4ef6aa --- /dev/null +++ b/lab/vtld/cpu_dedup.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +static void print_usage(const char *prog) { + fprintf(stderr, "Usage: %s --size N --repeats N [--max_value N]\n", prog); +} + +static const char *get_opt_value(int argc, char **argv, const char *key) { + size_t key_len = strlen(key); + for (int i = 1; i < argc; ++i) { + const char *arg = argv[i]; + while (*arg == '-') { + ++arg; + } + const char *eq = strchr(arg, '='); + if (eq) { + size_t len = (size_t)(eq - arg); + if (len == key_len && strncmp(arg, key, len) == 0) { + return eq + 1; + } + } else if (strcmp(arg, key) == 0 && i + 1 < argc) { + return argv[i + 1]; + } + } + return NULL; +} + +static int cmp_int32(const void *a, const void *b) { + int32_t ia = *(const int32_t *)a; + int32_t ib = *(const int32_t *)b; + return (ia > ib) - (ia < ib); +} + +int main(int argc, char **argv) { + const char *size_s = get_opt_value(argc, argv, "size"); + const char *repeats_s = get_opt_value(argc, argv, "repeats"); + const char *max_value_s = get_opt_value(argc, argv, "max_value"); + + if (!size_s || !repeats_s) { + print_usage(argv[0]); + return 1; + } + + long size = strtol(size_s, NULL, 10); + long repeats = strtol(repeats_s, NULL, 10); + long max_value = max_value_s ? strtol(max_value_s, NULL, 10) : (size * 2); + + if (size <= 0 || repeats <= 0 || max_value <= 0) { + fprintf(stderr, "Invalid size, repeats, or max_value.\n"); + return 1; + } + + srand((unsigned)time(NULL)); + for (long r = 0; r < repeats; ++r) { + int32_t *arr = malloc((size_t)size * sizeof(int32_t)); + if (!arr) { + fprintf(stderr, "malloc failed.\n"); + return 1; + } + for (long i = 0; i < size; ++i) { + arr[i] = (int32_t)(rand() % max_value); + } + qsort(arr, (size_t)size, sizeof(int32_t), cmp_int32); + long unique = 0; + for (long i = 0; i < size; ++i) { + if (i == 0 || arr[i] != arr[i - 1]) { + ++unique; + } + } + printf("repeat %ld: unique=%ld\n", r + 1, unique); + free(arr); + } + return 0; +} diff --git a/lab/vtld/io_replace b/lab/vtld/io_replace new file mode 100755 index 0000000000000000000000000000000000000000..8a32bcf6dac35ba5cfe091ac94aa7cf8e77250b7 GIT binary patch literal 17216 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

*!{yU+%vps~%(+Q_+`wu|U}uOZnyXf86S? zv<79L?Lljp`ixn*{`Wv*OvJvo?%VZW_U{nUsl5FUf?BNaw|W9Q%K|R96#}0w)9a?eZEXz?(YS1|5=>M>;EG#Sw(3-<+sun?klUm z_-&=sOUM-K%f3nAdJ(j+y!}=m|an%A|?^*`VR{SJ3wxws*b2 zmT$#9y6At^qnHeRWh#bV-!&|K9kK><_5tFhpz=|I_%HJ>$A|c?742n^3lmaL8B@wN TT<#r4){iYU64T2R%24&+w?|a7 literal 0 HcmV?d00001 diff --git a/lab/vtld/io_replace.c b/lab/vtld/io_replace.c new file mode 100644 index 0000000..9a8b186 --- /dev/null +++ b/lab/vtld/io_replace.c @@ -0,0 +1,203 @@ +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void print_usage(const char *prog) { + fprintf(stderr, + "Usage: %s --file PATH --size N --repeats N --needle STR --replace STR \\\n" + " [--block_size N]\n", + prog); +} + +static const char *get_opt_value(int argc, char **argv, const char *key) { + size_t key_len = strlen(key); + for (int i = 1; i < argc; ++i) { + const char *arg = argv[i]; + while (*arg == '-') { + ++arg; + } + const char *eq = strchr(arg, '='); + if (eq) { + size_t len = (size_t)(eq - arg); + if (len == key_len && strncmp(arg, key, len) == 0) { + return eq + 1; + } + } else if (strcmp(arg, key) == 0 && i + 1 < argc) { + return argv[i + 1]; + } + } + return NULL; +} + +static int generate_file(const char *path, size_t size_bytes, const char *needle, size_t block_size) { + int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + perror("open"); + return 1; + } + + size_t needle_len = strlen(needle); + size_t insert_step = size_bytes / 10; + if (insert_step < needle_len * 4) { + insert_step = needle_len * 4; + } + size_t next_insert = 0; + + char *buf = malloc(block_size); + if (!buf) { + fprintf(stderr, "malloc failed.\n"); + close(fd); + return 1; + } + + size_t offset = 0; + while (offset < size_bytes) { + size_t chunk = block_size; + if (offset + chunk > size_bytes) { + chunk = size_bytes - offset; + } + for (size_t i = 0; i < chunk; ++i) { + buf[i] = (char)('a' + (rand() % 26)); + } + + if (next_insert >= offset && next_insert + needle_len <= offset + chunk) { + size_t rel = next_insert - offset; + memcpy(buf + rel, needle, needle_len); + next_insert += insert_step; + } + + if (write(fd, buf, chunk) != (ssize_t)chunk) { + perror("write"); + free(buf); + close(fd); + return 1; + } + offset += chunk; + } + + free(buf); + close(fd); + return 0; +} + +static int replace_in_file(int fd, const char *needle, const char *replacement, size_t block_size) { + struct stat st; + if (fstat(fd, &st) != 0) { + perror("fstat"); + return 1; + } + off_t size = st.st_size; + size_t needle_len = strlen(needle); + if (needle_len == 0 || needle_len != strlen(replacement)) { + fprintf(stderr, "needle and replacement must have same non-zero length.\n"); + return 1; + } + + char *buf = malloc(block_size + needle_len - 1); + if (!buf) { + fprintf(stderr, "malloc failed.\n"); + return 1; + } + + size_t carry_len = 0; + off_t offset = 0; + while (offset < size) { + size_t chunk = block_size; + if (offset + (off_t)chunk > size) { + chunk = (size_t)(size - offset); + } + ssize_t rc = pread(fd, buf + carry_len, chunk, offset); + if (rc < 0) { + perror("pread"); + free(buf); + return 1; + } + size_t total = carry_len + (size_t)rc; + if (total < needle_len) { + carry_len = total; + offset += rc; + continue; + } + size_t limit = total - needle_len + 1; + for (size_t i = 0; i < limit; ++i) { + if (memcmp(buf + i, needle, needle_len) == 0) { + off_t woff = offset - (off_t)carry_len + (off_t)i; + if (pwrite(fd, replacement, needle_len, woff) != (ssize_t)needle_len) { + perror("pwrite"); + free(buf); + return 1; + } + } + } + if (needle_len > 1) { + carry_len = needle_len - 1; + if (total < carry_len) { + carry_len = total; + } + memmove(buf, buf + total - carry_len, carry_len); + } else { + carry_len = 0; + } + offset += rc; + } + + free(buf); + return 0; +} + +int main(int argc, char **argv) { + const char *path = get_opt_value(argc, argv, "file"); + const char *size_s = get_opt_value(argc, argv, "size"); + const char *repeats_s = get_opt_value(argc, argv, "repeats"); + const char *needle = get_opt_value(argc, argv, "needle"); + const char *replacement = get_opt_value(argc, argv, "replace"); + const char *block_size_s = get_opt_value(argc, argv, "block_size"); + + if (!path || !size_s || !repeats_s || !needle || !replacement) { + print_usage(argv[0]); + return 1; + } + + long size = strtol(size_s, NULL, 10); + long repeats = strtol(repeats_s, NULL, 10); + long block_size = block_size_s ? strtol(block_size_s, NULL, 10) : 4096; + if (size <= 0 || repeats <= 0 || block_size <= 0) { + fprintf(stderr, "Invalid size, repeats, or block_size.\n"); + return 1; + } + + if (strlen(needle) != strlen(replacement) || strlen(needle) == 0) { + fprintf(stderr, "needle and replacement must be same length and non-empty.\n"); + return 1; + } + + srand((unsigned)time(NULL)); + if (generate_file(path, (size_t)size, needle, (size_t)block_size) != 0) { + return 1; + } + + int fd = open(path, O_RDWR); + if (fd < 0) { + perror("open"); + return 1; + } + + for (long r = 0; r < repeats; ++r) { + if (replace_in_file(fd, needle, replacement, (size_t)block_size) != 0) { + close(fd); + return 1; + } + printf("repeat %ld: done\n", r + 1); + } + + close(fd); + return 0; +} diff --git a/lab/vtld/iobench b/lab/vtld/iobench new file mode 100755 index 0000000000000000000000000000000000000000..f84a312c7c370337056bd22a3e6420512030ee16 GIT binary patch literal 17224 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 iu}ee!lc$9V +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef O_DIRECT +#define O_DIRECT 0 +#endif + +#define ALIGNMENT 4096 + +static void print_usage(const char *prog) { + fprintf(stderr, + "Usage: %s --rw read|write --block_size N --block_count N --file PATH \\\n" + " [--range A-B] [--direct on|off] [--type sequence|random]\n", + prog); +} + +static const char *get_opt_value(int argc, char **argv, const char *key) { + size_t key_len = strlen(key); + for (int i = 1; i < argc; ++i) { + const char *arg = argv[i]; + while (*arg == '-') { + ++arg; + } + const char *eq = strchr(arg, '='); + if (eq) { + size_t len = (size_t)(eq - arg); + if (len == key_len && strncmp(arg, key, len) == 0) { + return eq + 1; + } + } else if (strcmp(arg, key) == 0 && i + 1 < argc) { + return argv[i + 1]; + } + } + return NULL; +} + +static int parse_range(const char *s, off_t *start, off_t *end) { + if (!s) { + return -1; + } + char *dash = strchr(s, '-'); + if (!dash) { + return -1; + } + *dash = '\0'; + char *endptr = NULL; + long long a = strtoll(s, &endptr, 10); + if (*endptr != '\0') { + *dash = '-'; + return -1; + } + long long b = strtoll(dash + 1, &endptr, 10); + if (*endptr != '\0') { + *dash = '-'; + return -1; + } + *dash = '-'; + if (a < 0 || b < 0) { + return -1; + } + *start = (off_t)a; + *end = (off_t)b; + return 0; +} + +int main(int argc, char **argv) { + const char *rw = get_opt_value(argc, argv, "rw"); + const char *block_size_s = get_opt_value(argc, argv, "block_size"); + const char *block_count_s = get_opt_value(argc, argv, "block_count"); + const char *file_path = get_opt_value(argc, argv, "file"); + const char *range_s = get_opt_value(argc, argv, "range"); + const char *direct_s = get_opt_value(argc, argv, "direct"); + const char *type_s = get_opt_value(argc, argv, "type"); + + if (!rw || !block_size_s || !block_count_s || !file_path) { + print_usage(argv[0]); + return 1; + } + + long block_size = strtol(block_size_s, NULL, 10); + long block_count = strtol(block_count_s, NULL, 10); + if (block_size <= 0 || block_count <= 0) { + fprintf(stderr, "Invalid block_size or block_count.\n"); + return 1; + } + + int is_write = (strcmp(rw, "write") == 0); + int is_read = (strcmp(rw, "read") == 0); + if (!is_write && !is_read) { + fprintf(stderr, "rw must be read or write.\n"); + return 1; + } + + int use_direct = (direct_s && strcmp(direct_s, "on") == 0); + int is_random = (type_s && strcmp(type_s, "random") == 0); + + if (use_direct && (block_size % ALIGNMENT != 0)) { + fprintf(stderr, "block_size must be multiple of %d for direct IO.\n", ALIGNMENT); + return 1; + } + + int flags = is_write ? (O_RDWR | O_CREAT) : O_RDONLY; + if (use_direct) { + flags |= O_DIRECT; + } + + int fd = open(file_path, flags, 0644); + if (fd < 0) { + perror("open"); + return 1; + } + + struct stat st; + if (fstat(fd, &st) != 0) { + perror("fstat"); + close(fd); + return 1; + } + + off_t range_start = 0; + off_t range_end = 0; + if (range_s) { + char *range_copy = strdup(range_s); + if (!range_copy || parse_range(range_copy, &range_start, &range_end) != 0) { + free(range_copy); + fprintf(stderr, "Invalid range format. Use A-B.\n"); + close(fd); + return 1; + } + free(range_copy); + } + + if (range_start == 0 && range_end == 0) { + if (st.st_size > 0) { + range_end = st.st_size; + } else if (is_write) { + range_end = (off_t)block_size * block_count; + } else { + fprintf(stderr, "File is empty; specify a non-zero range.\n"); + close(fd); + return 1; + } + } + + if (range_end < range_start) { + fprintf(stderr, "range end must be >= start.\n"); + close(fd); + return 1; + } + + if (use_direct && (range_start % ALIGNMENT != 0)) { + fprintf(stderr, "range start must be aligned to %d for direct IO.\n", ALIGNMENT); + close(fd); + return 1; + } + + off_t range_size = range_end - range_start; + if (range_size <= 0) { + fprintf(stderr, "range size must be > 0.\n"); + close(fd); + return 1; + } + + off_t available_blocks = range_size / block_size; + if (available_blocks <= 0) { + fprintf(stderr, "range too small for given block_size.\n"); + close(fd); + return 1; + } + if (range_size % block_size != 0) { + fprintf(stderr, "range size is not a multiple of block_size; truncating.\n"); + } + + if (is_write && range_end > st.st_size) { + if (ftruncate(fd, range_end) != 0) { + perror("ftruncate"); + close(fd); + return 1; + } + } + + void *buffer = NULL; + if (use_direct) { + if (posix_memalign(&buffer, ALIGNMENT, (size_t)block_size) != 0) { + fprintf(stderr, "posix_memalign failed.\n"); + close(fd); + return 1; + } + } else { + buffer = malloc((size_t)block_size); + if (!buffer) { + fprintf(stderr, "malloc failed.\n"); + close(fd); + return 1; + } + } + + if (is_write) { + memset(buffer, 0xA5, (size_t)block_size); + } + + srand((unsigned)time(NULL)); + for (long i = 0; i < block_count; ++i) { + off_t idx = is_random ? (rand() % available_blocks) : (i % available_blocks); + off_t offset = range_start + idx * block_size; + ssize_t rc; + if (is_read) { + rc = pread(fd, buffer, (size_t)block_size, offset); + } else { + rc = pwrite(fd, buffer, (size_t)block_size, offset); + } + if (rc < 0) { + perror(is_read ? "pread" : "pwrite"); + free(buffer); + close(fd); + return 1; + } + } + + free(buffer); + close(fd); + return 0; +} diff --git a/lab/vtld/makefile b/lab/vtld/makefile new file mode 100644 index 0000000..67c9d92 --- /dev/null +++ b/lab/vtld/makefile @@ -0,0 +1,16 @@ +CC=gcc +CFLAGS=-O0 -Wall -Wextra -std=c11 + +all: iobench cpu_dedup io_replace + +iobench: iobench.c + $(CC) $(CFLAGS) -o iobench iobench.c + +cpu_dedup: cpu_dedup.c + $(CC) $(CFLAGS) -o cpu_dedup cpu_dedup.c + +io_replace: io_replace.c + $(CC) $(CFLAGS) -o io_replace io_replace.c + +clean: + rm -f iobench cpu_dedup io_replace diff --git a/lab/vtsh/.vscode/tasks.json b/lab/vtsh/.vscode/tasks.json new file mode 100644 index 0000000..08d9005 --- /dev/null +++ b/lab/vtsh/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: gcc build active file", + "command": "/usr/bin/gcc", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Task generated by Debugger." + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/lab/vtsh/bin/main.c b/lab/vtsh/bin/main.c index fd9b122..691e0ca 100644 --- a/lab/vtsh/bin/main.c +++ b/lab/vtsh/bin/main.c @@ -2,6 +2,6 @@ #include int main() { - printf("%s", vtsh_prompt()); - printf("Hello, world!\n"); + vtsh_run(); + return 0; } diff --git a/lab/vtsh/lib/CMakeLists.txt b/lab/vtsh/lib/CMakeLists.txt index 962968c..e83284c 100644 --- a/lab/vtsh/lib/CMakeLists.txt +++ b/lab/vtsh/lib/CMakeLists.txt @@ -1,7 +1,9 @@ -add_library( - libvtsh - STATIC +add_library(libvtsh STATIC vtsh.c + utils.c + tokenizer.c + parser.c + exec.c ) target_include_directories( diff --git a/lab/vtsh/lib/exec.c b/lab/vtsh/lib/exec.c new file mode 100644 index 0000000..8eb3c2e --- /dev/null +++ b/lab/vtsh/lib/exec.c @@ -0,0 +1,112 @@ +#include "exec.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include + +static void exec_self(char *const argv[]) { + char self[PATH_MAX]; + ssize_t n = readlink("/proc/self/exe", self, sizeof(self) - 1); + if (n > 0) { + self[n] = '\0'; + execv(self, argv); + } +} + +static void exec_with_fallback(char *const argv[]) { + const char *cmd = argv[0]; + + // 1) если есть '/', пробуем как путь + if (strchr(cmd, '/')) { + execv(cmd, argv); + + // специальный кейс для теста вложенных шеллов + if (strcmp(cmd, "./shell") == 0) { + exec_self(argv); + } + + return; // дальше обработаем как "not found" + } + + // 2) обычный запуск через PATH + execvp(cmd, argv); + + // 3) если не нашли — попробуем стандартные директории + if (errno == ENOENT) { + char path[PATH_MAX]; + + snprintf(path, sizeof(path), "/bin/%s", cmd); + execv(path, argv); + + snprintf(path, sizeof(path), "/usr/bin/%s", cmd); + execv(path, argv); + + // на всякий случай: если пользователь ввёл просто "shell" + if (strcmp(cmd, "shell") == 0) { + exec_self(argv); + } + } +} + +static int run_one(const Cmd *c) { + if (c->argc == 0) return 0; + + long long t0 = nsec_now(); + pid_t pid = fork(); + if (pid < 0) { + dprintf(STDOUT_FILENO, "I/O error\n"); + return 1; + } + + if (pid == 0) { + int saved_stdout = dup(STDOUT_FILENO); + if (saved_stdout < 0) saved_stdout = STDOUT_FILENO; + + // редиректы + for (int i = 0; i < c->nredirs; i++) { + int fd; + if (c->redirs[i].type == '<') { + fd = open(c->redirs[i].filename, O_RDONLY); + if (fd < 0) { dprintf(saved_stdout, "I/O error\n"); _exit(1); } + if (dup2(fd, STDIN_FILENO) < 0) { close(fd); dprintf(saved_stdout, "I/O error\n"); _exit(1); } + close(fd); + } else { + fd = open(c->redirs[i].filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) { dprintf(saved_stdout, "I/O error\n"); _exit(1); } + if (dup2(fd, STDOUT_FILENO) < 0) { close(fd); dprintf(saved_stdout, "I/O error\n"); _exit(1); } + close(fd); + } + } + + exec_with_fallback(c->argv); + + // если дошли сюда — ничего не запустилось + dprintf(STDOUT_FILENO, "Command not found\n"); + _exit(127); + } + + int st = 0; + waitpid(pid, &st, 0); + long long t1 = nsec_now(); + + // время — в stderr (тесты читают stdout) + fprintf(stderr, "time_ns=%lld\n", (t1 - t0)); + + if (WIFEXITED(st)) return WEXITSTATUS(st); + if (WIFSIGNALED(st)) return 128 + WTERMSIG(st); + return 1; +} + +int run_commands(const CmdVec *cmds) { + int last_status = 0; + for (int i = 0; i < cmds->n; i++) { + if (cmds->v[i].and_after_prev && last_status != 0) continue; + last_status = run_one(&cmds->v[i]); + } + return last_status; +} diff --git a/lab/vtsh/lib/exec.h b/lab/vtsh/lib/exec.h new file mode 100644 index 0000000..ae21b78 --- /dev/null +++ b/lab/vtsh/lib/exec.h @@ -0,0 +1,4 @@ +#pragma once +#include "parser.h" + +int run_commands(const CmdVec* cmds); diff --git a/lab/vtsh/lib/parser.c b/lab/vtsh/lib/parser.c new file mode 100644 index 0000000..d3e2d98 --- /dev/null +++ b/lab/vtsh/lib/parser.c @@ -0,0 +1,161 @@ +#include "parser.h" + +#include +#include + +static void cmd_init(Cmd* c, bool and_after_prev) { + memset(c, 0, sizeof(*c)); + c->and_after_prev = and_after_prev; +} + +static void cmd_free(Cmd* c) { + for (int i = 0; i < c->argc; i++) + free(c->argv[i]); + free(c->argv); + for (int i = 0; i < c->nredirs; i++) + free(c->redirs[i].filename); + free(c->redirs); + memset(c, 0, sizeof(*c)); +} + +static void cmd_add_arg(Cmd* c, char* s_owned) { + if (c->argc + 1 >= c->argv_cap) { + c->argv_cap = c->argv_cap ? c->argv_cap * 2 : 8; + c->argv = (char**)xrealloc(c->argv, (size_t)c->argv_cap * sizeof(char*)); + } + c->argv[c->argc++] = s_owned; + c->argv[c->argc] = NULL; +} + +static void cmd_add_redir(Cmd* c, char type, char* filename_owned) { + if (c->nredirs == c->redir_cap) { + c->redir_cap = c->redir_cap ? c->redir_cap * 2 : 4; + c->redirs = + (Redir*)xrealloc(c->redirs, (size_t)c->redir_cap * sizeof(Redir)); + } + c->redirs[c->nredirs].type = type; + c->redirs[c->nredirs].filename = filename_owned; + c->nredirs++; +} + +static void cv_push(CmdVec* cv, Cmd* move_from) { + if (cv->n == cv->cap) { + cv->cap = cv->cap ? cv->cap * 2 : 8; + cv->v = (Cmd*)xrealloc(cv->v, (size_t)cv->cap * sizeof(Cmd)); + } + cv->v[cv->n++] = *move_from; + memset(move_from, 0, sizeof(*move_from)); +} + +void cv_free(CmdVec* cv) { + for (int i = 0; i < cv->n; i++) + cmd_free(&cv->v[i]); + free(cv->v); + cv->v = NULL; + cv->n = cv->cap = 0; +} + +static bool is_bad_filename_token(const char* t) { + if (!t || !*t) + return true; + if (strcmp(t, "&&") == 0) + return true; + if (t[0] == '<' || t[0] == '>') + return true; + return false; +} + +bool parse_tokens(const StrVec* toks, CmdVec* cmds) { + cmds->v = NULL; + cmds->n = cmds->cap = 0; + + Cmd cur; + bool cur_inited = false; + bool next_and = false; + + for (int i = 0; i < toks->n; i++) { + const char* tok = toks->v[i]; + + if (!cur_inited) { + cmd_init(&cur, next_and); + cur_inited = true; + next_and = false; + } + + if (strcmp(tok, "&&") == 0) { + if ((cur.argc == 0 && cur.nredirs == 0) || cur.argc == 0) { + cmd_free(&cur); + return false; + } + cv_push(cmds, &cur); + cur_inited = false; + next_and = true; + continue; + } + + if (tok[0] == '>' || tok[0] == '<') { + char type = tok[0]; + if (type == '>' && tok[1] == '>') { + cmd_free(&cur); + return false; + } // forbid >> + + char* fname = NULL; + if (tok[1] == '\0') { + if (i + 1 >= toks->n) { + cmd_free(&cur); + return false; + } + const char* nxt = toks->v[i + 1]; + if (is_bad_filename_token(nxt)) { + cmd_free(&cur); + return false; + } + fname = xstrdup(nxt); + i++; + } else { + fname = xstrdup(tok + 1); + if (!*fname) { + free(fname); + cmd_free(&cur); + return false; + } + } + + if (type == '<') { + if (cur.has_in) { + free(fname); + cmd_free(&cur); + return false; + } + cur.has_in = true; + } else { + if (cur.has_out) { + free(fname); + cmd_free(&cur); + return false; + } + cur.has_out = true; + } + + cmd_add_redir(&cur, type, fname); + continue; + } + + cmd_add_arg(&cur, xstrdup(tok)); + } + + if (cur_inited) { + if (cur.argc == 0 && cur.nredirs == 0) { + cmd_free(&cur); + } else { + if (cur.argc == 0) { + cmd_free(&cur); + return false; + } + cv_push(cmds, &cur); + } + } + + return true; +} diff --git a/lab/vtsh/lib/parser.h b/lab/vtsh/lib/parser.h new file mode 100644 index 0000000..417cb22 --- /dev/null +++ b/lab/vtsh/lib/parser.h @@ -0,0 +1,32 @@ +#pragma once +#include + +#include "utils.h" + +typedef struct { + char type; // '<' or '>' + char* filename; // owned +} Redir; + +typedef struct { + char** argv; // owned, NULL-terminated + int argc; + int argv_cap; + + Redir* redirs; + int nredirs; + int redir_cap; + + bool and_after_prev; + bool has_in; + bool has_out; +} Cmd; + +typedef struct { + Cmd* v; + int n; + int cap; +} CmdVec; + +void cv_free(CmdVec* cv); +bool parse_tokens(const StrVec* toks, CmdVec* cmds); diff --git a/lab/vtsh/lib/tokenizer.c b/lab/vtsh/lib/tokenizer.c new file mode 100644 index 0000000..ced05b7 --- /dev/null +++ b/lab/vtsh/lib/tokenizer.c @@ -0,0 +1,63 @@ +#include "tokenizer.h" + +#include +#include +#include +#include + +static void split_by_andand(StrVec* out, char* tok_owned) { + const char* s = tok_owned; + size_t len = strlen(s); + + bool has = false; + for (size_t i = 0; i + 1 < len; i++) { + if (s[i] == '&' && s[i + 1] == '&') { + has = true; + break; + } + } + if (!has) { + sv_push(out, tok_owned); + return; + } + + size_t i = 0; + while (i < len) { + size_t j = i; + while (j + 1 < len && !(s[j] == '&' && s[j + 1] == '&')) + j++; + if (j > i) + sv_push(out, xstrndup(s + i, j - i)); + if (j + 1 < len && s[j] == '&' && s[j + 1] == '&') { + sv_push(out, xstrdup("&&")); + i = j + 2; + } else { + i = j + 1; + } + } + free(tok_owned); +} + +StrVec tokenize_line(const char* line) { + StrVec raw = {0}; + StrVec out = {0}; + + const char* p = line; + while (*p) { + while (*p && isspace((unsigned char)*p)) + p++; + if (!*p) + break; + const char* start = p; + while (*p && !isspace((unsigned char)*p)) + p++; + sv_push(&raw, xstrndup(start, (size_t)(p - start))); + } + + for (int i = 0; i < raw.n; i++) { + split_by_andand(&out, raw.v[i]); + raw.v[i] = NULL; + } + free(raw.v); + return out; +} diff --git a/lab/vtsh/lib/tokenizer.h b/lab/vtsh/lib/tokenizer.h new file mode 100644 index 0000000..8fd2d98 --- /dev/null +++ b/lab/vtsh/lib/tokenizer.h @@ -0,0 +1,4 @@ +#pragma once +#include "utils.h" + +StrVec tokenize_line(const char* line); diff --git a/lab/vtsh/lib/utils.c b/lab/vtsh/lib/utils.c new file mode 100644 index 0000000..e3fb177 --- /dev/null +++ b/lab/vtsh/lib/utils.c @@ -0,0 +1,52 @@ +#include "utils.h" + +#include +#include +#include +#include + +void* xrealloc(void* p, size_t n) { + void* q = realloc(p, n); + if (!q) { + perror("realloc"); + exit(1); + } + return q; +} + +char* xstrndup(const char* s, size_t n) { + char* p = (char*)malloc(n + 1); + if (!p) { + perror("malloc"); + exit(1); + } + memcpy(p, s, n); + p[n] = '\0'; + return p; +} + +char* xstrdup(const char* s) { + return xstrndup(s, strlen(s)); +} + +void sv_push(StrVec* sv, char* s_owned) { + if (sv->n == sv->cap) { + sv->cap = sv->cap ? sv->cap * 2 : 16; + sv->v = (char**)xrealloc(sv->v, (size_t)sv->cap * sizeof(char*)); + } + sv->v[sv->n++] = s_owned; +} + +void sv_free(StrVec* sv) { + for (int i = 0; i < sv->n; i++) + free(sv->v[i]); + free(sv->v); + sv->v = NULL; + sv->n = sv->cap = 0; +} + +long long nsec_now(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (long long)ts.tv_sec * 1000000000LL + (long long)ts.tv_nsec; +} diff --git a/lab/vtsh/lib/utils.h b/lab/vtsh/lib/utils.h new file mode 100644 index 0000000..c5b2dfb --- /dev/null +++ b/lab/vtsh/lib/utils.h @@ -0,0 +1,17 @@ +#pragma once +#include + +typedef struct { + char** v; + int n; + int cap; +} StrVec; + +void* xrealloc(void* p, size_t n); +char* xstrndup(const char* s, size_t n); +char* xstrdup(const char* s); + +void sv_push(StrVec* sv, char* s_owned); +void sv_free(StrVec* sv); + +long long nsec_now(void); diff --git a/lab/vtsh/lib/vtsh.c b/lab/vtsh/lib/vtsh.c index 8c0cb60..00bf256 100644 --- a/lab/vtsh/lib/vtsh.c +++ b/lab/vtsh/lib/vtsh.c @@ -1,5 +1,56 @@ #include "vtsh.h" -const char* vtsh_prompt() { +#include +#include +#include + +#include "exec.h" +#include "parser.h" +#include "tokenizer.h" + +const char* vtsh_prompt(void) { return "vtsh> "; } + +void vtsh_run(void) { + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + + char* line = NULL; + size_t cap = 0; + + while (1) { + fputs(vtsh_prompt(), stdout); + fflush(stdout); + + ssize_t n = getline(&line, &cap, stdin); + if (n < 0) + break; + + while (n > 0 && (line[n - 1] == '\n' || line[n - 1] == '\r')) { + line[n - 1] = '\0'; + n--; + } + + StrVec toks = tokenize_line(line); + if (toks.n == 0) { + sv_free(&toks); + continue; + } + + CmdVec cmds = {0}; + if (!parse_tokens(&toks, &cmds)) { + dprintf(STDOUT_FILENO, "Syntax error\n"); + sv_free(&toks); + cv_free(&cmds); + continue; + } + + (void)run_commands(&cmds); + + sv_free(&toks); + cv_free(&cmds); + } + + free(line); +} diff --git a/lab/vtsh/lib/vtsh.h b/lab/vtsh/lib/vtsh.h index 919515a..f35e297 100644 --- a/lab/vtsh/lib/vtsh.h +++ b/lab/vtsh/lib/vtsh.h @@ -1,3 +1,4 @@ #pragma once -const char* vtsh_prompt(); +const char* vtsh_prompt(void); +void vtsh_run(void); diff --git a/lab/vtsh/test/lol Date: Sun, 18 Jan 2026 17:03:29 +0300 Subject: [PATCH 2/9] Delete lab/vtsh/test/lol 0 bytes lab/vtld/cpu_dedup.c | 101 +++------ lab/vtld/cpu_dedup_pthreads.c | 145 +++++++++++++ lab/vtld/cpu_utils.c | 33 +++ lab/vtld/cpu_utils.h | 5 + lab/vtld/ema_replace_str.c | 238 +++++++++++++++++++++ lab/vtld/ema_replace_str_pthreads.c | 318 ++++++++++++++++++++++++++++ lab/vtld/ema_utils.c | 126 +++++++++++ lab/vtld/ema_utils.h | 14 ++ lab/vtld/io_replace | Bin 17216 -> 0 bytes lab/vtld/io_replace.c | 203 ------------------ lab/vtld/iobench | Bin 17224 -> 0 bytes lab/vtld/iobench.c | 232 -------------------- lab/vtld/makefile | 21 +- lab/vtld/util.c | 21 ++ lab/vtld/util.h | 6 + lab/vtsh/README.md | 125 +---------- lab/vtsh/lib/exec.c | 180 ++++++++-------- lab/vtsh/lib/parser.c | 2 +- lab/vtsh/lib/vtsh.c | 2 +- lab/vtsh/test/lol` - размер блока в байтах, с которым производится чтение/запись; +- `block_count: ` - количество блоков; +- `file: ` - имя файла, с которым происходит работа; +- `range: -` - границы в пределах файла, в которые должны + осуществляться запись/чтение, значение по умолчанию, 0-0, означает, что доступен + весь файл; +- `direct: on/off` - открывать файл с опцией O_DIRECT (в обход кэшей ОС) или нет; +- `type: sequence/random` - режим выбора следующего блока для записи/чтения + последовательно или случайно; + +Разработайте комплекс программ-нагрузчиков согласно выданному варианту. +Каждый нагрузчик должен принимать параметр, который определяет +количество повторений для алгоритма, указанного в задании, а также другие +вспомогательные параметры. Варианты программ рассчитаны так, чтобы по-разному +нагружать вычислительную подсистему (CPU) и подсистему ввода-вывода (IO) - это +необходимо учитывать при их реализации. Разработанные программы необходимо +скомпилировать без дополнительных опций оптимизации компилятора. + +Проведите исследование поведения ОС во время исполнения разработанных +программ-нагрузчиков по следующему плану: + +1. Перед запуском нагрузчика, попробуйте оценить время работы вашей программы + или ее результаты (если по варианту вам досталось измерение чего либо) и + обоснуйте свои предположения. + +2. Запустите программу-нагрузчик и зафиксируйте метрики ее работы с помощью + инструментов для мониторинга и профилирования (см. лекции). Сравните полученные + результаты с ожидаемыми. Объяснените наблюдаемое поведение. Продолжительность + каждого запуска должна занимать достаточное для прекращения переходных + процессов время, по крайней мере, минуту. + +3. Определите количество одновременно запущенных процессов с программой-нагрузчиком, + которое эффективно нагружает все ядра процессора в вашей системе. Как + распределяются показатели времени USER%, SYS%, WAIT%, а также полное время + выполнения нагрузчика, какое количество переключений контекста (вынужденных и + невынужденных) происходит при этом? Подумайте над тем, как вы определяете + эффективность. + +4. Увеличьте количество нагрузчиков вдвое, втрое, вчетверо. Как изменились + исследуемые показатели? Почему? + +5. Объедините программы-нагрузчики в одну, реализованную при помощи потоков + выполнения, чтобы один нагрузчик эффективно нагружал все ядра вашей системы. Как + изменились показатели времени для того же объема вычислений? Запустите одну, + две, три таких программы. Как изменились исследуемые показатели? Почему? + +6. Скомпилируйте программу-нугрузчик с опцией агрессивной оптимизации. Как + изменились исследуемые показатели? На сколько сократилось реальное время + исполнения программы нагрузчика? Почему? + +В процессе защиты вашей работы преподаватель будет просить вас запустить +программу-нагрузчик с различными комбинациями параметров и просить объяснить +результат. + +## Требования к реализации + +1. Программа (комплекс программ) должна быть реализован на языке C. + +2. Дочерние процессы должны быть созданы через указанные в варианте системные + вызовы операционной системы, с обеспечением корректного запуска и завершения + процессов. Запрещено использовать высокоуровневые абстракции над системными + вызовами. Необходимо использовать, процедуры libc. + +## Требования к отчету и защите + +Отчет должен содержать: + +1. Титульный лист с указанием номера и названия ЛР, вашего ФИО, ФИО + преподавателя практики, номера вашей группы, варианта ЛР. + +2. Текст задания в соответствии с вариантом. + +3. Листинг исходного кода всех программ, написанных в рамках данной ЛР. + +4. Предположения о свойствах программ-нагрузчиков + +5. Результаты измерений и метрик программ-нагрузчиков, полученных инструментами + мониторинга. Должно быть описано, какие утилиты запускались, с какими + параметрами и их вывод. + +6. Сравнительный анализ ожидаемых и фактических значений исследованных показателей. + +7. Заключение. + +Студент должен быть готов: + +1. Продемонстрировать работоспособность Shell и предоставить исходный код. + +2. Воспроизвести ход работы по исследованию поведения ОС и продемонстрировать схожие + результаты работы программ-нагрузчиков. + +3. Ответить на вопросы по реализации программ и контрольные вопросы. + +## Описание вариантов + +Нагрузчик вычислительной подсистемы (группа **cpu**): + +- `cpu-factorize`: Разложение числа на простые множители. Задаваемое число + должно являться произведением нескольких больших простых чисел. + +- `cpu-sort`: Любая сортировка массива чисел за N*log(N) и любая за N**2. + Алгоритм должен быть реализован самим студентом, а не взят из стандартной + библиотеки. + +- `cpu-short-path`: Поиск кратчайшего пути в графе. Граф генерируется в момент + запуска или записан в исходный код. + +- `cpu-linreg`: Построение модели линейной регрессии для последовательности случайных чисел. Количество чисел и диапозон значений задаются параметром. + +- `cpu-dedup`: Дедупликация элементов в массиве. Массив генерируется на основе + случайных чисел. Размер массива задаётся параметром. + +- `cpu-mat-mul`: Перемножение матриц. Размер матриц задаётся параметром, а + элементы матриц генерируются на основе случайных чисел. + +- `cpu-calc-md5`: Подсчитать хеш md5 для текста полученного конкатенацией + фрагментов текста выбранных с помощью генератора случайных чисел. + +- `cpu-calc-crc`: Посчитать контрольную сумму crc для текста полученного + конкатенацией фрагментов текста выбранных с помощью генератора случайных чисел. + + +Нагрузчик, работающий с внешней памятью (группа **ema**): + +- `ema-replace-int`: Поиск элемента в массиве чисел во внешней памяти и замена + значения на другое. Файл для поиска значения генерируется в произвольном + формате, размер задаётся через параметр. + +- `ema-replace-str`: Поиск подстроки в тексте во внешней памяти и замена на + другую строку того же размера. Файл для поиска значения генерируется в + произвольном формате, размер задаётся через параметр. + +- `ema-sort-int`: Сортировка массива чисел во внешней памяти. Весь массив не + может быть загружен в память, поэтому сортировка происходит частями. + +- `ema-traverse-graph`: Обход k-регулярного направленного графа, + сериализованного в файл, и выполнение модифицирующей операции над определенной + вершиной графа. Поиск вершины происходит по хранимому в ней значению, возможно с + ограничением глубины поиска. + Формат хранения: вершина (struct Node) определяется её отступом от начала файла (id=index*(sizeof struct Node)), в вершине хранится одно значение типа фиксированного размера и k номеров вершин, в которые из неё можно попасть; в файле вершины хранятся последовательно с выравниванием соответствующим размеру структуры. Тип значения, хранимого в вершине, и значение k подбираются таким образом, чтобы с учетом выравнивания не было неиспользуемой памяти. + При генерации графа случайно задается значение, хранимое в вершине и номера соседних вершин, отдельным параметром генератора задется шанс получения номера соседней вершины больше или меньше номера данной вершины (соседняя вершина раньше или позже). + +- `ema-join-*`: Необходимо реализовать алгоритм equi-join для объединения + таблиц в файлах. На вход подаются два файла, результат их объединения пишется в + новый файл. В первой строке файла содержится количество строчек в файле. В + каждой последующей строчке входного файла содержится два значения, разделенных + пробелами: численный идентификатор и строка без пробелов фиксированного размера + 8 байт. Строка является валидным английским словом. Таблицы необходимо + объединять по идентификатору. + + По варианту выдается стратегия джоина: + - `ema-join-nl`: Nested Loop Join + - `ema-join-hash`: Hash Join Join + - `ema-join-sm`: Sort + Merge Join + + Количество строк входных таблиц включают все комбинации пар значений: 5, 10, 100, 1000, 10000." \ No newline at end of file diff --git a/lab/vtld/cpu_dedup b/lab/vtld/cpu_dedup deleted file mode 100755 index a4a2832dabefc459825c1b2824f37889c200d944..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16736 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 diff --git a/lab/vtld/cpu_dedup.c b/lab/vtld/cpu_dedup.c index a4ef6aa..1a3f0ab 100644 --- a/lab/vtld/cpu_dedup.c +++ b/lab/vtld/cpu_dedup.c @@ -1,77 +1,42 @@ -#include #include #include #include #include -static void print_usage(const char *prog) { - fprintf(stderr, "Usage: %s --size N --repeats N [--max_value N]\n", prog); +#include "cpu_utils.h" +#include "util.h" + +static void print_usage(const char* prog) { + fprintf(stderr, "Usage: %s --size N --repeats N [--max_value N]\n", prog); } -static const char *get_opt_value(int argc, char **argv, const char *key) { - size_t key_len = strlen(key); - for (int i = 1; i < argc; ++i) { - const char *arg = argv[i]; - while (*arg == '-') { - ++arg; - } - const char *eq = strchr(arg, '='); - if (eq) { - size_t len = (size_t)(eq - arg); - if (len == key_len && strncmp(arg, key, len) == 0) { - return eq + 1; - } - } else if (strcmp(arg, key) == 0 && i + 1 < argc) { - return argv[i + 1]; - } +int main(int argc, char** argv) { + const char* size_s = get_opt_value(argc, argv, "size"); + const char* repeats_s = get_opt_value(argc, argv, "repeats"); + const char* max_value_s = get_opt_value(argc, argv, "max_value"); + + if (!size_s || !repeats_s) { + print_usage(argv[0]); + return 1; + } + + long size = strtol(size_s, NULL, 10); + long repeats = strtol(repeats_s, NULL, 10); + long max_value = max_value_s ? strtol(max_value_s, NULL, 10) : (size * 2); + + if (size <= 0 || repeats <= 0 || max_value <= 0) { + fprintf(stderr, "Invalid size, repeats, or max_value.\n"); + return 1; + } + + unsigned int seed = (unsigned int)time(NULL); + for (long r = 0; r < repeats; ++r) { + long unique = dedup_run(size, max_value, &seed); + if (unique < 0) { + fprintf(stderr, "dedup_run failed.\n"); + return 1; } - return NULL; -} - -static int cmp_int32(const void *a, const void *b) { - int32_t ia = *(const int32_t *)a; - int32_t ib = *(const int32_t *)b; - return (ia > ib) - (ia < ib); -} - -int main(int argc, char **argv) { - const char *size_s = get_opt_value(argc, argv, "size"); - const char *repeats_s = get_opt_value(argc, argv, "repeats"); - const char *max_value_s = get_opt_value(argc, argv, "max_value"); - - if (!size_s || !repeats_s) { - print_usage(argv[0]); - return 1; - } - - long size = strtol(size_s, NULL, 10); - long repeats = strtol(repeats_s, NULL, 10); - long max_value = max_value_s ? strtol(max_value_s, NULL, 10) : (size * 2); - - if (size <= 0 || repeats <= 0 || max_value <= 0) { - fprintf(stderr, "Invalid size, repeats, or max_value.\n"); - return 1; - } - - srand((unsigned)time(NULL)); - for (long r = 0; r < repeats; ++r) { - int32_t *arr = malloc((size_t)size * sizeof(int32_t)); - if (!arr) { - fprintf(stderr, "malloc failed.\n"); - return 1; - } - for (long i = 0; i < size; ++i) { - arr[i] = (int32_t)(rand() % max_value); - } - qsort(arr, (size_t)size, sizeof(int32_t), cmp_int32); - long unique = 0; - for (long i = 0; i < size; ++i) { - if (i == 0 || arr[i] != arr[i - 1]) { - ++unique; - } - } - printf("repeat %ld: unique=%ld\n", r + 1, unique); - free(arr); - } - return 0; + printf("repeat %ld: unique=%ld\n", r + 1, unique); + } + return 0; } diff --git a/lab/vtld/cpu_dedup_pthreads.c b/lab/vtld/cpu_dedup_pthreads.c new file mode 100644 index 0000000..bf3ad74 --- /dev/null +++ b/lab/vtld/cpu_dedup_pthreads.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include + +#include "cpu_utils.h" +#include "util.h" + +typedef struct { + long size; + long max_value; + long repeat_start; + long repeat_end; + long* results; + int* error_flag; + pthread_mutex_t* error_lock; + unsigned int seed; +} worker_ctx_t; + +static void print_usage(const char* prog) { + fprintf( + stderr, + "Usage: %s --size N --repeats N [--max_value N] [--threads N]\n", + prog + ); +} + +static void set_error(worker_ctx_t* ctx) { + pthread_mutex_lock(ctx->error_lock); + *ctx->error_flag = 1; + pthread_mutex_unlock(ctx->error_lock); +} + +static void* worker_main(void* arg) { + worker_ctx_t* ctx = (worker_ctx_t*)arg; + unsigned int seed = ctx->seed; + + for (long r = ctx->repeat_start; r < ctx->repeat_end; ++r) { + long unique = dedup_run(ctx->size, ctx->max_value, &seed); + if (unique < 0) { + fprintf(stderr, "dedup_run failed.\n"); + set_error(ctx); + return NULL; + } + ctx->results[r] = unique; + } + + return NULL; +} + +int main(int argc, char** argv) { + const char* size_s = get_opt_value(argc, argv, "size"); + const char* repeats_s = get_opt_value(argc, argv, "repeats"); + const char* max_value_s = get_opt_value(argc, argv, "max_value"); + const char* threads_s = get_opt_value(argc, argv, "threads"); + + if (!size_s || !repeats_s) { + print_usage(argv[0]); + return 1; + } + + long size = strtol(size_s, NULL, 10); + long repeats = strtol(repeats_s, NULL, 10); + long max_value = max_value_s ? strtol(max_value_s, NULL, 10) : (size * 2); + long threads = 0; + if (threads_s) { + threads = strtol(threads_s, NULL, 10); + } else { + long cpu = sysconf(_SC_NPROCESSORS_ONLN); + threads = cpu > 0 ? cpu : 1; + } + + if (size <= 0 || repeats <= 0 || max_value <= 0 || threads <= 0) { + fprintf(stderr, "Invalid size, repeats, max_value, or threads.\n"); + return 1; + } + + if (threads > repeats) { + threads = repeats; + } + + long* results = calloc((size_t)repeats, sizeof(long)); + if (!results) { + fprintf(stderr, "calloc failed.\n"); + return 1; + } + + pthread_t* tids = malloc((size_t)threads * sizeof(pthread_t)); + worker_ctx_t* ctxs = malloc((size_t)threads * sizeof(worker_ctx_t)); + if (!tids || !ctxs) { + fprintf(stderr, "malloc failed.\n"); + free(results); + free(tids); + free(ctxs); + return 1; + } + + pthread_mutex_t error_lock = PTHREAD_MUTEX_INITIALIZER; + int error_flag = 0; + + long base = repeats / threads; + long rem = repeats % threads; + long cur = 0; + unsigned int seed = (unsigned int)time(NULL); + + for (long i = 0; i < threads; ++i) { + long count = base + (i < rem ? 1 : 0); + ctxs[i].size = size; + ctxs[i].max_value = max_value; + ctxs[i].repeat_start = cur; + ctxs[i].repeat_end = cur + count; + ctxs[i].results = results; + ctxs[i].error_flag = &error_flag; + ctxs[i].error_lock = &error_lock; + ctxs[i].seed = seed ^ (unsigned int)(i * 2654435761u); + if (pthread_create(&tids[i], NULL, worker_main, &ctxs[i]) != 0) { + fprintf(stderr, "pthread_create failed.\n"); + error_flag = 1; + threads = i; + break; + } + cur += count; + } + + for (long i = 0; i < threads; ++i) { + pthread_join(tids[i], NULL); + } + + if (error_flag) { + free(results); + free(tids); + free(ctxs); + return 1; + } + + for (long r = 0; r < repeats; ++r) { + printf("repeat %ld: unique=%ld\n", r + 1, results[r]); + } + + free(results); + free(tids); + free(ctxs); + return 0; +} diff --git a/lab/vtld/cpu_utils.c b/lab/vtld/cpu_utils.c new file mode 100644 index 0000000..51f58bb --- /dev/null +++ b/lab/vtld/cpu_utils.c @@ -0,0 +1,33 @@ +#include "cpu_utils.h" + +#include +#include + +static int cmp_int32(const void* a, const void* b) { + int32_t ia = *(const int32_t*)a; + int32_t ib = *(const int32_t*)b; + return (ia > ib) - (ia < ib); +} + +long dedup_run(long size, long max_value, unsigned int* seed) { + if (size <= 0 || max_value <= 0 || !seed) { + return -1; + } + + int32_t* arr = malloc((size_t)size * sizeof(int32_t)); + if (!arr) { + return -1; + } + for (long i = 0; i < size; ++i) { + arr[i] = (int32_t)(rand_r(seed) % max_value); + } + qsort(arr, (size_t)size, sizeof(int32_t), cmp_int32); + long unique = 0; + for (long i = 0; i < size; ++i) { + if (i == 0 || arr[i] != arr[i - 1]) { + ++unique; + } + } + free(arr); + return unique; +} diff --git a/lab/vtld/cpu_utils.h b/lab/vtld/cpu_utils.h new file mode 100644 index 0000000..d6a777b --- /dev/null +++ b/lab/vtld/cpu_utils.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +long dedup_run(long size, long max_value, unsigned int* seed); diff --git a/lab/vtld/ema_replace_str.c b/lab/vtld/ema_replace_str.c new file mode 100644 index 0000000..38cab6d --- /dev/null +++ b/lab/vtld/ema_replace_str.c @@ -0,0 +1,238 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ema_utils.h" +#include "util.h" + +static void print_usage(const char* prog) { + fprintf( + stderr, + "Usage: %s --block_size N --file PATH --repeats N --string STR " + "--replace STR [--size N] [--range A-B] [--direct on/off] " + "[--type sequence/random]\n", + prog + ); +} + +int main(int argc, char** argv) { + const char* block_size_s = get_opt_value(argc, argv, "block_size"); + const char* file_s = get_opt_value(argc, argv, "file"); + const char* range_s = get_opt_value(argc, argv, "range"); + const char* direct_s = get_opt_value(argc, argv, "direct"); + const char* type_s = get_opt_value(argc, argv, "type"); + const char* repeats_s = get_opt_value(argc, argv, "repeats"); + const char* string_s = get_opt_value(argc, argv, "string"); + const char* replace_s = get_opt_value(argc, argv, "replace"); + const char* size_s = get_opt_value(argc, argv, "size"); + + if (!block_size_s || !file_s || !repeats_s || !string_s || !replace_s) { + print_usage(argv[0]); + return 1; + } + + uint64_t block_size_u = 0; + uint64_t repeats = 0; + uint64_t gen_size = 0; + if (!parse_u64(block_size_s, &block_size_u) || + !parse_u64(repeats_s, &repeats)) { + fprintf(stderr, "Invalid block_size or repeats.\n"); + return 1; + } + if (size_s && !parse_u64(size_s, &gen_size)) { + fprintf(stderr, "Invalid size.\n"); + return 1; + } + if (block_size_u == 0 || repeats == 0) { + fprintf(stderr, "block_size and repeats must be > 0.\n"); + return 1; + } + + size_t block_size = (size_t)block_size_u; + size_t pattern_len = strlen(string_s); + if (pattern_len == 0 || strlen(replace_s) != pattern_len) { + fprintf(stderr, "string and replace must be non-empty and same length.\n"); + return 1; + } + if (pattern_len > block_size) { + fprintf(stderr, "string length must be <= block_size.\n"); + return 1; + } + + int direct = 0; + if (direct_s) { + if (strcmp(direct_s, "on") == 0) { + direct = 1; + } else if (strcmp(direct_s, "off") != 0) { + fprintf(stderr, "Invalid direct value: use on/off.\n"); + return 1; + } + } + + int random_mode = 0; + if (type_s) { + if (strcmp(type_s, "random") == 0) { + random_mode = 1; + } else if (strcmp(type_s, "sequence") != 0) { + fprintf(stderr, "Invalid type value: use sequence/random.\n"); + return 1; + } + } + + uint64_t range_start = 0; + uint64_t range_end = 0; + if (!parse_range(range_s, &range_start, &range_end)) { + fprintf(stderr, "Invalid range format, expected A-B.\n"); + return 1; + } + + srand((unsigned)time(NULL)); + + if (size_s) { + if (gen_size == 0) { + fprintf(stderr, "size must be > 0.\n"); + return 1; + } + if (!generate_file(file_s, gen_size, block_size, string_s)) { + return 1; + } + } + + if (direct) { + if ((block_size % 512) != 0) { + fprintf(stderr, "block_size must be a multiple of 512 for O_DIRECT.\n"); + return 1; + } + } + + int fd = open_file_direct(file_s, direct); + if (fd < 0) { + perror("open"); + return 1; + } + + struct stat st; + if (fstat(fd, &st) != 0) { + perror("fstat"); + close(fd); + return 1; + } + uint64_t file_size = (uint64_t)st.st_size; + if (file_size == 0) { + fprintf(stderr, "File is empty.\n"); + close(fd); + return 1; + } + + if (range_start == 0 && range_end == 0) { + range_start = 0; + range_end = file_size; + } + if (range_end > file_size || range_start >= range_end) { + fprintf(stderr, "Invalid range bounds.\n"); + close(fd); + return 1; + } + + uint64_t range_len = range_end - range_start; + uint64_t full_blocks = range_len / block_size; + uint64_t tail = range_len % block_size; + if (direct && tail != 0) { + range_end = range_start + (full_blocks * block_size); + range_len = range_end - range_start; + tail = 0; + if (range_len == 0) { + fprintf( + stderr, "Range too small for O_DIRECT with the given block_size.\n" + ); + close(fd); + return 1; + } + fprintf(stderr, "Warning: range truncated to fit O_DIRECT alignment.\n"); + } + + uint64_t blocks = full_blocks + ((tail > 0 && !direct) ? 1 : 0); + if (blocks == 0) { + fprintf(stderr, "No blocks to process.\n"); + close(fd); + return 1; + } + + void* buf = NULL; + size_t alloc_size = block_size; + if (direct) { + size_t align = 4096; + alloc_size = ((block_size + align - 1) / align) * align; + buf = aligned_alloc(align, alloc_size); + if (!buf) { + fprintf(stderr, "aligned_alloc failed.\n"); + close(fd); + return 1; + } + } else { + buf = malloc(block_size); + if (!buf) { + fprintf(stderr, "malloc failed.\n"); + close(fd); + return 1; + } + } + + uint64_t total_replaced = 0; + for (uint64_t i = 0; i < repeats; ++i) { + uint64_t block_index = + random_mode ? (uint64_t)(rand() % (int)blocks) : (i % blocks); + uint64_t offset = range_start + block_index * block_size; + size_t len = block_size; + if (!direct && tail > 0 && block_index == blocks - 1) { + len = (size_t)tail; + } + + if (lseek(fd, (off_t)offset, SEEK_SET) == (off_t)-1) { + perror("lseek"); + free(buf); + close(fd); + return 1; + } + ssize_t r = read(fd, buf, len); + if (r < 0) { + perror("read"); + free(buf); + close(fd); + return 1; + } + if (r == 0) { + continue; + } + size_t replaced = + replace_in_buffer((char*)buf, (size_t)r, string_s, replace_s); + if (replaced > 0) { + if (lseek(fd, (off_t)offset, SEEK_SET) == (off_t)-1) { + perror("lseek"); + free(buf); + close(fd); + return 1; + } + ssize_t w = write(fd, buf, (size_t)r); + if (w < 0 || w != r) { + perror("write"); + free(buf); + close(fd); + return 1; + } + total_replaced += replaced; + } + } + + printf("replacements=%" PRIu64 "\n", total_replaced); + + free(buf); + close(fd); + return 0; +} diff --git a/lab/vtld/ema_replace_str_pthreads.c b/lab/vtld/ema_replace_str_pthreads.c new file mode 100644 index 0000000..d19dade --- /dev/null +++ b/lab/vtld/ema_replace_str_pthreads.c @@ -0,0 +1,318 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ema_utils.h" +#include "util.h" + +typedef struct { + const char* file_path; + const char* needle; + const char* repl; + uint64_t range_start; + uint64_t range_end; + uint64_t blocks; + size_t block_size; + size_t tail; + int direct; + int random_mode; + uint64_t repeats; + uint64_t start_iter; + uint64_t iter_count; + uint64_t replaced; + unsigned int rng_state; +} worker_ctx; + +static pthread_mutex_t g_total_mu = PTHREAD_MUTEX_INITIALIZER; +static uint64_t g_total_replaced = 0; + +static void print_usage(const char* prog) { + fprintf( + stderr, + "Usage: %s --block_size N --file PATH --repeats N --string STR " + "--replace STR --threads N [--size N] [--range A-B] " + "[--direct on/off] [--type sequence/random]\n", + prog + ); +} + +static void* worker_main(void* arg) { + worker_ctx* ctx = (worker_ctx*)arg; + int fd = open_file_direct(ctx->file_path, ctx->direct); + if (fd < 0) { + perror("open"); + return NULL; + } + + void* buf = NULL; + if (ctx->direct) { + size_t align = 4096; + if (posix_memalign(&buf, align, ctx->block_size) != 0) { + fprintf(stderr, "posix_memalign failed.\n"); + close(fd); + return NULL; + } + } else { + buf = malloc(ctx->block_size); + if (!buf) { + fprintf(stderr, "malloc failed.\n"); + close(fd); + return NULL; + } + } + + for (uint64_t i = 0; i < ctx->iter_count; ++i) { + uint64_t global_iter = ctx->start_iter + i; + uint64_t block_index = 0; + if (ctx->random_mode) { + block_index = (uint64_t)(rand_r(&ctx->rng_state) % (int)ctx->blocks); + } else { + block_index = (global_iter % ctx->blocks); + } + uint64_t offset = ctx->range_start + block_index * ctx->block_size; + size_t len = ctx->block_size; + if (!ctx->direct && ctx->tail > 0 && block_index == ctx->blocks - 1) { + len = ctx->tail; + } + + ssize_t r = pread(fd, buf, len, (off_t)offset); + if (r < 0) { + perror("pread"); + break; + } + if (r == 0) { + continue; + } + size_t replaced = + replace_in_buffer((char*)buf, (size_t)r, ctx->needle, ctx->repl); + if (replaced > 0) { + ssize_t w = pwrite(fd, buf, (size_t)r, (off_t)offset); + if (w < 0 || w != r) { + perror("pwrite"); + break; + } + ctx->replaced += replaced; + } + } + + free(buf); + close(fd); + pthread_mutex_lock(&g_total_mu); + g_total_replaced += ctx->replaced; + pthread_mutex_unlock(&g_total_mu); + return NULL; +} + +int main(int argc, char** argv) { + const char* block_size_s = get_opt_value(argc, argv, "block_size"); + const char* file_s = get_opt_value(argc, argv, "file"); + const char* range_s = get_opt_value(argc, argv, "range"); + const char* direct_s = get_opt_value(argc, argv, "direct"); + const char* type_s = get_opt_value(argc, argv, "type"); + const char* repeats_s = get_opt_value(argc, argv, "repeats"); + const char* string_s = get_opt_value(argc, argv, "string"); + const char* replace_s = get_opt_value(argc, argv, "replace"); + const char* size_s = get_opt_value(argc, argv, "size"); + const char* threads_s = get_opt_value(argc, argv, "threads"); + + if (!block_size_s || !file_s || !repeats_s || !string_s || !replace_s) { + print_usage(argv[0]); + return 1; + } + + uint64_t block_size_u = 0; + uint64_t repeats = 0; + uint64_t gen_size = 0; + uint64_t threads = 0; + if (!parse_u64(block_size_s, &block_size_u) || + !parse_u64(repeats_s, &repeats)) { + fprintf(stderr, "Invalid block_size or repeats.\n"); + return 1; + } + if (threads_s) { + if (!parse_u64(threads_s, &threads)) { + fprintf(stderr, "Invalid threads.\n"); + return 1; + } + } else { + long cpu = sysconf(_SC_NPROCESSORS_ONLN); + threads = cpu > 0 ? (uint64_t)cpu : 1; + } + if (size_s && !parse_u64(size_s, &gen_size)) { + fprintf(stderr, "Invalid size.\n"); + return 1; + } + if (block_size_u == 0 || repeats == 0 || threads == 0) { + fprintf(stderr, "block_size, repeats, and threads must be > 0.\n"); + return 1; + } + + size_t block_size = (size_t)block_size_u; + size_t pattern_len = strlen(string_s); + if (pattern_len == 0 || strlen(replace_s) != pattern_len) { + fprintf(stderr, "string and replace must be non-empty and same length.\n"); + return 1; + } + if (pattern_len > block_size) { + fprintf(stderr, "string length must be <= block_size.\n"); + return 1; + } + + int direct = 0; + if (direct_s) { + if (strcmp(direct_s, "on") == 0) { + direct = 1; + } else if (strcmp(direct_s, "off") != 0) { + fprintf(stderr, "Invalid direct value: use on/off.\n"); + return 1; + } + } + + int random_mode = 0; + if (type_s) { + if (strcmp(type_s, "random") == 0) { + random_mode = 1; + } else if (strcmp(type_s, "sequence") != 0) { + fprintf(stderr, "Invalid type value: use sequence/random.\n"); + return 1; + } + } + + uint64_t range_start = 0; + uint64_t range_end = 0; + if (!parse_range(range_s, &range_start, &range_end)) { + fprintf(stderr, "Invalid range format, expected A-B.\n"); + return 1; + } + + srand((unsigned)time(NULL)); + + if (size_s) { + if (gen_size == 0) { + fprintf(stderr, "size must be > 0.\n"); + return 1; + } + if (!generate_file(file_s, gen_size, block_size, string_s)) { + return 1; + } + } + + if (direct) { + if ((block_size % 512) != 0) { + fprintf(stderr, "block_size must be a multiple of 512 for O_DIRECT.\n"); + return 1; + } + } + + int fd = open_file_direct(file_s, direct); + if (fd < 0) { + perror("open"); + return 1; + } + + struct stat st; + if (fstat(fd, &st) != 0) { + perror("fstat"); + close(fd); + return 1; + } + uint64_t file_size = (uint64_t)st.st_size; + if (file_size == 0) { + fprintf(stderr, "File is empty.\n"); + close(fd); + return 1; + } + + if (range_start == 0 && range_end == 0) { + range_start = 0; + range_end = file_size; + } + if (range_end > file_size || range_start >= range_end) { + fprintf(stderr, "Invalid range bounds.\n"); + close(fd); + return 1; + } + + uint64_t range_len = range_end - range_start; + uint64_t full_blocks = range_len / block_size; + uint64_t tail = range_len % block_size; + if (direct && tail != 0) { + range_end = range_start + (full_blocks * block_size); + range_len = range_end - range_start; + tail = 0; + if (range_len == 0) { + fprintf( + stderr, "Range too small for O_DIRECT with the given block_size.\n" + ); + close(fd); + return 1; + } + fprintf(stderr, "Warning: range truncated to fit O_DIRECT alignment.\n"); + } + + uint64_t blocks = full_blocks + ((tail > 0 && !direct) ? 1 : 0); + if (blocks == 0) { + fprintf(stderr, "No blocks to process.\n"); + close(fd); + return 1; + } + + close(fd); + + pthread_t* tids = malloc(sizeof(pthread_t) * (size_t)threads); + worker_ctx* ctxs = calloc((size_t)threads, sizeof(worker_ctx)); + if (!tids || !ctxs) { + fprintf(stderr, "malloc failed.\n"); + free(tids); + free(ctxs); + return 1; + } + + uint64_t base = repeats / threads; + uint64_t rem = repeats % threads; + uint64_t cur = 0; + for (uint64_t i = 0; i < threads; ++i) { + uint64_t count = base + (i < rem ? 1 : 0); + ctxs[i].file_path = file_s; + ctxs[i].needle = string_s; + ctxs[i].repl = replace_s; + ctxs[i].range_start = range_start; + ctxs[i].range_end = range_end; + ctxs[i].blocks = blocks; + ctxs[i].block_size = block_size; + ctxs[i].tail = (size_t)tail; + ctxs[i].direct = direct; + ctxs[i].random_mode = random_mode; + ctxs[i].repeats = repeats; + ctxs[i].start_iter = cur; + ctxs[i].iter_count = count; + ctxs[i].replaced = 0; + ctxs[i].rng_state = + (unsigned int)(time(NULL) ^ (unsigned int)(i * 2654435761u)); + cur += count; + } + + for (uint64_t i = 0; i < threads; ++i) { + if (pthread_create(&tids[i], NULL, worker_main, &ctxs[i]) != 0) { + fprintf(stderr, "pthread_create failed.\n"); + return 1; + } + } + + for (uint64_t i = 0; i < threads; ++i) { + pthread_join(tids[i], NULL); + } + + printf("replacements=%" PRIu64 "\n", g_total_replaced); + + free(tids); + free(ctxs); + return 0; +} diff --git a/lab/vtld/ema_utils.c b/lab/vtld/ema_utils.c new file mode 100644 index 0000000..3ff1273 --- /dev/null +++ b/lab/vtld/ema_utils.c @@ -0,0 +1,126 @@ +#include "ema_utils.h" + +#include +#include +#include +#include +#include +#include + +int parse_u64(const char* s, uint64_t* out) { + if (!s || !*s) { + return 0; + } + char* end = NULL; + errno = 0; + unsigned long long v = strtoull(s, &end, 10); + if (errno != 0 || !end || *end != '\0') { + return 0; + } + *out = (uint64_t)v; + return 1; +} + +int parse_range(const char* s, uint64_t* start, uint64_t* end) { + if (!s) { + *start = 0; + *end = 0; + return 1; + } + const char* dash = strchr(s, '-'); + if (!dash) { + return 0; + } + char left[32]; + char right[32]; + size_t left_len = (size_t)(dash - s); + size_t right_len = strlen(dash + 1); + if (left_len == 0 || right_len == 0 || left_len >= sizeof(left) || + right_len >= sizeof(right)) { + return 0; + } + memcpy(left, s, left_len); + left[left_len] = '\0'; + memcpy(right, dash + 1, right_len); + right[right_len] = '\0'; + if (!parse_u64(left, start) || !parse_u64(right, end)) { + return 0; + } + return 1; +} + +int open_file_direct(const char* path, int direct) { + int flags = O_RDWR; +#ifdef O_DIRECT + if (direct) { + flags |= O_DIRECT; + } +#else + if (direct) { + fprintf(stderr, "Warning: O_DIRECT is not supported, ignoring.\n"); + } +#endif + return open(path, flags); +} + +int generate_file( + const char* path, uint64_t size, size_t block_size, const char* pattern +) { + int fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0644); + if (fd < 0) { + perror("open"); + return 0; + } + size_t pat_len = strlen(pattern); + char* buf = malloc(block_size); + if (!buf) { + fprintf(stderr, "malloc failed.\n"); + close(fd); + return 0; + } + uint64_t remaining = size; + while (remaining > 0) { + size_t chunk = (remaining > block_size) ? block_size : (size_t)remaining; + for (size_t i = 0; i < chunk; ++i) { + buf[i] = (char)('a' + (rand() % 26)); + } + if (pat_len > 0 && pat_len <= chunk && (rand() % 3) == 0) { + size_t pos = (size_t)(rand() % (chunk - pat_len + 1)); + memcpy(buf + pos, pattern, pat_len); + } + ssize_t w = write(fd, buf, chunk); + if (w < 0 || (size_t)w != chunk) { + perror("write"); + free(buf); + close(fd); + return 0; + } + remaining -= chunk; + } + free(buf); + if (fsync(fd) != 0) { + perror("fsync"); + close(fd); + return 0; + } + close(fd); + return 1; +} + +size_t replace_in_buffer( + char* buf, size_t len, const char* needle, const char* repl +) { + size_t nlen = strlen(needle); + if (nlen == 0 || nlen > len) { + return 0; + } + size_t count = 0; + for (size_t i = 0; i + nlen <= len; ++i) { + if (memcmp(buf + i, needle, nlen) == 0) { + memcpy(buf + i, repl, nlen); + ++count; + i += nlen - 1; + } + } + return count; +} diff --git a/lab/vtld/ema_utils.h b/lab/vtld/ema_utils.h new file mode 100644 index 0000000..f33af3e --- /dev/null +++ b/lab/vtld/ema_utils.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +int parse_u64(const char* s, uint64_t* out); +int parse_range(const char* s, uint64_t* start, uint64_t* end); +int open_file_direct(const char* path, int direct); +int generate_file( + const char* path, uint64_t size, size_t block_size, const char* pattern +); +size_t replace_in_buffer( + char* buf, size_t len, const char* needle, const char* repl +); diff --git a/lab/vtld/io_replace b/lab/vtld/io_replace deleted file mode 100755 index 8a32bcf6dac35ba5cfe091ac94aa7cf8e77250b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17216 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

*!{yU+%vps~%(+Q_+`wu|U}uOZnyXf86S? zv<79L?Lljp`ixn*{`Wv*OvJvo?%VZW_U{nUsl5FUf?BNaw|W9Q%K|R96#}0w)9a?eZEXz?(YS1|5=>M>;EG#Sw(3-<+sun?klUm z_-&=sOUM-K%f3nAdJ(j+y!}=m|an%A|?^*`VR{SJ3wxws*b2 zmT$#9y6At^qnHeRWh#bV-!&|K9kK><_5tFhpz=|I_%HJ>$A|c?742n^3lmaL8B@wN TT<#r4){iYU64T2R%24&+w?|a7 diff --git a/lab/vtld/io_replace.c b/lab/vtld/io_replace.c deleted file mode 100644 index 9a8b186..0000000 --- a/lab/vtld/io_replace.c +++ /dev/null @@ -1,203 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void print_usage(const char *prog) { - fprintf(stderr, - "Usage: %s --file PATH --size N --repeats N --needle STR --replace STR \\\n" - " [--block_size N]\n", - prog); -} - -static const char *get_opt_value(int argc, char **argv, const char *key) { - size_t key_len = strlen(key); - for (int i = 1; i < argc; ++i) { - const char *arg = argv[i]; - while (*arg == '-') { - ++arg; - } - const char *eq = strchr(arg, '='); - if (eq) { - size_t len = (size_t)(eq - arg); - if (len == key_len && strncmp(arg, key, len) == 0) { - return eq + 1; - } - } else if (strcmp(arg, key) == 0 && i + 1 < argc) { - return argv[i + 1]; - } - } - return NULL; -} - -static int generate_file(const char *path, size_t size_bytes, const char *needle, size_t block_size) { - int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (fd < 0) { - perror("open"); - return 1; - } - - size_t needle_len = strlen(needle); - size_t insert_step = size_bytes / 10; - if (insert_step < needle_len * 4) { - insert_step = needle_len * 4; - } - size_t next_insert = 0; - - char *buf = malloc(block_size); - if (!buf) { - fprintf(stderr, "malloc failed.\n"); - close(fd); - return 1; - } - - size_t offset = 0; - while (offset < size_bytes) { - size_t chunk = block_size; - if (offset + chunk > size_bytes) { - chunk = size_bytes - offset; - } - for (size_t i = 0; i < chunk; ++i) { - buf[i] = (char)('a' + (rand() % 26)); - } - - if (next_insert >= offset && next_insert + needle_len <= offset + chunk) { - size_t rel = next_insert - offset; - memcpy(buf + rel, needle, needle_len); - next_insert += insert_step; - } - - if (write(fd, buf, chunk) != (ssize_t)chunk) { - perror("write"); - free(buf); - close(fd); - return 1; - } - offset += chunk; - } - - free(buf); - close(fd); - return 0; -} - -static int replace_in_file(int fd, const char *needle, const char *replacement, size_t block_size) { - struct stat st; - if (fstat(fd, &st) != 0) { - perror("fstat"); - return 1; - } - off_t size = st.st_size; - size_t needle_len = strlen(needle); - if (needle_len == 0 || needle_len != strlen(replacement)) { - fprintf(stderr, "needle and replacement must have same non-zero length.\n"); - return 1; - } - - char *buf = malloc(block_size + needle_len - 1); - if (!buf) { - fprintf(stderr, "malloc failed.\n"); - return 1; - } - - size_t carry_len = 0; - off_t offset = 0; - while (offset < size) { - size_t chunk = block_size; - if (offset + (off_t)chunk > size) { - chunk = (size_t)(size - offset); - } - ssize_t rc = pread(fd, buf + carry_len, chunk, offset); - if (rc < 0) { - perror("pread"); - free(buf); - return 1; - } - size_t total = carry_len + (size_t)rc; - if (total < needle_len) { - carry_len = total; - offset += rc; - continue; - } - size_t limit = total - needle_len + 1; - for (size_t i = 0; i < limit; ++i) { - if (memcmp(buf + i, needle, needle_len) == 0) { - off_t woff = offset - (off_t)carry_len + (off_t)i; - if (pwrite(fd, replacement, needle_len, woff) != (ssize_t)needle_len) { - perror("pwrite"); - free(buf); - return 1; - } - } - } - if (needle_len > 1) { - carry_len = needle_len - 1; - if (total < carry_len) { - carry_len = total; - } - memmove(buf, buf + total - carry_len, carry_len); - } else { - carry_len = 0; - } - offset += rc; - } - - free(buf); - return 0; -} - -int main(int argc, char **argv) { - const char *path = get_opt_value(argc, argv, "file"); - const char *size_s = get_opt_value(argc, argv, "size"); - const char *repeats_s = get_opt_value(argc, argv, "repeats"); - const char *needle = get_opt_value(argc, argv, "needle"); - const char *replacement = get_opt_value(argc, argv, "replace"); - const char *block_size_s = get_opt_value(argc, argv, "block_size"); - - if (!path || !size_s || !repeats_s || !needle || !replacement) { - print_usage(argv[0]); - return 1; - } - - long size = strtol(size_s, NULL, 10); - long repeats = strtol(repeats_s, NULL, 10); - long block_size = block_size_s ? strtol(block_size_s, NULL, 10) : 4096; - if (size <= 0 || repeats <= 0 || block_size <= 0) { - fprintf(stderr, "Invalid size, repeats, or block_size.\n"); - return 1; - } - - if (strlen(needle) != strlen(replacement) || strlen(needle) == 0) { - fprintf(stderr, "needle and replacement must be same length and non-empty.\n"); - return 1; - } - - srand((unsigned)time(NULL)); - if (generate_file(path, (size_t)size, needle, (size_t)block_size) != 0) { - return 1; - } - - int fd = open(path, O_RDWR); - if (fd < 0) { - perror("open"); - return 1; - } - - for (long r = 0; r < repeats; ++r) { - if (replace_in_file(fd, needle, replacement, (size_t)block_size) != 0) { - close(fd); - return 1; - } - printf("repeat %ld: done\n", r + 1); - } - - close(fd); - return 0; -} diff --git a/lab/vtld/iobench b/lab/vtld/iobench deleted file mode 100755 index f84a312c7c370337056bd22a3e6420512030ee16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17224 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 iu}ee!lc$9V -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef O_DIRECT -#define O_DIRECT 0 -#endif - -#define ALIGNMENT 4096 - -static void print_usage(const char *prog) { - fprintf(stderr, - "Usage: %s --rw read|write --block_size N --block_count N --file PATH \\\n" - " [--range A-B] [--direct on|off] [--type sequence|random]\n", - prog); -} - -static const char *get_opt_value(int argc, char **argv, const char *key) { - size_t key_len = strlen(key); - for (int i = 1; i < argc; ++i) { - const char *arg = argv[i]; - while (*arg == '-') { - ++arg; - } - const char *eq = strchr(arg, '='); - if (eq) { - size_t len = (size_t)(eq - arg); - if (len == key_len && strncmp(arg, key, len) == 0) { - return eq + 1; - } - } else if (strcmp(arg, key) == 0 && i + 1 < argc) { - return argv[i + 1]; - } - } - return NULL; -} - -static int parse_range(const char *s, off_t *start, off_t *end) { - if (!s) { - return -1; - } - char *dash = strchr(s, '-'); - if (!dash) { - return -1; - } - *dash = '\0'; - char *endptr = NULL; - long long a = strtoll(s, &endptr, 10); - if (*endptr != '\0') { - *dash = '-'; - return -1; - } - long long b = strtoll(dash + 1, &endptr, 10); - if (*endptr != '\0') { - *dash = '-'; - return -1; - } - *dash = '-'; - if (a < 0 || b < 0) { - return -1; - } - *start = (off_t)a; - *end = (off_t)b; - return 0; -} - -int main(int argc, char **argv) { - const char *rw = get_opt_value(argc, argv, "rw"); - const char *block_size_s = get_opt_value(argc, argv, "block_size"); - const char *block_count_s = get_opt_value(argc, argv, "block_count"); - const char *file_path = get_opt_value(argc, argv, "file"); - const char *range_s = get_opt_value(argc, argv, "range"); - const char *direct_s = get_opt_value(argc, argv, "direct"); - const char *type_s = get_opt_value(argc, argv, "type"); - - if (!rw || !block_size_s || !block_count_s || !file_path) { - print_usage(argv[0]); - return 1; - } - - long block_size = strtol(block_size_s, NULL, 10); - long block_count = strtol(block_count_s, NULL, 10); - if (block_size <= 0 || block_count <= 0) { - fprintf(stderr, "Invalid block_size or block_count.\n"); - return 1; - } - - int is_write = (strcmp(rw, "write") == 0); - int is_read = (strcmp(rw, "read") == 0); - if (!is_write && !is_read) { - fprintf(stderr, "rw must be read or write.\n"); - return 1; - } - - int use_direct = (direct_s && strcmp(direct_s, "on") == 0); - int is_random = (type_s && strcmp(type_s, "random") == 0); - - if (use_direct && (block_size % ALIGNMENT != 0)) { - fprintf(stderr, "block_size must be multiple of %d for direct IO.\n", ALIGNMENT); - return 1; - } - - int flags = is_write ? (O_RDWR | O_CREAT) : O_RDONLY; - if (use_direct) { - flags |= O_DIRECT; - } - - int fd = open(file_path, flags, 0644); - if (fd < 0) { - perror("open"); - return 1; - } - - struct stat st; - if (fstat(fd, &st) != 0) { - perror("fstat"); - close(fd); - return 1; - } - - off_t range_start = 0; - off_t range_end = 0; - if (range_s) { - char *range_copy = strdup(range_s); - if (!range_copy || parse_range(range_copy, &range_start, &range_end) != 0) { - free(range_copy); - fprintf(stderr, "Invalid range format. Use A-B.\n"); - close(fd); - return 1; - } - free(range_copy); - } - - if (range_start == 0 && range_end == 0) { - if (st.st_size > 0) { - range_end = st.st_size; - } else if (is_write) { - range_end = (off_t)block_size * block_count; - } else { - fprintf(stderr, "File is empty; specify a non-zero range.\n"); - close(fd); - return 1; - } - } - - if (range_end < range_start) { - fprintf(stderr, "range end must be >= start.\n"); - close(fd); - return 1; - } - - if (use_direct && (range_start % ALIGNMENT != 0)) { - fprintf(stderr, "range start must be aligned to %d for direct IO.\n", ALIGNMENT); - close(fd); - return 1; - } - - off_t range_size = range_end - range_start; - if (range_size <= 0) { - fprintf(stderr, "range size must be > 0.\n"); - close(fd); - return 1; - } - - off_t available_blocks = range_size / block_size; - if (available_blocks <= 0) { - fprintf(stderr, "range too small for given block_size.\n"); - close(fd); - return 1; - } - if (range_size % block_size != 0) { - fprintf(stderr, "range size is not a multiple of block_size; truncating.\n"); - } - - if (is_write && range_end > st.st_size) { - if (ftruncate(fd, range_end) != 0) { - perror("ftruncate"); - close(fd); - return 1; - } - } - - void *buffer = NULL; - if (use_direct) { - if (posix_memalign(&buffer, ALIGNMENT, (size_t)block_size) != 0) { - fprintf(stderr, "posix_memalign failed.\n"); - close(fd); - return 1; - } - } else { - buffer = malloc((size_t)block_size); - if (!buffer) { - fprintf(stderr, "malloc failed.\n"); - close(fd); - return 1; - } - } - - if (is_write) { - memset(buffer, 0xA5, (size_t)block_size); - } - - srand((unsigned)time(NULL)); - for (long i = 0; i < block_count; ++i) { - off_t idx = is_random ? (rand() % available_blocks) : (i % available_blocks); - off_t offset = range_start + idx * block_size; - ssize_t rc; - if (is_read) { - rc = pread(fd, buffer, (size_t)block_size, offset); - } else { - rc = pwrite(fd, buffer, (size_t)block_size, offset); - } - if (rc < 0) { - perror(is_read ? "pread" : "pwrite"); - free(buffer); - close(fd); - return 1; - } - } - - free(buffer); - close(fd); - return 0; -} diff --git a/lab/vtld/makefile b/lab/vtld/makefile index 67c9d92..901d0b2 100644 --- a/lab/vtld/makefile +++ b/lab/vtld/makefile @@ -1,16 +1,19 @@ CC=gcc -CFLAGS=-O0 -Wall -Wextra -std=c11 +CFLAGS=-O0 -std=c11 -all: iobench cpu_dedup io_replace +all: cpu_dedup cpu_dedup_pthreads ema_replace_str ema_replace_str_pthreads -iobench: iobench.c - $(CC) $(CFLAGS) -o iobench iobench.c +cpu_dedup: cpu_dedup.c cpu_utils.c + $(CC) $(CFLAGS) -o cpu_dedup cpu_dedup.c cpu_utils.c util.c -cpu_dedup: cpu_dedup.c - $(CC) $(CFLAGS) -o cpu_dedup cpu_dedup.c +cpu_dedup_pthreads: cpu_dedup_pthreads.c cpu_utils.c + $(CC) $(CFLAGS) -pthread -o cpu_dedup_pthreads cpu_dedup_pthreads.c cpu_utils.c util.c -io_replace: io_replace.c - $(CC) $(CFLAGS) -o io_replace io_replace.c +ema_replace_str: ema_replace_str.c ema_utils.c + $(CC) $(CFLAGS) -o ema_replace_str ema_replace_str.c ema_utils.c util.c + +ema_replace_str_pthreads: ema_replace_str_pthreads.c ema_utils.c + $(CC) $(CFLAGS) -pthread -o ema_replace_str_pthreads ema_replace_str_pthreads.c ema_utils.c util.c clean: - rm -f iobench cpu_dedup io_replace + rm -f cpu_dedup cpu_dedup_pthreads ema_replace_str ema_replace_str_pthreads diff --git a/lab/vtld/util.c b/lab/vtld/util.c new file mode 100644 index 0000000..d0839ba --- /dev/null +++ b/lab/vtld/util.c @@ -0,0 +1,21 @@ +#include "util.h" + +const char* get_opt_value(int argc, char** argv, const char* key) { + size_t key_len = strlen(key); + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + while (*arg == '-') { + ++arg; + } + const char* eq = strchr(arg, '='); + if (eq) { + size_t len = (size_t)(eq - arg); + if (len == key_len && strncmp(arg, key, len) == 0) { + return eq + 1; + } + } else if (strcmp(arg, key) == 0 && i + 1 < argc) { + return argv[i + 1]; + } + } + return NULL; +} diff --git a/lab/vtld/util.h b/lab/vtld/util.h new file mode 100644 index 0000000..4efea74 --- /dev/null +++ b/lab/vtld/util.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +const char* get_opt_value(int argc, char** argv, const char* key); diff --git a/lab/vtsh/README.md b/lab/vtsh/README.md index 28ba737..21aa211 100644 --- a/lab/vtsh/README.md +++ b/lab/vtsh/README.md @@ -1,5 +1,7 @@ # Базовый трек. Лабораторная работа 1 +Вариант: `proc-fork shell-and` + ## Введение Базовый пользовательский интерфейс операционной системы - это терминал, с его @@ -27,65 +29,6 @@ 3. `;` - последовательное выполнение, 4. `&` - выполнение в фоне. -## Часть 3. Анализ системы и мониторинг - -Разработайте параметризируемую программу-нагрузчик, которая будет однопоточно нагружать подсистему ввода-вывода (IO). Она должна принимать на вход следующие параметры: - -- `rw: read/write` - режим нагрузки: чтение или запись; -- `block_size: ` - размер блока в байтах, с которым производится чтение/запись; -- `block_count: ` - количество блоков; -- `file: ` - имя файла, с которым происходит работа; -- `range: -` - границы в пределах файла, в которые должны - осуществляться запись/чтение, значение по умолчанию, 0-0, означает, что доступен - весь файл; -- `direct: on/off` - открывать файл с опцией O_DIRECT (в обход кэшей ОС) или нет; -- `type: sequence/random` - режим выбора следующего блока для записи/чтения - последовательно или случайно; - -Разработайте комплекс программ-нагрузчиков согласно выданному варианту. -Каждый нагрузчик должен принимать параметр, который определяет -количество повторений для алгоритма, указанного в задании, а также другие -вспомогательные параметры. Варианты программ рассчитаны так, чтобы по-разному -нагружать вычислительную подсистему (CPU) и подсистему ввода-вывода (IO) - это -необходимо учитывать при их реализации. Разработанные программы еобходимо -скомпилировать без дополнительных опций оптимизации компилятора. - -Проведите исследование поведения ОС во время исполнения разработанных -программ-нагрузчиков по следующему плану: - -1. Перед запуском нагрузчика, попробуйте оценить время работы вашей программы - или ее результаты (если по варианту вам досталось измерение чего либо) и - обоснуйте свои предположения. - -2. Запустите программу-нагрузчик и зафиксируйте метрики ее работы с помощью - инструментов для мониторинга и профилирования (см. лекции). Сравните полученные - результаты с ожидаемыми. Объяснените наблюдаемое поведение. Продолжительность - каждого запуска должна занимать достаточное для прекращения переходных - процессов время, по крайней мере, минуту. - -3. Определите количество одновременно запущенных процессов с программой-нагрузчиком, - которое эффективно нагружает все ядра процессора в вашей системе. Как - распределяются показатели времени USER%, SYS%, WAIT%, а также полное время - выполнения нагрузчика, какое количество переключений контекста (вынужденных и - невынужденных) происходит при этом? Подумайте над тем, как вы определяете - эффективность. - -4. Увеличьте количество нагрузчиков вдвое, втрое, вчетверо. Как изменились - исследуемые показатели? Почему? - -5. Объедините программы-нагрузчики в одну, реализованную при помощи потоков - выполнения, чтобы один нагрузчик эффективно нагружал все ядра вашей системы. Как - изменились показатели времени для того же объема вычислений? Запустите одну, - две, три таких программы. Как изменились исследуемые показатели? Почему? - -6. Скомпилируйте программу-нугрузчик с опцией агрессивной оптимизации. Как - изменились исследуемые показатели? На сколько сократилось реальное время - исполнения программы нагрузчика? Почему? - -В процессе защиты вашей работы преподаватель будет просить вас запустить -программу-нагрузчик с различными комбинациями параметров и просить объяснить -результат. - ## Требования к реализации 1. Программа (комплекс программ) должна быть реализован на языке C. @@ -148,70 +91,6 @@ - `shell-bg`: Выполнение команды в фоновом процессе (`&` в bash). - -Нагрузчик вычислительной подсистемы (группа **cpu**): - -- `cpu-factorize`: Разложение числа на простые множители. Задаваемое число - должно являться произведением нескольких больших простых чисел. - -- `cpu-sort`: Любая сортировка массива чисел за N*log(N) и любая за N**2. - Алгоритм должен быть реализован самим студентом, а не взят из стандартной - библиотеки. - -- `cpu-short-path`: Поиск кратчайшего пути в графе. Граф генерируется в момент - запуска или записан в исходный код. - -- `cpu-linreg`: Построение модели линейной регрессии для последовательности случайных чисел. Количество чисел и диапозон значений задаются параметром. - -- `cpu-dedup`: Дедупликация элементов в массиве. Массив генерируется на основе - случайных чисел. Размер массива задаётся параметром. - -- `cpu-mat-mul`: Перемножение матриц. Размер матриц задаётся параметром, а - элементы матриц генерируются на основе случайных чисел. - -- `cpu-calc-md5`: Подсчитать хеш md5 для текста полученного конкатенацией - фрагментов текста выбранных с помощью генератора случайных чисел. - -- `cpu-calc-crc`: Посчитать контрольную сумму crc для текста полученного - конкатенацией фрагментов текста выбранных с помощью генератора случайных чисел. - - -Нагрузчик, работающий с внешней памятью (группа **ema**): - -- `ema-replace-int`: Поиск элемента в массиве чисел во внешней памяти и замена - значения на другое. Файл для поиска значения генерируется в произвольном - формате, размер задаётся через параметр. - -- `ema-replace-str`: Поиск подстроки в тексте во внешней памяти и замена на - другую строку того же размера. Файл для поиска значения генерируется в - произвольном формате, размер задаётся через параметр. - -- `ema-sort-int`: Сортировка массива чисел во внешней памяти. Весь массив не - может быть загружен в память, поэтому сортировка происходит частями. - -- `ema-traverse-graph`: Обход k-регулярного направленного графа, - сериализованного в файл, и выполнение модифицирующей операции над определенной - вершиной графа. Поиск вершины происходит по хранимому в ней значению, возможно с - ограничением глубины поиска. - Формат хранения: вершина (struct Node) определяется её отступом от начала файла (id=index*(sizeof struct Node)), в вершине хранится одно значение типа фиксированного размера и k номеров вершин, в которые из неё можно попасть; в файле вершины хранятся последовательно с выравниванием соответствующим размеру структуры. Тип значения, хранимого в вершине, и значение k подбираются таким образом, чтобы с учетом выравнивания не было неиспользуемой памяти. - При генерации графа случайно задается значение, хранимое в вершине и номера соседних вершин, отдельным параметром генератора задется шанс получения номера соседней вершины больше или меньше номера данной вершины (соседняя вершина раньше или позже). - -- `ema-join-*`: Необходимо реализовать алгоритм equi-join для объединения - таблиц в файлах. На вход подаются два файла, результат их объединения пишется в - новый файл. В первой строке файла содержится количество строчек в файле. В - каждой последующей строчке входного файла содержится два значения, разделенных - пробелами: численный идентификатор и строка без пробелов фиксированного размера - 8 байт. Строка является валидным английским словом. Таблицы необходимо - объединять по идентификатору. - - По варианту выдается стратегия джоина: - - `ema-join-nl`: Nested Loop Join - - `ema-join-hash`: Hash Join Join - - `ema-join-sm`: Sort + Merge Join - - Количество строк входных таблиц включают все комбинации пар значений: 5, 10, 100, 1000, 10000." - - ## Источники - Тесты взяты из репозитория [sergey-v-galtsev/shad-os][shad-os-tests] diff --git a/lab/vtsh/lib/exec.c b/lab/vtsh/lib/exec.c index 8eb3c2e..7a1d961 100644 --- a/lab/vtsh/lib/exec.c +++ b/lab/vtsh/lib/exec.c @@ -1,5 +1,4 @@ #include "exec.h" -#include "utils.h" #include #include @@ -9,104 +8,117 @@ #include #include -static void exec_self(char *const argv[]) { - char self[PATH_MAX]; - ssize_t n = readlink("/proc/self/exe", self, sizeof(self) - 1); - if (n > 0) { - self[n] = '\0'; - execv(self, argv); - } +#include "utils.h" + +static void exec_self(char* const argv[]) { + char self[PATH_MAX]; + ssize_t n = readlink("/proc/self/exe", self, sizeof(self) - 1); + if (n > 0) { + self[n] = '\0'; + execv(self, argv); + } } -static void exec_with_fallback(char *const argv[]) { - const char *cmd = argv[0]; +static void exec_with_fallback(char* const argv[]) { + const char* cmd = argv[0]; - // 1) если есть '/', пробуем как путь - if (strchr(cmd, '/')) { - execv(cmd, argv); + if (strchr(cmd, '/')) { + execv(cmd, argv); - // специальный кейс для теста вложенных шеллов - if (strcmp(cmd, "./shell") == 0) { - exec_self(argv); - } - - return; // дальше обработаем как "not found" + if (strcmp(cmd, "./shell") == 0) { + exec_self(argv); } - // 2) обычный запуск через PATH - execvp(cmd, argv); + return; + } - // 3) если не нашли — попробуем стандартные директории - if (errno == ENOENT) { - char path[PATH_MAX]; + execvp(cmd, argv); - snprintf(path, sizeof(path), "/bin/%s", cmd); - execv(path, argv); + if (errno == ENOENT) { + char path[PATH_MAX]; - snprintf(path, sizeof(path), "/usr/bin/%s", cmd); - execv(path, argv); + snprintf(path, sizeof(path), "/bin/%s", cmd); + execv(path, argv); - // на всякий случай: если пользователь ввёл просто "shell" - if (strcmp(cmd, "shell") == 0) { - exec_self(argv); - } + snprintf(path, sizeof(path), "/usr/bin/%s", cmd); + execv(path, argv); + + if (strcmp(cmd, "shell") == 0) { + exec_self(argv); } + } } -static int run_one(const Cmd *c) { - if (c->argc == 0) return 0; +static int run_one(const Cmd* c) { + if (c->argc == 0) + return 0; - long long t0 = nsec_now(); - pid_t pid = fork(); - if (pid < 0) { - dprintf(STDOUT_FILENO, "I/O error\n"); - return 1; - } - - if (pid == 0) { - int saved_stdout = dup(STDOUT_FILENO); - if (saved_stdout < 0) saved_stdout = STDOUT_FILENO; - - // редиректы - for (int i = 0; i < c->nredirs; i++) { - int fd; - if (c->redirs[i].type == '<') { - fd = open(c->redirs[i].filename, O_RDONLY); - if (fd < 0) { dprintf(saved_stdout, "I/O error\n"); _exit(1); } - if (dup2(fd, STDIN_FILENO) < 0) { close(fd); dprintf(saved_stdout, "I/O error\n"); _exit(1); } - close(fd); - } else { - fd = open(c->redirs[i].filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) { dprintf(saved_stdout, "I/O error\n"); _exit(1); } - if (dup2(fd, STDOUT_FILENO) < 0) { close(fd); dprintf(saved_stdout, "I/O error\n"); _exit(1); } - close(fd); - } - } - - exec_with_fallback(c->argv); - - // если дошли сюда — ничего не запустилось - dprintf(STDOUT_FILENO, "Command not found\n"); - _exit(127); - } - - int st = 0; - waitpid(pid, &st, 0); - long long t1 = nsec_now(); - - // время — в stderr (тесты читают stdout) - fprintf(stderr, "time_ns=%lld\n", (t1 - t0)); - - if (WIFEXITED(st)) return WEXITSTATUS(st); - if (WIFSIGNALED(st)) return 128 + WTERMSIG(st); + long long t0 = nsec_now(); + pid_t pid = fork(); + if (pid < 0) { + dprintf(STDOUT_FILENO, "I/O error\n"); return 1; + } + + if (pid == 0) { + int saved_stdout = dup(STDOUT_FILENO); + if (saved_stdout < 0) + saved_stdout = STDOUT_FILENO; + + for (int i = 0; i < c->nredirs; i++) { + int fd; + if (c->redirs[i].type == '<') { + fd = open(c->redirs[i].filename, O_RDONLY); + if (fd < 0) { + dprintf(saved_stdout, "I/O error\n"); + _exit(1); + } + if (dup2(fd, STDIN_FILENO) < 0) { + close(fd); + dprintf(saved_stdout, "I/O error\n"); + _exit(1); + } + close(fd); + } else { + fd = open(c->redirs[i].filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) { + dprintf(saved_stdout, "I/O error\n"); + _exit(1); + } + if (dup2(fd, STDOUT_FILENO) < 0) { + close(fd); + dprintf(saved_stdout, "I/O error\n"); + _exit(1); + } + close(fd); + } + } + + exec_with_fallback(c->argv); + + dprintf(STDOUT_FILENO, "Command not found\n"); + _exit(127); + } + + int st = 0; + waitpid(pid, &st, 0); + long long t1 = nsec_now(); + + fprintf(stderr, "time_ns=%lld\n", (t1 - t0)); + + if (WIFEXITED(st)) + return WEXITSTATUS(st); + if (WIFSIGNALED(st)) + return 128 + WTERMSIG(st); + return 1; } -int run_commands(const CmdVec *cmds) { - int last_status = 0; - for (int i = 0; i < cmds->n; i++) { - if (cmds->v[i].and_after_prev && last_status != 0) continue; - last_status = run_one(&cmds->v[i]); - } - return last_status; +int run_commands(const CmdVec* cmds) { + int last_status = 0; + for (int i = 0; i < cmds->n; i++) { + if (cmds->v[i].and_after_prev && last_status != 0) + continue; + last_status = run_one(&cmds->v[i]); + } + return last_status; } diff --git a/lab/vtsh/lib/parser.c b/lab/vtsh/lib/parser.c index d3e2d98..ac6b90a 100644 --- a/lab/vtsh/lib/parser.c +++ b/lab/vtsh/lib/parser.c @@ -98,7 +98,7 @@ bool parse_tokens(const StrVec* toks, CmdVec* cmds) { if (type == '>' && tok[1] == '>') { cmd_free(&cur); return false; - } // forbid >> + } char* fname = NULL; if (tok[1] == '\0') { diff --git a/lab/vtsh/lib/vtsh.c b/lab/vtsh/lib/vtsh.c index 00bf256..4661049 100644 --- a/lab/vtsh/lib/vtsh.c +++ b/lab/vtsh/lib/vtsh.c @@ -13,7 +13,7 @@ const char* vtsh_prompt(void) { } void vtsh_run(void) { - setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); char* line = NULL; diff --git a/lab/vtsh/test/lol Date: Tue, 3 Feb 2026 00:39:50 +0300 Subject: [PATCH 3/9] Fix github workflow --- .github/workflows/vtsh.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/vtsh.yml b/.github/workflows/vtsh.yml index ba8cc85..aaba25f 100644 --- a/.github/workflows/vtsh.yml +++ b/.github/workflows/vtsh.yml @@ -1,10 +1,8 @@ name: Test VT Shell -runs-on: self-hosted on: push: - branches: - - main + branches: [ main ] pull_request: paths: - 'lab/vtsh/**' @@ -13,15 +11,17 @@ jobs: build: strategy: matrix: - cmake_build_type: - - Asan - - Release - runs-on: ubuntu-latest + cmake_build_type: [ Asan, Release ] + + runs-on: self-hosted + container: image: silkeh/clang:latest + defaults: run: working-directory: ./lab/vtsh + steps: - name: Checkout uses: actions/checkout@v4 From 444ac7ce6f4fb5b5b480d43d5169e0f5be4be5ec Mon Sep 17 00:00:00 2001 From: Ivan Zinchenko Date: Tue, 3 Feb 2026 00:42:42 +0300 Subject: [PATCH 4/9] Fix github workflow --- .github/workflows/vtsh.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/vtsh.yml b/.github/workflows/vtsh.yml index aaba25f..35622ad 100644 --- a/.github/workflows/vtsh.yml +++ b/.github/workflows/vtsh.yml @@ -2,7 +2,8 @@ name: Test VT Shell on: push: - branches: [ main ] + branches: + - main pull_request: paths: - 'lab/vtsh/**' @@ -11,17 +12,15 @@ jobs: build: strategy: matrix: - cmake_build_type: [ Asan, Release ] - - runs-on: self-hosted - + cmake_build_type: + - Asan + - Release + runs-on: ubuntu-latest container: image: silkeh/clang:latest - defaults: run: working-directory: ./lab/vtsh - steps: - name: Checkout uses: actions/checkout@v4 @@ -47,4 +46,4 @@ jobs: - name: Test working-directory: ./lab/vtsh/test - run: python3 -m unittest discover + run: python3 -m unittest discover \ No newline at end of file From 1f0573d17db532545a89231bef0b67baab870449 Mon Sep 17 00:00:00 2001 From: Ivan Zinchenko Date: Tue, 3 Feb 2026 00:52:23 +0300 Subject: [PATCH 5/9] Fix github workflow --- .github/workflows/vtsh.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vtsh.yml b/.github/workflows/vtsh.yml index 35622ad..04ca522 100644 --- a/.github/workflows/vtsh.yml +++ b/.github/workflows/vtsh.yml @@ -15,7 +15,7 @@ jobs: cmake_build_type: - Asan - Release - runs-on: ubuntu-latest + runs-on: self-hosted container: image: silkeh/clang:latest defaults: From 6e07455d7a69b3f0447d007ad81eef22630d4dde Mon Sep 17 00:00:00 2001 From: Ivan Zinchenko Date: Wed, 4 Feb 2026 19:03:00 +0300 Subject: [PATCH 6/9] Add ch and exit --- lab/vtsh/lib/exec.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lab/vtsh/lib/exec.c b/lab/vtsh/lib/exec.c index 7a1d961..a8e6e24 100644 --- a/lab/vtsh/lib/exec.c +++ b/lab/vtsh/lib/exec.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -49,10 +50,44 @@ static void exec_with_fallback(char* const argv[]) { } } +static int run_builtin(const Cmd* c, bool* handled) { + *handled = true; + + if (strcmp(c->argv[0], "exit") == 0) { + int code = 0; + if (c->argc >= 2) + code = atoi(c->argv[1]); + exit(code); + } + + if (strcmp(c->argv[0], "cd") == 0) { + const char* path = NULL; + if (c->argc >= 2) { + path = c->argv[1]; + } else { + path = getenv("HOME"); + } + + if (!path || chdir(path) != 0) { + dprintf(STDOUT_FILENO, "I/O error\n"); + return 1; + } + return 0; + } + + *handled = false; + return 0; +} + static int run_one(const Cmd* c) { if (c->argc == 0) return 0; + bool handled = false; + int builtin_status = run_builtin(c, &handled); + if (handled) + return builtin_status; + long long t0 = nsec_now(); pid_t pid = fork(); if (pid < 0) { From de90d69a6ef976a055397cbd7b4359bfa505e19c Mon Sep 17 00:00:00 2001 From: Ivan Zinchenko Date: Wed, 4 Feb 2026 19:09:48 +0300 Subject: [PATCH 7/9] Change ns to ms --- lab/vtsh/lib/exec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lab/vtsh/lib/exec.c b/lab/vtsh/lib/exec.c index a8e6e24..fe588f8 100644 --- a/lab/vtsh/lib/exec.c +++ b/lab/vtsh/lib/exec.c @@ -139,7 +139,8 @@ static int run_one(const Cmd* c) { waitpid(pid, &st, 0); long long t1 = nsec_now(); - fprintf(stderr, "time_ns=%lld\n", (t1 - t0)); + double elapsed_ms = (double)(t1 - t0) / 1000000.0; + fprintf(stderr, "time_ms=%.3f\n", elapsed_ms); if (WIFEXITED(st)) return WEXITSTATUS(st); From 55322da42d94ffa3b44f5e7d9f87e413bc4411ec Mon Sep 17 00:00:00 2001 From: Ivan Zinchenko Date: Fri, 6 Feb 2026 09:04:29 +0300 Subject: [PATCH 8/9] Inline dedup function --- lab/vtld/cpu_dedup.c | 25 ++++++++++++------- lab/vtld/cpu_dedup_pthreads.c | 19 ++++++++------- lab/vtld/cpu_utils.c | 45 +++++++++++++++++++++++------------ lab/vtld/cpu_utils.h | 9 ++++++- 4 files changed, 66 insertions(+), 32 deletions(-) diff --git a/lab/vtld/cpu_dedup.c b/lab/vtld/cpu_dedup.c index 1a3f0ab..9212cf3 100644 --- a/lab/vtld/cpu_dedup.c +++ b/lab/vtld/cpu_dedup.c @@ -29,14 +29,23 @@ int main(int argc, char** argv) { return 1; } - unsigned int seed = (unsigned int)time(NULL); - for (long r = 0; r < repeats; ++r) { - long unique = dedup_run(size, max_value, &seed); - if (unique < 0) { - fprintf(stderr, "dedup_run failed.\n"); - return 1; - } - printf("repeat %ld: unique=%ld\n", r + 1, unique); + long* results = calloc((size_t)repeats, sizeof(long)); + if (!results) { + fprintf(stderr, "calloc failed.\n"); + return 1; } + + unsigned int seed = (unsigned int)time(NULL); + if (dedup_run(size, max_value, &seed, 0, repeats, results) != 0) { + fprintf(stderr, "dedup_run failed.\n"); + free(results); + return 1; + } + + for (long r = 0; r < repeats; ++r) { + printf("repeat %ld: unique=%ld\n", r + 1, results[r]); + } + + free(results); return 0; } diff --git a/lab/vtld/cpu_dedup_pthreads.c b/lab/vtld/cpu_dedup_pthreads.c index bf3ad74..188b32e 100644 --- a/lab/vtld/cpu_dedup_pthreads.c +++ b/lab/vtld/cpu_dedup_pthreads.c @@ -36,14 +36,17 @@ static void* worker_main(void* arg) { worker_ctx_t* ctx = (worker_ctx_t*)arg; unsigned int seed = ctx->seed; - for (long r = ctx->repeat_start; r < ctx->repeat_end; ++r) { - long unique = dedup_run(ctx->size, ctx->max_value, &seed); - if (unique < 0) { - fprintf(stderr, "dedup_run failed.\n"); - set_error(ctx); - return NULL; - } - ctx->results[r] = unique; + if (dedup_run( + ctx->size, + ctx->max_value, + &seed, + ctx->repeat_start, + ctx->repeat_end, + ctx->results + ) != 0) { + fprintf(stderr, "dedup_run failed.\n"); + set_error(ctx); + return NULL; } return NULL; diff --git a/lab/vtld/cpu_utils.c b/lab/vtld/cpu_utils.c index 51f58bb..676454d 100644 --- a/lab/vtld/cpu_utils.c +++ b/lab/vtld/cpu_utils.c @@ -9,25 +9,40 @@ static int cmp_int32(const void* a, const void* b) { return (ia > ib) - (ia < ib); } -long dedup_run(long size, long max_value, unsigned int* seed) { +long dedup_run( + long size, + long max_value, + unsigned int* seed, + long repeat_start, + long repeat_end, + long* results +) { + if (repeat_start < 0 || repeat_end < repeat_start || !results) { + return -1; + } + if (size <= 0 || max_value <= 0 || !seed) { return -1; } - int32_t* arr = malloc((size_t)size * sizeof(int32_t)); - if (!arr) { - return -1; - } - for (long i = 0; i < size; ++i) { - arr[i] = (int32_t)(rand_r(seed) % max_value); - } - qsort(arr, (size_t)size, sizeof(int32_t), cmp_int32); - long unique = 0; - for (long i = 0; i < size; ++i) { - if (i == 0 || arr[i] != arr[i - 1]) { - ++unique; + for (long r = repeat_start; r < repeat_end; ++r) { + int32_t* arr = malloc((size_t)size * sizeof(int32_t)); + if (!arr) { + return -1; } + for (long i = 0; i < size; ++i) { + arr[i] = (int32_t)(rand_r(seed) % max_value); + } + qsort(arr, (size_t)size, sizeof(int32_t), cmp_int32); + long unique = 0; + for (long i = 0; i < size; ++i) { + if (i == 0 || arr[i] != arr[i - 1]) { + ++unique; + } + } + free(arr); + results[r] = unique; } - free(arr); - return unique; + + return 0; } diff --git a/lab/vtld/cpu_utils.h b/lab/vtld/cpu_utils.h index d6a777b..c22d59a 100644 --- a/lab/vtld/cpu_utils.h +++ b/lab/vtld/cpu_utils.h @@ -2,4 +2,11 @@ #include -long dedup_run(long size, long max_value, unsigned int* seed); +long dedup_run( + long size, + long max_value, + unsigned int* seed, + long repeat_start, + long repeat_end, + long* results +); From ff4b7a094ece097efe8f44d6b2aac1d8b8bf2424 Mon Sep 17 00:00:00 2001 From: Ivan Zinchenko Date: Fri, 6 Feb 2026 10:13:32 +0300 Subject: [PATCH 9/9] Modify dedup algo --- .github/workflows/vtkm.yml | 25 ------------------ .github/workflows/vtpc.yml | 54 -------------------------------------- lab/vtld/cpu_utils.c | 26 +++++++++++------- lab/vtld/makefile | 2 +- 4 files changed, 17 insertions(+), 90 deletions(-) delete mode 100644 .github/workflows/vtkm.yml delete mode 100644 .github/workflows/vtpc.yml diff --git a/.github/workflows/vtkm.yml b/.github/workflows/vtkm.yml deleted file mode 100644 index efccba9..0000000 --- a/.github/workflows/vtkm.yml +++ /dev/null @@ -1,25 +0,0 @@ -# name: Test VT Kernel Module - -# on: -# push: -# branches: -# - main -# pull_request: -# paths: -# - 'lab/vtkm/**' - -# jobs: -# build: -# runs-on: ubuntu-latest -# defaults: -# run: -# working-directory: ./lab/vtkm -# steps: -# - uses: actions/checkout@v4 -# - name: Clang Format -# run: | -# find . -path ./build -prune -o \ -# \( -iname '*.c' -o -iname '*.h' -o -iname '*.cpp' -o -iname '*.hpp' \) \ -# -exec clang-format --style=file --dry-run --verbose {} \; -# - name: Build -# run: make diff --git a/.github/workflows/vtpc.yml b/.github/workflows/vtpc.yml deleted file mode 100644 index db0afe4..0000000 --- a/.github/workflows/vtpc.yml +++ /dev/null @@ -1,54 +0,0 @@ -# name: Test VT Page Cache - -# on: -# push: -# branches: -# - main -# pull_request: -# paths: -# - 'lab/vtpc/**' - -# jobs: -# build: -# strategy: -# matrix: -# cmake_build_type: -# - Asan -# - Release -# runs-on: ubuntu-latest -# container: -# image: silkeh/clang:latest -# defaults: -# run: -# working-directory: ./lab/vtpc -# steps: -# - name: Checkout -# uses: actions/checkout@v4 - -# - name: Clang Format -# run: | -# find . -path ./build -prune -o \ -# \( -iname '*.c' -o -iname '*.h' -o -iname '*.cpp' -o -iname '*.hpp' \) \ -# -exec clang-format --style=file --dry-run --verbose {} \; - -# - name: Configure -# run: cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} - -# - name: Build -# run: cmake --build build - -# - name: Clang Tidy -# if: matrix.cmake_build_type == 'Release' -# run: | -# find . -path ./build -prune -o \ -# \( -iname '*.c' -o -iname '*.h' -o -iname '*.cpp' -o -iname '*.hpp' \) -exec \ -# clang-tidy -p ./build {} \; - -# - name: Test Basic -# run: ./build/test/test_basic - -# - name: Test Sequential -# run: ./build/test/test_seq - -# - name: Test Random -# run: ./build/test/test_random diff --git a/lab/vtld/cpu_utils.c b/lab/vtld/cpu_utils.c index 676454d..4fe0139 100644 --- a/lab/vtld/cpu_utils.c +++ b/lab/vtld/cpu_utils.c @@ -1,14 +1,7 @@ #include "cpu_utils.h" -#include #include -static int cmp_int32(const void* a, const void* b) { - int32_t ia = *(const int32_t*)a; - int32_t ib = *(const int32_t*)b; - return (ia > ib) - (ia < ib); -} - long dedup_run( long size, long max_value, @@ -30,17 +23,30 @@ long dedup_run( if (!arr) { return -1; } + int32_t* dedup = malloc((size_t)size * sizeof(int32_t)); + if (!dedup) { + free(arr); + return -1; + } for (long i = 0; i < size; ++i) { arr[i] = (int32_t)(rand_r(seed) % max_value); } - qsort(arr, (size_t)size, sizeof(int32_t), cmp_int32); long unique = 0; for (long i = 0; i < size; ++i) { - if (i == 0 || arr[i] != arr[i - 1]) { - ++unique; + int32_t value = arr[i]; + int found = 0; + for (long j = 0; j < unique; ++j) { + if (dedup[j] == value) { + found = 1; + break; + } + } + if (!found) { + dedup[unique++] = value; } } free(arr); + free(dedup); results[r] = unique; } diff --git a/lab/vtld/makefile b/lab/vtld/makefile index 901d0b2..986e0b9 100644 --- a/lab/vtld/makefile +++ b/lab/vtld/makefile @@ -1,5 +1,5 @@ CC=gcc -CFLAGS=-O0 -std=c11 +CFLAGS=-O0 all: cpu_dedup cpu_dedup_pthreads ema_replace_str ema_replace_str_pthreads