2 Commits

Author SHA1 Message Date
b39c080d05 feat: part 2 complete 2025-09-25 23:32:22 +03:00
38752470c3 feat: part 1 complete 2025-09-25 23:12:44 +03:00
22 changed files with 0 additions and 1079 deletions

55
.gitignore vendored
View File

@ -1,55 +0,0 @@
# 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

View File

@ -1,11 +0,0 @@
cmake_minimum_required(VERSION 3.30)
# Set the project name
project(
lab4_win
LANGUAGES C
VERSION 0.0.1
DESCRIPTION "Лаба на тему виртуальной памяти"
)
add_executable(out main.c)

View File

@ -1,35 +0,0 @@
# Виртуальная память
Прошу обратить внимание на файлики report.txt и vm.bin. report.txt - место, куда будут сгружаться отчеты потока-монитора, vm.bin - файл, где находится программа для потока-симулятора. Подробнее про его команды можно почитать в методичке. Также прикладывается скрипт на питоне для генерации программ, его описание приложу, когда напишу
# Как запускать
Лаба требует файлов report.txt и vm.bin, и как вы их создадите в общем-то не важно. vm.bin я создавал через SIMASM. report создастся сам. Компилируете лабу любым нормальным для вас способом, она в терминальчик выдаст порядок своих действий. Там 2 потока, один делает вид, что работает с памятью и исполняет команды из vm.bin. Он будет писать какую команду исполнил и успешно ли. Второй поток - монитор, он раз в какое-то время (можно отрегулировать в сорцах) пишет отчетики в report.txt. Там он пишет статистику по занятой памяти, страничному файлу и подобному. Для его более удобного чтения есть скрипт util.py. Он тоже написан за 5 минут, поэтому для него важно, чтобы файл назывался именно report.txt, она помимо свободных страниц выведет еще и процентовку и немного другой инфы, в общем читать это поудобнее будет.
На выходе сейчас у вас должно получиться что запрашивается сколько-то памяти, потом пишется отчет, потом с памятью заигрывается, пишется новый отчет, потом память освобождается и пишется 3 отчет. на самом деле их будет 5 (все будут в одном файле), но это потому что с таймингами особо не угадаешь
В общем-то о том, что происходит в рамках отчета и надо будет рассказать на лабе
Касаемо кода сложно что-то хорошее сказать, только скажу, что не прям все я протестировал, так что если будете заигрывать с чем-то кроме аллокаций и освобождений, то там бог знает что выйдет
# SIMASM
Простой скрипт, фактически просто пачка регулярок, которые из человекочитаемого текста собирают бинарь, который надо по условию задачи
Каждая команда состоит из 5 частей и заканчивается точкой с запятой. Не все команды используют все части, но так как писать нормальный парсер мне лень, а simasm был написан за 10 минут, чтобы я во время тестов по 10 раз байты руками не перекладывал, он требует все поля. Теперь по каждому полю отдельно:
1. Поле, задающее минимальное время от начала процесса, которое должно пройти, прежде чем команда исполнится. Должно указываться в десятичных цифрах и может заканчиваться на 2 суффикса (ms - миллисекунды, s - секунды)
2. Поле, указывающее адрес региона, над которым будет производиться махинация. регион не должен быть слишком маленьким, потому что первые 64 КБ виртуальной памяти зарезервированы операционкой
3. Команда. Почти полностью соответствуют командам из методички:
- `exit` - выходит из программы, но фактически не используется
- `reserve_region` - резервирует какое-то количество памяти (указывается в 4 параметре), отдает страницы, но фактически еще память из системы не забирает. Чтобы ее забрать надо что-то со страницами поделать. Для этого есть `mess_region`
- `pass_block` - резервирует регион с `MEM_COMMIT`, не знаю, зачем это надо, но это единственное сходное в методичке что есть
- `non_save_block` - аллоцирует страницы, изменения на которых не пойдут на старничный файл
- `free_region` - освобождает занятые страницы
- `lock_block` - блокирует страницы и препятствует их попаданию в swap
- `unlock_block` - разблокирует страницы и разрешает им уходить в swap
- `mess_region` - выполняет тупые операции чтения или записи с блоком, чтобы ось его на самом деле выдала, а не просто сказала, что сделала это
4. Размер области или региона в памяти. Доступны суффиксы Gb - гигабайты, Mb - мегабайты, kb - килобайты
5. Параметр записи - что разрешается делать со страницами r - только читать, `rw` - читать и писать, `rwe` - читать, писать и исполнять, `re` - читать и исполнять, `e` - только исполнять
если подать флаг `-f FILE` - будет читать из указанного файла команды, иначе будет читать из стандартного потока ввода, если подать `-o OUTPUT` - запишет бинарный вывод в файл, в противном случае выдаст хексы в стандартный вывод

