asyan.org
добавить свой файл
1
Лабораторная работа №2

Тема: Керування потоками у Windows XP.

Мета: Вивчити програмний інтерфейс керування потоками у Windows XP.

Теоретичні відомості

Керування потоками у Windows ХР

Для того щоб виконувати код, у рамках процесу обов'язково необхідно створити потік. У системі Windows ХР реалізована модель потоків «у чистому вигляді». Процеси і потоки є різними сутностями в системі, що перебувають у чітко визначеному взаємозв’язку один з одним; для роботи з ними використовують різні системні виклики. У Windows ХР ніколи не використовували модель процесів, подібну до традиційної моделі UNIX.

Багатопотоковість Windows ХР базується на схемі 1:1. Кожному потоку користувача відповідає сутність у ядрі, при цьому ядро відповідає за планування потоків. Процеси не плануються.

Складові елементи потоку

Потік у Windows ХР складається з таких елементів:

  • вмісту набору регістрів, який визначає стан процесора;

  • двох стеків – один використовують для роботи в режимі користувача, інший – у режимі ядра; ці стеки розміщені в адресному просторі процесу, що створив цей потік;

  • локальної пам'яті потоку (ТLS);

  • унікального ідентифікатора потоку (thread id, tid), який вибирають із того самого простору імен, що й ідентифікатори процесів.

Сукупність стану процесора, стеків і локальної пам'яті потоку становить контекст потоку. Кожний потік має власний контекст. Усі інші ресурси процесу (його адресний простір, відкриті файли тощо) спільно використовуються потоками.

Розрізняють два види потоків: потоки користувача і потоки ядра, які у Windows ХР називають системними робочими потоками – system worker threads. Перші з них створюють у режимі користувача й тільки за необхідності перемикають у режим ядра. Інші створюють в ядрі під час його ініціалізації і виконують у режимі ядра протягом усього часу їхнього існування.

^ Структури даних потоку

Відображення потоків у системі, як і відображення процесів, засноване на об'єктній моделі Windows ХР. Для виконавчої системи Windows ХР кожен потік відображається об’єктом-потоком виконавчої системи (executive thread object), який також називають керуючим блоком потоку (executive thread block, ETHREAD). Для ядра системи потік відображається об'єктом-потоком ядра (kernel thread object), який також називають блоком потоку ядра (thread kernel block, KTHREAD).

У режимі користувача доступним є блок оточення потоку (thread environment block, TEB), який перебуває в адресному просторі процесу, що створив потік.

Неважко помітити, що кожній структурі даних потоку відповідає структура даних процесу (блоки EPROCESS, KPROCESS i PEB).

Керуючий блок потоку містить базову інформацію про потік, зокрема:

  • блок потоку ядра;

  • ідентифікатор процесу, до якого належить потік, і покажчик на керуючий блок цього процесу (ЕРRОСЕSS);

  • стартову адресу потоку, з якої почнеться виконання його коду;

  • інформацію для підсистеми безпеки.

Блок потоку ядра, у свою чергу, містить інформацію, необхідну ядру для організації планування і синхронізації потоків, зокрема:

  • покажчик на стек ядра;

  • інформацію для планувальника;

  • інформацію, необхідну для синхронізації цього потоку;

  • покажчик на блок оточення потоку.

Блок оточення потоку містить інформацію про потік, доступну для застосувань режиму користувача. До неї належать:

  • ідентифікатор потоку;

  • покажчик на стек режиму користувача;

  • покажчик на блок оточення процесу, до якого належить потік;

  • покажчик на локальну пам'ять потоку.

Створення потоків

