# Лабораторная работа №4: Каналы передачи данных ## Цель работы Получить навыки организации обмена информацией между процессами средствами неименованных или именованных каналов в программах на языке C в операционных системах семейства Linux. ## Описание задания Лабораторная работа является модификацией лабораторной работы №3, в которой обмен данными между родительской и дочерними программами производится через программные каналы. ### Вариант 12 Программа читает текстовый файл построчно. Для каждой строки первый символ становится ключом, и все вхождения этого символа в строке заменяются пробелами, пока не будет достигнуто максимальное количество замен. ## Реализованные решения ### 1. Решение с неименованными каналами (pipes) **Файлы:** - `parent_pipe.c` - родительская программа - `child_pipe.c` - дочерняя программа **Принцип работы:** 1. Родительский процесс создает три канала для каждого дочернего процесса: - `pipe_to_child` - для передачи данных дочернему процессу - `pipe_from_child` - для получения обработанных данных - `pipe_error` - для получения количества замен 2. Родительский процесс: - Читает данные из входного файла - Передает данные дочернему процессу через `pipe_to_child` - Получает обработанные данные через `pipe_from_child` - Записывает результат в выходной файл 3. Дочерний процесс: - Перенаправляет stdin на чтение из канала (dup2) - Перенаправляет stdout на запись в канал (dup2) - Обрабатывает данные согласно варианту 12 - Возвращает количество замен через stderr **Преимущества:** - Простая реализация для родственных процессов - Автоматическая синхронизация через блокирующие операции чтения/записи - Нет необходимости в файловых дескрипторах на диске **Недостатки:** - Работает только между родственными процессами - Ограниченный размер буфера канала ### 2. Решение с именованными каналами (FIFO) **Файлы:** - `fifo_server.c` - серверная программа - `fifo_client.c` - клиентская программа **Принцип работы:** 1. Сервер создает два именованных канала: - `/tmp/fifo_request` - для получения запросов - `/tmp/fifo_response` - для отправки ответов 2. Клиент: - Читает данные из входного файла - Отправляет данные серверу через `fifo_request` - Получает обработанные данные через `fifo_response` - Записывает результат в выходной файл 3. Сервер: - Ожидает запросы от клиентов - Обрабатывает данные согласно варианту 12 - Отправляет результат обратно клиенту **Преимущества:** - Работает между неродственными процессами - Клиент-серверная архитектура - Возможность обработки запросов от нескольких клиентов **Недостатки:** - Более сложная реализация - Требуется управление файлами в файловой системе - Необходима синхронизация между клиентом и сервером ## Компиляция и запуск ### Компиляция всех программ ```bash make -f Makefile_lab4 all ``` ### Тестирование с неименованными каналами ```bash make -f Makefile_lab4 test_pipes ``` ### Тестирование с именованными каналами **Вариант 1: Автоматический тест** ```bash make -f Makefile_lab4 test_fifo_auto ``` **Вариант 2: Ручной запуск в двух терминалах** Терминал 1 (сервер): ```bash make -f Makefile_lab4 test_fifo_server ``` Терминал 2 (клиент): ```bash make -f Makefile_lab4 test_fifo_client ``` ### Сравнение с оригинальной версией ```bash make -f Makefile_lab4 test_compare ``` ### Запуск всех тестов ```bash make -f Makefile_lab4 test_all ``` ## Примеры использования ### Неименованные каналы ```bash ./parent_pipe ./child_pipe 10 input1.txt output1.txt input2.txt output2.txt ``` ### Именованные каналы Запустить сервер: ```bash ./fifo_server 10 ``` В другом терминале запустить клиент: ```bash ./fifo_client input1.txt output1.txt ./fifo_client input2.txt output2.txt ``` ## Обработка исключительных ситуаций Программы предусматривают обработку следующих ошибок: - Отсутствие или неверное количество входных параметров - Ошибки открытия входного и/или выходного файла - Ошибки создания каналов - Ошибки создания дочерних процессов - Ошибки чтения и записи через каналы ## Технические детали ### Используемые системные вызовы **Неименованные каналы:** - `pipe()` - создание канала - `fork()` - создание дочернего процесса - `dup2()` - дублирование файлового дескриптора - `read()` / `write()` - чтение/запись данных - `close()` - закрытие файлового дескриптора - `waitpid()` - ожидание завершения дочернего процесса - `execl()` - запуск новой программы **Именованные каналы:** - `mkfifo()` - создание именованного канала - `open()` - открытие FIFO - `read()` / `write()` - чтение/запись данных - `close()` - закрытие FIFO - `unlink()` - удаление FIFO ### Схема работы каналов **Неименованные каналы (pipes):** ``` Родительский процесс Дочерний процесс [файл] → read stdin ← pipe ↓ ↓ pipe → write read → process ↓ read ← pipe write → stdout ↓ write → [файл] ``` **Именованные каналы (FIFO):** ``` Клиент Сервер [файл] → read /tmp/fifo_request ← open ↓ ↓ write → /tmp/fifo_request read → process ↓ read ← /tmp/fifo_response write → /tmp/fifo_response ↓ write → [файл] ``` ## Структура проекта ``` lab4/ ├── parent_pipe.c # Родительская программа (pipes) ├── child_pipe.c # Дочерняя программа (pipes) ├── fifo_server.c # Серверная программа (FIFO) ├── fifo_client.c # Клиентская программа (FIFO) ├── parent.c # Оригинальная версия (Lab 3) ├── lab1_var12.c # Оригинальная версия (Lab 1) ├── Makefile_lab4 # Makefile для компиляции и тестирования └── README_lab4.md # Документация ``` ## Контрольные вопросы (стр. 82) 1. **Что такое канал (pipe)?** - Канал - это механизм межпроцессного взаимодействия (IPC), представляющий собой однонаправленный канал связи между процессами. 2. **В чем разница между именованными и неименованными каналами?** - Неименованные каналы существуют только в памяти и доступны только родственным процессам - Именованные каналы (FIFO) создаются в файловой системе и доступны любым процессам 3. **Как создать канал?** - Неименованный: `int pipe(int fd[2])` - Именованный: `int mkfifo(const char *pathname, mode_t mode)` 4. **Что происходит при чтении из пустого канала?** - Процесс блокируется до тех пор, пока в канал не будут записаны данные 5. **Что происходит при записи в заполненный канал?** - Процесс блокируется до тех пор, пока в канале не освободится место 6. **Как передать данные через канал?** - Используя системные вызовы `write()` для записи и `read()` для чтения 7. **Зачем нужно закрывать неиспользуемые концы канала?** - Для корректного определения EOF при чтении - Для освобождения ресурсов - Для предотвращения deadlock ## Автор Лабораторная работа выполнена в рамках курса "Системное программирование в среде Linux"