From 2bfad7f8369ac9dd058193ed60cbc813d0d48c0b Mon Sep 17 00:00:00 2001 From: ElectronixTM Date: Thu, 18 Sep 2025 00:26:56 +0300 Subject: [PATCH 1/2] feat: lab3 done --- .gitignore | 55 +++++++++++++++ OSs/lab3/README.txt | 1 + OSs/lab3/level-2/Makefile | 12 ++++ OSs/lab3/level-2/buff.c | 142 ++++++++++++++++++++++++++++++++++++++ OSs/lab3/level-2/buff.h | 16 +++++ OSs/lab3/level-2/main.c | 105 ++++++++++++++++++++++++++++ 6 files changed, 331 insertions(+) create mode 100644 .gitignore create mode 100644 OSs/lab3/README.txt create mode 100644 OSs/lab3/level-2/Makefile create mode 100644 OSs/lab3/level-2/buff.c create mode 100644 OSs/lab3/level-2/buff.h create mode 100644 OSs/lab3/level-2/main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..845cda6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,55 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# debug information files +*.dwo diff --git a/OSs/lab3/README.txt b/OSs/lab3/README.txt new file mode 100644 index 0000000..f926699 --- /dev/null +++ b/OSs/lab3/README.txt @@ -0,0 +1 @@ +Здесь я не буду оформлять первый уровень, потому что он фактически выглядит как копипаста. Возможно я потом докину его diff --git a/OSs/lab3/level-2/Makefile b/OSs/lab3/level-2/Makefile new file mode 100644 index 0000000..5002cda --- /dev/null +++ b/OSs/lab3/level-2/Makefile @@ -0,0 +1,12 @@ +targets = main +all: $(targets) + +main: main.o buff.o + $(CC) -o $@ $^ $(CFLAGS) + +%.o: %.c + $(CC) -c $^ -o $@ $(CFLAGS) + +clean: + rm -f $(targets) + rm -f *.o diff --git a/OSs/lab3/level-2/buff.c b/OSs/lab3/level-2/buff.c new file mode 100644 index 0000000..6c22132 --- /dev/null +++ b/OSs/lab3/level-2/buff.c @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include + +#include "buff.h" + +#define NBUFF 32 +#define TIMEOUT_SECS 5 +#define TIMEOUT_NSECS 0 + +#define ARR_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) + +static void __zero_msg(struct message *msg) +{ + memset(msg->msg, 0, sizeof(msg->msg)); +} + +static void __copy_msg(struct message *dest, const struct message *src) +{ + memcpy(dest->msg, src->msg, sizeof(src->msg)); +} + +static struct { + pthread_mutex_t msgs_lock; + sem_t nstored; + sem_t nempty; + struct message msgs[NBUFF]; + size_t cidx; //< consumer offset + size_t pidx; //< producer offset +} buf = {0}; + +int buf_init(void) +{ + int ret = 0; + ret = sem_init(&buf.nstored, 0, 0); + if (ret) { + ret = -EFAULT; + goto error; + } + ret = sem_init(&buf.nempty, 0, ARR_SIZE(buf.msgs)); + if (ret) { + ret = -EFAULT; + goto destroy_nstored; + } + ret = pthread_mutex_init(&buf.msgs_lock, NULL); + if (ret) { + goto destroy_nempty; + } + return 0; + +destroy_nempty: + sem_destroy(&buf.nempty); +destroy_nstored: + sem_destroy(&buf.nstored); +error: + return ret; +}; + +int buf_deinit(void) +{ + int ret = 0, tmp_ret; + if ((tmp_ret = pthread_mutex_destroy(&buf.msgs_lock))) ret = tmp_ret; + if ((tmp_ret = sem_destroy(&buf.nempty))) ret = tmp_ret; + if ((tmp_ret = sem_destroy(&buf.nstored))) ret = tmp_ret; + return ret; +} + +#define __buf_wrap_idx(idx) (idx % ARR_SIZE(buf.msgs)) +#define __buf_consume_ptr (&buf.msgs[__buf_wrap_idx(buf.cidx)]) +#define __buf_produce_ptr (&buf.msgs[__buf_wrap_idx(buf.pidx)]) + +static int __timeout_to_abs(struct timespec *ts, time_t secs, ssize_t nsec) +{ + int ret = timespec_get(ts, TIME_UTC); + if (ret != TIME_UTC) return -EFAULT; + ts->tv_sec += secs; + ts->tv_nsec += nsec; + return 0; +} + +static int __sem_timedwait_rel(sem_t *sem, time_t secs, ssize_t nsecs) +{ + int ret; + struct timespec sem_timeout; + ret = __timeout_to_abs(&sem_timeout, secs, nsecs); + if (ret) return ret; + return sem_timedwait(sem, &sem_timeout); +} + +static int __buf_consume(struct message *dest) +{ + int ret = pthread_mutex_lock(&buf.msgs_lock); + if (ret) { + return -EFAULT; + } + __copy_msg(dest, __buf_consume_ptr); + __zero_msg(__buf_consume_ptr); + buf.cidx++; + // Осозннано игнорируем ошибки + ret = pthread_mutex_unlock(&buf.msgs_lock); + return ret; +} + +int buf_consume(struct message *dest) +{ + int ret = 0; + int semval = 0; + ret = __sem_timedwait_rel(&buf.nstored, TIMEOUT_SECS, TIMEOUT_NSECS); + if (ret) return ret; + + ret = __buf_consume(dest); + if (ret) return ret; + + ret = sem_post(&buf.nempty); + return ret; +} + +static int __buf_push(const struct message *src) +{ + int ret = pthread_mutex_lock(&buf.msgs_lock); + if (ret) { + return -EFAULT; + } + __copy_msg(__buf_produce_ptr, src); + buf.pidx++; + ret = pthread_mutex_unlock(&buf.msgs_lock); + return ret; +} + +int buf_push(const struct message *src) +{ + int ret = 0; + ret = __sem_timedwait_rel(&buf.nempty, TIMEOUT_SECS, TIMEOUT_NSECS); + if (ret) return ret; + + ret = __buf_push(src); + if (ret) return ret; + + return sem_post(&buf.nstored); +} diff --git a/OSs/lab3/level-2/buff.h b/OSs/lab3/level-2/buff.h new file mode 100644 index 0000000..c903a52 --- /dev/null +++ b/OSs/lab3/level-2/buff.h @@ -0,0 +1,16 @@ +#ifndef BUFF_H_ +#define BUFF_H_ + +#define MSG_MAX_LEN 256 + +struct message { + char msg[MSG_MAX_LEN]; +}; + +int buf_init(void); +int buf_deinit(void); + +int buf_consume(struct message *dest); +int buf_push(const struct message *src); + +#endif diff --git a/OSs/lab3/level-2/main.c b/OSs/lab3/level-2/main.c new file mode 100644 index 0000000..23ccc4d --- /dev/null +++ b/OSs/lab3/level-2/main.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include + +#include "buff.h" + +#define ERR_PTR ((void *)1) +#define VOID_CAST(num) ((void *) num) + +#define N_PRODUCERS 16 +#define N_CONSUMERS 1 +#define KEY_LEN 32 // len in chars + +const char key_syms[] = "abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ" + "1234567890"; + +#define ALPHABET_SIZE (sizeof(key_syms) - 1) + +pthread_t consumers[N_CONSUMERS] = {0}; +pthread_t producers[N_PRODUCERS] = {0}; + +/** + * Создает "ключи" - наборы "случайных" символов из + * списка допустимых + */ +void get_key(char key_dest[KEY_LEN]) +{ + for (size_t i = 0; i < KEY_LEN; i++) { + size_t sym_idx = rand() % ALPHABET_SIZE; + key_dest[i] = key_syms[sym_idx]; + } +} + +volatile bool run = true; + +void *producer_routine(void *arg) +{ + size_t pidx = (size_t) arg; + struct message msg = {0}; + while (run) { + get_key(msg.msg); + if (buf_push(&msg)) pthread_exit(ERR_PTR); + printf("[p%lu] pushed new key\n", pidx); + sleep(1); + } + pthread_exit(NULL); +} + +void *consumer_routine(void *arg) +{ + size_t cidx = (size_t) arg; + struct message msg = {0}; + while (run) { + if (buf_consume(&msg)) pthread_exit(ERR_PTR); + printf("[c%lu] obtained key %s\n", cidx, msg.msg); + sleep(1); + } + pthread_exit(NULL); +} + +int main() +{ + if (buf_init()) { + printf("failed to init work buffer\n"); + return EXIT_FAILURE; + } + + for (size_t i = 0; i < N_PRODUCERS; i++) { + if (pthread_create(&producers[i], NULL, + producer_routine, VOID_CAST(i))) { + printf("Failed to create enough consumer threads\n"); + return EXIT_FAILURE; + } + } + for (size_t i = 0; i < N_CONSUMERS; i++) { + if (pthread_create(&consumers[i], NULL, + consumer_routine, VOID_CAST(i))) { + printf("Failed to create enough consumer threads\n"); + return EXIT_FAILURE; + } + } + + sleep(10); + run = false; + + void *ret = NULL; + for (size_t i = 0; i < N_PRODUCERS; i++) { + if (pthread_join(producers[i], &ret)) { + printf("failed to join producer"); + return EXIT_FAILURE; + } + } + for (size_t i = 0; i < N_CONSUMERS; i++) { + if (pthread_join(consumers[i], &ret)) { + printf("failed to join consumer"); + return EXIT_FAILURE; + } + } + // не используем разделяемые ресурсы, так что код возврата + // можно игнорировать, linux справится + buf_deinit(); + return EXIT_SUCCESS; +} From bbbf39720b194351f3313ce9c9d4ebe49012015b Mon Sep 17 00:00:00 2001 From: ElectronixTM Date: Thu, 18 Sep 2025 00:35:13 +0300 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20=D1=80=D0=B5=D1=88=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BB?= =?UTF-8?q?=D0=B0=D0=B1=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OSs/lab3/level-2/README.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 OSs/lab3/level-2/README.txt diff --git a/OSs/lab3/level-2/README.txt b/OSs/lab3/level-2/README.txt new file mode 100644 index 0000000..f35a667 --- /dev/null +++ b/OSs/lab3/level-2/README.txt @@ -0,0 +1,5 @@ +Во продвинутом уровне задания необходимо было реализовать паттерн producer-consumer. Заводится буффер определенного размера, а также потоки, которые в него что-то кладут и потоки, которые из него что-то берут. + +Внутри buff.c сокрыта вся магия по синхронизации буффера, постановке на него предметов и прочего. В main же только создаются потоки. Я решил, что producer будет создавать "ключи", а consumer будет их печатать + +Чтобы не решать проблемы с зачисткой буффера в конце, семафоры поставлены на таймаут, по истечении которого поток вылетит