Будь умным!


У вас вопросы?
У нас ответы:) SamZan.net

з курсу Системне програмне забезпечення частина 2 для студентів спеціальності 7

Работа добавлена на сайт samzan.net:


КАФЕДРА

«СПЕЦІАЛІЗОВАНІ КОМП'ЮТЕРНІ СИСТЕМИ»

МЕТОДИЧНІ ВКАЗІВКИ

до виконання лабораторних робіт з курсу 

«Системне програмне забезпечення»

частина 2

(для студентів спеціальності 7.091503 

“Спеціалізовані комп’ютерні системи)

ЗАТВЕРДЖЕНО

на засіданні кафедри СКС

Протокол № 08 від 03.03.2006р.

РЕКОМЕНДОВАНО

на засіданні метод ради ДонДТУ

Протокол № 04 від 17.03.2006р.

Алчевськ

ДонДТУ

2006


УДК 004.42

Методичні вказівки до виконання лабораторних робіт з курсу «Системне програмне забезпечення» Частина 2 (для студентів спеціальності 7.091503 «Спеціалізовані комп'ютерні системи») / Укл.: Є.В. Мочалін – Алчевськ: ДонДТУ, 2006. – 62 с.

Розглянуто особливості створення та функціонування багатопоточних програм, а також засоби синхронізації потоків у середовищі Win32.

Укладач                                                        Є.В. Мочалін, доц.

Відповідальний редактор                          С.В. Гонтовий, доц.

Відповідальний за випуск                         О.М. Русанова


ЗМІСТ

[1]
1 Дослідження структури виконуваного файлу РЕ - формату

[1.1] 1.1 Мета роботи

[1.2] 1.2 Основні відомості

[1.3] 1.3 Самостійна підготовка

[1.4] 1.4 Порядок виконання роботи

[2] 2 Планування та створення потоків

[3] в середовищі Win32

[3.1] 2.1 Мета роботи

[3.2] 2.2 Основні відомості

[3.3] 2.3 Самостійна підготовка

[3.4] 2.4 Порядок виконання роботи

[4] 3 Синхронізація потоків у

[5] користувальницькому режимі

[5.1] 3.1 Мета роботи

[5.2] 3.2 Основні відомості

[5.3] 3.3 Самостійна підготовка

[6] 4 Синхронізація потоків у режимі ядра

[6.1] 4.1 Мета роботи

[6.2] 4.2 Основні відомості

[6.3] 4.3 Самостійна підготовка

[6.4] 4.4 Порядок виконання роботи

[7] Перелік посилань

[8]
Дотаток Б

[9]
Дотаток В

[10]
Дотаток Г


1 Дослідження структури виконуваного файлу РЕ - формату

1.1 Мета роботи

  •  Вивчення правил завантаження виконуваних файлів в операційних системах WINDOWS.
  •  Вивчення основ створення адресного простору процесів в середовищі WINDOWS.
  •  Вивчення основ використання імпорту та експорту коду та даних вWin32.

1.2 Основні відомості

Для 32-розрядних програм фірма Microsoft розробила спеціальний формат виконуваного файла. Він отримав назву переносний формат виконуваного файла (PEPortable Executable). Необхідно відмітити, що ідеї, які закладені в РЕ – формат для Win32 не є новими. Основи цього формату були закладені в операційній системі UNIX, де аналогічний формат називався COFF- формат (Common Object File Format – стандартний формат об’єктного файла). Формат виконуваного файла в 32-розрядних операційних системах названо форматом, що переноситься через прагнення фірми Microsoft створити єдиний формат виконуваного файла для реалізації ОС WINDOWS NT на різних апаратних платформах (що, звісно, не означає сумісності на рівні машинних команд).

Загальна структура РЕ – формату представлена на рисунку 1.1.

0000:0000h

MZ

This program requires

Microsoft Windows

0000:0018h

40h

0000:003Ch

xxxx

0000:xxxxh

PE/0/0

Основний РЕ – заголовок (20 байт)

Додатковий РЕ – заголовок

(змінна довжина)

Таблиця секцій

Секція коду

Секція ініціалізованих даних

Секція неініціалізованих даних

Секція експортуємих функцій

Секція імпортуємих функцій

Інші секції

Рисунок 1.1 – Загальна структура файла РЕ – формату

До основних особливостей цього формату відносяться:

  •  простота завантаження файлу РЕ – формату. Якщо порівнювати цей процес з завантаженням файлу NE формату (WINDOWS 3.x), то для РЕ – виконуваного файлу він значно простіше;
  •  підтримка суцільної моделі пам’яті. Конструкція РЕ-файла максимально спрощена с точки зору завантаження до пам’яті. Фактично, він становить собою зліпок ділянки оперативної пам’яті;
  •  не потрібно настроювати сегменти команд або даних, так як їх немає в тому вигляді, як це було в 16-розрядному середовищі;
  •  розташування багатьох полів в РЕ – файлі задається за допомогою відносного зсуву від початку РЕ – файла – Відносної Адреси Поля (ВАП). Сам РЕ – файл розташовується за Базовою Адресою Пам’яті (БАП) – фізичною адресою пам’яті, з якої починається завантажений у пам’ять модуль.

Інформація в РЕ-файлі є, в основному, високорівневою та використовується системою або програмами, щоб з’ясувати правила спілкування з конкретним виконуваним файлом.

Розглянемо основні складові частини в структурі файла РЕ – формату відповідно до рис. 1.1.

  1.  Программа-заглушка MS-DOS.

Основні міркування, з яких на початку РЕ – файла знаходиться програма-заглушка MS-DOS – при запуску в середовищі MS-DOS видати повідомлення типу This program requires Microsoft Windows. Сама програма-заглушка виконана як повноцінний MZ-файл для MS-DOS. Найбільший інтерес уявляє 4-байтове поле зі зсувом 3Сh. В ньому міститься зсув, що вказує на початок РЕ-файла.

б)  Заголовок РЕ – файла.

Заголовок РЕ-файла починається 4-байтовою сигнатурою 50 45 00 00h, що відповідає рядку “РЕ” з наступними двома байтами нулів.

Заголовок РЕ – файла можно розбити на такі частини:

  •  сигнатура РЕ – файла;
  •  основний заголовок РЕ – файла (PE – header);

в)  Додатковий заголовок РЕ-файла (PE – optional header).

Формат основного заголовку РЕ – файла представлено в таблиці 1.1. Він містить саму загальну інформацію про РЕ – файл.

Таблиця 1.1 – Структура основного заголовку РЕ – файла

Зсув

Довжина,

байт

Призначення

00h

2

Тип комп’ютера, для якого створювалася програма.

Деякі можливі значення:

0000h – невідомий тип комп’ютерної платформи;

014Ch – Intel i80x86;

0160h, 0162h, 166, 168 – MIPS;

0184h – DEC Alpha AXP;

01F0h – Power PC;

0268h – Motorola 68000.

02h

2

Кількість секцій в РЕ – файлі

04h

4

Час створення файлу. Відлік часу проводиться в секундах, що минули з 16:00 31.12.1969.

08h

4

0

0Ch

4

0

10h

2

Розмір додаткового заголовку РЕ-файла.

12h

2

Інформаційні прапори:

0001h – інформація про переміщення видалена з файла;

0002h – файл уявляє собою виконуване відображення, тобто не має нерозв’язаних зовнішніх посилань;

0004h – інформація про номери рядків видалена з файла;

0008h – розташування байт в машинному слові зворотне, тобто відповідає принципу “молодший байт за молодшою адресою”;

0100h – розмір машинного слова 32 біта;

0200h – відлагоджувальна інформація видалена з виконуваного файла в файл .dbg;

1000h – системний файл;

2000hdll-бібліотека;

в)  Додатковий заголовок.

