osrv_labs/lab01/main.c
2025-03-09 16:07:05 +03:00

198 lines
4.7 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/sysinfo.h>
#include <sys/mman.h>
#define eprintf(...) fprintf(stderr, __VA_ARGS__);
typedef struct _Config {
char ifile[PATH_MAX + 1];
char ofile[PATH_MAX + 1];
size_t size;
uint64_t x0;
uint64_t a;
uint64_t c;
uint64_t m;
char *pad;
} Config;
typedef struct _Context {
pthread_t id;
pthread_barrier_t *barrier;
size_t start;
size_t end;
size_t length;
char *pad;
char *input;
char *output;
} Context;
void *lcg_gen(void *arg) {
Config *cfg = (Config*) arg;
uint64_t x = cfg->x0;
uint64_t a = cfg->a;
uint64_t c = cfg->c;
uint64_t m = cfg->m;
for (size_t i = 0; i < cfg->size; i++) {
cfg->pad[i] = x;
x = (x * a + c) % m;
}
return NULL;
}
void *worker(void *arg) {
Context *ctx = (Context*) arg;
printf("[worker %ld] start: %zu, end: %zu, len: %zu\n", ctx->id, ctx->start, ctx->end, ctx->length);
for (size_t i = ctx->start; i < ctx->end; i++) {
ctx->output[i] = ctx->input[i] ^ ctx->pad[i];
}
pthread_barrier_wait(ctx->barrier);
return NULL;
}
int main(int argc, char *argv[]) {
Config cfg;
char current_opt;
extern char *optarg;
extern int optind, optopt, opterr;
while ((current_opt = (char) getopt(argc, argv, "hi:o:x:a:c:m:")) != -1) {
switch (current_opt) {
case 'x':
cfg.x0 = atoi(optarg);
break;
case 'a':
cfg.a = atoi(optarg);
break;
case 'c':
cfg.c = atoi(optarg);
break;
case 'm':
cfg.m = atoi(optarg);
break;
case 'i':
strncpy(cfg.ifile, optarg, PATH_MAX);
break;
case 'o':
strncpy(cfg.ofile, optarg, PATH_MAX);
break;
case 'h':
printf("< help text here >\n");
return EXIT_SUCCESS;
case ':':
eprintf("Option %c requires an argument!\n", optopt);
return EXIT_FAILURE;
case '?':
eprintf("Use \"%s -h\" to see help page\n", argv[0]);
return EXIT_FAILURE;
}
}
int ifd;
int ofd;
if ((ifd = open(cfg.ifile, O_RDONLY)) == -1) {
perror("input file");
return EXIT_FAILURE;
}
if ((ofd = open(cfg.ofile, O_WRONLY | O_CREAT, S_IREAD | S_IWRITE)) == -1) {
perror("output file");
return EXIT_FAILURE;
}
struct stat istat;
if (fstat(ifd, &istat) == -1) {
perror("input file");
return EXIT_FAILURE;
}
cfg.size = (size_t) istat.st_size;
char *input = mmap(NULL, cfg.size, PROT_READ, MAP_PRIVATE, ifd, 0);
if (input == MAP_FAILED) {
perror("input file");
return EXIT_FAILURE;
}
char *output = calloc(sizeof(char), cfg.size);
close(ifd);
if ((cfg.pad = calloc(sizeof(char), cfg.size)) == NULL) {
perror("pad");
return EXIT_FAILURE;
}
pthread_t th_lcg_gen;
if (pthread_create(&th_lcg_gen, NULL, lcg_gen, (void*) &cfg) != 0) {
return EXIT_FAILURE;
}
pthread_join(th_lcg_gen, NULL);
int workers_count = get_nprocs();
Context **workers = calloc(sizeof(Context*), workers_count);
size_t blen = cfg.size / workers_count; // averrage len for one block
printf("[main] workers count: %d\n", workers_count);
pthread_barrier_t barrier;
if (pthread_barrier_init(&barrier, NULL, workers_count + 1) != 0) {
return EXIT_FAILURE;
}
printf("[main] file size: %zu\n", cfg.size);
for (int i = 0; i < workers_count; i++) {
workers[i] = calloc(sizeof(Context), 1);
workers[i]->barrier = &barrier;
workers[i]->pad = cfg.pad;
workers[i]->input = input;
workers[i]->output = output;
workers[i]->start = i * blen;
if (i == workers_count - 1) {
workers[i]->end = cfg.size; // handle remainder after integer division
} else {
workers[i]->end = (i + 1) * blen;
}
workers[i]->length = workers[i]->end - workers[i]->start;
pthread_create(&workers[i]->id, NULL, worker, (void*) workers[i]);
}
pthread_barrier_wait(&barrier);
for (int i = 0; i < workers_count; i++) {
pthread_join(workers[i]->id, NULL);
}
pthread_barrier_destroy(&barrier);
munmap(input, cfg.size);
for (int i = 0; i < workers_count; i++) {
free(workers[i]);
}
free(workers);
free(cfg.pad);
write(ofd, output, cfg.size);
close(ofd);
free(output);
return EXIT_SUCCESS;
}