View File

@ -1,222 +0,0 @@
#include <stdio.h>
#include <windows.h>
#include <stdint.h>
enum {
THREAD_SIM,
THREAD_MONITOR,
THREAD_NUM
};
HANDLE threads[THREAD_NUM];
int threads_params[THREAD_NUM];
SYSTEM_INFO sys_info;
DWORD WINAPI thread_sim(LPVOID param);
DWORD WINAPI thread_monitor(LPVOID param);
#define SIMOP_EXIT 0x00
#define SIMOP_RESERVE 0x01
#define SIMOP_PASS_BLOCK 0x02
#define SIMOP_NON_SAVE 0x03
#define SIMOP_FREE_REG 0x04
#define SIMOP_RETURN_BLOCK 0x05
#define SIMOP_LOCK_BLOCK 0x06
#define SIMOP_UNLOCK_BLOCK 0x07
#define SIMOP_MESS_REGION 0x08
#define SLEEP_MILLIS 2000
// Некоторые поля увелчены для выравнивания
struct sim_cmd{
uint64_t millis_offset;
uint64_t op;
uint64_t region;
uint64_t reg_sz;
uint64_t access;
};
int main()
{
int ret = 0;
GetSystemInfo(&sys_info);
threads[THREAD_SIM] = CreateThread(NULL, 0, thread_sim,
&threads_params[THREAD_SIM], 0, NULL);
if (!threads[THREAD_SIM]) {
printf("Couldn't create simulator\n");
return EXIT_FAILURE;
}
threads[THREAD_MONITOR] = CreateThread(NULL, 0, thread_monitor,
&threads_params[THREAD_MONITOR], 0, NULL);
if (!threads[THREAD_MONITOR]) {
printf("Couldn't create monitor\n");
ret = EXIT_FAILURE;
goto close_sim_thread;
}
printf("waiting...\n");
DWORD status = WaitForMultipleObjects(THREAD_NUM, threads, TRUE, INFINITE);
printf("done waiting (%lu).\n", GetLastError());
close_monitor_thread:
CloseHandle(&threads[THREAD_MONITOR]);
close_sim_thread:
CloseHandle(&threads[THREAD_SIM]);
return ret;
}
int read_sim_cmd(FILE *src, struct sim_cmd *dest)
{
const size_t cmd_sz = sizeof(struct sim_cmd);
size_t bytes_read = fread(dest, sizeof(char), cmd_sz, src);
if (bytes_read != cmd_sz) return -ENODATA;
return 0;
}
int write_sim_cmd(FILE *src, struct sim_cmd *dest)
{
const size_t cmd_sz = sizeof(struct sim_cmd);
size_t bytes_written = fwrite(dest, sizeof(char), cmd_sz, src);
if (bytes_written != cmd_sz) return -ENODATA;
return 0;
}
#define QUIT_NUMBER 'q'
static uint64_t last_clock = 0;
static int do_cmd(struct sim_cmd *cmd)
{
if (cmd->millis_offset > last_clock) Sleep(cmd->millis_offset - last_clock);
switch (cmd->op) {
case SIMOP_RESERVE:
if (!VirtualAlloc((LPVOID)cmd->region, cmd->reg_sz,
MEM_RESERVE | MEM_COMMIT, cmd->access)) {
return -ENOMEM;
}
break;
case SIMOP_FREE_REG:
if (VirtualFree((LPVOID)cmd->region, cmd->reg_sz, MEM_DECOMMIT) == 0) {
return -EFAULT;
}
break;
case SIMOP_LOCK_BLOCK:
if (VirtualLock((LPVOID)cmd->region, cmd->reg_sz) == 0) {
return -ENOLCK;
}
break;
case SIMOP_UNLOCK_BLOCK:
if(VirtualUnlock((LPVOID)cmd->region, cmd->reg_sz) == 0) {
return -ENOLCK;
}
break;
case SIMOP_PASS_BLOCK:
if (!VirtualAlloc((LPVOID)cmd->region, cmd->reg_sz, MEM_COMMIT, cmd->access)) {
return -ENOMEM;
}
break;
case SIMOP_NON_SAVE:
if (!VirtualAlloc((LPVOID)cmd->region, cmd->reg_sz,
MEM_RESERVE | MEM_RESET, cmd->access)) {
return -ENOMEM;
}
case SIMOP_RETURN_BLOCK:
if (!VirtualFree((LPVOID)cmd->region, 0, MEM_RELEASE)) {
printf("error was %lu\n", GetLastError());
return -EFAULT;
}
break;
case SIMOP_MESS_REGION:
printf("messing with pages...\n");
if (cmd->access == PAGE_READWRITE || cmd->access == PAGE_EXECUTE_READWRITE) {
printf("memsetting\n");
memset((LPVOID)cmd->region, 0x77, cmd->reg_sz);
} else if (cmd->access == PAGE_READONLY) {
#define BUFF_SIZE 256
printf("reading to buffer...\n");
char buff[BUFF_SIZE] = {0};
for (size_t i = 0; i < cmd->reg_sz; i += BUFF_SIZE) {
memcpy(buff, &((char*)(cmd->region))[i], BUFF_SIZE);
}
}
break;
case SIMOP_EXIT:
return QUIT_NUMBER;
default:
break;
}
return 0;
}
DWORD WINAPI thread_sim(LPVOID param)
{
DWORD status = 0;
FILE *program = fopen("vm.bin", "r");
if (!program) {
printf("can't open file vm.bin\n");
status = EFAULT;
goto exit;
}
static struct sim_cmd cmd;
while(read_sim_cmd(program, &cmd) == 0) {
int ret = do_cmd(&cmd);
printf("command (%02llx) done with code (%d)\n", cmd.op, ret);
status = abs(ret);
if (ret < 0) {
break;
} else if (ret == QUIT_NUMBER) {
status = 0;
break;
}
}
close_program:
fclose(program);
exit:
printf("return from vm\n");
return status;
}
int write_report(FILE *f)
{
MEMORYSTATUS ms;
GlobalMemoryStatus(&ms);
size_t bytes_written = fprintf(
f,
"Memory status (Total/Avail):\n"
"Physical: %zu/%zu\n"
"Virtual: %zu/%zu\n"
"Page file: %zu/%zu\n"
"Page size (bytes): %lu\n"
"Granularity: %lu\n\n",
ms.dwTotalPhys, ms.dwAvailPhys,
ms.dwTotalVirtual, ms.dwAvailVirtual,
ms.dwTotalPageFile, ms.dwAvailPageFile,
sys_info.dwPageSize,
sys_info.dwAllocationGranularity
);
if (bytes_written < 1) {
return -EFAULT;
}
return 0;
}
DWORD WINAPI thread_monitor(LPVOID param)
{
DWORD status = 0;
FILE *report = fopen("report.txt", "w");
if (!report) {
printf("fault\n");
status = EFAULT;
goto close_report;
}
for (size_t i = 0; i < 5; i++) {
Sleep(SLEEP_MILLIS);
printf("report written with status: %d\n", write_report(report));
}
close_report:
fclose(report);
return status;
}