За основним заголовком РЕ – файла іде додатковий заголовок. В таблиці 1.2 представлені розташування і значення полів, які представляють найбільший інтерес. Всі зсуви відраховуються від початку додаткового заголовка.

Таблиця 1.2 – Структура додаткового заголовку РЕ – файла

Зсув

Довжина,

байт

Призначення

+00h

2

Сигнатура, що визначає стан РЕ – файла. Можливі значення:

0107h – відображення постійного запам’ятовуючого пристрою;

010Вh – звичайний виконуваний файл.

+02h

2

Версія компанувальника, який створив виконуваний файл.

+04h

4

Розмір секції коду в байтах.

+08h

4

Розмір секції ініціалізованих даних.

+0Ch

4

Розмір секції неініціалізованих даних.

+10h

4

Адреса (ВАП), з якої починається виконання модуля. Вона знаходиться в секції коду.

+14h

4

ВАП, з якої починаються програмні секції РЕ – файла. Якщо файл був створений компанувальником Mikrosoft, то програмна секція починається з адреси 1000h. Компанувальник Borland (Inprise) розміщує секцію коду з адреси 10000h.

+18h

4

ВАП, з якої починаються секції даних РЕ – файла. Звичайно вони розташовані за секціями коду. Усі об’єкти РЕ – файла, в тому числі секції коду і даних, вирівнюються на кратну границю: компанувальник Microsoft вирівнює об’єкти на границю кратну 4 Кбайт, компанувальник Borland – на границю 64 Кбайт.


Продовження таблиці 1.2

Зсув

Довжина,

байт

Призначення

+1Ch

4

Адреса оперативної пам’яті, куди передбачається завантаження файла. Цю адресу формує компанувальник. Якщо файл дійсно завантажується за цією адресою, то завантажувальнику не потрібно робити більше ніяких настроювань, тому процес завантаження відбувається швидше. Якщо завантажувальник не може розмістити файл за вказаною адресою, то він вимушений виконувати настроювання адрес.

+20h

4

Значення вирівнювання для секції. Кожна секція в РЕ – файлі вирівнюється на границю, чисельне значення якої кратно величині, що вказана в даному полі (див. Також описання поля 0018h).

+24h

4

Значення кратності, відповідно до якого вирівнюються дані в РЕ – файлі.

+28h

4

Номер самої старої версії (модифікації) операційної системи, в середовищі якої може працювати даний файл (звичайно 1.0).

+2Сh

4

Визначається користувачем за допомогою ключа компанувальника /VERSION:n.m (для link.exe (MASM)), або /Vn.m (для tlink32.exe (TASM)). Значення n и m заносяться до двох слів цього поля.

+30Ch

4

Номери самої старої версії (і модифікації) операційної системи WINDOWS NT, в середовищі якої може працювати даний файл (звичайно 4.0).

+34h

4

0

+38h

4

Розмір РЕ – файла, вирівняний на ближню границю секції.

+3Сh

4

Загальний розмір усіх заголовків в РЕ – файлі, включаючи заголовок MS-DOS, РЕ – заголовок, Додатковий РЕ – заголовок та заголовки РЕ – секцій.

Продовження таблиці 1.2

+40h

4

Для виконуваних файлів це поле дорівнює 0. Для файлів dll - бібліотек поле містить контрольний підсумок (crc).

+44h

2

Тип користувальницького інтерфейсу, що використовується РЕ – файлом:

0000h – невідома підсистема;

0001h – підсистема не потрібна (драйвер пристрою);

0002hWindows GUI;

0003h – консольна програма Windows;

0005hOS/2;

0007hPosix.

+46h

2

0

+48h

4

Потрібний об’єм пам’яті для стека.

+4Сh

4

Виділювана кількість пам’яті для стека.

+50h

4

Потрібний об’єм пам’яті для локальної кучі.

+54h

4

Виділювана кількість пам’яті для локальної кучі.

+58h

4

0

+5Ch

4

Кількість елементів масиву структур, що описують місцерозташування і розмір деяких значимих таблиць і областей виконуваного файла. Значення звичайно дорівнює 10h.

+60h

10h*2*4

Масив структур, кожна з яких складається з двох полів (розмірність кожного поля dword):

ВАП розташування даних в РЕ – файлі;

розмір даних.

Реальне наповнення мають перші 12 структур в даному масиві. Вони описують розташування таких даних в РЕ – файлі:

0 – таблиця експортуємих функцій;

1 – таблиця імпортуємих функцій;

2 – таблиця ресурсів;

3 – таблиця виключень;

4 – таблиця безпечності;

5 – таблиця настроювання;

та ін.

За масивом структур, якими закінчується заголовок РЕ-файла, починається таблиця секцій.

г)  Таблиця секцій.

Якщо не враховувати програми-заглушки для MS-DOS і необов’язкової області відлагоджувальної інформації, то РЕ – файл складається з двох великих частин – заголовка та області загальних об’єктів, які звуться секціями. Інформація про усі секції РЕ – файла зберігається в таблиці секцій. Таблиця секцій розташовується відразу після заголовка РЕ – файла. Кількість секцій визначається за значенням поля на початку основного заголовка +2h.

Таблиця секцій складається з структур розміром по 40 байт кожна. Структури детально описують відповідні секції. Поля, які складають структуру таблиці секцій, представлені в таблиці 1.3.

 

Таблиця 1.3 – Зміст структури в таблиці секцій

Зсув

Довжина,

байт

Призначення

+00h

8

Ім’я секції (якщо ім’я менше 8 байт, то воно додано справа нуями).

+08h

4

Розмір секції (потрібна кількість пам’яті).

+0Сh

4

ВАП, за якою завантажувальник повинен завантажити секцію.

+10h

4

Розмір секції, вирівняний на найближчу (в більший бік) границю, у відповідності з розміром вирівнювання (см. Поля +20h и +24h в табл. 1.2).

+14h

4

ВАП в РЕ – файлі, де знаходяться дані для секції. Це поле сумісно з полем +0Сh необхідне для того, щоб дозволити програмісту самому керувати завантаженням РЕ – файла.

+18h

12

0

+24h

4

Флаги, які характеризують атрибути секції.

Слідом за заголовком розміщена основна частина РЕ-файла – розділ секцій. Тут ми не будемо приводити докладний опис секцій. Цю інформацію можна отримати, наприклад, у [1].

1.3 Самостійна підготовка

Підготуйте бланк звіту. Проробіть рекомендовану літературу [1].

При підготовці зверніть увагу на такі питання:

  •  загальна структура РЕ-файла;
  •  структура основного заголовку РЕ- файла;
  •  структура додаткового заголовка РЕ – файла;
  •  основні секції РЕ – файла.

Уважно проаналізуйте текст консольної Windows програми read_pe.asm (додаток), за допомогою якої в отладчику td32.exe можна проаналізувати значення усіх полів заголовків виконуваного файла, ім’я якого задається в сегменті даних.

1.4 Порядок виконання роботи

  •  В будь якому текстовому редакторі наберіть текст модуля read_pe.asm, який наведено у лістінгу 1.1 і збережіть його на диску.
  •  Сформуйте модуль, що виконується (файл read_pe.exe).
  •  Перевірте роботу програми read_pe.exe.
  •  У відладчику настройте дамп пам’яті на буфер з началом досліджуваного виконуваного модуля. Користуючись засобами відладчика (зокрема, вікном Variables) дослідить зміст основних полів заголовку РЕ – файла.
  •  Занесить в звіт отриману інформацію згідно свого варіанту.

2 Планування та створення потоків

в середовищі Win32

2.1 Мета роботи

  •  Вивчення основ створення багатопоточних програм.
  •  Засвоєння особливостей створення, закриття та керування роботою дочірніх потоків.
  •  Засвоєння засобів створення та керування потоками, що надаються операційною системою Windows.

2.2 Основні відомості

