diff --git a/lab/vtsh/.gitignore b/lab/vtsh/.gitignore
index fb90d94..8f5d964 100644
--- a/lab/vtsh/.gitignore
+++ b/lab/vtsh/.gitignore
@@ -6,3 +6,7 @@ build/
# Python
__pycache__/
+
+.idea/*
+
+some.bin
\ No newline at end of file
diff --git a/lab/vtsh/.idea/.gitignore b/lab/vtsh/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/lab/vtsh/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/lab/vtsh/.idea/codeStyles/Project.xml b/lab/vtsh/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..81469bd
--- /dev/null
+++ b/lab/vtsh/.idea/codeStyles/Project.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab/vtsh/.idea/codeStyles/codeStyleConfig.xml b/lab/vtsh/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/lab/vtsh/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/lab/vtsh/.idea/editor.xml b/lab/vtsh/.idea/editor.xml
new file mode 100644
index 0000000..0093ef6
--- /dev/null
+++ b/lab/vtsh/.idea/editor.xml
@@ -0,0 +1,344 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab/vtsh/.idea/misc.xml b/lab/vtsh/.idea/misc.xml
new file mode 100644
index 0000000..0b76fe5
--- /dev/null
+++ b/lab/vtsh/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab/vtsh/.idea/modules.xml b/lab/vtsh/.idea/modules.xml
new file mode 100644
index 0000000..af61fa6
--- /dev/null
+++ b/lab/vtsh/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab/vtsh/.idea/vcs.xml b/lab/vtsh/.idea/vcs.xml
new file mode 100644
index 0000000..b2bdec2
--- /dev/null
+++ b/lab/vtsh/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab/vtsh/.idea/vtsh.iml b/lab/vtsh/.idea/vtsh.iml
new file mode 100644
index 0000000..f08604b
--- /dev/null
+++ b/lab/vtsh/.idea/vtsh.iml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/lab/vtsh/bin/CMakeLists.txt b/lab/vtsh/bin/CMakeLists.txt
index 7ef16bd..823b2f2 100644
--- a/lab/vtsh/bin/CMakeLists.txt
+++ b/lab/vtsh/bin/CMakeLists.txt
@@ -14,3 +14,14 @@ target_link_libraries(
PRIVATE
libvtsh
)
+
+add_executable(
+ cpu_linreg
+ cpu_linreg.c
+)
+
+target_include_directories(
+ cpu_linreg
+ PUBLIC
+ .
+)
diff --git a/lab/vtsh/bin/cpu_linreg b/lab/vtsh/bin/cpu_linreg
new file mode 100755
index 0000000..50aa45f
Binary files /dev/null and b/lab/vtsh/bin/cpu_linreg differ
diff --git a/lab/vtsh/bin/cpu_linreg.c b/lab/vtsh/bin/cpu_linreg.c
new file mode 100644
index 0000000..2689839
--- /dev/null
+++ b/lab/vtsh/bin/cpu_linreg.c
@@ -0,0 +1,74 @@
+#include
+#include
+#include
+#include
+#include
+
+#define XMIN 0.0
+#define XMAX 100.0
+#define A_TRUE 2.0
+#define B_TRUE 3.0
+#define NOISE_SIGMA 0.5
+
+double rand01() {
+ return (double)rand() / (double)RAND_MAX;
+}
+
+void run_linreg(long long n) {
+ double mean_x = 0.0, mean_y = 0.0;
+ double m2x = 0.0, cxy = 0.0;
+ long long k = 0;
+
+ for (long long i = 0; i < n; i++) {
+ double x = XMIN + rand01() * (XMAX - XMIN);
+ double noise = (rand01() - 0.5) * 2 * NOISE_SIGMA;
+ double y = A_TRUE * x + B_TRUE + noise;
+
+ k++;
+ double dx = x - mean_x;
+ double dy = y - mean_y;
+ mean_x += dx / k;
+ mean_y += dy / k;
+ m2x += dx * (x - mean_x);
+ cxy += dx * (y - mean_y);
+ }
+
+ double var_x = m2x / n;
+ double cov_xy = cxy / n;
+
+ double a = cov_xy / var_x;
+ double b = mean_y - a * mean_x;
+
+ printf("a = %.4f, b = %.4f\n", a, b);
+}
+
+int main(int argc, char *argv[]) {
+ long long count = 10000000;
+ int repeat = 3;
+
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "--count") == 0 && i + 1 < argc) {
+ count = atoll(argv[++i]);
+ } else if (strcmp(argv[i], "--repeat") == 0 && i + 1 < argc) {
+ repeat = atoi(argv[++i]);
+ } else {
+ printf("Unknown option: %s\n", argv[i]);
+ return 1;
+ }
+ }
+
+ srand(7);
+
+ struct timespec start, end;
+ clock_gettime(CLOCK_MONOTONIC, &start);
+
+ for (int r = 1; r <= repeat; r++) {
+ run_linreg(count);
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ double total_time = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
+ printf("Total time = %.3f s\n", total_time);
+
+ return 0;
+}
diff --git a/lab/vtsh/bin/ema_join.h b/lab/vtsh/bin/ema_join.h
new file mode 100644
index 0000000..eba0eb7
--- /dev/null
+++ b/lab/vtsh/bin/ema_join.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include
+#include
+
+typedef struct {
+ long long id;
+ char value[9];
+} Row;
+
+typedef struct {
+ long long id;
+ char left[9];
+ char right[9];
+} JoinedRow;
+
+Row* load_table(const char* path, size_t* out_count);
+int save_result(const char* path, const JoinedRow* rows, size_t count);
+JoinedRow* nested_loop_join(const Row* left,size_t left_count,const Row* right,size_t right_count,size_t* out_count);
+double seconds_between(const struct timespec* start,const struct timespec* end);
diff --git a/lab/vtsh/bin/ema_join_algo.c b/lab/vtsh/bin/ema_join_algo.c
new file mode 100644
index 0000000..6aaa03f
--- /dev/null
+++ b/lab/vtsh/bin/ema_join_algo.c
@@ -0,0 +1,49 @@
+#include "ema_join.h"
+
+#include
+#include
+#include
+
+JoinedRow* nested_loop_join(const Row* left,size_t left_count,const Row* right,size_t right_count,size_t* out_count) {
+ JoinedRow* result = NULL;
+ size_t capacity = 0;
+ size_t size = 0;
+
+ for (size_t i = 0; i < left_count; ++i) {
+ for (size_t j = 0; j < right_count; ++j) {
+ if (left[i].id == right[j].id) {
+ if (size == capacity) {
+ size_t new_capacity = (capacity == 0) ? 16 : (capacity * 2);
+ JoinedRow* tmp = realloc(result, new_capacity * sizeof(JoinedRow));
+ if (tmp == NULL) {
+ fprintf(stderr, "Не хватает памяти для результата\n");
+ free(result);
+ return NULL;
+ }
+ result = tmp;
+ capacity = new_capacity;
+ }
+
+ result[size].id = left[i].id;
+ strncpy(result[size].left, left[i].value, sizeof(result[size].left) - 1);
+ result[size].left[sizeof(result[size].left) - 1] = '\0';
+ strncpy(result[size].right, right[j].value, sizeof(result[size].right) - 1);
+ result[size].right[sizeof(result[size].right) - 1] = '\0';
+ size++;
+ }
+ }
+ }
+
+ *out_count = size;
+ return result;
+}
+
+double seconds_between(const struct timespec* start, const struct timespec* end) {
+ time_t sec = end->tv_sec - start->tv_sec;
+ long nsec = end->tv_nsec - start->tv_nsec;
+ if (nsec < 0) {
+ sec -= 1;
+ nsec += 1000000000L;
+ }
+ return (double)sec + (double)nsec / 1e9;
+}
diff --git a/lab/vtsh/bin/ema_join_io.c b/lab/vtsh/bin/ema_join_io.c
new file mode 100644
index 0000000..a6279d8
--- /dev/null
+++ b/lab/vtsh/bin/ema_join_io.c
@@ -0,0 +1,62 @@
+#include "ema_join.h"
+
+#include
+#include
+#include
+#include
+
+Row* load_table(const char* path, size_t* out_count) {
+ FILE* file = fopen(path, "r");
+ if (file == NULL) {
+ fprintf(stderr, "Не удалось открыть %s: %s\n", path, strerror(errno));
+ return NULL;
+ }
+
+ unsigned long long rows = 0;
+ if (fscanf(file, "%llu", &rows) != 1) {
+ fprintf(stderr, "Не удалось прочитать количество строк в %s\n", path);
+ fclose(file);
+ return NULL;
+ }
+
+ Row* data = calloc(rows, sizeof(Row));
+ if (data == NULL) {
+ fprintf(stderr, "Не хватает памяти для чтения %s\n", path);
+ fclose(file);
+ return NULL;
+ }
+
+ for (unsigned long long i = 0; i < rows; ++i) {
+ long long id = 0;
+ char word[16] = {0};
+ if (fscanf(file, "%lld %15s", &id, word) != 2) {
+ fprintf(stderr, "Строка %llu в %s некорректна\n", i + 1, path);
+ free(data);
+ fclose(file);
+ return NULL;
+ }
+ data[i].id = id;
+ strncpy(data[i].value, word, sizeof(data[i].value) - 1);
+ data[i].value[sizeof(data[i].value) - 1] = '\0';
+ }
+
+ fclose(file);
+ *out_count = (size_t)rows;
+ return data;
+}
+
+int save_result(const char* path, const JoinedRow* rows, size_t count) {
+ FILE* file = fopen(path, "w");
+ if (file == NULL) {
+ fprintf(stderr, "Не удалось открыть %s для записи: %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ fprintf(file, "%zu\n", count);
+ for (size_t i = 0; i < count; ++i) {
+ fprintf(file, "%lld %s %s\n", rows[i].id, rows[i].left, rows[i].right);
+ }
+
+ fclose(file);
+ return 0;
+}
diff --git a/lab/vtsh/bin/ema_join_nl.c b/lab/vtsh/bin/ema_join_nl.c
new file mode 100644
index 0000000..0ac9c9b
--- /dev/null
+++ b/lab/vtsh/bin/ema_join_nl.c
@@ -0,0 +1,84 @@
+#include "ema_join.h"
+
+#include
+#include
+#include
+
+static void usage(const char* prog) {
+ fprintf(stderr,"Использование: %s --left <файл> --right <файл> --out <файл> [--repeat N]\n",prog);
+}
+
+int main(int argc, char* argv[]) {
+ const char* left_path = NULL;
+ const char* right_path = NULL;
+ const char* out_path = NULL;
+ int repeat = 1;
+
+ for (int i = 1; i < argc; ++i) {
+ if (strcmp(argv[i], "--left") == 0 && i + 1 < argc) {
+ left_path = argv[++i];
+ } else if (strcmp(argv[i], "--right") == 0 && i + 1 < argc) {
+ right_path = argv[++i];
+ } else if (strcmp(argv[i], "--out") == 0 && i + 1 < argc) {
+ out_path = argv[++i];
+ } else if (strcmp(argv[i], "--repeat") == 0 && i + 1 < argc) {
+ repeat = atoi(argv[++i]);
+ if (repeat <= 0) {
+ fprintf(stderr, "--repeat должен быть больше нуля\n");
+ return 1;
+ }
+ } else {
+ usage(argv[0]);
+ return 1;
+ }
+ }
+
+ if (left_path == NULL || right_path == NULL || out_path == NULL) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ size_t left_count = 0;
+ size_t right_count = 0;
+ Row* left = load_table(left_path, &left_count);
+ if (left == NULL)
+ return 1;
+ Row* right = load_table(right_path, &right_count);
+ if (right == NULL) {
+ free(left);
+ return 1;
+ }
+
+ JoinedRow* result = NULL;
+ size_t result_count = 0;
+
+ for (int i = 1; i <= repeat; ++i) {
+ free(result);
+ result = NULL;
+ result_count = 0;
+
+ struct timespec start = {0}, end = {0};
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ result = nested_loop_join(left, left_count, right, right_count, &result_count);
+ clock_gettime(CLOCK_MONOTONIC, &end);
+
+ if (result == NULL) {
+ free(left);
+ free(right);
+ return 1;
+ }
+
+ printf("Итерация %d: совпадений=%zu время=%.6f с\n",i,result_count,seconds_between(&start, &end));
+ }
+
+ int rc = save_result(out_path, result, result_count);
+ free(result);
+ free(left);
+ free(right);
+
+ if (rc != 0)
+ return 1;
+
+ printf("Результат записан в %s\n", out_path);
+ return 0;
+}
diff --git a/lab/vtsh/bin/io_load.c b/lab/vtsh/bin/io_load.c
new file mode 100644
index 0000000..637807e
--- /dev/null
+++ b/lab/vtsh/bin/io_load.c
@@ -0,0 +1,16 @@
+#include "io_load.h"
+
+#include
+
+int main(int argc, char* argv[]) {
+ options_t opts;
+ if (parse_args(argc, argv, &opts) != 0) {
+ print_usage(argv[0]);
+ return 1;
+ }
+
+ if (run_io_workload(&opts) != 0)
+ return 1;
+
+ return 0;
+}
diff --git a/lab/vtsh/bin/io_load.h b/lab/vtsh/bin/io_load.h
new file mode 100644
index 0000000..537690a
--- /dev/null
+++ b/lab/vtsh/bin/io_load.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include
+#include
+#include
+
+typedef enum {
+ MODE_READ,
+ MODE_WRITE
+} io_mode_t;
+
+typedef enum {
+ ORDER_SEQUENCE,
+ ORDER_RANDOM
+} io_order_t;
+
+typedef struct {
+ io_mode_t mode;
+ size_t block_size;
+ size_t block_count;
+ const char* path;
+ int repeat;
+ bool use_direct;
+ io_order_t order;
+ bool range_set;
+ off_t range_start;
+ off_t range_end;
+} options_t;
+
+void print_usage(const char* prog);
+int parse_args(int argc, char* argv[], options_t* opts);
+int run_io_workload(const options_t* opts);
diff --git a/lab/vtsh/bin/io_load_args.c b/lab/vtsh/bin/io_load_args.c
new file mode 100644
index 0000000..17ce494
--- /dev/null
+++ b/lab/vtsh/bin/io_load_args.c
@@ -0,0 +1,125 @@
+#include "io_load.h"
+
+#include
+#include
+#include
+#include
+
+static bool parse_range(const char* text, off_t* start, off_t* end, bool* range_set) {
+ const char* dash = strchr(text, '-');
+ if (dash == NULL)
+ return false;
+
+ char left[32];
+ char right[32];
+ size_t left_len = (size_t)(dash - text);
+ size_t right_len = strlen(dash + 1);
+ if (left_len == 0 || right_len == 0 ||
+ left_len >= sizeof(left) || right_len >= sizeof(right))
+ return false;
+
+ memcpy(left, text, left_len);
+ left[left_len] = '\0';
+ memcpy(right, dash + 1, right_len + 1);
+
+ errno = 0;
+ long long start_val = strtoll(left, NULL, 10);
+ long long end_val = strtoll(right, NULL, 10);
+ if (errno != 0 || start_val < 0 || end_val < 0)
+ return false;
+
+ *start = (off_t)start_val;
+ *end = (off_t)end_val;
+ *range_set = !(start_val == 0 && end_val == 0);
+ return true;
+}
+
+void print_usage(const char* prog) {
+ fprintf(stderr,
+ "Usage: %s --rw --block_size --block_count \n"
+ " --file [--range start-end] [--direct on|off]\n"
+ " [--type sequence|random] [--repeat N]\n",
+ prog);
+}
+
+int parse_args(int argc, char* argv[], options_t* opts) {
+ opts->mode = MODE_READ;
+ opts->block_size = 4096;
+ opts->block_count = 1024;
+ opts->path = NULL;
+ opts->repeat = 1;
+ opts->use_direct = false;
+ opts->order = ORDER_SEQUENCE;
+ opts->range_set = false;
+ opts->range_start = 0;
+ opts->range_end = 0;
+
+ for (int i = 1; i < argc; ++i) {
+ if (strcmp(argv[i], "--rw") == 0 && i + 1 < argc) {
+ const char* mode = argv[++i];
+ if (strcmp(mode, "read") == 0) {
+ opts->mode = MODE_READ;
+ } else if (strcmp(mode, "write") == 0) {
+ opts->mode = MODE_WRITE;
+ } else {
+ fprintf(stderr, "Unknown --rw value: %s\n", mode);
+ return -1;
+ }
+ } else if (strcmp(argv[i], "--block_size") == 0 && i + 1 < argc) {
+ opts->block_size = (size_t)strtoull(argv[++i], NULL, 10);
+ if (opts->block_size == 0) {
+ fprintf(stderr, "block_size must be > 0\n");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "--block_count") == 0 && i + 1 < argc) {
+ opts->block_count = (size_t)strtoull(argv[++i], NULL, 10);
+ if (opts->block_count == 0) {
+ fprintf(stderr, "block_count must be > 0\n");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "--file") == 0 && i + 1 < argc) {
+ opts->path = argv[++i];
+ } else if (strcmp(argv[i], "--repeat") == 0 && i + 1 < argc) {
+ opts->repeat = atoi(argv[++i]);
+ if (opts->repeat <= 0) {
+ fprintf(stderr, "repeat must be > 0\n");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "--direct") == 0 && i + 1 < argc) {
+ const char* val = argv[++i];
+ if (strcmp(val, "on") == 0) {
+ opts->use_direct = true;
+ } else if (strcmp(val, "off") == 0) {
+ opts->use_direct = false;
+ } else {
+ fprintf(stderr, "--direct accepts on/off\n");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "--type") == 0 && i + 1 < argc) {
+ const char* val = argv[++i];
+ if (strcmp(val, "sequence") == 0) {
+ opts->order = ORDER_SEQUENCE;
+ } else if (strcmp(val, "random") == 0) {
+ opts->order = ORDER_RANDOM;
+ } else {
+ fprintf(stderr, "--type accepts sequence/random\n");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "--range") == 0 && i + 1 < argc) {
+ if (!parse_range(argv[++i], &opts->range_start, &opts->range_end, &opts->range_set)) {
+ fprintf(stderr, "Invalid range format\n");
+ return -1;
+ }
+ } else {
+ fprintf(stderr, "Unknown argument: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ if (opts->path == NULL) {
+ fprintf(stderr, "--file is required\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/lab/vtsh/bin/io_load_runner.c b/lab/vtsh/bin/io_load_runner.c
new file mode 100644
index 0000000..9d2a07a
--- /dev/null
+++ b/lab/vtsh/bin/io_load_runner.c
@@ -0,0 +1,176 @@
+#include "io_load.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static void* allocate_buffer(size_t size) {
+ void* buffer = malloc(size);
+ if (buffer == NULL)
+ fprintf(stderr, "malloc failed\n");
+ return buffer;
+}
+
+static double seconds_between(const struct timespec* start,
+ const struct timespec* end) {
+ time_t sec = end->tv_sec - start->tv_sec;
+ long nsec = end->tv_nsec - start->tv_nsec;
+ if (nsec < 0) {
+ sec -= 1;
+ nsec += 1000000000L;
+ }
+ return (double)sec + (double)nsec / 1e9;
+}
+
+static int check_range(const options_t* opts, const struct stat* st) {
+ off_t range_start = opts->range_start;
+ off_t range_end = opts->range_end;
+
+ if (!opts->range_set) {
+ range_start = 0;
+ if (opts->mode == MODE_READ) {
+ range_end = st->st_size;
+ } else {
+ range_end = range_start + (off_t)(opts->block_size * opts->block_count);
+ }
+ }
+
+ if (opts->mode == MODE_READ && range_end > st->st_size) {
+ fprintf(stderr, "Range exceeds file size for read mode\n");
+ return -1;
+ }
+
+ if (range_end <= range_start) {
+ fprintf(stderr, "Invalid range\n");
+ return -1;
+ }
+
+ off_t span = range_end - range_start;
+ if (opts->order == ORDER_SEQUENCE &&
+ span < (off_t)(opts->block_size * opts->block_count)) {
+ fprintf(stderr, "Range is too small for sequential access\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static off_t pick_offset(const options_t* opts, off_t range_start, size_t index) {
+ if (opts->order == ORDER_RANDOM) {
+ off_t range_bytes = opts->range_end - opts->range_start;
+ if (range_bytes < (off_t)opts->block_size)
+ return opts->range_start;
+
+ off_t slots = range_bytes / (off_t)opts->block_size;
+ if (slots <= 0)
+ slots = 1;
+ off_t slot = (off_t)(rand() % (int)slots);
+ return opts->range_start + slot * (off_t)opts->block_size;
+ }
+
+ return range_start + (off_t)(index * opts->block_size);
+}
+
+int run_io_workload(const options_t* opts) {
+ int flags = (opts->mode == MODE_READ) ? O_RDONLY : (O_WRONLY | O_CREAT);
+ int fd = open(opts->path, flags, 0666);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open %s: %s\n", opts->path, strerror(errno));
+ return 1;
+ }
+
+#if defined(F_NOCACHE)
+ if (opts->use_direct && fcntl(fd, F_NOCACHE, 1) != 0)
+ fprintf(stderr, "fcntl failed: %s\n", strerror(errno));
+#else
+ if (opts->use_direct)
+ fprintf(stderr, "Warning: F_NOCACHE not supported on this platform\n");
+#endif
+
+ struct stat st;
+ if (fstat(fd, &st) != 0) {
+ fprintf(stderr, "fstat failed: %s\n", strerror(errno));
+ close(fd);
+ return 1;
+ }
+
+ options_t local_opts = *opts;
+ if (!local_opts.range_set) {
+ local_opts.range_start = 0;
+ if (local_opts.mode == MODE_READ)
+ local_opts.range_end = st.st_size;
+ else
+ local_opts.range_end = local_opts.range_start + (off_t)(local_opts.block_size * local_opts.block_count);
+ }
+
+ if (check_range(&local_opts, &st) != 0) {
+ close(fd);
+ return 1;
+ }
+
+ void* buffer = allocate_buffer(local_opts.block_size);
+ if (buffer == NULL) {
+ close(fd);
+ return 1;
+ }
+
+ if (local_opts.mode == MODE_WRITE) {
+ for (size_t i = 0; i < local_opts.block_size; ++i)
+ ((unsigned char*)buffer)[i] = (unsigned char)('A' + (i % 26));
+ }
+
+ srand(0);
+
+ struct timespec total_start = {0}, total_end = {0};
+ clock_gettime(CLOCK_MONOTONIC, &total_start);
+
+ for (int r = 0; r < local_opts.repeat; ++r) {
+ struct timespec start, end;
+ clock_gettime(CLOCK_MONOTONIC, &start);
+
+ for (size_t i = 0; i < local_opts.block_count; ++i) {
+ off_t offset = pick_offset(&local_opts, local_opts.range_start, i);
+ ssize_t done;
+ if (local_opts.mode == MODE_READ) {
+ done = pread(fd, buffer, local_opts.block_size, offset);
+ } else {
+ done = pwrite(fd, buffer, local_opts.block_size, offset);
+ }
+ if (done < 0) {
+ fprintf(stderr, "I/O error at block %zu: %s\n", i, strerror(errno));
+ free(buffer);
+ close(fd);
+ return 1;
+ }
+ if ((size_t)done != local_opts.block_size) {
+ fprintf(stderr, "Short transfer at block %zu\n", i);
+ free(buffer);
+ close(fd);
+ return 1;
+ }
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ time_t sec = end.tv_sec - start.tv_sec;
+ long nsec = end.tv_nsec - start.tv_nsec;
+ if (nsec < 0) {
+ sec -= 1;
+ nsec += 1000000000L;
+ }
+ double elapsed = (double)sec + (double)nsec / 1e9;
+ printf("Iteration %d: blocks=%zu size=%zu bytes time=%.6f s\n", r + 1, local_opts.block_count, local_opts.block_size, elapsed);
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &total_end);
+ printf("Total time: %.6f s\n", seconds_between(&total_start, &total_end));
+
+ free(buffer);
+ close(fd);
+ return 0;
+}
diff --git a/lab/vtsh/bin/main b/lab/vtsh/bin/main
new file mode 100755
index 0000000..8ccf644
Binary files /dev/null and b/lab/vtsh/bin/main differ
diff --git a/lab/vtsh/bin/main.c b/lab/vtsh/bin/main.c
index fd9b122..ed7015e 100644
--- a/lab/vtsh/bin/main.c
+++ b/lab/vtsh/bin/main.c
@@ -1,7 +1,116 @@
#include
-#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MAX_INPUT 1024
+#define MAX_ARGS 64
+
+static char* skip_spaces(char* s) {
+ while (*s == ' ' || *s == '\t')
+ s++;
+ return s;
+}
+
+int run_command(char *cmd) {
+ char *args[MAX_ARGS];
+ pid_t pid;
+ int status;
+
+ int argc = 0;
+ char *token = strtok(cmd, " \t");
+ while (token != NULL && argc < MAX_ARGS - 1) {
+ args[argc++] = token;
+ token = strtok(NULL, " \t");
+ }
+ args[argc] = NULL;
+
+ if (argc == 0)
+ return 0;
+
+ if (strcmp(args[0], "exit") == 0)
+ exit(0);
+
+ struct timespec start = {0}, end = {0};
+ clock_gettime(CLOCK_MONOTONIC, &start);
+
+ pid = vfork();
+ if (pid < 0)
+ _exit(1);
+
+ if (pid == 0) {
+ execvp(args[0], args);
+ const char msg[] = "Command not found\n";
+ write(STDOUT_FILENO, msg, sizeof(msg) - 1);
+ _exit(127);
+ }
+
+ if (waitpid(pid, &status, 0) < 0)
+ return 1;
+
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ time_t sec = end.tv_sec - start.tv_sec;
+ long nsec = end.tv_nsec - start.tv_nsec;
+ if (nsec < 0) {
+ sec -= 1;
+ nsec += 1000000000L;
+ }
+ double time_spent = (double)sec + (double)nsec / 1e9;
+ fprintf(stderr, "time=%.6f\n", time_spent);
+
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ if (WIFSIGNALED(status))
+ return 128 + WTERMSIG(status);
+ return 1;
+}
int main() {
- printf("%s", vtsh_prompt());
- printf("Hello, world!\n");
+ char input[MAX_INPUT];
+ setvbuf(stdin, NULL, _IONBF, 0);
+
+ while (1) {
+ if (fgets(input, sizeof(input), stdin) == NULL)
+ break;
+
+ input[strcspn(input, "\n")] = '\0';
+ char *cmd = skip_spaces(input);
+ if (*cmd == '\0')
+ continue;
+
+ int code = 0;
+
+ while (cmd != NULL && *cmd != '\0') {
+ char *and_ptr = strstr(cmd, "&&");
+
+ if (and_ptr != NULL) {
+ *and_ptr = '\0';
+ }
+
+ char *segment = skip_spaces(cmd);
+ if (*segment == '\0') {
+ fprintf(stderr, "syntax error: empty command\n");
+ break;
+ }
+
+ code = run_command(segment);
+
+ if (code != 0)
+ break;
+
+ if (and_ptr == NULL)
+ break;
+
+ cmd = skip_spaces(and_ptr + 2);
+ if (*cmd == '\0') {
+ fprintf(stderr, "syntax error: trailing &&\n");
+ break;
+ }
+ }
+ }
+
+ return 0;
}
diff --git a/lab/vtsh/joined.tbl b/lab/vtsh/joined.tbl
new file mode 100644
index 0000000..d94eddb
--- /dev/null
+++ b/lab/vtsh/joined.tbl
@@ -0,0 +1,4 @@
+3
+1 alpha___ one_____
+2 bravo___ two_____
+3 charlie_ three___
diff --git a/lab/vtsh/left.tbl b/lab/vtsh/left.tbl
new file mode 100644
index 0000000..27644cf
--- /dev/null
+++ b/lab/vtsh/left.tbl
@@ -0,0 +1,4 @@
+3
+1 alpha___
+2 bravo___
+3 charlie_
diff --git a/lab/vtsh/out.bin b/lab/vtsh/out.bin
new file mode 100644
index 0000000..fecb092
Binary files /dev/null and b/lab/vtsh/out.bin differ
diff --git a/lab/vtsh/right.tbl b/lab/vtsh/right.tbl
new file mode 100644
index 0000000..d2895e2
--- /dev/null
+++ b/lab/vtsh/right.tbl
@@ -0,0 +1,5 @@
+4
+1 one_____
+3 three___
+4 four____
+2 two_____
diff --git a/lab/vtsh/test/shell b/lab/vtsh/test/shell
new file mode 120000
index 0000000..86ff261
--- /dev/null
+++ b/lab/vtsh/test/shell
@@ -0,0 +1 @@
+../build/bin/vtsh
\ No newline at end of file
diff --git a/lab/vtsh/test/test_redirection.py b/lab/vtsh/test/test_redirection.py
index cb14495..1759e4b 100644
--- a/lab/vtsh/test/test_redirection.py
+++ b/lab/vtsh/test/test_redirection.py
@@ -4,7 +4,7 @@
REQUIRED_REDIRECTION_FUNCTIONALITY = False
-@unittest.skipIf(not REQUIRED_REDIRECTION_FUNCTIONALITY,
+@unittest.skipIf(not REQUIRED_REDIRECTION_FUNCTIONALITY,
("Redirection functionality is not required in the task. "
"This functionality is for an additional task."))
class TestShellRedirection(BaseShellTest):