View File

@ -1,101 +0,0 @@
#/usr/bin/python3
"""
Маленький скрипт для сборки бинарных файликов для потока-симулятора из +- человекочитаемого текста
"""
import argparse
import sys
import struct
import re
time_suffixes = {
"": 1,
"ms": 1,
"s": 1000,
}
size_suffiexes = {
"": 1,
"kb": 2 ** 10,
"Mb": 2 ** 20,
"Gb": 2 ** 30
}
operations = {
"exit": 0x00,
"reserve_region": 0x01,
"pass_block": 0x02,
"non_save_block": 0x03,
"free_region": 0x04,
"return_block": 0x05,
"lock_block": 0x06,
"unlock_block": 0x07,
"mess_region": 0x08
}
access = {
"page_noaccess": 0x01,
"page_readonly": 0x02,
"r": 0x02,
"page_readwrite": 0x04,
"rw": 0x04,
"page_execute": 0x10,
"e": 0x10,
"page_execute_read": 0x20,
"re": 0x20,
"page_execute_readwrite": 0x40,
"rwe": 0x40
}
def main():
parser = argparse.ArgumentParser(prog="simasm")
parser.add_argument("--file", "-f", type=str, help="file with program")
parser.add_argument("--output", "-o", type=str, help="file to store program")
args = parser.parse_args()
program = ""
if args.file:
with open(args.file, "r", encoding="utf-8") as f:
program = f.read()
else:
program = sys.stdin.read()
program = re.sub("#.*$", "", program, flags=re.M)
program = " ".join(program.split())
lines = list(filter(lambda s: s!="", program.split(";")))
binary = bytearray()
for line in lines:
words = line.split()
if len(words) != 5:
print("wrong line")
sys.exit(1)
# timestamp
m = re.match(r"(\d+)(\w*)", words[0])
assert m
time_offset = int(m.group(1)) * time_suffixes[m.group(2)]
region_in = words[1]
region = 0
if region_in.startswith("0x"):
region = int(region_in, 16)
else:
region = int(region_in)
operation = operations[words[2]]
m = re.match(r"(\d+)(\w*)", words[3])
assert m
size = int(m.group(1)) * size_suffiexes[m.group(2)]
pg_access = access[words[4]]
binary += struct.pack("QQQQQ", time_offset, operation, region, size, pg_access)
if args.output:
with open(args.output, "wb") as f:
f.write(binary)
else:
print(binary.hex())
if __name__ == "__main__":
main()