Потік (Thread) визначає послідовність виконання коду в процесі. При ініціалізації процесу система завжди створює первинний потік. Більшість процесів обходиться одним первинним потоком. Але ж процеси можуть створювати додаткові потоки, що дозволяє їм ефективніше виконувати свою роботу.

Кожен потік починає свою роботу з деякої вхідної функції. На мові асемблера це оформлюється у такий спосіб:

TreadProc  proc

arg   pvParam:DWORD

ret

ThreadProc endp

Функція потоку може виконувати будь які завдання. Раніше або пізніше вона закінчує свою роботу та повертає керування. У цей момент потік зупиняється, пам’ять, що була відведена під його стек, звільняється, а лічильник користувачів його об’єкта ядра “потік” зменшується на 1. Коли лічильник обнулюється, цей об’єкт ядра руйнується системою. Але цей об’єкт ядра може жити значно довше, ніж потік, що зіставлений з ним.

При розробці функції потоку необхідно звертати увагу на такі основні питання:

  •  Функція потоку може мати будь яке ім’я. Але, якщо у програмі є декілька функцій потоків, їм треба присвоювати різні імена, інакше компілятор або компанувальник вирішать, що створюються декілька реалізацій однієї і тієї ж функції.
  •  Функції потоку передається один єдиний параметр, сенс якого визначається програмістом, а не операційною системою.
  •  Функція потоку може повертати значення (у регістрі еах), яке використовується як код завершення потоку.
  •  Функції потоків за мірою можливості повинні обходитися своїми параметрами та локальними змінними. Так як до статичної або глобальної змінної можуть одночасно звернутися декілька потоків, є ризик зіпсувати її вміст. Але ж параметри та локальні змінні створюються у стеку потоку, тому вони у значно меншому ступені піддані впливу інших потоків.

Для створення потоків у середовищі Win32 призначена функція API CreateThread.

При кожному виклику цієї функції система створює об’єкт ядра “потік”. Це не сам потік, а компактна структура даних, що використовується операційною системою для керування потоком та зберігає статистичну інформацію про потік.

Система виділяє пам’ять під стек потоку з адресного простору процесу. Новий потік виконується у контексті того ж процесу, що й батьківський потік. Тому він отримує доступ до всіх описувачів об’єктів ядра, всієї пам’яті і стеків усіх потоків процесу. За рахунок цього потоки у рамках одного процесу можуть легко взаємодіяти один з одним.

Функції CreateThread передаються 6 параметрів:

  •  Параметр psa є покажчиком на структуру типу SECURITY_ATTRIBUTES. Якщо достатньо, щоб об’єкту ядра “потік” були присвоєні атрибути захисту за умовчанням (що буває частіше за всього), то в цьому параметрі передають значення NULL. А для того, щоб дочірні процеси змогли успадкувати описувач цього об’єкту, треба визначити структуру типу SECURITY_ATTRIBUTES та ініціалізувати її елемент hinheritHandle значенням TRUE.
  •  Параметр cbStack визначає, яку частину адресного простору потік зможе використувати під свій стек. Якщо в цьому параметрі передається нульове значення, стек для нового потоку створюється на основі інформації, вбудованої компанувальником у ехе-файл.
  •  Параметр pfnStartAddr визначає адресу функції потоку, з якої буде починати роботу потік, що створюється.
  •  Параметр pvParam ідентичний однойменному параметру функції потоку. Таким чином, цей параметр дозволяє передавати функції потоку яке-небудь ініціализуюче значення. Воно може бути або просто числовим значенням, або покажчиком на структуру даних з додатковою інформацією.
  •  Параметр fdwCreate визначає додаткові прапори, що керують створенням потоку. Він приймає одне з двох значень: 0 (виконання потоку починається миттєве) або CREATE_SUSPENDED. В останньому випадку система створює потік, ініціализує його та призупиняє до наступних вказівок.
  •  Параметр pdwThreadID це адреса змінної типу DWORD, у якій функція звертає ідентифікатор, зіставлений системою новому потоку.
  •  Функцію потоку слід проектувати так, щоб потік завершувався тільки після того, як вона вертає керування. Це єдиний спосіб, який гарантує коректне очищення усіх ресурсів, що належать потоку.

Більш докладну інформацію про планування, створення та закриття потоків можна отримати, наприклад в [1].

2.3 Самостійна підготовка

  •  Підготуйте бланк звіту. Проробіть рекомендовану літературу [1].
  •  При підготовці зверніть увагу на такі питання:

а)  в яких випадках створюються потоки;

б)  побудова функції потоку;

в)  функція CreateThread та її параметри;

г)  завершення потоків;

д)  призупинення та відновлення потоків.

  •  Уважно проаналізуйте текст програми thread1.asm (додаток Б), в якої надано приклад створення дочірнього потоку для запису даних у файл на жорсткому диску.

2.4 Порядок виконання роботи

  •  В будь якому текстовому редакторі наберіть текст модуля thread1.asm, який наведено у додатку Б і збережіть його на диску.
  •  Сформуйте модуль, що виконується (файл thread1.exe).
  •  Перевірте роботу програми thread1.exe.
  •  Дослідіть як впливають на порядок виконання батьківського та дочірнього процесів значення параметра функції Sleep, який задається значенням змінної time_sleep в сегменті даних.
  •  Дослідіть як впливають на порядок виконання батьківського та дочірнього процесів об’єм даних, що виводяться у файл на диску. Об’єм даних визначається числом повторень запису рядка символів. Ця величина задається останнім елементом структури типу THREAD_PARAM (numrep).
  •  Про послідовність виконання потоків робить висновки на підставі порядку виведення на екран консолі повідомлень від батьківського та дочірнього потоків.
  •  Опишіть у звіті результати досліджень та зробіть належні висновки.

3 Синхронізація потоків у

користувальницькому режимі

3.1 Мета роботи

  •   Вивчення особливостей сумісного використання ресурсів різними потоками у багатозадачному операційному середовищі.
  •   Засвоєння особливостей маніпулювання сумісними даними.
  •   Засвоєння засобів синхронізації потоків у користувальницькому режим у середовищі Win32.

3.2 Основні відомості

Потоки повинні взаємодіяти один з одним у двох основних випадках:

  •  при сумісному використанні ресурсу, що розділяється (щоб не зруйнувати його);
  •  коли потрібно повідомляти інші потоки про завершення деяких операцій.

Значна частина питань синхронізації потоків пов’язана з так званим атомарним доступом (atomic access) – монопольним захватом ресурсу потоком, що звертається до нього. Атомарний доступ є необхідним у багатозадачних операційних системах тоді, коли потрібна гарантія, що під час звернення одного потоку до ресурсу, що розділяється, другий потік, якому може бути наданий процесорний час, не зможе також отримати доступ до цього ресурсу. Приклади можливих ситуацій можна знайти, наприклад у [1],[2].

Одним з засобів організації атомарного доступу до ресурсів є використання сімейства Interlocked-функций, які є у складі API Win32. Ці функції прості для розуміння і вельми корисні. Усі функції цього сімейства маніпулюють змінними на рівні атомарного доступу.

У якості приклада розглянемо функцію InterlockedExchange.

Цій функції передаються два параметри.

Параметр pTаrget є покажчиком на змінну розміром 32 біти, яка містить значення, зміна якого відбувається у режимі атомарного доступу.

Параметр lValue задає значення на яке буде змінено значення поля pTаrget.

Функція InterlockedExchange монопольно змінює значення поля, позначеного першим параметром на значення другого параметра. Початкове значення змінної pTаrget  вертається в якості значення функції.

Алгоритми синхронізації з використанням функцій типу InterlockedExchange засновані на організуванні циклів, в яких перевіряються та змінюються значення змінних у режимі атомарного доступу. Такий підхід стосовно входу до критичної секції ще називають спін-блокуванням. При використанні спін-блокування слід пам’ятати, що процесорний час витрачається вхолосту лише на циклічну перевірку значення змінної, яка визначає дозвіл на отримання ресурсу. Процесору доводиться постійно порівнювати два значення, поки одне з них не буде змінено іншим потоком.

