Compare commits
4 Commits
eea51b9f9c
...
02c4d80b5e
| Author | SHA1 | Date | |
|---|---|---|---|
| 02c4d80b5e | |||
| 27b7c6c923 | |||
| 8bf5a27880 | |||
| 43b29e59e7 |
62
lab_6/Makefile
Normal file
62
lab_6/Makefile
Normal file
@@ -0,0 +1,62 @@
|
||||
# Makefile для лабораторной: POSIX shared memory + POSIX semaphores. [file:21][web:47]
|
||||
# Сервер сам чистит старые IPC-объекты и корректно завершает работу. [file:21]
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -std=c99 -g
|
||||
LDFLAGS_IPC = -lrt -pthread
|
||||
|
||||
# Аргументы IPC: <shm_name> <sem_client_name> <sem_server_name> [iterations]. [file:21]
|
||||
SHM_NAME ?= /myshm
|
||||
SEM_CLIENT_NAME ?= /sem_client
|
||||
SEM_SERVER_NAME ?= /sem_server
|
||||
SERVER_ITERS ?= 1000
|
||||
|
||||
all: shm # Цель по умолчанию: сборка server и client. [file:22]
|
||||
|
||||
# ===== Сборка программ POSIX SHM + SEM =====
|
||||
shm: server client # Группа целей для IPC-программ. [file:21]
|
||||
|
||||
server: server.c
|
||||
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS_IPC) # Сборка сервера. [file:21][web:47]
|
||||
|
||||
client: client.c
|
||||
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS_IPC) # Сборка клиента. [file:21][web:47]
|
||||
|
||||
# ===== Тесты =====
|
||||
|
||||
# Ручной тест: сервер в одном терминале, клиент в другом. [file:22]
|
||||
test_server: shm
|
||||
@echo "=== Запуск сервера POSIX SHM+SEM ==="
|
||||
@echo "В другом терминале выполните: make test_client"
|
||||
./server $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME) $(SERVER_ITERS)
|
||||
|
||||
# Клиент: читает input.txt, выводит обработанные строки в stdout. [file:22]
|
||||
test_client: shm
|
||||
@echo "=== Запуск клиента, чтение input.txt, вывод на stdout ==="
|
||||
./client $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME)
|
||||
|
||||
# Автотест: сервер в фоне, клиент один раз читает input.txt. [file:22]
|
||||
test_all: shm
|
||||
@echo "=== Автотест POSIX SHM+SEM с файлами input.txt/output.txt ==="
|
||||
./server $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME) $(SERVER_ITERS) & \
|
||||
SRV=$$!; \
|
||||
sleep 1; \
|
||||
./client $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME); \
|
||||
wait $$SRV
|
||||
|
||||
# Очистка бинарников и файлов (IPC‑объекты чистит сам сервер). [file:21]
|
||||
clean:
|
||||
@echo "Очистка..."
|
||||
rm -f server client
|
||||
rm -f output.txt
|
||||
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo " shm - Build POSIX SHM+SEM programs"
|
||||
@echo " test_server - Run SHM+SEM server (client in another terminal)"
|
||||
@echo " test_client - Run client reading input.txt"
|
||||
@echo " test_all - Automatic end-to-end test with input.txt/output.txt"
|
||||
@echo " clean - Remove built files"
|
||||
@echo " help - Show this help"
|
||||
|
||||
.PHONY: all shm test_server test_client test_all clean help
|
||||
168
lab_6/client.c
Normal file
168
lab_6/client.c
Normal file
@@ -0,0 +1,168 @@
|
||||
// client.c
|
||||
// Клиент POSIX IPC: читает строки из input.txt, передаёт их серверу через
|
||||
// разделяемую память и выводит обработанные строки на stdout. [file:21][web:57]
|
||||
|
||||
#include <stdio.h> // printf, fprintf, perror, FILE, fopen, fclose, fgets [web:60]
|
||||
#include <stdlib.h> // exit, EXIT_FAILURE
|
||||
#include <string.h> // memset, strncpy, strlen
|
||||
#include <errno.h> // errno
|
||||
#include <fcntl.h> // O_RDWR [web:29]
|
||||
#include <sys/mman.h> // shm_open, mmap, munmap [web:47]
|
||||
#include <sys/stat.h> // mode_t
|
||||
#include <semaphore.h> // sem_t, sem_open, sem_close, sem_wait, sem_post [file:21]
|
||||
#include <unistd.h> // close
|
||||
|
||||
// Размер буфера обмена (должен совпадать с server.c). [file:21]
|
||||
#define SHM_BUFFER_SIZE 1024
|
||||
|
||||
// Та же структура, что и у сервера. [file:21]
|
||||
typedef struct {
|
||||
int has_data; // Флаг наличия данных от клиента. [file:21]
|
||||
int result_code; // Код результата обработки. [file:21]
|
||||
char buffer[SHM_BUFFER_SIZE]; // Буфер строки. [file:21]
|
||||
} shared_block_t;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Ожидаемые аргументы:
|
||||
// argv[1] - имя shared memory (должно совпадать с сервером). [file:21]
|
||||
// argv[2] - имя семафора клиента. [file:21]
|
||||
// argv[3] - имя семафора сервера. [file:21]
|
||||
if (argc < 4) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <shm_name> <sem_client_name> <sem_server_name>\n",
|
||||
argv[0]); // Сообщаем о правильном формате запуска. [file:22]
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *shm_name = argv[1];
|
||||
const char *sem_client_name = argv[2];
|
||||
const char *sem_server_name = argv[3];
|
||||
|
||||
// Открываем существующий объект shared memory, созданный сервером. [web:47]
|
||||
int shm_fd = shm_open(shm_name, O_RDWR, 0);
|
||||
if (shm_fd == -1) {
|
||||
perror("shm_open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Отображаем shared memory в адресное пространство клиента. [web:47]
|
||||
shared_block_t *shm_ptr = mmap(NULL,
|
||||
sizeof(shared_block_t),
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
shm_fd,
|
||||
0);
|
||||
if (shm_ptr == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
close(shm_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Дескриптор можно закрыть, mmap продолжает работать. [file:21]
|
||||
if (close(shm_fd) == -1) {
|
||||
perror("close");
|
||||
}
|
||||
|
||||
// Открываем именованные семафоры, которые создал сервер. [file:21][web:40]
|
||||
sem_t *sem_client = sem_open(sem_client_name, 0);
|
||||
if (sem_client == SEM_FAILED) {
|
||||
perror("sem_open(sem_client)");
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
return -1;
|
||||
}
|
||||
|
||||
sem_t *sem_server = sem_open(sem_server_name, 0);
|
||||
if (sem_server == SEM_FAILED) {
|
||||
perror("sem_open(sem_server)");
|
||||
sem_close(sem_client);
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Открываем файл input.txt для чтения исходных строк. [web:57]
|
||||
FILE *fin = fopen("input.txt", "r");
|
||||
if (!fin) {
|
||||
perror("fopen(input.txt)");
|
||||
sem_close(sem_client);
|
||||
sem_close(sem_server);
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
return -1;
|
||||
}
|
||||
|
||||
char input[SHM_BUFFER_SIZE]; // Локальный буфер для чтения строки. [file:21]
|
||||
|
||||
// Читаем input.txt построчно до конца файла. [web:51]
|
||||
while (fgets(input, sizeof(input), fin) != NULL) {
|
||||
size_t len = strlen(input); // Длина считанной строки. [web:54]
|
||||
|
||||
// Удаляем конечный '\n', если он есть. [web:54]
|
||||
if (len > 0 && input[len - 1] == '\n') {
|
||||
input[len - 1] = '\0';
|
||||
}
|
||||
|
||||
// Очищаем буфер в shared memory перед записью новой строки. [file:21]
|
||||
memset(shm_ptr->buffer, 0, sizeof(shm_ptr->buffer));
|
||||
|
||||
// Копируем строку из локального буфера в shared memory. [file:21]
|
||||
strncpy(shm_ptr->buffer, input, SHM_BUFFER_SIZE - 1);
|
||||
shm_ptr->buffer[SHM_BUFFER_SIZE - 1] = '\0';
|
||||
|
||||
// Устанавливаем флаг наличия данных для сервера. [file:21]
|
||||
shm_ptr->has_data = 1;
|
||||
|
||||
// Поднимаем семафор клиента, уведомляя сервер. [file:21][web:40]
|
||||
if (sem_post(sem_client) == -1) {
|
||||
perror("sem_post(sem_client)");
|
||||
fclose(fin);
|
||||
sem_close(sem_client);
|
||||
sem_close(sem_server);
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Ждём, пока сервер обработает строку. [file:21][web:40]
|
||||
if (sem_wait(sem_server) == -1) {
|
||||
perror("sem_wait(sem_server)");
|
||||
fclose(fin);
|
||||
sem_close(sem_client);
|
||||
sem_close(sem_server);
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Проверяем код результата, выставленный сервером. [file:21]
|
||||
if (shm_ptr->result_code != 0) {
|
||||
fprintf(stderr,
|
||||
"Server reported error, result_code = %d\n",
|
||||
shm_ptr->result_code);
|
||||
fclose(fin);
|
||||
sem_close(sem_client);
|
||||
sem_close(sem_server);
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Выводим обработанную строку на стандартный вывод. [file:22]
|
||||
printf("%s\n", shm_ptr->buffer);
|
||||
}
|
||||
|
||||
// Закрываем input.txt после окончания чтения. [web:57]
|
||||
if (fclose(fin) == EOF) {
|
||||
perror("fclose(input.txt)");
|
||||
}
|
||||
|
||||
// Закрываем семафоры. [file:21]
|
||||
if (sem_close(sem_client) == -1) {
|
||||
perror("sem_close(sem_client)");
|
||||
}
|
||||
if (sem_close(sem_server) == -1) {
|
||||
perror("sem_close(sem_server)");
|
||||
}
|
||||
|
||||
// Отсоединяем shared memory. [web:47]
|
||||
if (munmap(shm_ptr, sizeof(shared_block_t)) == -1) {
|
||||
perror("munmap");
|
||||
}
|
||||
|
||||
return 0; // Нормальное завершение клиента. [file:22]
|
||||
}
|
||||
6
lab_6/input.txt
Normal file
6
lab_6/input.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
abacaba
|
||||
xxxxxx
|
||||
hello
|
||||
aaaaa
|
||||
1abc1d1e1
|
||||
qwerty
|
||||
319
lab_6/server.c
Normal file
319
lab_6/server.c
Normal file
@@ -0,0 +1,319 @@
|
||||
// server.c
|
||||
// Сервер POSIX IPC: разделяемая память + именованные семафоры. [file:21]
|
||||
// - Сам удаляет "висячие" объекты (shm и семафоры) при старте. [file:21]
|
||||
// - Создаёт новую shared memory и новые именованные семафоры. [web:47][web:40]
|
||||
// - Обрабатывает строки по варианту 12 и пишет результат в shared memory и output.txt. [file:22]
|
||||
|
||||
#include <stdio.h> // printf, fprintf, perror, FILE, fopen, fclose, fprintf [web:60]
|
||||
#include <stdlib.h> // exit, EXIT_FAILURE, strtoul
|
||||
#include <string.h> // memset, strncpy, strlen
|
||||
#include <errno.h> // errno
|
||||
#include <fcntl.h> // O_CREAT, O_EXCL, O_RDWR [web:29]
|
||||
#include <sys/mman.h> // shm_open, mmap, munmap [web:47]
|
||||
#include <sys/stat.h> // mode_t, S_IRUSR, S_IWUSR [web:39]
|
||||
#include <semaphore.h> // sem_t, sem_open, sem_close, sem_unlink, sem_wait, sem_post [file:21]
|
||||
#include <unistd.h> // ftruncate, close [file:21]
|
||||
#include <signal.h> // signal, SIGINT, SIGTERM
|
||||
|
||||
// Максимальная длина строки (включая '\0') для обмена через shared memory. [file:21]
|
||||
#define SHM_BUFFER_SIZE 1024
|
||||
|
||||
// Структура в разделяемой памяти. [file:21]
|
||||
typedef struct {
|
||||
int has_data; // 0 - нет данных, 1 - клиент записал строку. [file:21]
|
||||
int result_code; // 0 - успех, -1 - ошибка обработки. [file:21]
|
||||
char buffer[SHM_BUFFER_SIZE]; // Буфер, в который клиент пишет, а сервер изменяет. [file:21]
|
||||
} shared_block_t;
|
||||
|
||||
// Глобальные указатели/имена для корректной очистки в обработчике сигналов. [file:21]
|
||||
static shared_block_t *g_shm_ptr = NULL; // Указатель на mmap(shared memory). [web:47]
|
||||
static char g_shm_name[256] = ""; // Имя объекта shared memory. [file:21]
|
||||
static char g_sem_client_name[256] = ""; // Имя семафора клиента. [file:21]
|
||||
static char g_sem_server_name[256] = ""; // Имя семафора сервера. [file:21]
|
||||
static sem_t *g_sem_client = NULL; // Дескриптор семафора клиента. [file:21]
|
||||
static sem_t *g_sem_server = NULL; // Дескриптор семафора сервера. [file:21]
|
||||
static FILE *g_fout = NULL; // Файл output.txt. [web:57]
|
||||
|
||||
// Обработчик сигналов для аккуратного завершения (Ctrl+C, kill). [file:21]
|
||||
static void handle_signal(int signo) {
|
||||
(void) signo; // Неиспользуемый параметр, подавляем предупреждения. [file:21]
|
||||
|
||||
// Пытаемся корректно закрыть семафоры, если они открыты. [file:21]
|
||||
if (g_sem_client) {
|
||||
sem_close(g_sem_client); // Закрываем дескриптор семафора клиента. [file:21]
|
||||
g_sem_client = NULL;
|
||||
}
|
||||
if (g_sem_server) {
|
||||
sem_close(g_sem_server); // Закрываем дескриптор семафора сервера. [file:21]
|
||||
g_sem_server = NULL;
|
||||
}
|
||||
|
||||
// Удаляем именованные семафоры по их именам, если они заданы. [file:21][web:40]
|
||||
if (g_sem_client_name[0] != '\0') {
|
||||
sem_unlink(g_sem_client_name); // Удаляем именованный семафор клиента. [web:40]
|
||||
}
|
||||
if (g_sem_server_name[0] != '\0') {
|
||||
sem_unlink(g_sem_server_name); // Удаляем именованный семафор сервера. [web:40]
|
||||
}
|
||||
|
||||
// Если есть активное отображение shared memory, отсоединяем его. [web:47]
|
||||
if (g_shm_ptr) {
|
||||
munmap(g_shm_ptr, sizeof(shared_block_t)); // Освобождаем виртуальную память. [web:47]
|
||||
g_shm_ptr = NULL;
|
||||
}
|
||||
|
||||
// Удаляем объект shared memory, если имя известно. [web:47]
|
||||
if (g_shm_name[0] != '\0') {
|
||||
shm_unlink(g_shm_name); // Удаляем объект POSIX shared memory. [web:47]
|
||||
}
|
||||
|
||||
// Закрываем файл вывода, если он открыт. [web:57]
|
||||
if (g_fout) {
|
||||
fclose(g_fout); // Закрываем output.txt. [web:57]
|
||||
g_fout = NULL;
|
||||
}
|
||||
|
||||
// Завершаем процесс. [file:21]
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
// Обработка строки по варианту 12. [file:22]
|
||||
static void process_line(char *s) {
|
||||
if (s == NULL || s[0] == '\0') {
|
||||
// Пустой буфер или пустая строка не обрабатываем. [file:21]
|
||||
return;
|
||||
}
|
||||
|
||||
char first = s[0]; // Сохраняем первый символ. [file:22]
|
||||
|
||||
// Проходим по строке начиная со второго символа. [file:22]
|
||||
for (size_t i = 1; s[i] != '\0'; ++i) {
|
||||
if (s[i] == first) {
|
||||
// Если символ совпадает с первым, заменяем его на пробел. [file:22]
|
||||
s[i] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Ожидаемые аргументы:
|
||||
// argv[1] - имя shared memory (например, "/myshm"). [file:21]
|
||||
// argv[2] - имя семафора клиента (например, "/sem_client"). [file:21]
|
||||
// argv[3] - имя семафора сервера (например, "/sem_server"). [file:21]
|
||||
// argv[4] - (опционально) число обрабатываемых строк. [file:22]
|
||||
if (argc < 4) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <shm_name> <sem_client_name> <sem_server_name> [iterations]\n",
|
||||
argv[0]); // Выводим подсказку по использованию. [file:22]
|
||||
return -1; // По заданию -1 при ошибке. [file:22]
|
||||
}
|
||||
|
||||
// Сохраняем имена в глобальные переменные для очистки в сигналах. [file:21]
|
||||
strncpy(g_shm_name, argv[1], sizeof(g_shm_name) - 1);
|
||||
strncpy(g_sem_client_name, argv[2], sizeof(g_sem_client_name) - 1);
|
||||
strncpy(g_sem_server_name, argv[3], sizeof(g_sem_server_name) - 1);
|
||||
g_shm_name[sizeof(g_shm_name) - 1] = '\0';
|
||||
g_sem_client_name[sizeof(g_sem_client_name) - 1] = '\0';
|
||||
g_sem_server_name[sizeof(g_sem_server_name) - 1] = '\0';
|
||||
|
||||
const char *shm_name = g_shm_name; // Удобные локальные псевдонимы. [file:21]
|
||||
const char *sem_client_name = g_sem_client_name;
|
||||
const char *sem_server_name = g_sem_server_name;
|
||||
|
||||
// Разбираем количество итераций (строк), если указано. [file:22]
|
||||
int iterations = -1; // -1 означает "работать до сигнала". [file:22]
|
||||
if (argc >= 5) {
|
||||
char *endptr = NULL;
|
||||
unsigned long tmp = strtoul(argv[4], &endptr, 10);
|
||||
if (endptr == argv[4] || *endptr != '\0') {
|
||||
fprintf(stderr, "Invalid iterations value: '%s'\n", argv[4]);
|
||||
return -1;
|
||||
}
|
||||
iterations = (int) tmp;
|
||||
}
|
||||
|
||||
// Настраиваем обработчики сигналов для аккуратного завершения. [file:21]
|
||||
signal(SIGINT, handle_signal); // Обработка Ctrl+C. [file:21]
|
||||
signal(SIGTERM, handle_signal); // Обработка внешнего kill. [file:21]
|
||||
|
||||
// Пытаемся удалить старые IPC-объекты, если они остались от прошлых запусков. [file:21]
|
||||
shm_unlink(shm_name); // Игнорируем ошибку, если не существует. [web:47]
|
||||
sem_unlink(sem_client_name); // То же для семафоров. [web:40]
|
||||
sem_unlink(sem_server_name);
|
||||
|
||||
// Создаём новый объект shared memory. [web:47]
|
||||
int shm_fd = shm_open(shm_name,
|
||||
O_CREAT | O_EXCL | O_RDWR,
|
||||
S_IRUSR | S_IWUSR);
|
||||
if (shm_fd == -1) {
|
||||
perror("shm_open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Устанавливаем размер shared memory под нашу структуру. [file:21]
|
||||
if (ftruncate(shm_fd, sizeof(shared_block_t)) == -1) {
|
||||
perror("ftruncate");
|
||||
close(shm_fd);
|
||||
shm_unlink(shm_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Отображаем shared memory в адресное пространство сервера. [web:47]
|
||||
g_shm_ptr = mmap(NULL,
|
||||
sizeof(shared_block_t),
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
shm_fd,
|
||||
0);
|
||||
if (g_shm_ptr == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
g_shm_ptr = NULL;
|
||||
close(shm_fd);
|
||||
shm_unlink(shm_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Закрываем файловый дескриптор, оставляем только mmap. [file:21]
|
||||
if (close(shm_fd) == -1) {
|
||||
perror("close");
|
||||
// Продолжаем работу, так как mmap уже есть. [file:21]
|
||||
}
|
||||
|
||||
// Инициализируем структуру в shared memory. [file:21]
|
||||
g_shm_ptr->has_data = 0;
|
||||
g_shm_ptr->result_code = 0;
|
||||
memset(g_shm_ptr->buffer, 0, sizeof(g_shm_ptr->buffer));
|
||||
|
||||
// Создаём именованный семафор клиента (клиент будет делать post, сервер - wait). [file:21][web:40]
|
||||
g_sem_client = sem_open(sem_client_name,
|
||||
O_CREAT | O_EXCL,
|
||||
S_IRUSR | S_IWUSR,
|
||||
0); // Начальное значение 0. [file:21]
|
||||
if (g_sem_client == SEM_FAILED) {
|
||||
perror("sem_open(sem_client)");
|
||||
g_sem_client = NULL;
|
||||
munmap(g_shm_ptr, sizeof(shared_block_t));
|
||||
g_shm_ptr = NULL;
|
||||
shm_unlink(shm_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Создаём именованный семафор сервера (сервер post, клиент wait). [file:21][web:40]
|
||||
g_sem_server = sem_open(sem_server_name,
|
||||
O_CREAT | O_EXCL,
|
||||
S_IRUSR | S_IWUSR,
|
||||
0); // Начальное значение 0. [file:21]
|
||||
if (g_sem_server == SEM_FAILED) {
|
||||
perror("sem_open(sem_server)");
|
||||
sem_close(g_sem_client);
|
||||
sem_unlink(sem_client_name);
|
||||
g_sem_client = NULL;
|
||||
munmap(g_shm_ptr, sizeof(shared_block_t));
|
||||
g_shm_ptr = NULL;
|
||||
shm_unlink(shm_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Открываем файл вывода для записи всех обработанных строк. [web:57]
|
||||
g_fout = fopen("output.txt", "w");
|
||||
if (!g_fout) {
|
||||
perror("fopen(output.txt)");
|
||||
// Если файл не открылся, продолжаем работать только через shared memory. [file:21]
|
||||
}
|
||||
|
||||
int processed_count = 0; // Счётчик успешно обработанных строк. [file:22]
|
||||
|
||||
// Основной цикл обработки запросов от клиента. [file:22]
|
||||
while (iterations < 0 || processed_count < iterations) {
|
||||
// Ждём, пока клиент поднимет семафор клиента. [file:21][web:40]
|
||||
if (sem_wait(g_sem_client) == -1) {
|
||||
perror("sem_wait(sem_client)");
|
||||
processed_count = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Проверяем, что клиент установил флаг has_data. [file:21]
|
||||
if (!g_shm_ptr->has_data) {
|
||||
fprintf(stderr,
|
||||
"Warning: sem_client posted, but has_data == 0\n");
|
||||
g_shm_ptr->result_code = -1;
|
||||
} else {
|
||||
// Обрабатываем строку по варианту 12. [file:22]
|
||||
process_line(g_shm_ptr->buffer);
|
||||
|
||||
// Сохраняем код успешной обработки. [file:21]
|
||||
g_shm_ptr->result_code = 0;
|
||||
|
||||
// Если файл открыт, дублируем результат в output.txt. [web:57]
|
||||
if (g_fout) {
|
||||
fprintf(g_fout, "%s\n", g_shm_ptr->buffer);
|
||||
fflush(g_fout);
|
||||
}
|
||||
|
||||
// Сбрасываем флаг наличия данных. [file:21]
|
||||
g_shm_ptr->has_data = 0;
|
||||
|
||||
// Увеличиваем счётчик обработанных строк. [file:22]
|
||||
processed_count++;
|
||||
}
|
||||
|
||||
// Сигнализируем клиенту, что результат готов. [file:21][web:40]
|
||||
if (sem_post(g_sem_server) == -1) {
|
||||
perror("sem_post(sem_server)");
|
||||
processed_count = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Если файл вывода был открыт, закрываем его. [web:57]
|
||||
if (g_fout) {
|
||||
if (fclose(g_fout) == EOF) {
|
||||
perror("fclose(output.txt)");
|
||||
}
|
||||
g_fout = NULL;
|
||||
}
|
||||
|
||||
// Выводим количество выполненных операций или -1 при ошибке. [file:22]
|
||||
if (processed_count >= 0) {
|
||||
printf("%d\n", processed_count);
|
||||
} else {
|
||||
printf("-1\n");
|
||||
}
|
||||
|
||||
// Корректно закрываем семафоры. [file:21]
|
||||
if (g_sem_client) {
|
||||
if (sem_close(g_sem_client) == -1) {
|
||||
perror("sem_close(sem_client)");
|
||||
}
|
||||
g_sem_client = NULL;
|
||||
}
|
||||
if (g_sem_server) {
|
||||
if (sem_close(g_sem_server) == -1) {
|
||||
perror("sem_close(sem_server)");
|
||||
}
|
||||
g_sem_server = NULL;
|
||||
}
|
||||
|
||||
// Удаляем именованные семафоры. [file:21][web:40]
|
||||
if (sem_unlink(sem_client_name) == -1) {
|
||||
perror("sem_unlink(sem_client)");
|
||||
}
|
||||
if (sem_unlink(sem_server_name) == -1) {
|
||||
perror("sem_unlink(sem_server)");
|
||||
}
|
||||
|
||||
// Отсоединяем shared memory. [web:47]
|
||||
if (g_shm_ptr) {
|
||||
if (munmap(g_shm_ptr, sizeof(shared_block_t)) == -1) {
|
||||
perror("munmap");
|
||||
}
|
||||
g_shm_ptr = NULL;
|
||||
}
|
||||
|
||||
// Удаляем объект shared memory. [web:47]
|
||||
if (shm_unlink(shm_name) == -1) {
|
||||
perror("shm_unlink");
|
||||
}
|
||||
|
||||
return 0; // Нормальное завершение сервера. [file:22]
|
||||
}
|
||||
51
lab_6/vlad/Makefile
Normal file
51
lab_6/vlad/Makefile
Normal file
@@ -0,0 +1,51 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -std=c99 -g
|
||||
LDFLAGS_IPC = -lrt -pthread
|
||||
|
||||
SHM_NAME ?= /myshm
|
||||
SEM_CLIENT_NAME ?= /sem_client
|
||||
SEM_SERVER_NAME ?= /sem_server
|
||||
SERVER_ITERS ?= 1000
|
||||
|
||||
all: shm
|
||||
|
||||
shm: server client
|
||||
|
||||
server: server.c
|
||||
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS_IPC)
|
||||
|
||||
client: client.c
|
||||
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS_IPC)
|
||||
|
||||
test_server: shm
|
||||
@echo "=== Запуск сервера POSIX SHM+SEM ==="
|
||||
@echo "В другом терминале выполните: make test_client"
|
||||
./server $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME) $(SERVER_ITERS)
|
||||
|
||||
test_client: shm
|
||||
@echo "=== Запуск клиента, чтение input.txt, вывод на stdout ==="
|
||||
./client $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME)
|
||||
|
||||
test_all: shm
|
||||
@echo "=== Автотест POSIX SHM+SEM с файлами input.txt/output.txt ==="
|
||||
./server $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME) $(SERVER_ITERS) & \
|
||||
SRV=$$!; \
|
||||
sleep 1; \
|
||||
./client $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME); \
|
||||
wait $$SRV
|
||||
|
||||
clean:
|
||||
@echo "Очистка..."
|
||||
rm -f server client
|
||||
rm -f output.txt
|
||||
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo " shm - Build POSIX SHM+SEM programs"
|
||||
@echo " test_server - Run SHM+SEM server (client in another terminal)"
|
||||
@echo " test_client - Run client reading input.txt"
|
||||
@echo " test_all - Automatic end-to-end test with input.txt/output.txt"
|
||||
@echo " clean - Remove built files"
|
||||
@echo " help - Show this help"
|
||||
|
||||
.PHONY: all shm test_server test_client test_all clean help
|
||||
147
lab_6/vlad/client.c
Normal file
147
lab_6/vlad/client.c
Normal file
@@ -0,0 +1,147 @@
|
||||
// client.c
|
||||
// Клиент: читает строки из input.txt, передаёт их серверу через shared memory,
|
||||
// ожидает обработку и печатает результат на stdout. [file:21][file:22]
|
||||
|
||||
#include <stdio.h> // printf, fprintf, perror, FILE, fopen, fclose, fgets
|
||||
#include <stdlib.h> // exit, EXIT_FAILURE
|
||||
#include <string.h> // memset, strncpy, strlen
|
||||
#include <errno.h> // errno
|
||||
#include <fcntl.h> // O_RDWR
|
||||
#include <sys/mman.h> // shm_open, mmap, munmap
|
||||
#include <sys/stat.h> // mode_t
|
||||
#include <semaphore.h> // sem_t, sem_open, sem_close, sem_wait, sem_post
|
||||
#include <unistd.h> // close
|
||||
|
||||
#define SHM_BUFFER_SIZE 1024 // Должен совпадать с server.c. [file:21]
|
||||
|
||||
typedef struct {
|
||||
int has_data; // Флаг наличия данных. [file:21]
|
||||
int result_code; // Код результата обработки. [file:21]
|
||||
char buffer[SHM_BUFFER_SIZE]; // Буфер строки. [file:21]
|
||||
} shared_block_t;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 4) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <shm_name> <sem_client_name> <sem_server_name>\n",
|
||||
argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *shm_name = argv[1];
|
||||
const char *sem_client_name = argv[2];
|
||||
const char *sem_server_name = argv[3];
|
||||
|
||||
// Открываем существующий shm. [file:21]
|
||||
int shm_fd = shm_open(shm_name, O_RDWR, 0);
|
||||
if (shm_fd == -1) {
|
||||
perror("shm_open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
shared_block_t *shm_ptr = mmap(NULL,
|
||||
sizeof(shared_block_t),
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
shm_fd,
|
||||
0);
|
||||
if (shm_ptr == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
close(shm_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (close(shm_fd) == -1) {
|
||||
perror("close");
|
||||
}
|
||||
|
||||
// Открываем семафоры. [file:21]
|
||||
sem_t *sem_client = sem_open(sem_client_name, 0);
|
||||
if (sem_client == SEM_FAILED) {
|
||||
perror("sem_open(sem_client)");
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
return -1;
|
||||
}
|
||||
|
||||
sem_t *sem_server = sem_open(sem_server_name, 0);
|
||||
if (sem_server == SEM_FAILED) {
|
||||
perror("sem_open(sem_server)");
|
||||
sem_close(sem_client);
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Открываем input.txt. [file:22]
|
||||
FILE *fin = fopen("input.txt", "r");
|
||||
if (!fin) {
|
||||
perror("fopen(input.txt)");
|
||||
sem_close(sem_client);
|
||||
sem_close(sem_server);
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
return -1;
|
||||
}
|
||||
|
||||
char input[SHM_BUFFER_SIZE];
|
||||
|
||||
while (fgets(input, sizeof(input), fin) != NULL) {
|
||||
size_t len = strlen(input);
|
||||
|
||||
if (len > 0 && input[len - 1] == '\n') {
|
||||
input[len - 1] = '\0';
|
||||
}
|
||||
|
||||
memset(shm_ptr->buffer, 0, sizeof(shm_ptr->buffer));
|
||||
strncpy(shm_ptr->buffer, input, SHM_BUFFER_SIZE - 1);
|
||||
shm_ptr->buffer[SHM_BUFFER_SIZE - 1] = '\0';
|
||||
|
||||
shm_ptr->has_data = 1;
|
||||
|
||||
if (sem_post(sem_client) == -1) {
|
||||
perror("sem_post(sem_client)");
|
||||
fclose(fin);
|
||||
sem_close(sem_client);
|
||||
sem_close(sem_server);
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sem_wait(sem_server) == -1) {
|
||||
perror("sem_wait(sem_server)");
|
||||
fclose(fin);
|
||||
sem_close(sem_client);
|
||||
sem_close(sem_server);
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (shm_ptr->result_code != 0) {
|
||||
fprintf(stderr,
|
||||
"Server reported error, result_code = %d\n",
|
||||
shm_ptr->result_code);
|
||||
fclose(fin);
|
||||
sem_close(sem_client);
|
||||
sem_close(sem_server);
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s\n", shm_ptr->buffer);
|
||||
}
|
||||
|
||||
if (fclose(fin) == EOF) {
|
||||
perror("fclose(input.txt)");
|
||||
}
|
||||
|
||||
if (sem_close(sem_client) == -1) {
|
||||
perror("sem_close(sem_client)");
|
||||
}
|
||||
if (sem_close(sem_server) == -1) {
|
||||
perror("sem_close(sem_server)");
|
||||
}
|
||||
|
||||
if (munmap(shm_ptr, sizeof(shared_block_t)) == -1) {
|
||||
perror("munmap");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
6
lab_6/vlad/input.txt
Normal file
6
lab_6/vlad/input.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
abacaba
|
||||
xxxxxx
|
||||
hello
|
||||
aaaaa
|
||||
1abc1d1e1
|
||||
qwerty
|
||||
210
lab_6/vlad/server.c
Normal file
210
lab_6/vlad/server.c
Normal file
@@ -0,0 +1,210 @@
|
||||
// server.c
|
||||
// Сервер POSIX IPC: разделяемая память + именованные семафоры. [file:21]
|
||||
// Задача: во всех парах одинаковых соседних символов второй символ заменить на пробел. [file:22]
|
||||
|
||||
#include <stdio.h> // printf, fprintf, perror, FILE, fopen, fclose, fprintf
|
||||
#include <stdlib.h> // exit, EXIT_FAILURE, strtoul
|
||||
#include <string.h> // memset, strncpy, strlen
|
||||
#include <errno.h> // errno
|
||||
#include <fcntl.h> // O_CREAT, O_EXCL, O_RDWR
|
||||
#include <sys/mman.h> // shm_open, mmap, munmap
|
||||
#include <sys/stat.h> // S_IRUSR, S_IWUSR
|
||||
#include <semaphore.h> // sem_t, sem_open, sem_close, sem_unlink, sem_wait, sem_post
|
||||
#include <unistd.h> // ftruncate, close
|
||||
|
||||
#define SHM_BUFFER_SIZE 1024 // Максимальный размер строки в shared memory. [file:21]
|
||||
|
||||
// Структура разделяемой памяти. [file:21]
|
||||
typedef struct {
|
||||
int has_data; // 1, если клиент записал строку. [file:21]
|
||||
int result_code; // 0 - успех, -1 - ошибка. [file:21]
|
||||
char buffer[SHM_BUFFER_SIZE]; // Буфер для строки. [file:21]
|
||||
} shared_block_t;
|
||||
|
||||
// Обработка строки по новому заданию: [file:22]
|
||||
// "Во всех парах одинаковых символов второй символ заменить на пробел".
|
||||
static void process_line(char *s) {
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; s[i] != '\0'; ++i) {
|
||||
// Идём по всей строке. [file:22]
|
||||
if (s[i] != '\0' && s[i + 1] != '\0' && s[i] == s[i + 1]) {
|
||||
// Если два соседних символа равны, второй заменяем на пробел. [file:22]
|
||||
s[i + 1] = ' ';
|
||||
// Можно сдвинуться дальше, чтобы не склеивать новые пары искусственно. [file:22]
|
||||
// Но по условию достаточно просто заменить второй символ.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// argv[1] - имя shm, argv[2] - имя sem_client, argv[3] - имя sem_server, argv[4] - итерации. [file:22]
|
||||
if (argc < 4) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <shm_name> <sem_client_name> <sem_server_name> [iterations]\n",
|
||||
argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *shm_name = argv[1];
|
||||
const char *sem_client_name = argv[2];
|
||||
const char *sem_server_name = argv[3];
|
||||
|
||||
int iterations = -1; // -1 = работать до сигнала/внешнего завершения. [file:22]
|
||||
if (argc >= 5) {
|
||||
char *endptr = NULL;
|
||||
unsigned long tmp = strtoul(argv[4], &endptr, 10);
|
||||
if (endptr == argv[4] || *endptr != '\0') {
|
||||
fprintf(stderr, "Invalid iterations value: '%s'\n", argv[4]);
|
||||
return -1;
|
||||
}
|
||||
iterations = (int) tmp;
|
||||
}
|
||||
|
||||
// Простая зачистка возможных старых IPC-объектов. [file:21]
|
||||
shm_unlink(shm_name);
|
||||
sem_unlink(sem_client_name);
|
||||
sem_unlink(sem_server_name);
|
||||
|
||||
// Создаём shared memory. [file:21]
|
||||
int shm_fd = shm_open(shm_name,
|
||||
O_CREAT | O_EXCL | O_RDWR,
|
||||
S_IRUSR | S_IWUSR);
|
||||
if (shm_fd == -1) {
|
||||
perror("shm_open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ftruncate(shm_fd, sizeof(shared_block_t)) == -1) {
|
||||
perror("ftruncate");
|
||||
close(shm_fd);
|
||||
shm_unlink(shm_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
shared_block_t *shm_ptr = mmap(NULL,
|
||||
sizeof(shared_block_t),
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
shm_fd,
|
||||
0);
|
||||
if (shm_ptr == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
close(shm_fd);
|
||||
shm_unlink(shm_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (close(shm_fd) == -1) {
|
||||
perror("close");
|
||||
}
|
||||
|
||||
// Инициализация shared memory. [file:21]
|
||||
shm_ptr->has_data = 0;
|
||||
shm_ptr->result_code = 0;
|
||||
memset(shm_ptr->buffer, 0, sizeof(shm_ptr->buffer));
|
||||
|
||||
// Создаём семафоры. [file:21]
|
||||
sem_t *sem_client = sem_open(sem_client_name,
|
||||
O_CREAT | O_EXCL,
|
||||
S_IRUSR | S_IWUSR,
|
||||
0);
|
||||
if (sem_client == SEM_FAILED) {
|
||||
perror("sem_open(sem_client)");
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
shm_unlink(shm_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sem_t *sem_server = sem_open(sem_server_name,
|
||||
O_CREAT | O_EXCL,
|
||||
S_IRUSR | S_IWUSR,
|
||||
0);
|
||||
if (sem_server == SEM_FAILED) {
|
||||
perror("sem_open(sem_server)");
|
||||
sem_close(sem_client);
|
||||
sem_unlink(sem_client_name);
|
||||
munmap(shm_ptr, sizeof(shared_block_t));
|
||||
shm_unlink(shm_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Открываем файл вывода. [file:22]
|
||||
FILE *fout = fopen("output.txt", "w");
|
||||
if (!fout) {
|
||||
perror("fopen(output.txt)");
|
||||
// Продолжаем работать только через shared memory. [file:21]
|
||||
}
|
||||
|
||||
int processed_count = 0; // Сколько строк обработано. [file:22]
|
||||
|
||||
while (iterations < 0 || processed_count < iterations) {
|
||||
// Ждём строку от клиента. [file:21]
|
||||
if (sem_wait(sem_client) == -1) {
|
||||
perror("sem_wait(sem_client)");
|
||||
processed_count = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!shm_ptr->has_data) {
|
||||
fprintf(stderr, "Warning: sem_client posted, but has_data == 0\n");
|
||||
shm_ptr->result_code = -1;
|
||||
} else {
|
||||
// Обрабатываем строку по новому заданию. [file:22]
|
||||
process_line(shm_ptr->buffer);
|
||||
|
||||
shm_ptr->result_code = 0;
|
||||
shm_ptr->has_data = 0;
|
||||
processed_count++;
|
||||
|
||||
if (fout) {
|
||||
fprintf(fout, "%s\n", shm_ptr->buffer);
|
||||
fflush(fout);
|
||||
}
|
||||
}
|
||||
|
||||
// Сообщаем клиенту, что результат готов. [file:21]
|
||||
if (sem_post(sem_server) == -1) {
|
||||
perror("sem_post(sem_server)");
|
||||
processed_count = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fout && fclose(fout) == EOF) {
|
||||
perror("fclose(output.txt)");
|
||||
}
|
||||
|
||||
// Выводим число операций или -1. [file:22]
|
||||
if (processed_count >= 0) {
|
||||
printf("%d\n", processed_count);
|
||||
} else {
|
||||
printf("-1\n");
|
||||
}
|
||||
|
||||
// Очистка IPC-объектов. [file:21]
|
||||
if (sem_close(sem_client) == -1) {
|
||||
perror("sem_close(sem_client)");
|
||||
}
|
||||
if (sem_close(sem_server) == -1) {
|
||||
perror("sem_close(sem_server)");
|
||||
}
|
||||
|
||||
if (sem_unlink(sem_client_name) == -1) {
|
||||
perror("sem_unlink(sem_client)");
|
||||
}
|
||||
if (sem_unlink(sem_server_name) == -1) {
|
||||
perror("sem_unlink(sem_server)");
|
||||
}
|
||||
|
||||
if (munmap(shm_ptr, sizeof(shared_block_t)) == -1) {
|
||||
perror("munmap");
|
||||
}
|
||||
if (shm_unlink(shm_name) == -1) {
|
||||
perror("shm_unlink");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user