Files
CS-LABS/lab_4/README_lab4.md
2025-10-28 17:00:14 +07:00

11 KiB
Raw Blame History

Лабораторная работа №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
    • Отправляет результат обратно клиенту

Преимущества:

  • Работает между неродственными процессами
  • Клиент-серверная архитектура
  • Возможность обработки запросов от нескольких клиентов

Недостатки:

  • Более сложная реализация
  • Требуется управление файлами в файловой системе
  • Необходима синхронизация между клиентом и сервером

Компиляция и запуск

Компиляция всех программ

make -f Makefile_lab4 all

Тестирование с неименованными каналами

make -f Makefile_lab4 test_pipes

Тестирование с именованными каналами

Вариант 1: Автоматический тест

make -f Makefile_lab4 test_fifo_auto

Вариант 2: Ручной запуск в двух терминалах

Терминал 1 (сервер):

make -f Makefile_lab4 test_fifo_server

Терминал 2 (клиент):

make -f Makefile_lab4 test_fifo_client

Сравнение с оригинальной версией

make -f Makefile_lab4 test_compare

Запуск всех тестов

make -f Makefile_lab4 test_all

Примеры использования

Неименованные каналы

./parent_pipe ./child_pipe 10 input1.txt output1.txt input2.txt output2.txt

Именованные каналы

Запустить сервер:

./fifo_server 10

В другом терминале запустить клиент:

./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"