Неефективність спін-блокування, особливо на однопросесорних машинах, пов’язана з порожньою тратою процесорного часу. Більш ефективний механізм повинен заборонити потоку, що очікує доступу до ресурсу витрачати процесорний час. Поки ресурс зайнятий, або поки не відбулася “особлива подія”, система переводить потік в режим очікування, виключаючи його з числа плануємих, та бере на себе роль агента, що діє в інтересах сплячого потоку. Вона виводить його з режиму очікування, коли звільниться потрібний ресурс, або відбудеться “особлива подія”.

В складі функцій API Win32 є група функцій, яка призначена для контролю входу до критичних секції. Критична секція – це невелика ділянка коду, що потребує монопольного доступу до деяких спільних даних. Функції цієї групи дозволяють зробити так, щоб одночасно тільки один потік отримував доступ до ресурсу, що контролюється. Система може в любу мить витиснути Ваш потік і підключити до процесору другий, але ні один з потоків, яким потрібен зайнятий ресурс, не отримає процесорного часу до тих пір, поки Ваш потік не вийде за межі критичної секції.

До цієї групи належать функції: InitializeCriticalSection, DeleteCriticalSection, EnterCriticalSection, LeaveCriticalSection. В якості параметра цім функціям передається покажчик на структуру типу CRITICAL_SECTION.

Правила застосування цих функцій стають понятими з аналізу програми synchr1.asm, яку приведено у додатку С.

Більш докладну інформацію про синхронізацію потоків в режимі користувача при програмуванні під Win32 можна отримати, наприклад в [1].

3.3 Самостійна підготовка

  •  Підготуйте бланк звіту. Проробіть рекомендовану літературу [1],[2].
  •  При підготовці зверніть увагу на такі питання:

а) необхідність монопольного користування спільними даними в мнопоточній програмі;

б) основи спін-блокування, переваги та недоліки;

в) interlocked-функції API Win32;

г) поняття і організація критичних секцій;

д) правила використання функцій роботи з критичними секціями.

  •  Уважно проаналізуйте текст програми synchr1.asm (додаток С), у якій два потоки реалізують запис даних в один файл на жорсткому диску в асинхронному режимі. Ділянки коду, що відповідають запису даних, оформлені як критична секція в обох потоках. Для контролю монопольного доступу кожного потоку до файлу використовуються функції роботи з критичними секціями.


3.4 Порядок виконання роботи

  •  В будь якому текстовому редакторі наберіть текст модуля synchr1.asm, який наведено у додатку С і збережіть його на диску.
  •  Сформуйте модуль, що виконується (файл synchr1.exe).
  •  Перевірте роботу програми synchr1.exe.
  •  Дослідіть як зміниться результат запису у файл, якщо закоментувати рядки коду, пов’язані з викликом функцій EnterCriticalSection, LeaveCriticalSection. Зробіть у звіті відповідні висновки.
  •  Модифікуйте програму з використанням замість спеціалізованих функцій роботи с критичною секцією функції InterlockedExchange.
  •  Підготуйте модифіковану Вами програму до виконання за допомогою засобів пакету TASM.
  •  Протестуйте роботу Вашої програми і занесіть у звіт її текст.

4 Синхронізація потоків у режимі ядра

4.1 Мета роботи

  •  Вивчення особливостей організації взаємодії між паралельними потоками за допомогою об’єктів ядра Win32.
  •  Засвоєння функцій API, які призначені для синхронізації потоків і процесів з переводом у режим ядра.
  •  Опанування навичками синхронізації потоків за допомогою об’єктів ядра.

4.2 Основні відомості

Хоча механізми синхронізації в користувальницькому режимі забезпечують високу бистродію, їм властива низка обмежень, і в деяких випадках вони просто не будуть працювати.

Використання об’єктів ядра для синхронізації дає значно більші можливості, ніж механізми синхронізації в користувальницькому режимі. Єдиний їх недолік – менша швидкодія. Справа у тому, що при викликанні функцій, які забезпечують синхронізацію за допомогою об’єктів ядра, потік повинен перейти з користувальницького режиму у режим ядра. Цей перехід займає понад 1000 процесорних тактів на платформі х86. Сюди також треба додати час на виконання коду цих функцій в режимі ядра.

Об’єкти ядра “процес” та “потік” перебувають в зайнятому стані доки виконується відповідний процес або потік, та переходять у вільний стан коли процес або потік завершуються. Усередині цих об’єктів підтримується булєва змінна, яка при створенні об’єкта приймає значення FALSE (“зайнято”). По закінченні роботи відповідного процесу або потоку операційна система змінює значення цієї змінної на TRUE, що свідчить про те, що об’єкт є вільним.

У вільному та зайнятому стані можуть перебувати такі об’єкти ядра:

  •  процеси,
  •  потоки,
  •  завдання,
  •  файли,
  •  консольне введення,
  •  повідомлення про зміну файлів,
  •  події,
  •  очікувані таймери,
  •  семафори,
  •  м’ютекси.

У складі API Win32 є функції, які дозволяють потоку в любий момент зупинитися та очікувати доки визначений об’єкт ядра не перейде у вільний стан. З усього сімейства цих функцій частіше за всі використовується WaitForSingleObject.

Цій функції передаються два параметри.

  •  Параметр hObject є 32-бітним дескриптором (описувачем) відповідного об’єкту ядра.
  •  Параметр dwMilliseconds (типу DWORD) вказує скільки часу (у мілісекундах) потік готовий очікувати звільнення об’єкту. Значення INFINITE цього параметра (яке визначене як –1) свідчить про те, що потік готовий очікувати скільки завгодно.

Протягом очікування потоку не надається процесорний час, що економить ресурси системи. Значення, що повертається цією функцією характеризує об’єкт, дескриптор якого передається у якості аргументу. Докладніше про можливі значення можна узнати з довідкової інформації (див., наприклад, Platform SDK documentation на сайті компанії Microsoft).

Самим простим об’єктом ядра, який може використовуватись для синхронізації потоків, є подія. Ці об’єкти місять лічильник (як усі об’єкти ядра), та дві булєві змінні. Одна визначає тип даного об’єкту-події, друга – його стан (“зайнято” або “вільно”). Події просто повідомляють про закінчення деякої операції. Об’єкти – події бувають двох типів: зі скиданням вручну або з автоскиданням. Перші дозволяють після здійснення події виконуватися декільком потокам, другі – тільки одному. Ініціалізуючий потік переводить об’єкт – подію у стан “зайнято” і переходить до своєї операції. Після закінчення він переводить об’єкт – подію у вільний стан, що дає можливість другим потокам, що очікували переходу події у такий стан, прокинутись і знов стати такими, що плануються.

Об’єкт ядра “подія” створюється функцією CreateEvent.

Цієї функції передаються три параметри.

  •  Параметр psa є покажчиком на структуру типу SECURITY_ATTRIBUTES. Якщо достатньо, щоб об’єкту ядра “подія” були присвоєні атрибути захисту за умовчанням (що буває частіше за всього), то в цьому параметрі передають значення NULL. А для того, щоб дочірні процеси змогли успадкувати описувач цього об’єкту, треба визначити структуру типу SECURITY_ATTRIBUTES та ініціалізувати її елемент hinheritHandle значенням TRUE.
  •  Параметр fManualReset (булєва змінна) повідомляє системі, чи буде об’єкт зі скиданням вручну (TRUE), або з автоскиданням (FALSE).
  •  Параметр fInitialState визначає початковий стан події — вільний (TRUE), або зайнятий (FALSE).
  •  Параметр pszName є покажчиком на рядок, що визначає ім’я об’єкту. Цей параметр потрібний для сумісного використання об’єкту різними процесами. Якщо таке використання не планується, цей параметр може бути заданий як NULL.