View File

@ -1,4 +0,0 @@
# time | region | operation | size | access
1000ms 0x010000 reserve_region 1Gb rw;
3000ms 0x010000 mess_region 1Gb rw;
3000ms 0x010000 return_block 1Gb rw;

View File

@ -1,20 +0,0 @@
#/usr/bin/python3
# чтобы удобнее было посмотреть разницу
import re
with open("report.txt", 'r') as f:
lines = f.readlines()
while len(lines) > 0:
for line in lines[1:4]:
line = line.strip()
m = re.match(r"([\w ]+): (\d+)/(\d+)", line)
assert m
name = m.group(1)
total = int(m.group(2))
avail = int(m.group(3))
print(f"{name}: {total}/{avail} ({avail/total*100:.2f}%, "
f"delta: {total - avail})")
print()
lines = lines[7:]

Binary file not shown.

View File

@ -1 +0,0 @@
Здесь я не буду оформлять первый уровень, потому что он фактически выглядит как копипаста. Возможно я потом докину его

View File

@ -1,12 +0,0 @@
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

View File

@ -1,5 +0,0 @@
Во продвинутом уровне задания необходимо было реализовать паттерн producer-consumer. Заводится буффер определенного размера, а также потоки, которые в него что-то кладут и потоки, которые из него что-то берут.
Внутри buff.c сокрыта вся магия по синхронизации буффера, постановке на него предметов и прочего. В main же только создаются потоки. Я решил, что producer будет создавать "ключи", а consumer будет их печатать
Чтобы не решать проблемы с зачисткой буффера в конце, семафоры поставлены на таймаут, по истечении которого поток вылетит

View File

@ -1,142 +0,0 @@
#include <stdio.h>
#include <semaphore.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#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);
}

View File

@ -1,16 +0,0 @@
#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

View File

@ -1,105 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdbool.h>
#include <unistd.h>
#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;
}

BIN
OSs/lab4/part-1/main Executable file

Binary file not shown.

BIN
OSs/lab4/part-1/main.o Normal file

Binary file not shown.

View File

@ -1,12 +0,0 @@
targets = main
all: $(targets)
main: main.o
$(CC) -o $@ $^ $(CFLAGS)
%.o: %.c
$(CC) -c $^ -o $@ $(CFLAGS)
clean:
rm -f $(targets)
rm -f *.o

View File

