Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Готовые отчеты / ОСиС. Лабораторная работа 7

.pdf
Скачиваний:
9
Добавлен:
21.11.2020
Размер:
443.34 Кб
Скачать

Федеральное агентство связи ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ

ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ «САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М. А. БОНЧ-БРУЕВИЧА» (СПбГУТ)

Факультет инфокоммуникационных сетей и систем Кафедра программной инженерии и вычислительной техники

ЛАБОРАТОРНАЯ РАБОТА №7 по дисциплине «Операционные системы и сети»

на тему «Разработка многопроцессной программы под Linux»

Выполнил: студент 3-го курса дневного отделения группы ИКПИ-85

Коваленко Леонид Александрович Преподаватель:

доцент кафедры ПИиВТ Дагаев Александр Владимирович

Санкт-Петербург 2020

Цель работы Разработать многопроцессную программу с применением механизмов

синхронизации процессов под Linux. Постановка задачи

1.Написать программу на Си для односторонней передачи данных при помощи пайпа и продемонстрировать ее работу.

2.Написать программу на Си для двусторонней передачи данных при помощи пайпов и продемонстрировать ее работу.

Ход работы

Работа выполняется в операционной системе Linux Debian.

Напишем программу на Си для односторонней передачи данных при помощи пайпа (табл. 1).

Таблица 1 — Файл main.c (односторонняя передача данных)

#include <errno.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <unistd.h>

#define NAMEDPIPE_NAME "/tmp/spec_pipe" #define BUFSIZE 50

int main() {

int file_descriptor; char buffer[BUFSIZE];

// Попытка создания пайпа

int status = mkfifo(NAMEDPIPE_NAME, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);

if (!(status == 0 || (status == -1 && errno == EEXIST))) { perror("mkfifo");

return 1;

}

// Если ошибки нет, то пайп был создан if (errno != EEXIST) {

// Открываем только для записи

if ((file_descriptor = open(NAMEDPIPE_NAME, O_WRONLY)) <= 0) { perror("open");

return 2;

}

while (1) {

memset(buffer, '\0', BUFSIZE); // Очистка printf("Введите сообщение или exit: "); fgets(buffer, BUFSIZE, stdin);

if (strncmp(buffer, "exit", sizeof(char) * 4) == 0) { close(file_descriptor);

remove(NAMEDPIPE_NAME); return 0;

}

if (write(file_descriptor, buffer, BUFSIZE) <= 0) { close(file_descriptor); remove(NAMEDPIPE_NAME);

2

return 3;

}

printf("Отправлено: %s\n", buffer);

}

return 0;

}

// Если ошибка есть (пайп уже был создан), то будем получать сообщения if ((file_descriptor = open(NAMEDPIPE_NAME, O_RDONLY)) <= 0) {

perror("open"); return 4;

}

printf("Получаем сообщения...\n"); while (1) {

memset(buffer, '\0', BUFSIZE); // Очистка

if (read(file_descriptor, buffer, BUFSIZE) <= 0) { close(file_descriptor);

return 5;

}

printf("Получено: %s\n", buffer);

}

return 0;

}

Продемонстрируем работу программы (рис. 1, 2, 3, 4, 5).

Рисунок 1 — Первый запуск (процесс создал пайп, ожидает читателя)

Рисунок 2 — Процесс-создатель после присоединения читателя

Рисунок 3 — Читатель ожидает сообщения от создателя пайпа

Рисунок 4 — Процесс-создатель после отправки сообщения

3

Рисунок 5 — Читатель получает первое сообщение от создателя пайпа Сообщение отправляется только первому читателю. В этом и есть суть

пайпа (трубы), у которого есть только два конца.

Закроем текущего читателя и запустим другого (рис. 6, 7).

Рисунок 6 — Процесс-создатель после отправки сообщения второму читателю

Рисунок 7 — Второй читатель получает сообщение от создателя пайпа После ввода слова exit процесс-создатель закрывает пайп и прекращает

свою работу и работу процессов-читателей.

Напишем программу на Си для двусторонней передачи данных при помощи пайпов и потоков (табл. 2).

Таблица 2 — Файл main.c (двусторонняя передача данных)

#include <errno.h> #include <fcntl.h> #include <pthread.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <unistd.h>

#define NAMEDPIPE1_NAME "/tmp/spec_pipe_one" #define NAMEDPIPE2_NAME "/tmp/spec_pipe_two" #define BUFSIZE 50

// Функция для потока записи void *runWriteThread(void *arg) {

int fileDescriptor = *(int *)arg; char buffer[BUFSIZE];