Об’єкти ядра мютекси гарантують потокам взаємовиключаючий доступ до єдиного ресурсу. Вони містять лічильник числа користувачів, лічильник рекурсії, та змінну, в якої запам’ятовується ідентифікатор потоку, що володіє цим об’єктом. М’ютекси ведуть себе також, як критичні секції, але є об’єктами ядра і дозволяють синхронізувати доступ до ресурсу декілька потоків з різних процесів. При цьому можна задавати максимальний час очікування доступу до ресурсу.

Ідентифікатор потоку визначає, який потік захопив м’ютекс, а лічильник рекурсій – скільки разів. У м’ютексів багато застосувань і це найчастіше використовувані об’єкти ядра. Як правило, за їх допомогою здійснюється захист блоків пам’яті, до яких звертаються багато потоків. М’ютекси гарантують, що кожен потік отримає монопольний доступ до блоку пам’яті, і в такий спосіб захищають цілісність даних, що зберігаються у ньому.

Для м’ютексів визначені такі правила:

  •  якщо його ідентифікатор потоку дорівнює 0 (у самого потоку не може бути такого ідентифікатору), м’ютекс не захоплений жодним потоком і перебуває у вільному стані;
  •  якщо його ідентифікатор потоку не дорівнює 0, м’ютекс захоплений одним з потоків і перебуває у зайнятому стані;
  •  на відміну від інших об’єктів ядра м’ютекси можуть порушувати звичайні правила, що діють в операційній системі.

Для використання м’ютексу один з потоків повинен створити його за допомогою функції CreateMutex. Цієї функції передаються три параметра. Перший та останній з них ідентичні відповідним параметрам функції CreateEvent. Другий параметр fIniLialOwner визначає початковий стан м’ютекса. Якщо в ньому передається FALSE, то м’ютекс не належить жодному потоку і перебуває у вільному стані. Якщо значення параметру TRUE, ідентифікатор потоку м’ютекса, стає рівним ідентифікатору потоку, що викликає функцію CreateMutex, а лічильник рекурсії отримує значення 1. При цьому м’ютекс спочатку перебуває у зайнятому стані.

Потік отримує доступ до ресурсу, що поділяється, викликаючи одну з Wait – функцій з переданням до неї описувача м’ютекса, що охороняє цей ресурс.

Більш докладне описання роботи з розглянутими та іншими об’єктами ядра, що використовуються для синхронізації потоків, є, наприклад, в [1].

4.3 Самостійна підготовка

  •  Підготуйте бланк звіту. Проробіть рекомендовану літературу [1],[2].
  •  При підготовці зверніть увагу на такі питання:

а) основні особливості використання об’єктів ядра для синхронізації;

б) перелік об’єктів ядра та їх створення;

в) використання Wait – функцій;

г) організація доступу до ресурсів, що розділяються, за допомогою подій і м’ютексів.

  •  Уважно проаналізуйте текст програми synchr3.asm (додаток Г), в якої два дочірніх потоки видають запит на введення рядку з консолі та записують свої рядки в один буфер, який потім виводиться батьківським потоком на екран консолі. Введення з консолі і зміна змісту буфера виконуються як клієнтський запит в монопольному режимі. Для розподілу доступу до виконання цієї операції використаний об’єкт “подія”.

4.4 Порядок виконання роботи

  •  В будь якому текстовому редакторі наберіть текст модуля synchr3.asm, який наведено у додатку Г і збережіть його на диску.
  •  Сформуйте модуль, що виконується (файл synchr3.exe).
  •  Перевірте роботу програми synchr3.exe.
  •  Дослідіть як зміниться результат виводу на консоль, якщо закоментувати рядки коду, пов’язані з викликом функції WaitForSingleObject та функції SetEvent, яка переводить об’єкт – “подію” у вільний стан.
  •  Модифікуйте програму таким чином, щоб та же задача була вирішена за допомогою об’єкта ядра – “м’ютекс”.
  •   Підготуйте модифіковану Вами програму до виконання за допомогою засобів пакету TASM.
  •  Протестуйте роботу Вашої програми і занесіть у звіт її текст.

Перелік посилань

1.  Рихтер Д. Windows для профессионалов: создание эффективных Win32 – приложений., 2001.- 825с.

2.  Гордеев А.В., Молчанов А.Ю. Системное программное обеспечение., 2001.- 736с.


Дотаток А

Текст програми read_pe.asm

;-----------------------------------------------------------------------------

;read_pe.asm - Win32-консольна програма для дослідження

;структури файлів формату PE за допомогою відладчика.

;-----------------------------------------------------------------------------

.486

.model flat,STDCALL ;модель пам’яті flat,

;STDCALL - передача параметрів в стилі С (справа наліво),

; процедура, що викликається, чистить за собою стек

%NOINCL ;заборонити виведення тексту файлів включення.

include WindowConA.inc

;Объявлення зовнішніми функцій Win32 (ASCII), що використовуються:

extrn AllocConsole:PROC

extrn SetConsoleTitleA:PROC

extrn   ReadFile:PROC

extrn CloseHandle:PROC

extrn ExitProcess:PROC

extrn CopyFileA:PROC

extrn MoveFileA:PROC

extrn CreateFileA:PROC

extrn GetFileInformationByHandle:PROC

extrn GetLastError:PROC

extrn GetFileTime:PROC

extrn   SetFileTime:PROC

extrn            BeginPaint:PROC

extrn            CreateWindowExA:PROC

extrn            DefWindowProcA:PROC

extrn            DispatchMessageA:PROC

extrn            EndPaint:PROC

extrn            ExitProcess:PROC

extrn            FindWindowA:PROC

extrn            GetMessageA:PROC

.data

lpbuff  db    1000 dup(0)

TitleText db ‘Читання файла в форматі PE’,0

lpfnam db "my_file.exe",0

hFile dd 0

numbyte dd 1000

numread dd 0

pheader dd 0

.code

start proc near  ;точка входу до програми

;запит консолі

call AllocConsole

;перевірка успіху запиту консолі

test ax,ax

jz exit ;якщо невдача

;----------------------CreateFile--------------------------------------

;відкриття файлу (CreateFileA)

push 0

push NULL ;атрибути (вони ігноруються)

push OPEN_EXISTING ;відкриття існуючого файлу,

;якщо його немає - помилка

push 0 ;захист файла не потребується

push FILE_SHARE_READ ;дозволено сумісне

; використання файла (за читанням)

push GENERIC_READ ;дозволено читання з файла

push offset lpfnam

call CreateFileA

cmp eax,0ffffffffh

je exit   ;якщо невдача

mov hFile,eax  ;дескриптор файла

;-----Читання перших numbyte байтів з відкритого файла (ReadFile)-----

push NULL  ;синхронне введення

push offset numread ;покажчик на буфер з кількістю

; прочитаних байтів

push numbyte  ;кількість байтів для читання

push offset lpbuff ;покажчик на буфер для читання

push hFile  ;дескриптор відкритого файла

call ReadFile

cmp eax,NULL

je exit  ;якщо неуспіх

;------------Закриття файла (CloseHandle)-------------------------

mov eax,dword ptr (lpbuff+003ch)

mov ebx,dword ptr (lpbuff+eax)

mov pheader,ebx

 

;результат дивимось у відладчику TD32.exe

;… … …

push hFile

call CloseHandle

exit:

;вихід з програми

;готуємо виклик VOID ExitProcess(UINT uExitCode)

push 0

call ExitProcess

start endp

end start


Дотаток Б

Текст програми thread1.asm

;-----------------------------------------------------------------------

;thread1.asm - Win32 консольна программа

;для створення і закриття дочірнього потоку.

;------------------------------------------------------------------------