@ -1,132 +0,0 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#define PIPE_READ_IDX 0
#define PIPE_WRITE_IDX 1
int client(int fd_read, int fd_write);
int server(int fd_read, int fd_write);
#define DO_OR_FAIL(cond) if (cond) return EXIT_FAILURE
int main()
{
int pipe_to_server[2], pipe_to_client[2];
DO_OR_FAIL(pipe(pipe_to_server) < 0);
DO_OR_FAIL(pipe(pipe_to_client) < 0);
pid_t client_pid;
if ((client_pid = fork()) == 0) { // server
DO_OR_FAIL(close(pipe_to_server[PIPE_WRITE_IDX]));
DO_OR_FAIL(close(pipe_to_client[PIPE_READ_IDX]));
DO_OR_FAIL(
server(
pipe_to_server[PIPE_READ_IDX],
pipe_to_client[PIPE_WRITE_IDX]
)
);
}
// client
DO_OR_FAIL(close(pipe_to_server[PIPE_READ_IDX]));
DO_OR_FAIL(close(pipe_to_client[PIPE_WRITE_IDX]));
DO_OR_FAIL(
client(
pipe_to_client[PIPE_READ_IDX],
pipe_to_server[PIPE_WRITE_IDX]
)
);
return 0;
}
char *ltrim(char *s)
{
while(isspace(*s)) s++;
return s;
}
char *rtrim(char *s)
{
char* back = s + strlen(s);
while(isspace(*--back));
*(back+1) = '\0';
return s;
}
char *trim(char *s)
{
return rtrim(ltrim(s));
}
#define BUF_SIZE 256
#define DO_OR_PROPAGATE(call, expected, tmp_var) \
if ((tmp_var = (call)) != expected)) return tmp_var
/**
* Performs client actions
*
* @fd_read - pipe to read from
* @fd_write - pipe to write t\no
*
* returns 0 on success or -errno on fail
*/
int client(int fd_read, int fd_write)
{
char buf[BUF_SIZE] = {0};
if (!fgets(buf, BUF_SIZE, stdin)) return -EFAULT;
const char *s = trim(buf);
size_t len = strlen(s);
ssize_t n_written = write(fd_write, s, len);
if (n_written < 0 || n_written < len) {
return -EFAULT;
}
memset(buf, 0, BUF_SIZE);
size_t n;
while ((n = read(fd_read, buf, BUF_SIZE)) > 0) {
len = strlen(buf);
n_written = fwrite(buf, sizeof(buf[0]), strlen(buf), stdout);
if (n_written < 0 || n_written < len) {
return -EFAULT;
}
memset(buf, 0, sizeof(buf));
}
return 0;
}
/**
* Performs server actions
*
* @fd_read - pipe to read from
* @fd_write - pipe to write to
*
* returns 0 on success or -errno on fail
*/
int server(int fd_read, int fd_write)
{
char buf[BUF_SIZE] = {0};
size_t n = read(fd_read, buf, BUF_SIZE);
if (n == 0) return -EFAULT;
FILE *f = fopen(buf, "r");
// не 100% правильно, но наиболее вероятно
if (f == NULL) {
const char error_msg[] = "Can't open requested file\n";
write(fd_write, error_msg, sizeof(error_msg));
goto epilog;
}
while ((n = fread(buf, sizeof(buf[0]), BUF_SIZE, f)) > 0) {
write(fd_write, buf, n);
memset(buf, 0, sizeof(buf));
}
epilog:
fclose(f);
return 0;
}

View File

@ -1,15 +0,0 @@
targets = server client
all: $(targets)
server: server.o
$(CC) -o $@ $^ $(CFLAGS)
client: client.o
$(CC) -o $@ $^ $(CFLAGS)
%.o: %.c
$(CC) -c $^ -o $@ $(CFLAGS)
clean:
rm -f $(targets)
rm -f *.o

View File

