working lab4 mine
This commit is contained in:
BIN
lab_4/Lab4_Pipes_IPC_Report.pdf
Normal file
BIN
lab_4/Lab4_Pipes_IPC_Report.pdf
Normal file
Binary file not shown.
99
lab_4/Makefile
Normal file
99
lab_4/Makefile
Normal file
@@ -0,0 +1,99 @@
|
||||
# Makefile for lab 4 - FIFO (named pipes) only
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -std=c99 -g
|
||||
|
||||
# Default target - build FIFO programs
|
||||
all: fifo
|
||||
|
||||
# ===== FIFO targets =====
|
||||
fifo: fifo_server fifo_client
|
||||
|
||||
fifo_server: fifo_server.c
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
fifo_client: fifo_client.c
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# ===== Test files =====
|
||||
test_files:
|
||||
@echo "Создание тестовых файлов..."
|
||||
echo "abbaabbaabbaabbaabbaabbaabbaabba" > input1.txt
|
||||
echo "xyzxyzxyzxyzxyzxyzxyzxyz" >> input1.txt
|
||||
echo "hello world hello" >> input1.txt
|
||||
echo "testtest" > input2.txt
|
||||
echo "aaaaaaa" >> input2.txt
|
||||
echo "programming" > input3.txt
|
||||
echo "ppppython" >> input3.txt
|
||||
|
||||
# ===== FIFO tests =====
|
||||
test_fifo_server: fifo test_files
|
||||
@echo "=== Запуск FIFO сервера ==="
|
||||
@echo "В другом терминале выполните: make test_fifo_client"
|
||||
./fifo_server 10
|
||||
|
||||
test_fifo_client: fifo test_files
|
||||
@echo "=== Запуск FIFO клиента ===" & \
|
||||
./fifo_client input1.txt output1_fifo.txt; \
|
||||
./fifo_client input2.txt output2_fifo.txt; \
|
||||
./fifo_client input3.txt output3_fifo.txt;
|
||||
@echo "\n=== Результаты FIFO ==="
|
||||
@echo "--- output1_fifo.txt ---"
|
||||
@cat output1_fifo.txt || true
|
||||
@echo "\n--- output2_fifo.txt ---"
|
||||
@cat output2_fifo.txt || true
|
||||
@echo "\n--- output3_fifo.txt ---"
|
||||
@cat output3_fifo.txt || true
|
||||
|
||||
# Automatic FIFO test (server in background)
|
||||
test_all: fifo test_files
|
||||
@echo "=== Автоматический тест FIFO ==="
|
||||
@./fifo_server 10 & \
|
||||
SERVER_PID=$$!; \
|
||||
sleep 1; \
|
||||
./fifo_client input1.txt output1_fifo.txt; \
|
||||
./fifo_client input2.txt output2_fifo.txt; \
|
||||
./fifo_client input3.txt output3_fifo.txt; \
|
||||
kill $$SERVER_PID 2>/dev/null || true; \
|
||||
wait $$SERVER_PID 2>/dev/null || true
|
||||
@echo "\n=== Результаты FIFO ==="
|
||||
@echo "--- output1_fifo.txt ---"
|
||||
@cat output1_fifo.txt || true
|
||||
@echo "\n--- output2_fifo.txt ---"
|
||||
@cat output2_fifo.txt || true
|
||||
@echo "\n--- output3_fifo.txt ---"
|
||||
@cat output3_fifo.txt || true
|
||||
|
||||
# Error handling test for FIFO
|
||||
test_error: fifo
|
||||
@echo "\n=== Тест обработки ошибки (несуществующий файл) - FIFO ==="
|
||||
@./fifo_server 5 & \
|
||||
SERVER_PID=$$!; \
|
||||
sleep 1; \
|
||||
./fifo_client nonexistent.txt output_error.txt || true; \
|
||||
kill $$SERVER_PID 2>/dev/null || true; \
|
||||
wait $$SERVER_PID 2>/dev/null || true
|
||||
|
||||
# Cleanup
|
||||
clean:
|
||||
@echo "Очистка..."
|
||||
rm -f fifo_server fifo_client
|
||||
rm -f input1.txt input2.txt input3.txt
|
||||
rm -f output*.txt
|
||||
rm -f /tmp/fifo_request /tmp/fifo_response
|
||||
|
||||
# Help
|
||||
help:
|
||||
@echo "Доступные цели:"
|
||||
@echo " all - Скомпилировать FIFO программы"
|
||||
@echo " fifo - Скомпилировать fifo_server и fifo_client"
|
||||
@echo " test_files - Создать тестовые входные файлы"
|
||||
@echo " test_fifo_server - Запустить FIFO сервер (использовать с клиентом в другом терминале)"
|
||||
@echo " test_fifo_client - Запустить FIFO клиент и показать результат"
|
||||
@echo " test_fifo_auto - Автоматический тест FIFO (сервер в фоне)"
|
||||
@echo " test_all - Запустить все тесты (FIFO)"
|
||||
@echo " test_error_fifo - Тест обработки ошибок (несуществующий файл)"
|
||||
@echo " clean - Удалить скомпилированные файлы и тесты"
|
||||
|
||||
.PHONY: all fifo test_files test_fifo_server test_fifo_client test_all \
|
||||
test_error clean help
|
||||
238
lab_4/README_lab4.md
Normal file
238
lab_4/README_lab4.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# Лабораторная работа №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"
|
||||
144
lab_4/fifo_client.c
Normal file
144
lab_4/fifo_client.c
Normal file
@@ -0,0 +1,144 @@
|
||||
// fifo_client.c - Клиентская программа с использованием именованных каналов (FIFO)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define FIFO_REQUEST "/tmp/fifo_request"
|
||||
#define FIFO_RESPONSE "/tmp/fifo_response"
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
void print_usage(const char *progname) {
|
||||
fprintf(stderr, "Usage: %s <input_file> <output_file>\n", progname);
|
||||
fprintf(stderr, "Example: %s input.txt output.txt\n", progname);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "ERROR: Неверное количество аргументов\n");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *input_file = argv[1];
|
||||
const char *output_file = argv[2];
|
||||
|
||||
printf("=== FIFO Client ===\n");
|
||||
printf("Client PID: %d\n", getpid());
|
||||
printf("Входной файл: %s\n", input_file);
|
||||
printf("Выходной файл: %s\n", output_file);
|
||||
|
||||
// Открываем входной файл
|
||||
int in_fd = open(input_file, O_RDONLY);
|
||||
if (in_fd < 0) {
|
||||
fprintf(stderr, "ERROR: Не удалось открыть входной файл %s: %s\n",
|
||||
input_file, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Читаем данные из файла
|
||||
char *buffer = malloc(BUFFER_SIZE);
|
||||
if (!buffer) {
|
||||
fprintf(stderr, "ERROR: Не удалось выделить память\n");
|
||||
close(in_fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssize_t bytes_read = read(in_fd, buffer, BUFFER_SIZE - 1);
|
||||
close(in_fd);
|
||||
|
||||
if (bytes_read < 0) {
|
||||
fprintf(stderr, "ERROR: Не удалось прочитать файл: %s\n", strerror(errno));
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
buffer[bytes_read] = '\0';
|
||||
printf("Прочитано байт из файла: %zd\n", bytes_read);
|
||||
|
||||
// Открываем FIFO для отправки запроса
|
||||
printf("Отправка запроса серверу...\n");
|
||||
int fd_req = open(FIFO_REQUEST, O_WRONLY);
|
||||
if (fd_req == -1) {
|
||||
fprintf(stderr, "ERROR: Не удалось открыть FIFO запроса: %s\n", strerror(errno));
|
||||
fprintf(stderr, "Убедитесь, что сервер запущен!\n");
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Отправляем данные серверу
|
||||
ssize_t bytes_written = write(fd_req, buffer, bytes_read);
|
||||
close(fd_req);
|
||||
|
||||
if (bytes_written != bytes_read) {
|
||||
fprintf(stderr, "ERROR: Ошибка отправки данных\n");
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Отправлено байт: %zd\n", bytes_written);
|
||||
|
||||
// Открываем FIFO для получения ответа
|
||||
printf("Ожидание ответа от сервера...\n");
|
||||
int fd_resp = open(FIFO_RESPONSE, O_RDONLY);
|
||||
if (fd_resp == -1) {
|
||||
fprintf(stderr, "ERROR: Не удалось открыть FIFO ответа: %s\n", strerror(errno));
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Читаем обработанные данные
|
||||
ssize_t response_bytes = read(fd_resp, buffer, BUFFER_SIZE - 1);
|
||||
close(fd_resp);
|
||||
|
||||
if (response_bytes < 0) {
|
||||
fprintf(stderr, "ERROR: Ошибка чтения ответа\n");
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
buffer[response_bytes] = '\0';
|
||||
printf("Получено байт от сервера: %zd\n", response_bytes);
|
||||
|
||||
// Ищем информацию о количестве замен
|
||||
char *replacements_info = strstr(buffer, "\nREPLACEMENTS:");
|
||||
long long replacements = 0;
|
||||
|
||||
if (replacements_info) {
|
||||
sscanf(replacements_info, "\nREPLACEMENTS:%lld", &replacements);
|
||||
*replacements_info = '\0'; // Обрезаем служебную информацию
|
||||
response_bytes = replacements_info - buffer;
|
||||
}
|
||||
|
||||
// Открываем выходной файл
|
||||
int out_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
if (out_fd < 0) {
|
||||
fprintf(stderr, "ERROR: Не удалось открыть выходной файл %s: %s\n",
|
||||
output_file, strerror(errno));
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Записываем обработанные данные
|
||||
ssize_t written = write(out_fd, buffer, response_bytes);
|
||||
close(out_fd);
|
||||
|
||||
if (written != response_bytes) {
|
||||
fprintf(stderr, "ERROR: Ошибка записи в выходной файл\n");
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Записано байт в выходной файл: %zd\n", written);
|
||||
printf("Количество выполненных замен: %lld\n", replacements);
|
||||
printf("\nОбработка завершена успешно!\n");
|
||||
|
||||
free(buffer);
|
||||
return 0;
|
||||
}
|
||||
177
lab_4/fifo_server.c
Normal file
177
lab_4/fifo_server.c
Normal file
@@ -0,0 +1,177 @@
|
||||
// fifo_server.c - Серверная программа с использованием именованных каналов (FIFO)
|
||||
// Вариант повышенной сложности для Lab 4
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define FIFO_REQUEST "/tmp/fifo_request"
|
||||
#define FIFO_RESPONSE "/tmp/fifo_response"
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
volatile sig_atomic_t running = 1;
|
||||
|
||||
void signal_handler(int sig) {
|
||||
(void)sig;
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static long long parse_ll(const char *s) {
|
||||
char *end = NULL;
|
||||
errno = 0;
|
||||
long long v = strtoll(s, &end, 10);
|
||||
if (errno != 0 || end == s || *end != '\0' || v < 0) {
|
||||
return -1;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
long long process_data(const char *input, size_t input_len, char *output,
|
||||
size_t output_size, long long max_replacements) {
|
||||
long long total_replacements = 0;
|
||||
int at_line_start = 1;
|
||||
unsigned char line_key = 0;
|
||||
size_t out_pos = 0;
|
||||
|
||||
for (size_t i = 0; i < input_len && out_pos < output_size - 1; i++) {
|
||||
unsigned char c = (unsigned char)input[i];
|
||||
|
||||
if (at_line_start) {
|
||||
line_key = c;
|
||||
at_line_start = 0;
|
||||
}
|
||||
|
||||
unsigned char outc = c;
|
||||
if (c == '\n') {
|
||||
at_line_start = 1;
|
||||
} else if (c == line_key && total_replacements < max_replacements) {
|
||||
outc = ' ';
|
||||
total_replacements++;
|
||||
}
|
||||
|
||||
output[out_pos++] = (char)outc;
|
||||
}
|
||||
|
||||
output[out_pos] = '\0';
|
||||
return total_replacements;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Usage: %s <max_replacements>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
long long max_replacements = parse_ll(argv[1]);
|
||||
if (max_replacements < 0) {
|
||||
fprintf(stderr, "ERROR: Invalid max_replacements\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("=== FIFO Server запущен ===\n");
|
||||
printf("Server PID: %d\n", getpid());
|
||||
printf("Максимум замен: %lld\n", max_replacements);
|
||||
|
||||
// Устанавливаем обработчик сигналов
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
// Удаляем старые FIFO, если существуют
|
||||
unlink(FIFO_REQUEST);
|
||||
unlink(FIFO_RESPONSE);
|
||||
|
||||
// Создаем именованные каналы
|
||||
if (mkfifo(FIFO_REQUEST, 0666) == -1) {
|
||||
perror("mkfifo request");
|
||||
return 1;
|
||||
}
|
||||
if (mkfifo(FIFO_RESPONSE, 0666) == -1) {
|
||||
perror("mkfifo response");
|
||||
unlink(FIFO_REQUEST);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("FIFO каналы созданы:\n");
|
||||
printf(" Request: %s\n", FIFO_REQUEST);
|
||||
printf(" Response: %s\n", FIFO_RESPONSE);
|
||||
printf("Ожидание запросов от клиентов...\n\n");
|
||||
|
||||
while (running) {
|
||||
// Открываем FIFO для чтения запроса
|
||||
int fd_req = open(FIFO_REQUEST, O_RDONLY);
|
||||
if (fd_req == -1) {
|
||||
if (errno == EINTR) continue;
|
||||
perror("open request FIFO");
|
||||
break;
|
||||
}
|
||||
|
||||
// Читаем данные от клиента
|
||||
char *input_buffer = malloc(BUFFER_SIZE);
|
||||
char *output_buffer = malloc(BUFFER_SIZE);
|
||||
|
||||
if (!input_buffer || !output_buffer) {
|
||||
fprintf(stderr, "ERROR: Memory allocation failed\n");
|
||||
close(fd_req);
|
||||
free(input_buffer);
|
||||
free(output_buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
ssize_t bytes_read = read(fd_req, input_buffer, BUFFER_SIZE - 1);
|
||||
close(fd_req);
|
||||
|
||||
if (bytes_read <= 0) {
|
||||
free(input_buffer);
|
||||
free(output_buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
input_buffer[bytes_read] = '\0';
|
||||
printf("Получен запрос: %zd байт\n", bytes_read);
|
||||
|
||||
// Обрабатываем данные
|
||||
long long replacements = process_data(input_buffer, bytes_read,
|
||||
output_buffer, BUFFER_SIZE,
|
||||
max_replacements);
|
||||
|
||||
printf("Выполнено замен: %lld\n", replacements);
|
||||
|
||||
// Открываем FIFO для отправки ответа
|
||||
int fd_resp = open(FIFO_RESPONSE, O_WRONLY);
|
||||
if (fd_resp == -1) {
|
||||
perror("open response FIFO");
|
||||
free(input_buffer);
|
||||
free(output_buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Отправляем обработанные данные
|
||||
size_t output_len = strlen(output_buffer);
|
||||
ssize_t bytes_written = write(fd_resp, output_buffer, output_len);
|
||||
|
||||
// Отправляем количество замен (в отдельной строке)
|
||||
char result[64];
|
||||
snprintf(result, sizeof(result), "\nREPLACEMENTS:%lld\n", replacements);
|
||||
write(fd_resp, result, strlen(result));
|
||||
|
||||
close(fd_resp);
|
||||
|
||||
printf("Отправлен ответ: %zd байт\n\n", bytes_written);
|
||||
|
||||
free(input_buffer);
|
||||
free(output_buffer);
|
||||
}
|
||||
|
||||
// Очистка
|
||||
printf("\nЗавершение работы сервера...\n");
|
||||
unlink(FIFO_REQUEST);
|
||||
unlink(FIFO_RESPONSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
399
lab_4/Инструкция_по_запуску.md
Normal file
399
lab_4/Инструкция_по_запуску.md
Normal file
@@ -0,0 +1,399 @@
|
||||
# Инструкция по запуску Лабораторной работы №4
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
### 1. Подготовка файлов
|
||||
|
||||
Создайте рабочую директорию и поместите в неё следующие файлы:
|
||||
- `parent_pipe.c` - родительская программа (pipes)
|
||||
- `child_pipe.c` - дочерняя программа (pipes)
|
||||
- `fifo_server.c` - серверная программа (FIFO)
|
||||
- `fifo_client.c` - клиентская программа (FIFO)
|
||||
- `Makefile_lab4` - Makefile для компиляции
|
||||
- Оригинальные файлы: `parent.c`, `lab1_var12.c`, `Makefile` (для сравнения)
|
||||
|
||||
### 2. Компиляция
|
||||
|
||||
```bash
|
||||
# Компилировать все программы
|
||||
make -f Makefile_lab4 all
|
||||
|
||||
# Или компилировать отдельно:
|
||||
make -f Makefile_lab4 pipes # Только pipes
|
||||
make -f Makefile_lab4 fifo # Только FIFO
|
||||
make -f Makefile_lab4 original # Оригинальная версия
|
||||
```
|
||||
|
||||
### 3. Автоматическое тестирование
|
||||
|
||||
```bash
|
||||
# Тест с неименованными каналами (pipes)
|
||||
make -f Makefile_lab4 test_pipes
|
||||
|
||||
# Автоматический тест с именованными каналами (FIFO)
|
||||
make -f Makefile_lab4 test_fifo_auto
|
||||
|
||||
# Сравнение с оригинальной версией
|
||||
make -f Makefile_lab4 test_compare
|
||||
|
||||
# Запустить все тесты
|
||||
make -f Makefile_lab4 test_all
|
||||
```
|
||||
|
||||
## Подробные инструкции
|
||||
|
||||
### Вариант 1: Неименованные каналы (Pipes)
|
||||
|
||||
#### Компиляция
|
||||
```bash
|
||||
gcc -Wall -Wextra -std=c99 -g -o parent_pipe parent_pipe.c
|
||||
gcc -Wall -Wextra -std=c99 -g -o child_pipe child_pipe.c
|
||||
```
|
||||
|
||||
#### Создание тестовых файлов
|
||||
```bash
|
||||
echo "abbaabbaabbaabbaabbaabbaabbaabba" > input1.txt
|
||||
echo "xyzxyzxyzxyzxyzxyzxyzxyz" >> input1.txt
|
||||
echo "hello world hello" >> input1.txt
|
||||
|
||||
echo "testtest" > input2.txt
|
||||
echo "aaaaaaa" >> input2.txt
|
||||
|
||||
echo "programming" > input3.txt
|
||||
echo "ppppython" >> input3.txt
|
||||
```
|
||||
|
||||
#### Запуск
|
||||
```bash
|
||||
# Формат: ./parent_pipe <child_program> <max_replacements> <in1> <out1> [<in2> <out2> ...]
|
||||
|
||||
# Обработка одного файла
|
||||
./parent_pipe ./child_pipe 10 input1.txt output1.txt
|
||||
|
||||
# Обработка нескольких файлов параллельно
|
||||
./parent_pipe ./child_pipe 10 input1.txt output1.txt input2.txt output2.txt input3.txt output3.txt
|
||||
|
||||
# Разные лимиты замен
|
||||
./parent_pipe ./child_pipe 5 input1.txt output1_5.txt
|
||||
./parent_pipe ./child_pipe 20 input1.txt output1_20.txt
|
||||
```
|
||||
|
||||
#### Просмотр результатов
|
||||
```bash
|
||||
cat output1.txt
|
||||
cat output2.txt
|
||||
cat output3.txt
|
||||
```
|
||||
|
||||
### Вариант 2: Именованные каналы (FIFO)
|
||||
|
||||
#### Компиляция
|
||||
```bash
|
||||
gcc -Wall -Wextra -std=c99 -g -o fifo_server fifo_server.c
|
||||
gcc -Wall -Wextra -std=c99 -g -o fifo_client fifo_client.c
|
||||
```
|
||||
|
||||
#### Вариант 2.1: Запуск в двух терминалах
|
||||
|
||||
**Терминал 1 (Сервер):**
|
||||
```bash
|
||||
./fifo_server 10
|
||||
```
|
||||
Сервер запустится и будет ожидать подключения клиентов.
|
||||
|
||||
**Терминал 2 (Клиент):**
|
||||
```bash
|
||||
# Формат: ./fifo_client <input_file> <output_file>
|
||||
|
||||
./fifo_client input1.txt output1_fifo.txt
|
||||
./fifo_client input2.txt output2_fifo.txt
|
||||
./fifo_client input3.txt output3_fifo.txt
|
||||
```
|
||||
|
||||
После обработки всех файлов остановите сервер: Ctrl+C в терминале 1.
|
||||
|
||||
#### Вариант 2.2: Автоматический запуск (одна команда)
|
||||
|
||||
```bash
|
||||
# Сервер запустится в фоне, обработает запросы и автоматически остановится
|
||||
make -f Makefile_lab4 test_fifo_auto
|
||||
```
|
||||
|
||||
#### Вариант 2.3: Ручной запуск в фоне
|
||||
|
||||
```bash
|
||||
# Запустить сервер в фоне
|
||||
./fifo_server 10 &
|
||||
SERVER_PID=$!
|
||||
|
||||
# Подождать запуска сервера
|
||||
sleep 1
|
||||
|
||||
# Отправить запросы
|
||||
./fifo_client input1.txt output1_fifo.txt
|
||||
./fifo_client input2.txt output2_fifo.txt
|
||||
./fifo_client input3.txt output3_fifo.txt
|
||||
|
||||
# Остановить сервер
|
||||
kill $SERVER_PID
|
||||
```
|
||||
|
||||
## Тестирование обработки ошибок
|
||||
|
||||
### Несуществующий входной файл
|
||||
|
||||
```bash
|
||||
# Pipes
|
||||
./parent_pipe ./child_pipe 5 nonexistent.txt output.txt
|
||||
|
||||
# FIFO
|
||||
./fifo_server 5 &
|
||||
sleep 1
|
||||
./fifo_client nonexistent.txt output.txt
|
||||
killall fifo_server
|
||||
```
|
||||
|
||||
### Неверное количество аргументов
|
||||
|
||||
```bash
|
||||
# Недостаточно аргументов
|
||||
./parent_pipe ./child_pipe 5
|
||||
|
||||
# Нечетное количество файлов
|
||||
./parent_pipe ./child_pipe 5 input1.txt
|
||||
```
|
||||
|
||||
### Клиент без сервера (FIFO)
|
||||
|
||||
```bash
|
||||
# Попытка подключиться при выключенном сервере
|
||||
./fifo_client input1.txt output1.txt
|
||||
# Ожидается: "ERROR: Не удалось открыть FIFO запроса"
|
||||
```
|
||||
|
||||
## Сравнение результатов
|
||||
|
||||
### Сравнение pipes vs original
|
||||
|
||||
```bash
|
||||
# Запустить обе версии
|
||||
./parent_pipe ./child_pipe 10 input1.txt output1_pipe.txt
|
||||
./parent ./lab1_var12 10 input1.txt output1_orig.txt
|
||||
|
||||
# Сравнить результаты
|
||||
diff output1_pipe.txt output1_orig.txt
|
||||
|
||||
# Если различий нет - программа работает корректно
|
||||
```
|
||||
|
||||
### Сравнение FIFO vs pipes
|
||||
|
||||
```bash
|
||||
# Pipes
|
||||
./parent_pipe ./child_pipe 10 input1.txt output1_pipe.txt
|
||||
|
||||
# FIFO
|
||||
./fifo_server 10 &
|
||||
sleep 1
|
||||
./fifo_client input1.txt output1_fifo.txt
|
||||
killall fifo_server
|
||||
|
||||
# Сравнение
|
||||
diff output1_pipe.txt output1_fifo.txt
|
||||
```
|
||||
|
||||
## Анализ работы программ
|
||||
|
||||
### Просмотр процессов
|
||||
|
||||
```bash
|
||||
# Во время работы parent_pipe в другом терминале:
|
||||
ps aux | grep child_pipe
|
||||
ps aux | grep parent_pipe
|
||||
|
||||
# Просмотр открытых файловых дескрипторов
|
||||
lsof -c parent_pipe
|
||||
lsof -c child_pipe
|
||||
```
|
||||
|
||||
### Просмотр FIFO файлов
|
||||
|
||||
```bash
|
||||
# Во время работы сервера:
|
||||
ls -la /tmp/fifo_*
|
||||
|
||||
# Информация о типе файла
|
||||
file /tmp/fifo_request
|
||||
file /tmp/fifo_response
|
||||
```
|
||||
|
||||
### Трассировка системных вызовов
|
||||
|
||||
```bash
|
||||
# Трассировка работы с каналами
|
||||
strace -e trace=pipe,read,write,fork,execl ./parent_pipe ./child_pipe 10 input1.txt output1.txt
|
||||
|
||||
# Трассировка FIFO операций
|
||||
strace -e trace=open,read,write,mkfifo ./fifo_server 10
|
||||
```
|
||||
|
||||
## Очистка
|
||||
|
||||
```bash
|
||||
# Удалить скомпилированные программы и тестовые файлы
|
||||
make -f Makefile_lab4 clean
|
||||
|
||||
# Или вручную:
|
||||
rm -f parent_pipe child_pipe fifo_server fifo_client
|
||||
rm -f parent lab1_var12
|
||||
rm -f input*.txt output*.txt
|
||||
rm -f /tmp/fifo_request /tmp/fifo_response
|
||||
```
|
||||
|
||||
## Типичные проблемы и решения
|
||||
|
||||
### Проблема 1: "Permission denied" при запуске
|
||||
|
||||
**Решение:**
|
||||
```bash
|
||||
chmod +x parent_pipe child_pipe fifo_server fifo_client
|
||||
```
|
||||
|
||||
### Проблема 2: FIFO клиент зависает
|
||||
|
||||
**Причина:** Сервер не запущен.
|
||||
|
||||
**Решение:** Убедитесь, что сервер запущен:
|
||||
```bash
|
||||
ps aux | grep fifo_server
|
||||
```
|
||||
|
||||
### Проблема 3: "Address already in use" для FIFO
|
||||
|
||||
**Причина:** FIFO файлы остались после некорректного завершения.
|
||||
|
||||
**Решение:**
|
||||
```bash
|
||||
rm -f /tmp/fifo_request /tmp/fifo_response
|
||||
```
|
||||
|
||||
### Проблема 4: Зомби-процессы
|
||||
|
||||
**Причина:** Родительский процесс не вызвал wait() для дочерних.
|
||||
|
||||
**Решение:** В коде уже реализован waitpid(), но если проблема возникла:
|
||||
```bash
|
||||
# Просмотр зомби
|
||||
ps aux | grep defunct
|
||||
|
||||
# Завершить родительский процесс
|
||||
killall parent_pipe
|
||||
```
|
||||
|
||||
### Проблема 5: Broken pipe
|
||||
|
||||
**Причина:** Попытка записи в канал, у которого закрыт конец для чтения.
|
||||
|
||||
**Решение:** Убедитесь, что оба конца канала правильно настроены и дочерний процесс не завершился преждевременно.
|
||||
|
||||
## Дополнительные команды
|
||||
|
||||
### Справка по использованию
|
||||
|
||||
```bash
|
||||
# Показать все доступные команды Makefile
|
||||
make -f Makefile_lab4 help
|
||||
```
|
||||
|
||||
### Компиляция с отладочной информацией
|
||||
|
||||
```bash
|
||||
# Уже включено флагом -g, запуск с gdb:
|
||||
gdb ./parent_pipe
|
||||
(gdb) run ./child_pipe 10 input1.txt output1.txt
|
||||
```
|
||||
|
||||
### Проверка утечек памяти
|
||||
|
||||
```bash
|
||||
# Для pipes
|
||||
valgrind --leak-check=full ./parent_pipe ./child_pipe 10 input1.txt output1.txt
|
||||
|
||||
# Для FIFO сервера
|
||||
valgrind --leak-check=full ./fifo_server 10
|
||||
```
|
||||
|
||||
## Примеры вывода
|
||||
|
||||
### Успешное выполнение pipes
|
||||
|
||||
```
|
||||
=== Запуск родительского процесса с каналами ===
|
||||
Родительский PID: 12345
|
||||
Программа для запуска: ./child_pipe
|
||||
Максимум замен: 10
|
||||
Количество файловых пар: 2
|
||||
|
||||
Создание процесса 1 для файлов: input1.txt -> output1.txt
|
||||
Создание процесса 2 для файлов: input2.txt -> output2.txt
|
||||
|
||||
=== Обработка файлов через каналы ===
|
||||
|
||||
Обработка процесса PID=12346 (input1.txt -> output1.txt)
|
||||
Отправлено байт: 87
|
||||
Получено байт: 87
|
||||
Количество замен: 10
|
||||
Статус: SUCCESS
|
||||
|
||||
Обработка процесса PID=12347 (input2.txt -> output2.txt)
|
||||
Отправлено байт: 17
|
||||
Получено байт: 17
|
||||
Количество замен: 6
|
||||
Статус: SUCCESS
|
||||
|
||||
=== Итоговая статистика ===
|
||||
Всего запущено процессов: 2
|
||||
Успешно завершено: 2
|
||||
Завершено с ошибкой: 0
|
||||
|
||||
ОБЩИЙ СТАТУС: Все процессы завершены успешно
|
||||
```
|
||||
|
||||
### Успешное выполнение FIFO
|
||||
|
||||
```
|
||||
=== FIFO Server запущен ===
|
||||
Server PID: 12350
|
||||
Максимум замен: 10
|
||||
FIFO каналы созданы:
|
||||
Request: /tmp/fifo_request
|
||||
Response: /tmp/fifo_response
|
||||
Ожидание запросов от клиентов...
|
||||
|
||||
Получен запрос: 87 байт
|
||||
Выполнено замен: 10
|
||||
Отправлен ответ: 87 байт
|
||||
```
|
||||
|
||||
```
|
||||
=== FIFO Client ===
|
||||
Client PID: 12351
|
||||
Входной файл: input1.txt
|
||||
Выходной файл: output1.txt
|
||||
Прочитано байт из файла: 87
|
||||
Отправка запроса серверу...
|
||||
Отправлено байт: 87
|
||||
Ожидание ответа от сервера...
|
||||
Получено байт от сервера: 100
|
||||
Записано байт в выходной файл: 87
|
||||
Количество выполненных замен: 10
|
||||
|
||||
Обработка завершена успешно!
|
||||
```
|
||||
|
||||
## Контакты и поддержка
|
||||
|
||||
При возникновении вопросов обратитесь к:
|
||||
- Отчету лабораторной работы (PDF)
|
||||
- Комментариям в исходном коде
|
||||
- Документации Linux man pages: `man pipe`, `man mkfifo`, `man fork`, `man dup2`
|
||||
Reference in New Issue
Block a user