.486

.model flat,STDCALL ;модель пам'яті flat,

;STDCALL - передача параметрів в стилі С (справа наліво),

;процедура, що викликається чистить за собою стек

%NOINCL ;заборонити вивід тексту файлів включення

include WindowConA.inc

;Об’явлення зовнішніми функцій Win32 (ASCII),

;що використовуються у програмі:

extrn AllocConsole:PROC

extrn SetConsoleTitleA:PROC

extrn SetConsoleCursorPosition:PROC

extrn   ReadFile:PROC

extrn CloseHandle:PROC

extrn ExitProcess:PROC

extrn MoveFileA:PROC

extrn CreateFileA:PROC

extrn GetStdHandle:PROC

extrn   WriteConsoleA:PROC

extrn ReadConsoleA:PROC

extrn   WriteFile:PROC

extrn GetLastError:PROC

extrn CreateThread:PROC

extrn   ExitProcess:PROC

extrn Sleep:PROC

;Макрокос установки курсору у задану позицію

Set_cursor macro cursor,posx,posy,descr,lbl

mov cursor.xx,posx

push eax

mov ax,posy

mov cursor.yy,ax

pop eax

push cursor

push descr

call SetConsoleCursorPosition

cmp eax, 0                           

jz lbl ;якщо невдача

endm

;Структура для передання параметрів у процедуру

THREAD_PARAM struc

fnam db "outputfile.dat",16 dup(0) ;ім’я файла для запису

str db "string for writing",13,11 dup(0) ;рядок для запису

nums dd 19

numrep dd 50000  ;число повторень запису рядку

THREAD_PARAM ends

;структура для установки положення курсору в консолі:

COORD  struc

xx dw 0

yy dw 0

COORD  ends

.data

t_param         THREAD_PARAM <>

con  COORD  <>

pThreadID dd 0

hThread  dd 0

dOut  dd      0 ;дескриптор виводу консолі

dIn  dd      0 ;дескриптор вводу консолі

NumWri  dd 0

string1  db "Thread creation",13,0

len1= $-string1

string2  db "Press <Enter> to terminate parent thread",13,0

len2= $-string2

string3  db "Child thread begins operation",13,0

len3= $-string3

string4  db "Child thread terminates operation",13,0

len4= $-string4

title_text db "Multythread application",0

numrow  dw 1

time_sleep dd 0 ;Затримка у виконанні потоку

.code

start proc

;точка входу до програми:

;запит консоли

call AllocConsole

;перевірити успіх запиту консолі

test ax,ax

jz exit  ;якщо невдача

;Виведення заголовку консолі

push offset title_text

call SetConsoleTitleA

;------отримати стандартний дескриптор виводу--------------------------

push STD_OUTPUT_HANDLE

call GetStdHandle

mov dOut,eax ;у dOut-дескриптор виводу консолі

cmp eax, 0                           

jz exit  ;якщо невдача

;-------отримати стандартний дескриптор введення------------------------

push STD_INPUT_HANDLE

call GetStdHandle

mov dIn,eax  ;у dIn-дескриптор введення консолі

cmp eax, 0                           

jz exit  ;якщо невдача

;------вивести повідомлення------------------------------------------------------

Set_cursor con,1,numrow,dOut,exit

inc numrow

push 0

push offset NumWri ;кількість виведених на екран

;символів

push len1 ;довжина рядку для виводу на екран

push offset string1 ;адреса рядку для виводу на екран

push dOut

call WriteConsoleA

cmp eax,0

jz exit ;якщо невдача

;----------створення дочірнього потоку------------------------------------------

push offset pThreadID

push NULL

push offset t_param

push offset ThreadProc

push NULL

push NULL

call CreateThread

cmp eax,NULL

je exit   ;якщо невдача

mov hThread,eax

;----------Закриття описувача дочірнього потоку-----------------------------

push hThread

call CloseHandle

;------------Затримка у виконанні потоку---------------------------------------

push time_sleep

call    Sleep

;------------Вивести повідомлення------------------------------------------------

Set_cursor con,1,numrow,dOut,exit

inc numrow

push 0

push offset NumWri ;кількість виведених на екран

;символів

push len2 ;довжина рядку для виводу на екран

push offset string2 ;адреса рядку для виводу на екран

push dOut

call WriteConsoleA

cmp eax,0

jz exit  ;якщо невдача

;-------очикування переводу каретки для виходу---------------------------

push 0

push offset NumWri ;кількість виведених символів

push len1  ;розмір буферу вводу

push offset string1

push dIn

call ReadConsoleA  

cmp eax, 0

jz exit  ;якщо невдача

exit:

;Вихід з програми

;Підготовка виклику VOID ExitProcess(UINT uExitCode)

push 0

call ExitProcess

start endp

;--------------Процедура потоку--------------------------------------------

ThreadProc proc

arg pt_param:DWORD

uses ebx,edi,esi

local @hFile:DWORD

;-------Вивести повідомлення---------------------------------------------

Set_cursor con,1,numrow,dOut,exit_proc

inc numrow

push 0

push offset NumWri ;кількість виведених на екран

;символів

push len3  ;довжина рядку для виводу на екран

push offset string3 ;адреса рядку для виводу на екран

push dOut

call WriteConsoleA

;Відчиняємо файл (CreateFileA)

push 0

push NULL  ;атрибути (вони ігноруються)

push OPEN_ALWAYS ;відчинити або створити файл

push 0  ;захист файлу не потрібний

push FILE_SHARE_WRITE ;дозволено сумісне

;використання файлу (за записом)

push GENERIC_WRITE ;дозволено записувати у файл

push    pt_param

call CreateFileA

cmp eax,0ffffffffh

je exit_proc ;якщо невдача

mov @hFile,eax ;дескриптор файла

;----------------------------------------------------------------------------------

mov ebx,pt_param

mov edx,ebx

add edx,30

mov ecx,dword ptr[ebx+64]

jecxz exit_proc

cycl:

push ecx

push edx

;-----------Запись строки в файл (WriteFile)------------------------

push NULL  ;синхронне введення

push offset NumWri ;покажчик на буфер з кількістю

;записаних байтів

push dword ptr [ebx+60] ;число байтів для виводу

push edx  ;покажчик на рядок для запису

push @hFile  ;дескриптор відчиненого файлу

call WriteFile

cmp eax,NULL

je exit_proc ;якщо неуспіх

pop edx

pop ecx

loop cycl

exit_proc:

;------------Закриття файла (CloseHandle)-------------------------------

push @hFile

call CloseHandle

;------------Вивести повідомлення----------------------------------------

Set_cursor con,1,numrow,dOut,exit_proc

inc numrow

push 0

push offset NumWri ;кількість виведених на екран

;символів

push len4 ;длина рядку для виводу на екран

push offset string4 ;адреса рядку для виводу на екран

push dOut

call WriteConsoleA

ret

ThreadProc  endp

end start


Дотаток В

Текст програми synchr1.asm

;------------------------------------------------------------------------

synchr1.asm - Win32 консольна программа

;для створення і закриття дочернего потоку.    

;------------------------------------------------------------------------

.486

.model flat,STDCALL ;модель пам'яті flat,

;STDCALL - передача параметрів в стилі С (справа наліво),

;процедура, що викликається чистить за собою стек

%NOINCL ;заборонити вивід тексту файлів включення

include WindowConA.inc

;Объявлення зовнішніми функцій Win32 (ASCII),

; що використовуються у програмі:

extrn AllocConsole:PROC

extrn SetConsoleTitleA:PROC

extrn SetConsoleCursorPosition:PROC

extrn ReadFile:PROC

extrn CloseHandle:PROC

extrn ExitProcess:PROC

extrn MoveFileA:PROC

extrn CreateFileA:PROC

extrn GetStdHandle:PROC

extrn WriteConsoleA:PROC

extrn ReadConsoleA:PROC

