mirror of
https://github.com/zinchenko291/os-course.git
synced 2026-09-12 20:09:58 +05:00
WIP
This commit is contained in:
parent
b292b7f2ae
commit
acad738f12
@ -7,6 +7,7 @@ project(
|
||||
)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
add_compile_definitions(_GNU_SOURCE)
|
||||
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(test)
|
||||
|
||||
@ -12,3 +12,9 @@ target_include_directories(
|
||||
PUBLIC
|
||||
.
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
vtpc
|
||||
PRIVATE
|
||||
_GNU_SOURCE
|
||||
)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
|
||||
16
lab/vtpc/vtld/.clang-format
Normal file
16
lab/vtpc/vtld/.clang-format
Normal file
@ -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
|
||||
5
lab/vtpc/vtld/.gitignore
vendored
Normal file
5
lab/vtpc/vtld/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
cpu_dedup
|
||||
cpu_dedup_pthreads
|
||||
ema_replace_str
|
||||
ema_replace_str_pthreads
|
||||
*.bin
|
||||
5
lab/vtpc/vtld/.vscode/settings.json
vendored
Normal file
5
lab/vtpc/vtld/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"errno.h": "c"
|
||||
}
|
||||
}
|
||||
165
lab/vtpc/vtld/README.md
Normal file
165
lab/vtpc/vtld/README.md
Normal file
@ -0,0 +1,165 @@
|
||||
# Базовый трек. Лабораторная работа 1
|
||||
|
||||
Вариант: `cpu-dedup ema-replace-str`
|
||||
|
||||
## Часть 3. Анализ системы и мониторинг
|
||||
|
||||
Разработайте параметризируемую программу-нагрузчик, которая будет однопоточно нагружать подсистему ввода-вывода (IO). Она должна принимать на вход следующие параметры:
|
||||
|
||||
- `rw: read/write` - режим нагрузки: чтение или запись;
|
||||
- `block_size: <number>` - размер блока в байтах, с которым производится чтение/запись;
|
||||
- `block_count: <number>` - количество блоков;
|
||||
- `file: <path string>` - имя файла, с которым происходит работа;
|
||||
- `range: <number>-<number>` - границы в пределах файла, в которые должны
|
||||
осуществляться запись/чтение, значение по умолчанию, 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."
|
||||
201
lab/vtpc/vtld/ema_replace_str.c
Normal file
201
lab/vtpc/vtld/ema_replace_str.c
Normal file
@ -0,0 +1,201 @@
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
126
lab/vtpc/vtld/ema_utils.c
Normal file
126
lab/vtpc/vtld/ema_utils.c
Normal file
@ -0,0 +1,126 @@
|
||||
#include "ema_utils.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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;
|
||||
}
|
||||
14
lab/vtpc/vtld/ema_utils.h
Normal file
14
lab/vtpc/vtld/ema_utils.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
);
|
||||
10
lab/vtpc/vtld/makefile
Normal file
10
lab/vtpc/vtld/makefile
Normal file
@ -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
|
||||
21
lab/vtpc/vtld/util.c
Normal file
21
lab/vtpc/vtld/util.c
Normal file
@ -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;
|
||||
}
|
||||
6
lab/vtpc/vtld/util.h
Normal file
6
lab/vtpc/vtld/util.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
const char* get_opt_value(int argc, char** argv, const char* key);
|
||||
Loading…
Reference in New Issue
Block a user