works no files
This commit is contained in:
61
lab_6/Makefile
Normal file
61
lab_6/Makefile
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
CC = gcc
|
||||||
|
CFLAGS = -Wall -Wextra -std=c99 -g
|
||||||
|
LDFLAGS_IPC = -lrt -pthread
|
||||||
|
|
||||||
|
# SHM/SEM args: <shm_name> <sem_client_name> <sem_server_name> [iterations]
|
||||||
|
SHM_NAME = /myshm
|
||||||
|
SEM_CLIENT_NAME = /sem_client
|
||||||
|
SEM_SERVER_NAME = /sem_server
|
||||||
|
SERVER_ITERS = 3
|
||||||
|
|
||||||
|
all: shm
|
||||||
|
|
||||||
|
# ===== POSIX SHM + SEM targets =====
|
||||||
|
shm: server client
|
||||||
|
|
||||||
|
server: server.c
|
||||||
|
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS_IPC)
|
||||||
|
|
||||||
|
client: client.c
|
||||||
|
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS_IPC)
|
||||||
|
|
||||||
|
# ===== Tests =====
|
||||||
|
test_server: shm
|
||||||
|
@echo "=== Запуск сервера POSIX SHM+SEM ==="
|
||||||
|
@echo "В другом терминале выполните: make test_clients"
|
||||||
|
./server $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME) $(SERVER_ITERS)
|
||||||
|
|
||||||
|
test_clients: shm
|
||||||
|
@echo "=== Запуск нескольких клиентов ==="
|
||||||
|
echo "abacaba" | ./client $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME) & \
|
||||||
|
echo "xxxxxx" | ./client $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME) & \
|
||||||
|
echo "hello world" | ./client $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME) & \
|
||||||
|
wait
|
||||||
|
|
||||||
|
# Автотест: сервер в фоне, несколько клиентов
|
||||||
|
test_all: shm
|
||||||
|
@echo "=== Автотест POSIX SHM+SEM ==="
|
||||||
|
./server $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME) $(SERVER_ITERS) & \
|
||||||
|
SRV=$$!; \
|
||||||
|
sleep 1; \
|
||||||
|
echo "abacaba" | ./client $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME) & \
|
||||||
|
echo "kekekek" | ./client $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME) & \
|
||||||
|
echo "foobar" | ./client $(SHM_NAME) $(SEM_CLIENT_NAME) $(SEM_SERVER_NAME) & \
|
||||||
|
wait; \
|
||||||
|
wait $$SRV
|
||||||
|
|
||||||
|
# Очистка
|
||||||
|
clean:
|
||||||
|
@echo "Очистка..."
|
||||||
|
rm -f server client
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Available targets:"
|
||||||
|
@echo " shm - Build POSIX SHM+SEM programs"
|
||||||
|
@echo " test_server - Run SHM+SEM server (clients in another terminal)"
|
||||||
|
@echo " test_clients - Run several client processes"
|
||||||
|
@echo " test_all - Automatic end-to-end test"
|
||||||
|
@echo " clean - Remove built files"
|
||||||
|
@echo " help - Show this help"
|
||||||
|
|
||||||
|
.PHONY: all shm test_server test_clients test_all clean help
|
||||||
157
lab_6/client.c
Normal file
157
lab_6/client.c
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
// client.c
|
||||||
|
// Клиент POSIX IPC: подключается к существующей разделяемой памяти
|
||||||
|
// и именованным семафорам, передаёт строку серверу и получает результат. [web:8]
|
||||||
|
|
||||||
|
#include <stdio.h> // printf, fprintf, perror, 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
|
||||||
|
|
||||||
|
|
||||||
|
// Структура данных в разделяемой памяти, та же, что и у сервера.
|
||||||
|
typedef struct {
|
||||||
|
int has_data;
|
||||||
|
int result_code;
|
||||||
|
char buffer[SHM_BUFFER_SIZE];
|
||||||
|
} shared_block_t;
|
||||||
|
|
||||||
|
|
||||||
|
// Основная функция клиента.
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
// Ожидаем минимум три аргумента:
|
||||||
|
// argv[1] - имя объекта shared memory (должно совпадать с сервером).
|
||||||
|
// argv[2] - имя семафора клиента.
|
||||||
|
// argv[3] - имя семафора сервера.
|
||||||
|
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];
|
||||||
|
|
||||||
|
// Открываем уже существующий объект разделяемой памяти. [web:8]
|
||||||
|
int shm_fd = shm_open(shm_name, O_RDWR, 0);
|
||||||
|
if (shm_fd == -1) {
|
||||||
|
perror("shm_open");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отображаем его в адресное пространство процесса. [web:8]
|
||||||
|
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.
|
||||||
|
if (close(shm_fd) == -1) {
|
||||||
|
perror("close");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Открываем существующие именованные семафоры. [web:16]
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Читаем строку из stdin.
|
||||||
|
char input[SHM_BUFFER_SIZE];
|
||||||
|
if (fgets(input, sizeof(input), stdin) == NULL) {
|
||||||
|
// Ошибка чтения из stdin или EOF.
|
||||||
|
fprintf(stderr, "No input line provided\n");
|
||||||
|
sem_close(sem_client);
|
||||||
|
sem_close(sem_server);
|
||||||
|
munmap(shm_ptr, sizeof(shared_block_t));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем перевод строки в конце, если он есть.
|
||||||
|
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));
|
||||||
|
|
||||||
|
// Копируем строку, гарантируя наличие '\0'.
|
||||||
|
strncpy(shm_ptr->buffer, input, SHM_BUFFER_SIZE - 1);
|
||||||
|
shm_ptr->buffer[SHM_BUFFER_SIZE - 1] = '\0';
|
||||||
|
|
||||||
|
// Устанавливаем флаг наличия данных.
|
||||||
|
shm_ptr->has_data = 1;
|
||||||
|
|
||||||
|
// Передаём управление серверу: sem_post(sem_client). [web:16]
|
||||||
|
if (sem_post(sem_client) == -1) {
|
||||||
|
perror("sem_post(sem_client)");
|
||||||
|
sem_close(sem_client);
|
||||||
|
sem_close(sem_server);
|
||||||
|
munmap(shm_ptr, sizeof(shared_block_t));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ждём завершения обработки: sem_wait(sem_server). [web:16]
|
||||||
|
if (sem_wait(sem_server) == -1) {
|
||||||
|
perror("sem_wait(sem_server)");
|
||||||
|
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);
|
||||||
|
sem_close(sem_client);
|
||||||
|
sem_close(sem_server);
|
||||||
|
munmap(shm_ptr, sizeof(shared_block_t));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выводим обработанную строку на stdout.
|
||||||
|
printf("%s\n", shm_ptr->buffer);
|
||||||
|
|
||||||
|
// Закрываем семафоры и отсоединяемся от разделяемой памяти. [web:8]
|
||||||
|
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;
|
||||||
|
}
|
||||||
221
lab_6/server.c
Normal file
221
lab_6/server.c
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
// server.c
|
||||||
|
// Сервер POSIX IPC: разделяемая память + именованные семафоры.
|
||||||
|
// Обрабатывает строки по заданию варианта 12:
|
||||||
|
// "Заменить на пробелы все символы, совпадающие с первым символом в строке,
|
||||||
|
// кроме самого первого символа."
|
||||||
|
|
||||||
|
#include <stdio.h> // printf, fprintf, perror
|
||||||
|
#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> // mode_t, S_IRUSR, S_IWUSR
|
||||||
|
#include <semaphore.h> // sem_t, sem_open, sem_close, sem_unlink, sem_wait, sem_post
|
||||||
|
#include <unistd.h> // ftruncate, close
|
||||||
|
|
||||||
|
// Максимальная длина строки для обмена (включая '\0').
|
||||||
|
#define SHM_BUFFER_SIZE 1024
|
||||||
|
|
||||||
|
// Структура данных в разделяемой памяти.
|
||||||
|
typedef struct {
|
||||||
|
// Флаг наличия данных от клиента: 0 - нет, 1 - есть.
|
||||||
|
int has_data;
|
||||||
|
// Флаг, который сервер выставляет как код возврата:
|
||||||
|
// 0 - нормальное завершение обработки
|
||||||
|
// -1 - ошибка обработки (например, некорректные данные).
|
||||||
|
int result_code;
|
||||||
|
// Буфер для строки.
|
||||||
|
char buffer[SHM_BUFFER_SIZE];
|
||||||
|
} shared_block_t;
|
||||||
|
|
||||||
|
// Функция обработки строки по варианту 12.
|
||||||
|
static void process_line(char *s) {
|
||||||
|
// Если строка пустая или первый символ - '\0', делать нечего.
|
||||||
|
if (s == NULL || s[0] == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сохраняем первый символ строки.
|
||||||
|
char first = s[0];
|
||||||
|
|
||||||
|
// Идём по строке начиная со второго символа.
|
||||||
|
for (size_t i = 1; s[i] != '\0'; ++i) {
|
||||||
|
// Если текущий символ совпадает с первым, заменяем его на пробел.
|
||||||
|
if (s[i] == first) {
|
||||||
|
s[i] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Основная функция сервера.
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
// Ожидаем минимум два аргумента:
|
||||||
|
// argv[1] - имя объекта shared memory (например, "/myshm").
|
||||||
|
// argv[2] - имя семафора для клиента (например, "/sem_client").
|
||||||
|
// argv[3] - имя семафора для сервера (например, "/sem_server").
|
||||||
|
// Дополнительно можно передать количество итераций.
|
||||||
|
if (argc < 4) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: %s <shm_name> <sem_client_name> <sem_server_name> [iterations]\n",
|
||||||
|
argv[0]);
|
||||||
|
// -1 по заданию в случае ошибки.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Извлекаем имена объектов POSIX IPC.
|
||||||
|
const char *shm_name = argv[1];
|
||||||
|
const char *sem_client_name = argv[2];
|
||||||
|
const char *sem_server_name = argv[3];
|
||||||
|
|
||||||
|
// Количество итераций обработки (по умолчанию - бесконечно).
|
||||||
|
int iterations = -1;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаём или открываем объект разделяемой памяти.
|
||||||
|
// Используем флаги O_CREAT | O_EXCL | O_RDWR, чтобы сервер был тем,
|
||||||
|
// кто создаёт объект впервые. В случае существования - ошибка.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализируем структуру в разделяемой памяти.
|
||||||
|
shm_ptr->has_data = 0;
|
||||||
|
shm_ptr->result_code = 0;
|
||||||
|
memset(shm_ptr->buffer, 0, sizeof(shm_ptr->buffer));
|
||||||
|
|
||||||
|
// Закрываем дескриптор файла, отображение уже есть.
|
||||||
|
if (close(shm_fd) == -1) {
|
||||||
|
perror("close");
|
||||||
|
// Не выходим немедленно, поскольку отображение активно.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаём именованные семафоры. [web:16]
|
||||||
|
// sem_client: клиент делает sem_post, сервер делает sem_wait. [web:16]
|
||||||
|
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_server: сервер делает sem_post, клиент делает sem_wait. [web:16]
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Основной цикл обработки.
|
||||||
|
// Если iterations == -1, цикл бесконечный, прерывается по сигналу.
|
||||||
|
int processed_count = 0;
|
||||||
|
while (iterations < 0 || processed_count < iterations) {
|
||||||
|
// Ожидаем, пока клиент запишет строку и выполнит sem_post(sem_client). [web:16]
|
||||||
|
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 {
|
||||||
|
// Обрабатываем строку.
|
||||||
|
process_line(shm_ptr->buffer);
|
||||||
|
|
||||||
|
// В данном примере считаем, что обработка всегда успешна.
|
||||||
|
shm_ptr->result_code = 0;
|
||||||
|
|
||||||
|
// Сбрасываем флаг наличия необработанных данных.
|
||||||
|
shm_ptr->has_data = 0;
|
||||||
|
|
||||||
|
// Увеличиваем счётчик обработанных операций.
|
||||||
|
processed_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сообщаем клиенту, что результат готов. [web:16]
|
||||||
|
if (sem_post(sem_server) == -1) {
|
||||||
|
perror("sem_post(sem_server)");
|
||||||
|
processed_count = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выводим количество выполненных операций или -1 в случае ошибки.
|
||||||
|
if (processed_count >= 0) {
|
||||||
|
printf("%d\n", processed_count);
|
||||||
|
} else {
|
||||||
|
printf("-1\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Освобождаем ресурсы: закрываем и удаляем семафоры. [web:16]
|
||||||
|
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)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отсоединяем разделяемую память и удаляем объект. [web:8]
|
||||||
|
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