extrn WriteFile:PROC

extrn GetLastError:PROC

extrn CreateThread:PROC

extrn ExitProcess:PROC

extrn SwitchToThread:PROC

extrn Sleep:PROC

extrn InitializeCriticalSection:PROC

extrn EnterCriticalSection:PROC

extrn LeaveCriticalSection:PROC

;Макрокос установки курсора у задану позицію

Set_cursor macro cursor,posx,posy,descr,lbl

mov cursor.xx,posx

push eax

mov ax,posy

mov cursor.yy,ax

pop eax

push cursor

push descr

call SetConsoleCursorPosition

cmp eax, 0                           

jz lbl ;якщо невдача

endm

;Макрос затримки виконання програми

delay macro time

local labl

push ecx

mov ecx,time

labl:

loop labl

pop ecx

endm

;структура для установки положення курсору в консолі:

COORD  struc

xx dw 0

yy dw 0

COORD  ends

.data

fnam db "outputfile.dat" ; ім’я файла для запису

g_cs CRITICAL_SECTION <>     ; екземпляр структури

con  COORD  <>

dummy  dd 0 ;фіктивний аргумент функції потоку

pThreadID dd 0

hThread  dd 0 ;дескриптор потоку

@hFile  dd 0 ;дескриптор файлу

dOut  dd 0 ;дескриптор виводу консолі

dIn  dd  0 ;дескриптор вводу консолі

numwrit0 dd 1000 ;число повторень виводу у файл рядка

   ;з батьківського потоку

numwrit1 dd 1000 ;число повторень виводу у файл рядка

   ;з дочірнього потоку

str_out0 db "Output from parent thread",13,0

len_out0=$-str_out0

str_out1 db "Output from child thread",13,0

len_out1=$-str_out1

NumWri  dd 0

string1  db "Thread creation",13,0

len1= $-string1

string2  db "Press <Enter> to terminate parent thread",13,0

len2= $-string2

string3  db "Child thread begins operation",13,0

len3=$-string3

title_text db "Synchronisation test",0

numrow  dw 1

time_delay dd 100000 ;Параметр, що визначає затримку

.code

start proc

;точка входу до програми:

;запит консоли

call AllocConsole

;перевіріти успіх запиту консолі

test ax,ax

jz exit  ;якщо невдача

;Виведення заголовку консолі

push offset title_text

call SetConsoleTitleA

;------отримати стандартний дескриптор виводу--------------------------

push STD_OUTPUT_HANDLE

call GetStdHandle

mov dOut,eax ;у dOut-дескриптор виводу консолі

cmp eax, 0                           

jz exit  ;якщо невдача

;-------отримати стандартний дескриптор введення------------------------

push STD_INPUT_HANDLE

call GetStdHandle

mov dIn,eax  ;у dIn-дескриптор введення консолі

cmp eax, 0                           

jz exit  ;якщо невдача

;-------відчиняємо файл (CreateFileA)-----------------------------------

push 0

push NULL  ;атрибути (вони ігноруються)

push OPEN_ALWAYS ;відчинити або створити файл

push 0  ;захист файлу не потрібний

push FILE_SHARE_WRITE ;дозволено сумісне

;використання файлу (за записом)

push GENERIC_WRITE ;дозволено записувати у файл

push    offset fnam     ;ім'я файла

call CreateFileA

cmp eax,0ffffffffh

je exit  ;якщо невдача

mov @hFile,eax ;дескриптор файла

;-------вивести повідомлення--------------------------------------------

Set_cursor con,1,numrow,dOut,exit

inc numrow

push 0

push offset NumWri ;кількість виведених на екран символів

push len1 ;довжина рядку для виводу на екран

push offset string1 ;адреса рядку для виводу на екран

push dOut

call WriteConsoleA

cmp eax,0

jz exit ;якщо невдача

inc numrow

;-----------Створення дочірнього потоку----------------------------------

push offset pThreadID

push NULL

push offset dummy

push offset ThreadProc

push NULL

push NULL

call CreateThread

cmp eax,NULL

je exit   ;якщо невдача

mov hThread,eax

;-----------Закрыття описувача дочірнього потоку-----------------------

push hThread

call CloseHandle

;------------ініціалізація об'єкту "критична секція"------------------

push offset g_cs ;покажчик на структуру

call InitializeCriticalSection

;------------Переключення на другий потік------------------------------

push NULL

call    Sleep

;-------Вхід до критичної секції--------------------------------------

push offset g_cs

call EnterCriticalSection

;-------Запис у файл--------------------------------------------------

mov ecx,numwrit0

cmp ecx,0

je exit

cycl0:

push ecx

push NULL  ;синхронне введення

push offset NumWri ;покажчик на буфер з кількістю

; записаних байтів

push len_out0  ;число байтів для виводу

push offset str_out0 ;покажчик на рядок для запису

push @hFile  ;дескриптор відчиненого файлу

call WriteFile

cmp eax,NULL

je exit  ;якщо невдача

pop ecx

loop cycl0

;-------Вихід з критичної секції--------------------------------------

push offset g_cs

call LeaveCriticalSection

;-------вивести повідомлення------------------------------------------

Set_cursor con,1,numrow,dOut,exit

inc numrow

push 0

push offset NumWri ;кількість виведених на екран

; символів

push len2 ;довжина рядку для виводу на екран

push offset string2 ;адреса рядку для виводу на екран

push dOut

call WriteConsoleA

cmp eax,0

jz exit  ;якщо невдача

inc numrow

;-------очикування переводу каретки для виходу---------------------------

push 0

push offset NumWri ;кількість введених символів

push len1  ;розмір буферу введення

push offset string1

push dIn

call ReadConsoleA  

cmp eax, 0

jz exit  ;якщо невдача

exit:

;------------Закриття файла (CloseHandle)--------------------------------

push @hFile

call CloseHandle

;------------Вихід з програми--------------------------------------------

;підготовка виклику VOID ExitProcess(UINT uExitCode)

push 0

call ExitProcess

start endp

;--------------Процедура потоку----------------------------------------

ThreadProc proc

arg param:DWORD

uses ebx,edi,esi

;-------вивести повідомлення--------------------------------------------

Set_cursor con,1,numrow,dOut,exit_proc

inc numrow

push 0

push offset NumWri ;кількість виведених на екран

; символів

push len3  ;довжина рядку для виводу на екран

push offset string3 ;адреса рядку для виводу на екран

push dOut

call WriteConsoleA

;-------Вхід до критичної секції--------------------------------------

push offset g_cs

call EnterCriticalSection

;-------Запис у файл--------------------------------------------------

mov ecx,numwrit1

jecxz exit_proc

cycl1:

push ecx

push NULL  ;синхронне введення

push offset NumWri ;покажчик на буфер з кількістю

; записаних байтів

push len_out1 ;число байтів для виводу

push offset str_out1 ;покажчик на рядок для запису

push @hFile  ;дескриптор відчиненого файлу

call WriteFile

cmp eax,NULL

je exit_proc  ;якщо невдача

pop ecx

delay time_delay

loop cycl1

;-------Вихід з критичної секції--------------------------------------

push offset g_cs

call LeaveCriticalSection

exit_proc:

ret

ThreadProc  endp

end start


Дотаток Г

Текст програми synchr3.asm

;------------------------------------------------------------------------------

;synch3.asm - Win32 консольна программа

;для вивчення засобів синхронізації потоків в режимі ядра.    

;------------------------------------------------------------------------------

.486

.model flat,STDCALL ;модель пам'яти flat,

;STDCALL - передача параметрів в стилі С (справа наліво),

;процедура, що викликається чистить за собою стек

%NOINCL ;заборонити виведення тексту файлів включення

include WindowConA.inc

;Об’явлення зовнішніми функцій Win32 (ASCII), що використовуються у програмі:

extrn AllocConsole:PROC

