/
dbazhenov
/
rt-isolation-study
Обзор
Документация
Войти
/
dbazhenov
/
rt-isolation-study
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/loader/cpu_loader.c
278 строк
13 KB
dbazhenov
Публикация диссертации
14 июн 2026, 16:28
14 июн 2026, 16:28
fc3d9ea
Код
Авторство
О чём код?
/* ================================================================ * cpu_loader.c — генератор управляемой конкурентной нагрузки на CPU * * НАЗНАЧЕНИЕ В СТЕНДЕ: * Программа создаёт контролируемую вычислительную нагрузку на * выбранных ядрах процессора с заданным уровнем использования * (1–100 %). Используется в экспериментах по оценке влияния * конкурентной нагрузки соседних ядер (cross-core interference) * на величину джиттера и WCET RT-обработчика, запущенного * на отдельном ядре. Поддерживаются два режима полезной работы: * - матричное умножение C = A·B плотных матриц (нагрузка * на ALU и иерархию кэшей); * - вычисление множества Мандельброта (нагрузка на FPU * без существенного давления на память). * * ИСПОЛЬЗУЕМЫЕ МЕХАНИЗМЫ LINUX: * - pthread_create — один рабочий поток на каждое целевое ядро; * - sched_setaffinity (pin_to_core) — закрепление каждого * потока за «своим» ядром, гарантирует изоляцию от ядра RT-задачи; * - clock_gettime(CLOCK_MONOTONIC) и nanosleep — реализация * ШИМ-подобного управления долей загрузки: фиксированное * окно SLOT_NS = 10 мс, из которого work_ns активной работы * и SLOT_NS − work_ns сна. * * ПАРАМЕТРЫ ЗАПУСКА: * --cores=0,1,2 — список ядер через запятую; * --load=75 — целевой уровень загрузки (1..100, %); * --mode=matrix|mandelbrot — тип вычислительного ядра; * --duration=s — длительность работы, с (0 — бессрочно); * --matrix-size=N — размер матрицы для режима matrix. * * ПРИМЕРЫ: * ./cpu_loader --cores=1,2,3 --load=100 --mode=matrix * ./cpu_loader --cores=1 --load=50 --mode=mandelbrot --duration=30 * ================================================================ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <time.h> #include <signal.h> #include <unistd.h> #include <pthread.h> #include <errno.h> #include "../common/platform.h" /* ── Глобальные переменные ──────────────────────────────────────── */ /* Флаг продолжения работы; сбрасывается обработчиком SIGINT/SIGTERM. */ static volatile sig_atomic_t g_running = 1; static void on_signal(int s) { (void)s; g_running = 0; } #define MAX_CORES 64 /* верхняя граница числа целевых ядер */ #define MAX_MATRIX 512 /* верхняя граница размера матрицы */ /* Доступные режимы нагрузки. */ typedef enum { MODE_MATRIX = 0, MODE_MANDELBROT } LoadMode; /* Параметры, передаваемые рабочему потоку при запуске. */ typedef struct { int core_id; int load_pct; /* целевая загрузка ядра, 1..100 % */ LoadMode mode; int matrix_n; /* сторона квадратной матрицы (для режима matrix) */ int duration_s; /* длительность работы потока в секундах (0 — без ограничения) */ } WorkerArgs; /* ── Реализации полезных нагрузок ───────────────────────────────── */ /* Плотное матричное умножение C = A × B (квадратные матрицы N×N, double). * Тройной вложенный цикл с порядком (i,k,j) обеспечивает * благоприятный шаблон доступа к памяти: внутренний цикл идёт * по последовательным элементам строк A и B. */ static void work_matrix(int N, double *A, double *B, double *C) { for (int i = 0; i < N; i++) for (int k = 0; k < N; k++) { double a_ik = A[i * N + k]; for (int j = 0; j < N; j++) C[i * N + j] += a_ik * B[k * N + j]; } } /* Расчёт изображения множества Мандельброта; результат не сохраняется, * накапливается только сумма итераций в переменной dummy, объявленной * volatile, чтобы исключить устранение цикла оптимизатором. */ static void work_mandelbrot(int width, int height, int max_iter) { volatile int dummy = 0; for (int py = 0; py < height; py++) { for (int px = 0; px < width; px++) { double x0 = -2.5 + 3.5 * px / width; double y0 = -1.25 + 2.5 * py / height; double x = 0, y = 0; int iter = 0; while (x*x + y*y <= 4.0 && iter < max_iter) { double xt = x*x - y*y + x0; y = 2*x*y + y0; x = xt; iter++; } dummy += iter; } } (void)dummy; } /* ── Рабочий поток: создаёт нагрузку на одно конкретное ядро ─────── */ /* Поток привязывается к ядру a->core_id и в цикле чередует фазу * активной работы длительностью work_ns и фазу сна sleep_ns; их * сумма составляет фиксированный интервал SLOT_NS = 10 мс. Отношение * work_ns/SLOT_NS соответствует целевой загрузке ядра. */ static void *worker(void *arg) { WorkerArgs *a = (WorkerArgs *)arg; /* Привязка потока к назначенному ядру (sched_setaffinity). */ pin_to_core(a->core_id); printf("[loader] поток на ядре %d, load=%d%%, mode=%s\n", a->core_id, a->load_pct, a->mode == MODE_MATRIX ? "matrix" : "mandelbrot"); /* Выделение буферов под исходные и результирующую матрицы (только * для режима MODE_MATRIX). Размер матрицы задаётся параметром --matrix-size. */ int N = a->matrix_n; double *A = NULL, *B = NULL, *C = NULL; if (a->mode == MODE_MATRIX) { A = malloc((size_t)N * N * sizeof(double)); B = malloc((size_t)N * N * sizeof(double)); C = malloc((size_t)N * N * sizeof(double)); if (!A || !B || !C) { perror("malloc"); goto done; } /* Заполнение матриц A и B случайными значениями. */ for (int i = 0; i < N * N; i++) { A[i] = (double)rand() / RAND_MAX; B[i] = (double)rand() / RAND_MAX; C[i] = 0.0; } } /* Окно ШИМ-подобного управления загрузкой: SLOT_NS = 10 мс, * work_ns = SLOT_NS · load_pct / 100, sleep_ns — остаток окна. */ const long SLOT_NS = 10000000L; /* 10 мс */ long work_ns = SLOT_NS * a->load_pct / 100; long sleep_ns = SLOT_NS - work_ns; struct timespec start; clock_gettime(CLOCK_MONOTONIC, &start); long t_start = (long)start.tv_sec * 1000000000L + start.tv_nsec; long iter = 0; while (g_running) { /* ── Фаза активной работы: повторяем полезную нагрузку до * истечения work_ns текущего окна. ── */ struct timespec work_start, now; clock_gettime(CLOCK_MONOTONIC, &work_start); long work_end_ns = (long)work_start.tv_sec * 1000000000L + work_start.tv_nsec + work_ns; while (g_running) { if (a->mode == MODE_MATRIX) work_matrix(N, A, B, C); else work_mandelbrot(320, 180, 64); clock_gettime(CLOCK_MONOTONIC, &now); long t = (long)now.tv_sec * 1000000000L + now.tv_nsec; if (t >= work_end_ns) break; } /* ── Фаза сна: освобождаем ядро на оставшуюся часть окна. ── */ if (sleep_ns > 0) { struct timespec ts = { sleep_ns / 1000000000L, sleep_ns % 1000000000L }; nanosleep(&ts, NULL); } iter++; /* Контроль ограничения по длительности работы потока. */ if (a->duration_s > 0) { clock_gettime(CLOCK_MONOTONIC, &now); long elapsed = (long)now.tv_sec * 1000000000L + now.tv_nsec - t_start; if (elapsed >= (long)a->duration_s * 1000000000L) break; } } printf("[loader] ядро %d: выполнено %ld итераций\n", a->core_id, iter); done: free(A); free(B); free(C); return NULL; } /* ── Разбор списка ядер вида «1,2,3» в массив целых чисел ──────── */ static int parse_cores(const char *s, int *cores, int max) { int n = 0; char buf[256]; strncpy(buf, s, sizeof(buf) - 1); buf[sizeof(buf) - 1] = '\0'; char *tok = strtok(buf, ","); while (tok && n < max) { cores[n++] = atoi(tok); tok = strtok(NULL, ","); } return n; } /* ── Главная функция ────────────────────────────────────────────── */ /* Точка входа генератора нагрузки. Разбирает параметры командной * строки, формирует список целевых ядер, после чего создаёт по * одному pthread-потоку на каждое ядро и ожидает их завершения. */ int main(int argc, char **argv) { int cores[MAX_CORES]; int n_cores = 0; int load_pct = 100; int duration = 0; int matrix_n = 128; LoadMode mode = MODE_MATRIX; /* Разбор аргументов командной строки. */ for (int i = 1; i < argc; i++) { char tmp[256]; if (sscanf(argv[i], "--cores=%255s", tmp) == 1) n_cores = parse_cores(tmp, cores, MAX_CORES); else if (sscanf(argv[i], "--load=%d", &load_pct) == 1) {} else if (sscanf(argv[i], "--duration=%d", &duration) == 1) {} else if (sscanf(argv[i], "--matrix-size=%d", &matrix_n) == 1) {} else if (strcmp(argv[i], "--mode=matrix") == 0) mode = MODE_MATRIX; else if (strcmp(argv[i], "--mode=mandelbrot") == 0) mode = MODE_MANDELBROT; else { fprintf(stderr, "Неизвестный аргумент: %s\n", argv[i]); return 1; } } if (n_cores == 0) { /* Если ядра не заданы, по умолчанию используются 1, 2 и 3 * (нулевое ядро обычно резервируется под RT-обработчик). */ cores[0] = 1; cores[1] = 2; cores[2] = 3; n_cores = 3; fprintf(stderr, "[loader] параметр --cores не задан, используются ядра 1,2,3\n"); } /* Ограничение допустимого диапазона значений параметров. */ if (load_pct < 1) load_pct = 1; if (load_pct > 100) load_pct = 100; if (matrix_n > MAX_MATRIX) matrix_n = MAX_MATRIX; printf("[cpu_loader] cores="); for (int i = 0; i < n_cores; i++) printf("%d%s", cores[i], i+1<n_cores?",":""); printf(" load=%d%% mode=%s duration=%ds\n", load_pct, mode == MODE_MATRIX ? "matrix" : "mandelbrot", duration); signal(SIGINT, on_signal); signal(SIGTERM, on_signal); srand((unsigned)time(NULL)); /* Запуск одного рабочего потока на каждое целевое ядро. */ pthread_t threads[MAX_CORES]; WorkerArgs args[MAX_CORES]; for (int i = 0; i < n_cores; i++) { args[i].core_id = cores[i]; args[i].load_pct = load_pct; args[i].mode = mode; args[i].matrix_n = matrix_n; args[i].duration_s = duration; pthread_create(&threads[i], NULL, worker, &args[i]); } /* Ожидание завершения всех рабочих потоков (барьер join). */ for (int i = 0; i < n_cores; i++) pthread_join(threads[i], NULL); printf("[cpu_loader] все потоки завершили работу\n"); return 0; }