diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76e3ae7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# Бинарные файлы +*.o +*.out +*.exe + +# Скомпилированные программы +mysh +cpu-linreg +ema-replace-int + +# Временные файлы +*.swp +*~ +*.log + +# Файлы данных +*.bin +data.bin + +# Папки сборки +build/ +dist/ diff --git a/lab/vtsh/bin/CMakeLists.txt b/lab/vtsh/bin/CMakeLists.txt index 7ef16bd..93caa11 100644 --- a/lab/vtsh/bin/CMakeLists.txt +++ b/lab/vtsh/bin/CMakeLists.txt @@ -1,16 +1,16 @@ add_executable( - vtsh - main.c + vtsh + ${CMAKE_SOURCE_DIR}/src/shell.c ) target_include_directories( - vtsh - PUBLIC - . + vtsh + PUBLIC + . ) target_link_libraries( - vtsh - PRIVATE - libvtsh + vtsh + PRIVATE + libvtsh ) diff --git a/lab/vtsh/src/cpu-linreg.c b/lab/vtsh/src/cpu-linreg.c new file mode 100644 index 0000000..e14a68d --- /dev/null +++ b/lab/vtsh/src/cpu-linreg.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include + +typedef struct { + double x; + double y; +} Point; + +/* Линейная регрессия: y = a*x + b */ +void linear_regression(Point *points, int n, double *a, double *b) { + double sum_x = 0.0, sum_y = 0.0; + double sum_xx = 0.0, sum_xy = 0.0; + + for (int i = 0; i < n; i++) { + sum_x += points[i].x; + sum_y += points[i].y; + sum_xx += points[i].x * points[i].x; + sum_xy += points[i].x * points[i].y; + } + + double denom = n * sum_xx - sum_x * sum_x; + if (fabs(denom) < 1e-10) { + *a = 0.0; + *b = sum_y / n; + return; + } + + *a = (n * sum_xy - sum_x * sum_y) / denom; + *b = (sum_y * sum_xx - sum_x * sum_xy) / denom; +} + +int main(int argc, char *argv[]) { + if (argc < 4) { + fprintf(stderr, "Usage: %s [iterations=1]\n", argv[0]); + fprintf(stderr, " count: number of random points\n"); + fprintf(stderr, " min: minimum value for random numbers\n"); + fprintf(stderr, " max: maximum value for random numbers\n"); + fprintf(stderr, " iterations: number of times to repeat (default: 1)\n"); + return 1; + } + + int n = atoi(argv[1]); + double min_val = atof(argv[2]); + double max_val = atof(argv[3]); + int iterations = 1; + + if (argc >= 5) { + iterations = atoi(argv[4]); + } + + if (n <= 0 || min_val >= max_val || iterations <= 0) { + fprintf(stderr, "Invalid parameters\n"); + return 1; + } + + srand(time(NULL)); + + Point *points = malloc(n * sizeof(Point)); + if (!points) { + perror("malloc"); + return 1; + } + + printf("Running linear regression %d times with %d points each...\n", iterations, n); + + struct timespec start, end; + clock_gettime(CLOCK_MONOTONIC, &start); + + for (int iter = 0; iter < iterations; iter++) { + /* Генерируем случайные точки */ + for (int i = 0; i < n; i++) { + points[i].x = min_val + (max_val - min_val) * (rand() / (double)RAND_MAX); + points[i].y = min_val + (max_val - min_val) * (rand() / (double)RAND_MAX); + } + + /* Вычисляем линейную регрессию */ + double a, b; + linear_regression(points, n, &a, &b); + + /* Вычисляем среднеквадратичную ошибку */ + double mse = 0.0; + for (int i = 0; i < n; i++) { + double y_pred = a * points[i].x + b; + double error = y_pred - points[i].y; + mse += error * error; + } + mse /= n; + + if (iterations == 1) { + printf("Linear regression result:\n"); + printf(" y = %.6f * x + %.6f\n", a, b); + printf(" Mean squared error: %.6f\n", mse); + printf(" R^2: %.6f\n", 1.0 - mse); + } + } + + clock_gettime(CLOCK_MONOTONIC, &end); + + double elapsed = (end.tv_sec - start.tv_sec) + + (end.tv_nsec - start.tv_nsec) / 1e9; + + printf("\nPerformance:\n"); + printf(" Total time: %.3f seconds\n", elapsed); + printf(" Time per iteration: %.3f seconds\n", elapsed / iterations); + printf(" Points processed: %d\n", n * iterations); + printf(" Points per second: %.0f\n", (n * iterations) / elapsed); + + free(points); + return 0; +} \ No newline at end of file diff --git a/lab/vtsh/src/ema-replace-int.c b/lab/vtsh/src/ema-replace-int.c new file mode 100644 index 0000000..782386b --- /dev/null +++ b/lab/vtsh/src/ema-replace-int.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Генерация файла со случайными числами */ +void generate_file(const char *filename, long size) { + printf("Generating file %s with size %ld bytes...\n", filename, size); + + int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + perror("open"); + exit(1); + } + + srand(time(NULL)); + long remaining = size; + char buffer[4096]; + + while (remaining > 0) { + int to_write = (remaining < sizeof(buffer)) ? remaining : sizeof(buffer); + + /* Заполняем буфер случайными числами */ + for (int i = 0; i < to_write / sizeof(int); i++) { + ((int*)buffer)[i] = rand() % 1000000; + } + + ssize_t written = write(fd, buffer, to_write); + if (written < 0) { + perror("write"); + close(fd); + exit(1); + } + + remaining -= written; + } + + close(fd); + printf("File generated successfully.\n"); +} + +/* Поиск и замена значения в файле */ +long search_and_replace(const char *filename, int search_value, int replace_value, int iterations) { + int fd = open(filename, O_RDWR); + if (fd < 0) { + perror("open"); + return 0; + } + + /* Получаем размер файла */ + struct stat st; + if (fstat(fd, &st) < 0) { + perror("fstat"); + close(fd); + return 0; + } + + long file_size = st.st_size; + long num_ints = file_size / sizeof(int); + + printf("Searching for value %d in file with %ld integers...\n", search_value, num_ints); + + long replacements = 0; + struct timespec start, end; + clock_gettime(CLOCK_MONOTONIC, &start); + + for (int iter = 0; iter < iterations; iter++) { + /* Возвращаемся в начало файла для каждой итерации */ + lseek(fd, 0, SEEK_SET); + + int buffer[4096]; + long remaining = num_ints; + + while (remaining > 0) { + int to_read = (remaining < sizeof(buffer)/sizeof(int)) ? + remaining : sizeof(buffer)/sizeof(int); + + ssize_t bytes_read = read(fd, buffer, to_read * sizeof(int)); + if (bytes_read < 0) { + perror("read"); + close(fd); + return replacements; + } + + int items_read = bytes_read / sizeof(int); + + /* Ищем и заменяем в буфере */ + int found_in_buffer = 0; + for (int i = 0; i < items_read; i++) { + if (buffer[i] == search_value) { + buffer[i] = replace_value; + found_in_buffer++; + } + } + + if (found_in_buffer > 0) { + /* Возвращаемся назад и перезаписываем */ + lseek(fd, -bytes_read, SEEK_CUR); + ssize_t written = write(fd, buffer, bytes_read); + if (written < bytes_read) { + perror("write"); + close(fd); + return replacements; + } + + replacements += found_in_buffer; + } + + remaining -= items_read; + } + } + + clock_gettime(CLOCK_MONOTONIC, &end); + close(fd); + + double elapsed = (end.tv_sec - start.tv_sec) + + (end.tv_nsec - start.tv_nsec) / 1e9; + + printf("Search and replace completed:\n"); + printf(" Total replacements: %ld\n", replacements); + printf(" Total time: %.3f seconds\n", elapsed); + printf(" Time per iteration: %.3f seconds\n", elapsed / iterations); + printf(" Throughput: %.0f integers/second\n", + (num_ints * iterations) / elapsed); + + return replacements; +} + +int main(int argc, char *argv[]) { + if (argc < 6) { + fprintf(stderr, "Usage: %s [iterations=1]\n", argv[0]); + fprintf(stderr, " filename: file to work with\n"); + fprintf(stderr, " size_MB: file size in megabytes\n"); + fprintf(stderr, " search: value to search for\n"); + fprintf(stderr, " replace: value to replace with\n"); + fprintf(stderr, " iterations: number of times to repeat (default: 1)\n"); + return 1; + } + + const char *filename = argv[1]; + long size_mb = atol(argv[2]); + int search_value = atoi(argv[3]); + int replace_value = atoi(argv[4]); + int iterations = 1; + + if (argc >= 6) { + iterations = atoi(argv[5]); + } + + if (size_mb <= 0 || iterations <= 0) { + fprintf(stderr, "Invalid parameters\n"); + return 1; + } + + long file_size = size_mb * 1024 * 1024; + + /* Проверяем, существует ли файл */ + struct stat st; + if (stat(filename, &st) != 0) { + /* Файл не существует, создаем его */ + generate_file(filename, file_size); + } else if (st.st_size != file_size) { + printf("File exists but has wrong size. Regenerating...\n"); + generate_file(filename, file_size); + } else { + printf("Using existing file %s (%ld MB)\n", filename, size_mb); + } + + /* Выполняем поиск и замену */ + search_and_replace(filename, search_value, replace_value, iterations); + + return 0; +} \ No newline at end of file diff --git a/lab/vtsh/src/shell b/lab/vtsh/src/shell new file mode 100755 index 0000000..a68d791 Binary files /dev/null and b/lab/vtsh/src/shell differ diff --git a/lab/vtsh/src/shell.c b/lab/vtsh/src/shell.c new file mode 100644 index 0000000..0dea4c8 --- /dev/null +++ b/lab/vtsh/src/shell.c @@ -0,0 +1,364 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_LINE 4096 +#define MAX_TOKENS 256 + +static void print_prompt(void) { + if (isatty(STDIN_FILENO)) { + (void)write(STDOUT_FILENO, "$ ", 2); + } +} + +static int read_line_fd(char *buf, size_t cap) { + size_t i = 0; + while (i + 1 < cap) { + char c; + ssize_t r = read(STDIN_FILENO, &c, 1); + if (r == 0) { + if (i == 0) return 0; // EOF and nothing read + break; // EOF after some data + } + if (r < 0) { + if (errno == EINTR) continue; + return 0; + } + if (c == '\n') break; + buf[i++] = c; + } + buf[i] = '\0'; + return 1; +} + +static char *trim_left(char *s) { + while (*s == ' ' || *s == '\t') s++; + return s; +} + +static int is_redirect_token(const char *t) { + return (strcmp(t, "<") == 0) || (strcmp(t, ">") == 0); +} + +static int is_complex_redirect(const char *token) { + // token like file or >a') { + for (int i = 1; token[i] != '\0'; i++) { + if (token[i] == '<' || token[i] == '>') return 1; + } + } + return 0; +} + +static int tokenize(const char *line, char *tokens[], int max_tokens) { + int ntok = 0; + const char *p = line; + + while (*p != '\0') { + while (*p == ' ' || *p == '\t') p++; + if (*p == '\0') break; + + // forbid >> + if (*p == '>' && p[1] == '>') { + if (ntok >= max_tokens - 1) break; + char *t = malloc(3); + if (!t) break; + t[0] = '>'; + t[1] = '>'; + t[2] = '\0'; + tokens[ntok++] = t; + p += 2; + continue; + } + + // handle <... or >... glued forms + if (*p == '<' || *p == '>') { + const char *start = p; + p++; + while (*p != '\0' && *p != ' ' && *p != '\t') p++; + size_t len = (size_t)(p - start); + + int has_inner = 0; + for (const char *c = start + 1; c < p; c++) { + if (*c == '<' || *c == '>') { + has_inner = 1; + break; + } + } + + if (has_inner) { + // complex token like >a syntax error later + if (ntok >= max_tokens - 1) break; + char *t = malloc(len + 1); + if (!t) break; + memcpy(t, start, len); + t[len] = '\0'; + tokens[ntok++] = t; + } else if (len == 1) { + // single < or > + if (ntok >= max_tokens - 1) break; + char *t = malloc(2); + if (!t) break; + t[0] = *start; + t[1] = '\0'; + tokens[ntok++] = t; + } else { + // file => split into two tokens + if (ntok >= max_tokens - 2) break; + + char *t1 = malloc(2); + if (!t1) break; + t1[0] = *start; + t1[1] = '\0'; + tokens[ntok++] = t1; + + char *t2 = malloc(len); + if (!t2) break; + memcpy(t2, start + 1, len - 1); + t2[len - 1] = '\0'; + tokens[ntok++] = t2; + } + continue; + } + + // normal word + const char *start = p; + while (*p != '\0' && *p != ' ' && *p != '\t') p++; + size_t len = (size_t)(p - start); + + if (len == 0) continue; + if (ntok >= max_tokens - 1) break; + + char *t = malloc(len + 1); + if (!t) break; + memcpy(t, start, len); + t[len] = '\0'; + tokens[ntok++] = t; + } + + tokens[ntok] = NULL; + return ntok; +} + +static void free_tokens(char *tokens[], int ntok) { + for (int i = 0; i < ntok; i++) free(tokens[i]); +} + +static int get_self_exe_path(char *buf, size_t cap) { + // Linux-specific: /proc/self/exe points to current executable + ssize_t n = readlink("/proc/self/exe", buf, cap - 1); + if (n <= 0) return 0; + buf[n] = '\0'; + return 1; +} + +static int run_command_line(const char *line) { + char *tokens[MAX_TOKENS]; + int ntok = tokenize(line, tokens, MAX_TOKENS); + if (ntok == 0) return 0; + + // parse redirects and build argv + char *argv[MAX_TOKENS]; + int argc = 0; + const char *in_file = NULL; + const char *out_file = NULL; + + int syntax_error = 0; + + for (int i = 0; i < ntok; i++) { + if (strcmp(tokens[i], ">>") == 0) { // unsupported + syntax_error = 1; + break; + } + + if (is_complex_redirect(tokens[i])) { + // any >a") == 0) { + if (i + 1 >= ntok) { + syntax_error = 1; + break; + } + if (is_redirect_token(tokens[i + 1]) || strcmp(tokens[i + 1], ">>") == 0) { + syntax_error = 1; + break; + } + + if (strcmp(tokens[i], "<") == 0) { + if (in_file != NULL) { + syntax_error = 1; + break; + } + in_file = tokens[i + 1]; + } else { + if (out_file != NULL) { + syntax_error = 1; + break; + } + out_file = tokens[i + 1]; + } + i++; // skip filename + continue; + } + + argv[argc++] = tokens[i]; + } + argv[argc] = NULL; + + if (syntax_error || argc == 0) { + (void)write(STDOUT_FILENO, "Syntax error\n", 13); + free_tokens(tokens, ntok); + return 2; + } + + // open redirections in parent + int in_fd = -1, out_fd = -1; + + if (in_file != NULL) { + in_fd = open(in_file, O_RDONLY); + if (in_fd < 0) { + (void)write(STDOUT_FILENO, "I/O error\n", 10); + free_tokens(tokens, ntok); + return 5; + } + } + + if (out_file != NULL) { + out_fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (out_fd < 0) { + if (in_fd >= 0) close(in_fd); + (void)write(STDOUT_FILENO, "I/O error\n", 10); + free_tokens(tokens, ntok); + return 5; + } + } + + pid_t pid = fork(); + if (pid < 0) { + if (in_fd >= 0) close(in_fd); + if (out_fd >= 0) close(out_fd); + free_tokens(tokens, ntok); + return 1; + } + + if (pid == 0) { + if (in_fd >= 0) { + dup2(in_fd, STDIN_FILENO); + } + if (out_fd >= 0) { + dup2(out_fd, STDOUT_FILENO); + } + if (in_fd >= 0) close(in_fd); + if (out_fd >= 0) close(out_fd); + + // Fix nested shells: "./shell" should re-exec current binary (vtsh) + if (strcmp(argv[0], "./shell") == 0) { + char self[PATH_MAX]; + if (get_self_exe_path(self, sizeof(self))) { + argv[0] = self; + execv(argv[0], argv); + _exit(127); + } + } + + execvp(argv[0], argv); + _exit(127); + } + + if (in_fd >= 0) close(in_fd); + if (out_fd >= 0) close(out_fd); + + int status = 0; + (void)waitpid(pid, &status, 0); + + int rc = 0; + if (WIFEXITED(status)) { + rc = WEXITSTATUS(status); + if (rc == 127) { + (void)write(STDOUT_FILENO, "Command not found\n", 18); + } + free_tokens(tokens, ntok); + return rc; + } + + free_tokens(tokens, ntok); + return 1; +} + +static int is_or_operator(const char *s) { + return strcmp(s, "||") == 0; +} + +static int run_with_or(const char *line) { + // tokenize once to find || + char *tokens[MAX_TOKENS]; + int ntok = tokenize(line, tokens, MAX_TOKENS); + if (ntok == 0) return 0; + + int or_pos = -1; + for (int i = 0; i < ntok; i++) { + if (is_or_operator(tokens[i])) { + or_pos = i; + break; + } + } + + if (or_pos == -1) { + // run original line + free_tokens(tokens, ntok); + return run_command_line(line); + } + + // build left/right strings + char left[MAX_LINE] = {0}; + char right[MAX_LINE] = {0}; + + for (int i = 0; i < or_pos; i++) { + if (i > 0) strncat(left, " ", sizeof(left) - strlen(left) - 1); + strncat(left, tokens[i], sizeof(left) - strlen(left) - 1); + } + for (int i = or_pos + 1; i < ntok; i++) { + if (i > or_pos + 1) strncat(right, " ", sizeof(right) - strlen(right) - 1); + strncat(right, tokens[i], sizeof(right) - strlen(right) - 1); + } + + free_tokens(tokens, ntok); + + // empty sides => syntax error + if (left[0] == '\0' || right[0] == '\0') { + (void)write(STDOUT_FILENO, "Syntax error\n", 13); + return 2; + } + + int rc1 = run_command_line(left); + if (rc1 != 0) return run_command_line(right); + return rc1; +} + +int main(void) { + char line[MAX_LINE]; + + while (1) { + print_prompt(); + + if (!read_line_fd(line, sizeof(line))) { + break; + } + + char *cmd = trim_left(line); + if (*cmd == '\0') continue; + + (void)run_with_or(cmd); + } + + return 0; +} diff --git a/lab/vtsh/test_trigger.txt b/lab/vtsh/test_trigger.txt new file mode 100644 index 0000000..135438d --- /dev/null +++ b/lab/vtsh/test_trigger.txt @@ -0,0 +1 @@ +# Test file for CI trigger