Основним засобом створення потоків у Windows ХР є функція CreateThread() Win32 АРІ. Назвемо етапи виконання цієї функції.

  1. В адресному просторі процесу створюють стек режиму користувача для потоку.

  2. Ініціалізують апаратний контекст потоку (у процесор завантажують дані, що визначають його стан). Цей крок залежить від архітектури процесора.

  3. Створюють об'єкт-потік виконавчої системи у призупиненому стані, для чого в режимі ядра:

    1. створюють та ініціалізують структури даних потоку (блоки ЕТНRЕАD, КТНRЕАD, ТЕВ);

    2. задають стартову адресу потоку (використовуючи передану як параметр адресу процедури потоку);

    3. задають інформацію для підсистеми безпеки та ідентифікатор потоку;

    4. виділяють місце під стек потоку ядра.

  4. Підсистемі Win32 повідомляють про створення нового потоку.

  5. Дескриптор та ідентифікатор потоку повертають у процес, що ініціював створення потоку (викликав CreateThread()).

Починають виконання потоку (виконують перехід за стартовою адресою).

Завершення потоків у Win32 АРІ

Функцію потоку можна завершити двома способами.

  1. Виконати у ній звичайний оператор return (цей спосіб є найнадійнішим):

unsigned WINAPI thread_fun (void *num) {

return 0;}

  1. Викликати функцію _endthreadex() з параметром, що дорівнює коду повернення:

unsigned WINAPI thread_fun (void *num) {

_endthreadex(0);

}

^ Приєднання потоків у Win32 АРІ

Приєднання потоків у Win32 АРІ, подібно до очікування завершення процесів, здійснюється за допомогою функції WaitForSingleObject(). Базовий синтаксис її використання з потоками такий:

HANDLE th = (HANDLE) _beginthreadex (...);

if (WaitForSingleObject(th. INFINITE) != WAIT_FAILED) {

// потік завершений успішно

}

CloseHandle(th);

Завдання

  1. Застосовуючи інтерфейс потоків Win32, розробити додаток для Windows XP, що реалізує паралельне виконання коду двома потоками. Основний потік додатку Т створює потік t. Далі кожен із потоків виконує цикл (наприклад, до 30). На кожній ітерації циклу він збільшує значення локального лічильника на одиницю, відображає це значення з нового рядка і призупиняється на деякий час (потік Т – на час Т, потік t – t). Після завершення циклу потік Т приєднує t.

  2. Відповісти на наступні запитання:

  • Як залежать результати виконання цього застосування від значень Т і t?

  • Як зміняться ці результати, якщо потік t не буде приєднано?


Блок-схема алгоритму основної програми



^ Блок-схема алгоритму функції потоку



Текст програми:

#include

#include


#include

#include

#include

#include

#include

unsigned int WINAPI thread_fun (void *num); //Функция потока

int main()

{

unsigned tid; //идентификатор потока

int number=0, //номер потока

i=0; //переменная-счётчик, определяющая количество потоков

for(i=0;i<=1;i++)

HANDLE th = (HANDLE) _beginthreadex(NULL,0,thread_fun,(void *)++number,0, &tid); //создание потока

if (WaitForSingleObject(th,INFINITE)!= WAIT_FAILED) //присоединение потоков

{

printf("Potoki prisoedeneni uspeshno\n");

}

CloseHandle(th); //Закрытие дескриптора потока

getch(); //ожидание нажатия клавиши

return (0); //Выход из программы без ошибки

}

unsigned int WINAPI thread_fun (void *num) //функция потока

{

int count=0,j=0;

printf("Potok %d nachal vipolnenie\n", num);

for(j=0;j<10;j++)

{

count++;

printf("Znachenie schetchka v POTOKE %d: count= %d \n",num, count);

getch(); //ожидание нажатия клавиши

}

printf("Potok %d zavershil vipolnenie\n", num);

return (0); //Выход из функции без ошибки

}

^ Результат виконання програми



Висновок: При виконанні даної лабораторної роботи я навчився керувати потоками у WINDOWS XP, вивчив програмний інтерфейс керування потоками. При цьому я створив програму, що створює 2 потоки. Ці потоки паралельно змінюють значення внутрішніх лічильників. Після закінчення роботи потоки приєднуються. Також я визначив, що якщо потік t не приєднати після закінчення роботи, то робота як потоків, так і самої програми може бути завершена раніше ніж, вони виконають весь програмний код.