![](/user_photo/46612_vacAi.jpg)
Готовые отчеты / ОСиС. Лабораторная работа 7
.pdfФедеральное агентство связи ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ
ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ «САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М. А. БОНЧ-БРУЕВИЧА» (СПбГУТ)
Факультет инфокоммуникационных сетей и систем Кафедра программной инженерии и вычислительной техники
ЛАБОРАТОРНАЯ РАБОТА №7 по дисциплине «Операционные системы и сети»
на тему «Разработка многопроцессной программы под Linux»
Выполнил: студент 3-го курса дневного отделения группы ИКПИ-85
Коваленко Леонид Александрович Преподаватель:
доцент кафедры ПИиВТ Дагаев Александр Владимирович
Санкт-Петербург 2020
![](/html/46612/250/html_MHshbIg6CK.AvPY/htmlconvd-IpL2Uy2x1.jpg)
Цель работы Разработать многопроцессную программу с применением механизмов
синхронизации процессов под 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
![](/html/46612/250/html_MHshbIg6CK.AvPY/htmlconvd-IpL2Uy3x1.jpg)
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
![](/html/46612/250/html_MHshbIg6CK.AvPY/htmlconvd-IpL2Uy4x1.jpg)
Рисунок 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
![](/html/46612/250/html_MHshbIg6CK.AvPY/htmlconvd-IpL2Uy5x1.jpg)
}
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
![](/html/46612/250/html_MHshbIg6CK.AvPY/htmlconvd-IpL2Uy6x1.jpg)
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