Готовые отчеты / ОСиС. Лабораторная работа 5
.pdfФедеральное агентство связи ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ
ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ «САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М. А. БОНЧ-БРУЕВИЧА» (СПбГУТ)
Факультет инфокоммуникационных сетей и систем Кафедра программной инженерии и вычислительной техники
ЛАБОРАТОРНАЯ РАБОТА №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