287 lines
10 KiB
C
287 lines
10 KiB
C
// Сервер POSIX IPC: использует POSIX shared memory (shm_open + mmap)
|
||
// и именованные POSIX семафоры (sem_open, sem_wait, sem_post) для обмена с клиентом.
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
#include <fcntl.h> // O_CREAT, O_EXCL, O_RDWR для shm_open
|
||
#include <sys/mman.h> // shm_open, mmap, munmap — POSIX разделяемая память
|
||
#include <sys/stat.h> // Права доступа к объектам POSIX shared memory и семафорам
|
||
#include <semaphore.h> // POSIX именованные семафоры: sem_open, sem_close, sem_unlink, sem_wait, sem_post
|
||
#include <unistd.h> // ftruncate, close
|
||
#include <signal.h>
|
||
|
||
// Максимальная длина строки (включая '\0') для обмена через shared memory.
|
||
#define SHM_BUFFER_SIZE 1024
|
||
|
||
// Структура расположена целиком в POSIX shared memory и разделяется сервером и клиентом.
|
||
typedef struct {
|
||
int has_data;
|
||
int result_code;
|
||
char buffer[SHM_BUFFER_SIZE];
|
||
} shared_block_t;
|
||
|
||
// Глобальные объекты, связанные с POSIX shared memory и семафорами.
|
||
static shared_block_t *g_shm_ptr = NULL; // Указатель на mmap(shared memory).
|
||
static char g_shm_name[256] = ""; // Имя объекта POSIX shared memory (для shm_unlink).
|
||
static char g_sem_client_name[256] = ""; // Имя именованного семафора клиента (sem_open/sem_unlink).
|
||
static char g_sem_server_name[256] = ""; // Имя именованного семафора сервера (sem_open/sem_unlink).
|
||
static sem_t *g_sem_client = NULL; // Дескриптор POSIX семафора клиента.
|
||
static sem_t *g_sem_server = NULL; // Дескриптор POSIX семафора сервера.
|
||
static FILE *g_fout = NULL;
|
||
|
||
// Обработчик сигналов завершения: корректно удаляет POSIX shared memory и семафоры.
|
||
static void handle_signal(int signo) {
|
||
(void) signo;
|
||
|
||
if (g_sem_client) {
|
||
sem_close(g_sem_client);
|
||
g_sem_client = NULL;
|
||
}
|
||
if (g_sem_server) {
|
||
sem_close(g_sem_server);
|
||
g_sem_server = NULL;
|
||
}
|
||
|
||
// sem_unlink удаляет именованные POSIX семафоры из системы.
|
||
if (g_sem_client_name[0] != '\0') {
|
||
sem_unlink(g_sem_client_name);
|
||
}
|
||
if (g_sem_server_name[0] != '\0') {
|
||
sem_unlink(g_sem_server_name);
|
||
}
|
||
|
||
// munmap отсоединяет область POSIX shared memory из адресного пространства процесса.
|
||
if (g_shm_ptr) {
|
||
munmap(g_shm_ptr, sizeof(shared_block_t));
|
||
g_shm_ptr = NULL;
|
||
}
|
||
|
||
// shm_unlink удаляет объект POSIX shared memory.
|
||
if (g_shm_name[0] != '\0') {
|
||
shm_unlink(g_shm_name);
|
||
}
|
||
|
||
if (g_fout) {
|
||
fclose(g_fout);
|
||
g_fout = NULL;
|
||
}
|
||
|
||
_exit(0);
|
||
}
|
||
|
||
static void process_line(char *s) {
|
||
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] - имя POSIX 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]);
|
||
return -1;
|
||
}
|
||
|
||
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;
|
||
const char *sem_client_name = g_sem_client_name;
|
||
const char *sem_server_name = g_sem_server_name;
|
||
|
||
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;
|
||
}
|
||
|
||
signal(SIGINT, handle_signal);
|
||
signal(SIGTERM, handle_signal);
|
||
|
||
// Удаляем старые объекты POSIX shared memory и семафоров, если они остались от прошлых запусков.
|
||
shm_unlink(shm_name);
|
||
sem_unlink(sem_client_name);
|
||
sem_unlink(sem_server_name);
|
||
|
||
// Создаём новый объект POSIX shared memory (shm_open с 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;
|
||
}
|
||
|
||
// ftruncate задаёт размер объекта POSIX shared memory под нашу структуру.
|
||
if (ftruncate(shm_fd, sizeof(shared_block_t)) == -1) {
|
||
perror("ftruncate");
|
||
close(shm_fd);
|
||
shm_unlink(shm_name);
|
||
return -1;
|
||
}
|
||
|
||
// Отображаем POSIX shared memory в адресное пространство сервера (mmap с MAP_SHARED).
|
||
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 файловый дескриптор POSIX shared memory можно закрыть.
|
||
if (close(shm_fd) == -1) {
|
||
perror("close");
|
||
}
|
||
|
||
g_shm_ptr->has_data = 0;
|
||
g_shm_ptr->result_code = 0;
|
||
memset(g_shm_ptr->buffer, 0, sizeof(g_shm_ptr->buffer));
|
||
|
||
// Создаём именованный семафор клиента: клиент будет делать sem_post, сервер — sem_wait.
|
||
g_sem_client = sem_open(sem_client_name,
|
||
O_CREAT | O_EXCL,
|
||
S_IRUSR | S_IWUSR,
|
||
0); // начальное значение 0, сервер сразу блокируется на sem_wait.
|
||
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;
|
||
}
|
||
|
||
// Создаём именованный семафор сервера: сервер делает sem_post, клиент — sem_wait.
|
||
g_sem_server = sem_open(sem_server_name,
|
||
O_CREAT | O_EXCL,
|
||
S_IRUSR | S_IWUSR,
|
||
0);
|
||
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;
|
||
}
|
||
|
||
g_fout = fopen("output.txt", "w");
|
||
if (!g_fout) {
|
||
perror("fopen(output.txt)");
|
||
}
|
||
|
||
int processed_count = 0;
|
||
|
||
while (iterations < 0 || processed_count < iterations) {
|
||
// Сервер блокируется на семафоре клиента (sem_wait), ожидая данных в shared memory.
|
||
if (sem_wait(g_sem_client) == -1) {
|
||
perror("sem_wait(sem_client)");
|
||
processed_count = -1;
|
||
break;
|
||
}
|
||
|
||
if (!g_shm_ptr->has_data) {
|
||
fprintf(stderr,
|
||
"Warning: sem_client posted, but has_data == 0\n");
|
||
g_shm_ptr->result_code = -1;
|
||
} else {
|
||
process_line(g_shm_ptr->buffer);
|
||
|
||
g_shm_ptr->result_code = 0;
|
||
|
||
if (g_fout) {
|
||
fprintf(g_fout, "%s\n", g_shm_ptr->buffer);
|
||
fflush(g_fout);
|
||
}
|
||
|
||
g_shm_ptr->has_data = 0;
|
||
|
||
processed_count++;
|
||
}
|
||
|
||
// Сервер увеличивает семафор сервера (sem_post), разблокируя клиента (sem_wait).
|
||
if (sem_post(g_sem_server) == -1) {
|
||
perror("sem_post(sem_server)");
|
||
processed_count = -1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (g_fout) {
|
||
if (fclose(g_fout) == EOF) {
|
||
perror("fclose(output.txt)");
|
||
}
|
||
g_fout = NULL;
|
||
}
|
||
|
||
printf("%d\n", processed_count >= 0 ? processed_count : -1);
|
||
|
||
// Закрываем дескрипторы POSIX семафоров в процессе сервера.
|
||
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;
|
||
}
|
||
|
||
// sem_unlink полностью удаляет именованные семафоры из системы.
|
||
if (sem_unlink(sem_client_name) == -1) {
|
||
perror("sem_unlink(sem_client)");
|
||
}
|
||
if (sem_unlink(sem_server_name) == -1) {
|
||
perror("sem_unlink(sem_server)");
|
||
}
|
||
|
||
// Отсоединяем область POSIX shared memory и удаляем сам объект.
|
||
if (g_shm_ptr) {
|
||
if (munmap(g_shm_ptr, sizeof(shared_block_t)) == -1) {
|
||
perror("munmap");
|
||
}
|
||
g_shm_ptr = NULL;
|
||
}
|
||
|
||
if (shm_unlink(shm_name) == -1) {
|
||
perror("shm_unlink");
|
||
}
|
||
|
||
return 0;
|
||
}
|