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

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

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

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

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

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

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

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

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

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

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

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

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

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

Постановка задачи

1.Написать скрипт для отображения списка запущенных потоков, поиска по названию процессов и запуска монитора процессов и продемонстрировать его работу.

2.Написать программу на C++ для решения задачи об обедающих философах и продемонстрировать ее работу.

Ход работы

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

Напишем скрипт для отображения списка запущенных потоков, поиска по названию процессов и запуска монитора процессов (табл. 1).

Таблица 1 — Файл script.sh

#!/bin/bash while [ true ] do

echo "-== Меню ==-"

echo "1. Список всех потоков." echo "2. Поиск по всем процессам." echo "3. Системный монитор."

echo "4. XKILL." read n

if [[ $n = "1"* ]] then

ps -A

elif [[ $n = "2"* ]] then

read -p "Введите вхождение: " substr

read -p "Остановить все найденные процессы? (y/n): " is_all if [ $is_all = "y" ] || [ $is_all = "Y" ]

then

killall $substr

fi

elif [[ $n = "3"* ]] then

top -H

elif [[ $n = "4"* ]] then

xkill

elif [[ $n = "exit"* ]] then

exit

else

echo "Ошибка ввода"

fi

done

Запустим скрипт на выполнение и проверим его работу (рис. 1, 2, 3, 4). 2

Рисунок 1 — Проверка первого пункта меню

Рисунок 2 — Проверка второго пункта меню

Рисунок 3 — Проверка третьего пункта меню

Рисунок 4 — Проверка четвертого пункта меню 3

Для того, чтобы выйти, достаточно ввести exit (и нажать Enter) или нажать Ctrl+C.

Напишем программу на C++ для решения задачи об обедающих философах (табл. 2).

Таблица 2 — Файл main.cpp

#include <iostream> #include <mutex> #include <thread>

// Класс "Вилка" class Fork {

public:

// Конструктор

Fork(std::string name) : mName(name) {}

//Ожидание и взятие вилки void take() { mMutex.lock(); }

//Попытка взятия вилки

bool tryTake() { return mMutex.try_lock(); }

// Освобождение вилки

void put() { mMutex.unlock(); }

// Получить условное название вилки std::string getName() const { return mName; }

private:

//Мьютекс std::mutex mMutex;

//Условное название вилки std::string mName;

};

// Класс "Философ" class Philosopher {

public:

// Конструктор

Philosopher(const std::string &name, Fork &leftFork, Fork &rightFork)

:mName(name), mLeftFork(&leftFork), mRightFork(&rightFork) {}

//Функция потока

void run(int iteration) {

std::string in = mName + " философ начал кушать\n"; std::string out = mName + " философ приостановил трапезу\n"; while (iteration > 0) {

//Думаем

wait();

//Берем две вилки или блокируемся mLeftFork->take();

if (!mRightFork->tryTake()) { mLeftFork->put(); continue;

}

//Обедаем

std::cout << in; std::cout.flush(); wait();

std::cout << out;

4

std::cout.flush();

// Кладем на стол обе вилки mLeftFork->put();

wait(); mRightFork->put(); wait(); --iteration;

}

}

private:

//Ожидание milliseconds миллисекунд void wait(int milliseconds = 100) const {

std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));

}

//Имя философа

std::string mName;

// Левая и правая вилки

Fork *mLeftFork, *mRightFork;

};

int main() {

using namespace std;

std::cout << "[Старт] Начался обед двух философов" << std::endl;

{

Fork f1("#1"), f2("#2");

Philosopher p1("#1", f1, f2), p2("#2", f2, f1);

thread thread1(&Philosopher::run, &p1, 2); thread thread2(&Philosopher::run, &p2, 2);

thread1.join();

thread2.join();

}

std::cout << "[Финиш] Два философа пообедали" << std::endl; std::cout << "[Старт] Начался обед пяти философов" << std::endl;

{

Fork f1("#1"), f2("#2"), f3("#3"), f4("#4"), f5("#5"); Philosopher p1("#1", f1, f2), p2("#2", f2, f3), p3("#3", f3, f4),

p4("#4", f4, f5), p5("#5", f5, f1);

thread thread1(&Philosopher::run, &p1, 2); thread thread2(&Philosopher::run, &p2, 2); thread thread3(&Philosopher::run, &p3, 2); thread thread4(&Philosopher::run, &p4, 2); thread thread5(&Philosopher::run, &p5, 2);

thread1.join();

thread2.join();

thread3.join();

thread4.join();

thread5.join();

}

std::cout << "[Финиш] Пять философов пообедали" << std::endl; return 0;

}