while (1) {

memset(buffer, '\0', BUFSIZE); // Очистка printf("#> ");

fgets(buffer, BUFSIZE, stdin);

if (strncmp(buffer, "exit", sizeof(char) * 4) == 0) { _exit(0);

4

}

if (write(fileDescriptor, buffer, BUFSIZE) <= 0) { printf("Сообщение не было отправлено, т. к. клиентов нет.\n"); fflush(stdout);

continue;

}

printf("Отправлено: %s", buffer);

}

}

// Функция для потока чтения void *runReadThread(void *arg) {

int fileDescriptor = *(int *)arg; char buffer[BUFSIZE];

while (1) {

memset(buffer, '\0', BUFSIZE); // Очистка

if (read(fileDescriptor, buffer, BUFSIZE) <= 0) { printf("\nСоединение прервано."); fflush(stdout);

while (read(fileDescriptor, buffer, BUFSIZE) <= 0) continue; printf("\nСоединение установлено.");

}

printf("\nПолучено: %s#> ", buffer); fflush(stdout);

}

}

int main() {

printf("Начало работы\n1. Освободить ресурсы и установить соединение\n2. Установить соединение\n>> ");

if (getchar() == '1') { struct stat statFile;

if (stat(NAMEDPIPE1_NAME, &statFile) == 0 && remove(NAMEDPIPE1_NAME)) perror("remove");

if (stat(NAMEDPIPE2_NAME, &statFile) == 0 && remove(NAMEDPIPE2_NAME)) perror("remove");

printf("Ресурсы освобождены. "); fflush(stdout);

}

//Очистка буфера ввода while (getchar() != '\n')

continue;

//Попытка создания первого пайпа

int status = mkfifo(NAMEDPIPE1_NAME, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);

if (!(status == 0 || (status == -1 && errno == EEXIST))) { printf("Пайпы заняты. Попробуйте позже.\n");

return 1;

}

if (status == 0) {

// Попытка создания второго пайпа

if ((status = mkfifo(NAMEDPIPE2_NAME, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) != 0) {

printf("Пайпы заняты. Попробуйте позже.\n"); return 2;

}

printf("Ожидаем второй процесс...\n"); fflush(stdout);

// Открываем для записи и чтения int wFile, rFile;

if ((wFile = open(NAMEDPIPE1_NAME, O_WRONLY)) <= 0) { perror("open");

return 3;

}

if ((rFile = open(NAMEDPIPE2_NAME, O_RDONLY)) <= 0) {

5

perror("open"); return 4;

}

pthread_t readThreadId, writeThreadId;

printf("Вы можете отправлять сообщения либо прервать соединение при " "помощи команды exit\n");

pthread_create(&writeThreadId, NULL, runWriteThread, &wFile); pthread_create(&readThreadId, NULL, runReadThread, &rFile); pthread_join(writeThreadId, NULL);

return 0;

}

printf("Ожидаем второй процесс...\n"); fflush(stdout);

// Открываем для чтения и записи int rFile, wFile;

if ((rFile = open(NAMEDPIPE1_NAME, O_RDONLY)) <= 0) { perror("open");

return 5;

}

if ((wFile = open(NAMEDPIPE2_NAME, O_WRONLY)) <= 0) { perror("open");

return 6;

}

printf("Вы можете отправлять сообщения либо прервать соединение при помощи команды exit\n");

pthread_t readThreadId, writeThreadId; pthread_create(&writeThreadId, NULL, runWriteThread, &wFile); pthread_create(&readThreadId, NULL, runReadThread, &rFile); pthread_join(writeThreadId, NULL);

return 0;

}

Продемонстрируем работу программы (рис. 8).

Рисунок 8 — Процесс коммуникации двух процессов Суть пунктов меню:

1.Первый пункт для сервера;

2.Второй пункт для клиента.

Текст сообщения вводится вручную с клавиатуры.

6

Сначала левый процесс создает пайпы и ожидает правого. Правый подключается. Затем оба читают и пишут друг другу сообщения. Если левый процесс пишет stop или exit, то соединение прерывается. Команда stop используется для прерывания соединения с текущим клиентом и ожидания нового. Команда exit — для выхода из программы.

Важно понимать, что пайпы (каналы) в Linux не смещены к взаимодействию «клиент-сервер» и не работают подобно сокетам (см. лаб. 9). Функция mkfifo создает один экземпляр пайпа, который работает только с одним клиентом в один момент времени (по аналогии с трубой). Если текущий клиент отсоединяется, то следующий может занять его место — читать из пайпа / писать в пайп — такая логика организована в обоих программах.

В работе мы использовали следующие POSIX функции (табл. 3). Таблица 3 — Используемые в программе POSIX функции

close

memset

printf

remove

 

 

 

 

fflush

mkfifo

pthread_create

stat

 

 

 

 

fgets

open

pthread_join

strncmp

 

 

 

 

getchar

perror

read

write

 

 

 

 

Заключение В результате выполнения лабораторной работы мы ознакомились с

применением механизмов синхронизации процессов под Linux.

7