// server.c // Сервер POSIX IPC: разделяемая память + именованные семафоры. // Обрабатывает строки по заданию варианта 12: // "Заменить на пробелы все символы, совпадающие с первым символом в строке, // кроме самого первого символа." #include // printf, fprintf, perror #include // exit, EXIT_FAILURE, strtoul #include // memset, strncpy, strlen #include // errno #include // O_CREAT, O_EXCL, O_RDWR #include // shm_open, mmap, munmap #include // mode_t, S_IRUSR, S_IWUSR #include // sem_t, sem_open, sem_close, sem_unlink, sem_wait, sem_post #include // 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 [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; }