@ -1,80 +0,0 @@
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include "common.h"
int client(int fd_read, int fd_write);
int main()
{
int read_fd, write_fd;
FAIL_ON((write_fd=open(FIFO_SERVER_PATH, O_WRONLY)) < 0);
FAIL_ON((read_fd=open(FIFO_CLIENT_PATH, O_RDONLY)) < 0);
FAIL_ON(client(read_fd, write_fd));
FAIL_ON(unlink(FIFO_SERVER_PATH) < 0);
printf("unlinked %s\n", FIFO_SERVER_PATH);
FAIL_ON(unlink(FIFO_CLIENT_PATH) < 0);
printf("unlinked %s\n", FIFO_CLIENT_PATH);
return EXIT_SUCCESS;
}
#define BUF_SIZE 256
char *ltrim(char *s)
{
while(isspace(*s)) s++;
return s;
}
char *rtrim(char *s)
{
char* back = s + strlen(s);
while(isspace(*--back));
*(back+1) = '\0';
return s;
}
char *trim(char *s)
{
return rtrim(ltrim(s));
}
/**
* Performs client actions
*
* @fd_read - pipe to read from
* @fd_write - pipe to write t\no
*
* returns 0 on success or -errno on fail
*/
int client(int fd_read, int fd_write)
{
char buf[BUF_SIZE] = {0};
if (!fgets(buf, BUF_SIZE, stdin)) return -EFAULT;
const char *s = trim(buf);
size_t len = strlen(s);
ssize_t n_written = write(fd_write, s, len);
if (n_written < 0 || n_written < len) {
return -EFAULT;
}
memset(buf, 0, BUF_SIZE);
size_t n;
struct custom_ds ds;
while ((n = recv_msg(fd_read, &ds)) > 0) {
len = ds.hdr.len;
n_written = fwrite(ds.msg, sizeof(ds.msg[0]), len, stdout);
if (n_written < 0 || n_written < len) {
return -EFAULT;
}
memset(buf, 0, sizeof(buf));
}
return 0;
}

View File

@ -1,45 +0,0 @@
#ifndef COMMON_H_
#define COMMON_H_
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define FIFO_SERVER_PATH "/tmp/fifo.to_server" // server reads this file
#define FIFO_CLIENT_PATH "/tmp/fifo.to_client" // client reads this file
#define __DATA_STRUCT_MSG_LEN 4096
enum ds_types {
DS_MESSAGE,
DS_FAIL
};
struct custom_ds {
struct {
long len;
long type;
} hdr;
char msg[__DATA_STRUCT_MSG_LEN];
};
static inline ssize_t send_msg(int fd, const struct custom_ds *ds)
{
return write(fd, ds, sizeof(ds->hdr) + ds->hdr.len);
}
static inline ssize_t recv_msg(int fd, struct custom_ds *reciever)
{
size_t n_hdr = read(fd, reciever, sizeof(reciever->hdr));
if (n_hdr == 0) return 0; // eof reached
if (n_hdr != sizeof(reciever->hdr)) return -ENOMSG;
if (reciever->hdr.len == 0) return 0;
size_t n_payload = read(fd, &reciever->msg, reciever->hdr.len);
if (n_payload != reciever->hdr.len) return -EFAULT;
return n_hdr + n_payload;
}
#define FAIL_ON(cond) if (cond) return EXIT_FAILURE
#endif

View File

@ -1,66 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "common.h"
int server(int fd_read, int fd_write);
int main()
{
FAIL_ON(mknod(FIFO_SERVER_PATH, S_IFIFO | 0666, 0) < 0);
printf("created %s\n", FIFO_SERVER_PATH);
FAIL_ON(mknod(FIFO_CLIENT_PATH, S_IFIFO | 0666, 0) < 0);
printf("created %s\n", FIFO_CLIENT_PATH);
int read_fd, write_fd;
FAIL_ON((read_fd=open(FIFO_SERVER_PATH, O_RDONLY)) < 0);
FAIL_ON((write_fd=open(FIFO_CLIENT_PATH, O_WRONLY)) < 0);
return server(read_fd, write_fd);
}
#define BUF_SIZE 256
/**
* Performs server actions
*
* @fd_read - pipe to read from
* @fd_write - pipe to write to
*
* returns 0 on success or -errno on fail
*/
int server(int fd_read, int fd_write)
{
char buf[BUF_SIZE] = {0};
size_t n = read(fd_read, buf, BUF_SIZE);
if (n == 0) return -EFAULT;
FILE *f = fopen(buf, "r");
// не 100% правильно, но наиболее вероятно
if (f == NULL) {
const char error_msg[] = "Can't open requested file\n";
struct custom_ds payload = {
.hdr.len = sizeof(error_msg),
.hdr.type = DS_FAIL,
};
strncpy(payload.msg, error_msg, sizeof(error_msg));
send_msg(fd_write, &payload);
goto epilog;
}
while ((n = fread(buf, sizeof(buf[0]), BUF_SIZE, f)) > 0) {
struct custom_ds payload = {
.hdr.len = n,
.hdr.type = DS_MESSAGE,
};
strncpy(payload.msg, buf, n);
send_msg(fd_write, &payload);
memset(buf, 0, sizeof(buf));
}
epilog:
fclose(f);
return 0;
}