diff --git a/lab/vtpc/CMakeLists.txt b/lab/vtpc/CMakeLists.txt index 478cbae..0dca19c 100644 --- a/lab/vtpc/CMakeLists.txt +++ b/lab/vtpc/CMakeLists.txt @@ -7,6 +7,7 @@ project( ) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +add_compile_definitions(_GNU_SOURCE) add_subdirectory(lib) add_subdirectory(test) diff --git a/lab/vtpc/lib/CMakeLists.txt b/lab/vtpc/lib/CMakeLists.txt index d565ca5..f5d3ab3 100644 --- a/lab/vtpc/lib/CMakeLists.txt +++ b/lab/vtpc/lib/CMakeLists.txt @@ -12,3 +12,9 @@ target_include_directories( PUBLIC . ) + +target_compile_definitions( + vtpc + PRIVATE + _GNU_SOURCE +) diff --git a/lab/vtpc/lib/vtpc.c b/lab/vtpc/lib/vtpc.c index e91ce30..9532a8b 100644 --- a/lab/vtpc/lib/vtpc.c +++ b/lab/vtpc/lib/vtpc.c @@ -14,14 +14,8 @@ int vtpc_open(const char* path, int mode, int access) { return -1; } - int open_mode = mode; - open_mode |= O_DIRECT; - + int open_mode = mode | O_DIRECT; int os_fd = open(path, open_mode, access); - if (os_fd < 0 && - (errno == EINVAL || errno == EOPNOTSUPP || errno == ENOTSUP)) { - os_fd = vtpc_open_fallback(path, mode, access); - } if (os_fd < 0) { return -1; } @@ -99,6 +93,7 @@ ssize_t vtpc_read(int fd, void* buf, size_t count) { return -1; } + // O_ACCMODE extracts access bits from open flags. int acc_mode = file->open_flags & O_ACCMODE; if (!(acc_mode == O_RDONLY || acc_mode == O_RDWR)) { errno = EBADF; @@ -204,6 +199,7 @@ off_t vtpc_lseek(int fd, off_t offset, int whence) { return -1; } + // whence defines the seek base: start, current position, or end. off_t base = 0; if (whence == SEEK_SET) { base = 0; diff --git a/lab/vtpc/lib/vtpc_cache.c b/lab/vtpc/lib/vtpc_cache.c index 1eb8e73..222c415 100644 --- a/lab/vtpc/lib/vtpc_cache.c +++ b/lab/vtpc/lib/vtpc_cache.c @@ -32,6 +32,7 @@ int vtpc_alloc_resident_slot(void) { vtpc_resident_page* page = &g_state.resident[i]; if (!page->used) { if (page->data == NULL) { + // O_DIRECT requires aligned buffers. if (posix_memalign( (void**)&page->data, g_state.page_size, g_state.page_size ) != 0) { @@ -137,6 +138,7 @@ void vtpc_trim_a1out(void) { } } +// Evict one page by 2Q policy and preserve metadata in A1out when needed. int vtpc_evict_one(void) { int victim = -1; int victim_from_a1in = 0; diff --git a/lab/vtpc/lib/vtpc_internal.h b/lab/vtpc/lib/vtpc_internal.h index 1f00cdd..64e98a0 100644 --- a/lab/vtpc/lib/vtpc_internal.h +++ b/lab/vtpc/lib/vtpc_internal.h @@ -20,12 +20,16 @@ typedef struct { } vtpc_list; typedef struct { + // Slot is occupied by a resident cached page. int used; int prev; int next; int list; + // Page was modified in cache and must be flushed. int dirty; + // Owner file index in g_state.files[]. int file_slot; + // File page number (offset / page_size). off_t page_no; unsigned char* data; } vtpc_resident_page; @@ -43,7 +47,9 @@ typedef struct { int os_fd; int open_flags; off_t pos; + // Logical file size tracked by vtpc. off_t size; + // 1 means ftruncate(size) is still pending. int size_dirty; } vtpc_file; @@ -52,6 +58,7 @@ typedef struct { size_t page_size; size_t max_resident; size_t max_a1out; + // 2Q threshold: preferred upper bound for A1in size. size_t kin; vtpc_list a1in; @@ -66,7 +73,6 @@ typedef struct { extern vtpc_state g_state; size_t vtpc_min_size(size_t lhs, size_t rhs); -int vtpc_open_fallback(const char* path, int mode, int access); int vtpc_state_init(void); vtpc_file* vtpc_get_file(int fd); diff --git a/lab/vtpc/lib/vtpc_state.c b/lab/vtpc/lib/vtpc_state.c index 4a0bcce..29a5663 100644 --- a/lab/vtpc/lib/vtpc_state.c +++ b/lab/vtpc/lib/vtpc_state.c @@ -1,5 +1,4 @@ #include -#include #include #include "vtpc_internal.h" @@ -10,10 +9,6 @@ size_t vtpc_min_size(size_t lhs, size_t rhs) { return lhs < rhs ? lhs : rhs; } -int vtpc_open_fallback(const char* path, int mode, int access) { - return open(path, mode, access); -} - int vtpc_state_init(void) { if (g_state.initialized) { return 0; diff --git a/lab/vtpc/vtld/.clang-format b/lab/vtpc/vtld/.clang-format new file mode 100644 index 0000000..dd99110 --- /dev/null +++ b/lab/vtpc/vtld/.clang-format @@ -0,0 +1,16 @@ +BasedOnStyle: Google + +AccessModifierOffset: -2 + +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false + +DerivePointerAlignment: false +BreakConstructorInitializers: BeforeComma +AlignAfterOpenBracket: BlockIndent + +BinPackArguments: false +BinPackParameters: false + +AlignArrayOfStructures: Right diff --git a/lab/vtpc/vtld/.gitignore b/lab/vtpc/vtld/.gitignore new file mode 100644 index 0000000..cfb1085 --- /dev/null +++ b/lab/vtpc/vtld/.gitignore @@ -0,0 +1,5 @@ +cpu_dedup +cpu_dedup_pthreads +ema_replace_str +ema_replace_str_pthreads +*.bin \ No newline at end of file diff --git a/lab/vtpc/vtld/.vscode/settings.json b/lab/vtpc/vtld/.vscode/settings.json new file mode 100644 index 0000000..f094915 --- /dev/null +++ b/lab/vtpc/vtld/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "errno.h": "c" + } +} \ No newline at end of file diff --git a/lab/vtpc/vtld/README.md b/lab/vtpc/vtld/README.md new file mode 100644 index 0000000..5f45463 --- /dev/null +++ b/lab/vtpc/vtld/README.md @@ -0,0 +1,165 @@ +# Базовый трек. Лабораторная работа 1 + +Вариант: `cpu-dedup ema-replace-str` + +## Часть 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. + +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/vtpc/vtld/ema_replace_str.c b/lab/vtpc/vtld/ema_replace_str.c new file mode 100644 index 0000000..a7abcc3 --- /dev/null +++ b/lab/vtpc/vtld/ema_replace_str.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ema_utils.h" +#include "util.h" +#include "vtpc.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] " + "[--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* 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 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; + } + } + + int fd = vtpc_open(file_s, O_RDWR, 0); + if (fd < 0) { + perror("vtpc_open"); + return 1; + } + + off_t file_size_off = vtpc_lseek(fd, 0, SEEK_END); + if (file_size_off < 0) { + perror("vtpc_lseek"); + vtpc_close(fd); + return 1; + } + uint64_t file_size = (uint64_t)file_size_off; + if (file_size == 0) { + fprintf(stderr, "File is empty.\n"); + vtpc_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"); + vtpc_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; + uint64_t blocks = full_blocks + ((tail > 0) ? 1 : 0); + if (blocks == 0) { + fprintf(stderr, "No blocks to process.\n"); + vtpc_close(fd); + return 1; + } + + void* buf = malloc(block_size); + if (!buf) { + fprintf(stderr, "malloc failed.\n"); + vtpc_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 (tail > 0 && block_index == blocks - 1) { + len = (size_t)tail; + } + + if (vtpc_lseek(fd, (off_t)offset, SEEK_SET) == (off_t)-1) { + perror("vtpc_lseek"); + free(buf); + vtpc_close(fd); + return 1; + } + ssize_t r = vtpc_read(fd, buf, len); + if (r < 0) { + perror("vtpc_read"); + free(buf); + vtpc_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 (vtpc_lseek(fd, (off_t)offset, SEEK_SET) == (off_t)-1) { + perror("vtpc_lseek"); + free(buf); + vtpc_close(fd); + return 1; + } + ssize_t w = vtpc_write(fd, buf, (size_t)r); + if (w < 0 || w != r) { + perror("vtpc_write"); + free(buf); + vtpc_close(fd); + return 1; + } + total_replaced += replaced; + } + } + + printf("replacements=%" PRIu64 "\n", total_replaced); + + if (vtpc_fsync(fd) != 0) { + perror("vtpc_fsync"); + free(buf); + vtpc_close(fd); + return 1; + } + + free(buf); + vtpc_close(fd); + return 0; +} diff --git a/lab/vtpc/vtld/ema_utils.c b/lab/vtpc/vtld/ema_utils.c new file mode 100644 index 0000000..3ff1273 --- /dev/null +++ b/lab/vtpc/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/vtpc/vtld/ema_utils.h b/lab/vtpc/vtld/ema_utils.h new file mode 100644 index 0000000..f33af3e --- /dev/null +++ b/lab/vtpc/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/vtpc/vtld/makefile b/lab/vtpc/vtld/makefile new file mode 100644 index 0000000..a541767 --- /dev/null +++ b/lab/vtpc/vtld/makefile @@ -0,0 +1,10 @@ +CC=gcc +CFLAGS=-O3 -D_GNU_SOURCE -I../lib + +all: ema_replace_str + +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 ../lib/vtpc.c ../lib/vtpc_state.c ../lib/vtpc_list.c ../lib/vtpc_cache.c + +clean: + rm -f ema_replace_str diff --git a/lab/vtpc/vtld/util.c b/lab/vtpc/vtld/util.c new file mode 100644 index 0000000..d0839ba --- /dev/null +++ b/lab/vtpc/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/vtpc/vtld/util.h b/lab/vtpc/vtld/util.h new file mode 100644 index 0000000..4efea74 --- /dev/null +++ b/lab/vtpc/vtld/util.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +const char* get_opt_value(int argc, char** argv, const char* key);