extrn SetConsoleTitleA:PROC

extrn SetConsoleCursorPosition:PROC

extrn ReadFile:PROC

extrn CloseHandle:PROC

extrn ExitProcess:PROC

extrn MoveFileA:PROC

extrn CreateFileA:PROC

extrn GetStdHandle:PROC

extrn WriteConsoleA:PROC

extrn ReadConsoleA:PROC

extrn WriteFile:PROC

extrn GetLastError:PROC

extrn CreateThread:PROC

extrn ExitProcess:PROC

extrn SwitchToThread:PROC

extrn Sleep:PROC

extrn InitializeCriticalSection:PROC

extrn WaitForSingleObject:PROC

extrnCreateEventA:PROC

extrn SetEvent:PROC

;Макрокос установки курсора у задану позицію

Set_cursor macro cursor,posx,posy,descr,lbl

mov cursor.xx,posx

push eax

mov ax,posy

mov cursor.yy,ax

pop eax

push cursor

push descr

call SetConsoleCursorPosition

cmp eax, 0                           

jz lbl ;якщо невдача

endm

;структура для установки положення курсору в консолі:

COORD  struc

xx dw 0

yy dw 0

COORD  ends

.data

con  COORD  <>

dummy  dd 0 ;фіктивний аргумент функції потоку

pThreadID dd 0

hThread  dd 0        ;дескриптор потоку 0

pThreadID1 dd 0

hThread1 dd 0        ;дескриптор потоку 1

hEvent  dd 0 ;дескриптор події

dOut            dd 0        ;дескриптор виводу консолі

dIn             dd 0        ;дескриптор вводу консолі

str_out0 db "Client 0: ",0

len_out0=$-str_out0

str_out1 db "Client 1:",0

len_out1=$-str_out1

NumWri  dd 0

buffer  db 20 dup(0)

nbuff  dd 20

title_text db "Synchronisation test",0

.code

start proc

;точка входу до програми:

;запит консоли

call AllocConsole

;перевіріти успіх запиту консолі

test ax,ax

jz exit  ;якщо невдача

;Виведення заголовку консолі

push offset title_text

call SetConsoleTitleA

;-------отримати стандартний дескриптор виводу-----------------

push STD_OUTPUT_HANDLE

call GetStdHandle

mov       dOut,eax ;у dOut-дескриптор виводу ;консолі

cmp eax, 0                           

jz exit  ;якщо невдача

;-------отримати стандартний дескриптор введення--------------

push STD_INPUT_HANDLE

call GetStdHandle

mov dIn,eax  ;у dIn-дескриптор введення консолі

cmp eax, 0                           

jz exit  ;якщо невдача

;-----------Створення події-----------------------------------------------

push NULL

 push TRUE

push FALSE

push NULL

call CreateEventA

cmp eax,NULL

je exit  ;якщо невдача

 mov hEvent,eax

;-----------Створення дочірніх потоків-------------------------------

 push offset pThreadID

push NULL

push offset dummy

push offset ThreadProc

push NULL

push NULL

call CreateThread

cmp eax,NULL

je exit   ;якщо невдача

mov hThread,eax

push offset pThreadID1

push NULL

push offset dummy

push offset ThreadProc1

push NULL

push NULL

call CreateThread

cmp eax,NULL

je exit   ;якщо невдача

mov hThread1,eax

;--------очикування закінчення роботи потоків----------------------

push 0ffffffffh

push hThread

call WaitForSingleObject

push 0ffffffffh

push hThread1

call WaitForSingleObject

;-------вивести зміст буферу--------------------------------------------

 Set_cursor con,1,5,dOut,exit

push 0

push offset NumWri ;кількість виведених на екран

; символів

push nbuff  ;длина рядку для виводу на екран

 push offset buffer ;адреса рядку для виводу на екран

 push dOut

call WriteConsoleA

cmp eax,0

jz exit  ;якщо невдача

;-------Перевести подію у вільний стан-------------------------------

 push hEvent

 call SetEvent

;-------очикування переводу каретки для виходу-------------------

 push 0

 push offset NumWri ;кількість виведених символів

 push len_out0  ;розмір буферу введення

push offset str_out0

push dIn

call ReadConsoleA  

cmp eax, 0

jz exit  ;якщо невдача

exit:

;-----------Закриття описувача дочірнього потоку-----------------

 push hThread

call CloseHandle

push hThread1

call CloseHandle

;------------Вихід з програми-------------------------------------------

;підготовка виклику VOID ExitProcess(UINT uExitCode)

 push 0

call ExitProcess

start endp

;--------------Процедура потоку 0 -------------------------------------

ThreadProc proc

arg param:DWORD

uses ebx,edi,esi

;-------Чекання події----------------------------------------------------

push 0ffffffffh

push hEvent

call WaitForSingleObject

;-------вивести повідомлення----------------------------------------

Set_cursor con,1,1,dOut,exit_proc

push 0

push offset NumWri ;кількість виведених

; на екран символів

 push len_out0  ;довжина рядку для виводу на екран

 push offset str_out0 ;адреса рядку для виводу на екран

 push dOut

call WriteConsoleA

;-------ввести рядок-------------------------------------------------

 push 0

 push offset NumWri ;кількість введених символів

 push nbuff  ;довжина рядку для виводу на екран

 push offset buffer ;адреса буферу для введеного рядку

 push dIn

call ReadConsoleA

cmp eax,0

jz exit  ;якщо невдача

exit_proc:

        push hEvent

call SetEvent

ret

ThreadProc  endp

;--------------Процедура потоку 1-------------------------------------------

ThreadProc1 proc

arg param:DWORD

uses ebx,edi,esi

;-------Чекання події---------------------------------------------------------------

push 0ffffffffh

push hEvent

call WaitForSingleObject

;-------вивести повідомлення----------------------------------------------------

Set_cursor con,1,3,dOut,exit_proc1

push 0

push offset NumWri ;кількість виведених

; на екран символів

 push len_out1  ;довжина рядку для виводу на екран

 push offset str_out1 ;адреса рядку для виводу на екран

 push dOut

call WriteConsoleA

;------ввести рядок-----------------------------------------------------------------

 push 0

 push offset NumWri ;кількість введених символів

 push nbuff  ;довжина рядку для виводу на екран

 push offset buffer ;адреса буферу для введеного рядку

 push dIn

call ReadConsoleA

cmp eax,0

jz exit ;якщо невдача

exit_proc1:

push hEvent

call SetEvent

ret

ThreadProc1  endp

end start




1. на тему- Принятие управленческого решения на примере ООО БурятТерминал
2. процессуального задержания в условиях дальнейшей демократизации советского общества Диссертация на сои
3. музыке революции Двенадцать поэма переворота
4.  Эмпиризм в психологии на деле исходил столь же стихийно из идеалистических предпосылок как естествознание
5. Тема- Словарная статья Учебник- Т
6. Бюрократизм и бюрократия в государственном управлении
7. Введение Целью данной работы является проектирование электроснабжения потребителей Новоаннинского райо.html
8.  Поварова Варвара зачтено Лименько Елена зачтено Седова Анастасия незачтено нет обоснования в
9. Тема. Ребенок раннего возраста План- Закономерности развития ребенка раннего возраста Воспитание
10. Тема 1. Место культурологии в системе вузовского образования
11. тема гемостаза- 1
12. Туризм как составляющая непроизводственной сферы деятельности
13. Россия в 1894-1913 годах
14. Гомельский государственный технический университет имени П
15. вариантов схем электроснабжения на низком напряжении Расчет электрических нагрузок
16. Курсовая работа- Удосконалення управління персоналом організації
17. Государственная идеология
18. Сверхпроводимость и ее применение в физическом эксперименте
19. Роль слияния и поглощения в финансовой стратегии компаний
20. ориентированный подход и диаграммы классов в UML К