// server.c // Сервер POSIX IPC: разделяемая память + именованные семафоры. [file:21] // - Сам удаляет "висячие" объекты (shm и семафоры) при старте. [file:21] // - Создаёт новую shared memory и новые именованные семафоры. [web:47][web:40] // - Обрабатывает строки по варианту 12 и пишет результат в shared memory и output.txt. [file:22] #include // printf, fprintf, perror, FILE, fopen, fclose, fprintf [web:60] #include // exit, EXIT_FAILURE, strtoul #include // memset, strncpy, strlen #include // errno #include // O_CREAT, O_EXCL, O_RDWR [web:29] #include // shm_open, mmap, munmap [web:47] #include // mode_t, S_IRUSR, S_IWUSR [web:39] #include // sem_t, sem_open, sem_close, sem_unlink, sem_wait, sem_post [file:21] #include // ftruncate, close [file:21] #include // 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 [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] }