5

Алгоритм решения аналогичен алгоритму решения на основе монитора1. (Недостатком является отсутствие решения проблемы голодания: один из философов (или несколько) может долго ждать своих соседей.)

Запустим программу на выполнение и проверим её работу (рис. 5).

Рисунок 5 — Работа алгоритма решения задачи об обедающих философах В данном случае мы использовали C++, а не Си, так как задача лежит

ближе к ООП, нежели к чисто процедурной парадигме. В соответствии с выбранной парадигмой и языком, мы выбрали класс thread библиотеки поддержки потоков языка C++. Методы и их описания приведены в табл. 3.

Таблица 3 — Методы класса thread (#include <thread>)

bool joinable() const noexcept;

Проверяет, можно ли вызвать метод join;

 

 

std::thread::id get_id() const

Возвращает идентификатор потока;

noexcept;

native_handle_type

Возвращает базовый дескриптор потока,

native_handle();

определяемый реализацией;

1https://ru.wikipedia.org/wiki/Задача_об_обедающих_философах#Решение_на_основе_монитора

6

static unsigned int

Возвращает количество параллельных

hardware_concurrency()

потоков, поддерживаемых реализацией;

noexcept;

 

 

void join();

Ждет, пока поток закончит свое

выполнение;

 

 

Отделяет поток от объекта thread, позволяя

void detach();

потоку продолжить выполнение

 

независимо;

void swap(std::thread& other)

Меняет местами два объекта потока.

noexcept;

По желанию можно переписать эту программу с использованием

POSIX функций (табл. 4).

 

Таблица 4 — Функции POSIX для работы с потоками (#include <pthread.h>)

int pthread_create(pthread_t

Создает поток, идентификатор созданного

*restrict thread, const

pthread_attr_t *restrict attr,

потока возвращается в качестве выходного

void *(*start_routine)(void*),

параметра;

void *restrict arg);

 

pthread_t pthread_self(void);

Получает идентификатор текущего потока;

Сравнивает два идентификатора потоков

(может использоваться, к примеру, для int pthread_equal(pthread_t t1, сравнения некоторого идентификатора

pthread_t t2);

потока со значением, возвращаемым

функцией pthread_self);

int pthread_join(pthread_t

Ожидает завершения некоторого потока и

освобождает ресурсы, выделенные при его

thread, void **status);

 

создании;

int pthread_detach(pthread_t

Отделяет поток от дескриптора thread,

позволяя потоку продолжить выполнение

thread);

 

независимо;

void pthread_exit(void

Завершает текущий поток;

*value_ptr);

int pthread_cancel(pthread_t

Запрашивает принудительное завершение

target_thread);

другого потока.

Важно не путать POSIX функции и функции языка Си (табл. 5) для работы с потоками.

Таблица 5 — Функции языка C для работы с потоками (#include <threads.h>)

int thrd_create(thrd_t *thr,

Создает поток;

thrd_start_t func, void *arg);

int thrd_equal(thrd_t lhs,

Проверяет, относятся ли два

thrd_t rhs);

идентификатора к одному и тому же потоку;

thrd_t thrd_current(void);

Получает идентификатор текущего потока;

 

 

int thrd_sleep(const struct

Приостанавливает выполнение

timespec* duration, struct

 

7

timespec* remaining);

 

вызывающего потока на заданный период

 

времени;

 

 

 

 

 

 

void thrd_yield(void);

 

Дает подсказку реализации об ожидании;

 

 

 

 

_Noreturn void thrd_exit(int

 

Завершает вызывающий поток;

res);

 

int thrd_detach(thrd_t thr);

 

Отделяет поток от дескриптора thr;

 

 

 

 

int thrd_join(thrd_t thr, int

 

Блокируется, пока поток не завершится.

*res);

 

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

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

8