7 Commits

17 changed files with 909 additions and 0 deletions

View File

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

@ -0,0 +1,35 @@
# Виртуальная память
Прошу обратить внимание на файлики 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` - запишет бинарный вывод в файл, в противном случае выдаст хексы в стандартный вывод

222
OSs/lab-virt-win/main.c Normal file
View File

@ -0,0 +1,222 @@
#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;
}

101
OSs/lab-virt-win/simasm.py Normal file
View File

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

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

20
OSs/lab-virt-win/util.py Normal file
View File

@ -0,0 +1,20 @@
#/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:]

BIN
OSs/lab-virt-win/vm.bin Normal file

Binary file not shown.

12
OSs/lab4/part-1/Makefile Normal file
View File

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

99
OSs/lab4/part-1/main.c Normal file
View File

@ -0,0 +1,99 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <time.h>
#include <signal.h>
#include <semaphore.h>
#include <errno.h>
enum events{
EVENT_VICTIM_THREAD_READY,
EVENT_PRINT_BUFFER_UPDATED,
EVENTS_NR
};
enum threads_roles {
ROLE_VICTIM,
ROLE_KILLER,
ROLE_PRINTER,
ROLES_NR
};
#define ERR_PTR(ret) ((void*)(long)ret)
#define PRINTER_TIMEOUT_SECS 2
#define PRINT_BUFF_SIZE 256
static sem_t sems[EVENTS_NR];
static pthread_t threads[ROLES_NR];
static char print_buffer[PRINT_BUFF_SIZE];
static volatile bool run = true;
void sig_handler(int signal)
{
snprintf(print_buffer, PRINT_BUFF_SIZE, "signal %d recieved", signal);
pthread_exit(ERR_PTR(sem_post(&sems[EVENT_PRINT_BUFFER_UPDATED])));
}
void *killer_thread(void* _)
{
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGUSR1);
if (pthread_sigmask(SIG_BLOCK, &ss, NULL))
pthread_exit(ERR_PTR(-EFAULT));
int ret = sem_wait(&sems[EVENT_VICTIM_THREAD_READY]);
if (ret) pthread_exit(ERR_PTR(ret));
pthread_exit(ERR_PTR(pthread_kill(threads[ROLE_VICTIM], SIGUSR1)));
}
void *victim_thread(void* _)
{
int ret = sem_post(&sems[EVENT_VICTIM_THREAD_READY]);
if (ret) pthread_exit(ERR_PTR(ret));
volatile int x = 0; // for compiler not to optimize infinite loop
while (run) {
x++;
}
pthread_exit(NULL);
}
void *printer_thread(void* _)
{
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGUSR1);
if (pthread_sigmask(SIG_BLOCK, &ss, NULL)) pthread_exit(ERR_PTR(-EFAULT));
while (run) {
struct timespec ts = {0};
int ret = timespec_get(&ts, TIME_UTC);
if (ret != TIME_UTC) pthread_exit(ERR_PTR(-EFAULT));
ts.tv_sec += PRINTER_TIMEOUT_SECS;
if (sem_timedwait(&sems[EVENT_PRINT_BUFFER_UPDATED], &ts))
pthread_exit(ERR_PTR(-EFAULT));
puts(print_buffer);
memset(print_buffer, 0, PRINT_BUFF_SIZE);
}
pthread_exit(NULL);
}
#define DO_OR_FAIL(cond) if (cond) return EXIT_FAILURE
int main()
{
struct sigaction sa = {
.sa_handler = sig_handler,
};
DO_OR_FAIL(sigaction(SIGUSR1, &sa, NULL));
DO_OR_FAIL(sem_init(&sems[EVENT_VICTIM_THREAD_READY], 0, 0));
DO_OR_FAIL(sem_init(&sems[EVENT_PRINT_BUFFER_UPDATED], 0, 0));
DO_OR_FAIL(pthread_create(&threads[ROLE_KILLER], NULL, killer_thread, NULL));
DO_OR_FAIL(pthread_create(&threads[ROLE_PRINTER], NULL, printer_thread, NULL));
DO_OR_FAIL(pthread_create(&threads[ROLE_VICTIM], NULL, victim_thread, NULL));
for (size_t i = 0; i < ROLES_NR; i++) {
DO_OR_FAIL(pthread_join(threads[i], NULL));
}
return EXIT_SUCCESS;
}

12
OSs/lab4/part-2/Makefile Normal file
View File

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

43
OSs/lab4/part-2/main.c Normal file
View File

@ -0,0 +1,43 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#define SLEEP_TIME 1
volatile int x;
void sigint_handler(int signal)
{
puts("sigint handler");
sleep(SLEEP_TIME);
}
void sigusr1_handler(int signal)
{
puts("sigusr1 handler");
kill(getpid(), SIGINT);
}
#define DO_OR_FAIL(cond) if (cond) return EXIT_FAILURE
int main()
{
struct sigaction sa_int = {
.sa_handler = sigint_handler
};
struct sigaction sa_usr1 = {
.sa_handler = sigusr1_handler
};
struct sigaction old_sa = {0};
DO_OR_FAIL(sigemptyset(&sa_int.sa_mask));
DO_OR_FAIL(sigemptyset(&sa_usr1.sa_mask));
DO_OR_FAIL(sigaddset(&sa_int.sa_mask, SIGINT));
DO_OR_FAIL(sigaddset(&sa_usr1.sa_mask, SIGUSR1));
DO_OR_FAIL(sigaction(SIGINT, &sa_int, &old_sa));
DO_OR_FAIL(sigaction(SIGUSR1, &sa_usr1, NULL));
while (true) {
x++; // чтобы не соптимизировался
}
return EXIT_SUCCESS;
}

12
OSs/lab5/part-A/Makefile Normal file
View File

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

132
OSs/lab5/part-A/main.c Normal file
View File

@ -0,0 +1,132 @@
#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;
}

15
OSs/lab5/part-B/Makefile Normal file
View File

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

80
OSs/lab5/part-B/client.c Normal file
View File

@ -0,0 +1,80 @@
#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;
}

45
OSs/lab5/part-B/common.h Normal file
View File

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

66
OSs/lab5/part-B/server.c Normal file
View File

@ -0,0 +1,66 @@
#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;
}