Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
КАФЕДРА
«СПЕЦІАЛІЗОВАНІ КОМП'ЮТЕРНІ СИСТЕМИ»
МЕТОДИЧНІ ВКАЗІВКИ
до виконання лабораторних робіт з курсу
«Системне програмне забезпечення»
частина 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.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] |
Для 32-розрядних програм фірма Microsoft розробила спеціальний формат виконуваного файла. Він отримав назву переносний формат виконуваного файла (PE Portable 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 Загальна структура файла РЕ формату
До основних особливостей цього формату відносяться:
Інформація в РЕ-файлі є, в основному, високорівневою та використовується системою або програмами, щоб зясувати правила спілкування з конкретним виконуваним файлом.
Розглянемо основні складові частини в структурі файла РЕ формату відповідно до рис. 1.1.
Основні міркування, з яких на початку РЕ файла знаходиться програма-заглушка MS-DOS при запуску в середовищі MS-DOS видати повідомлення типу This program requires Microsoft Windows. Сама програма-заглушка виконана як повноцінний MZ-файл для MS-DOS. Найбільший інтерес уявляє 4-байтове поле зі зсувом 3Сh. В ньому міститься зсув, що вказує на початок РЕ-файла.
б) Заголовок РЕ файла.
Заголовок РЕ-файла починається 4-байтовою сигнатурою 50 45 00 00h, що відповідає рядку “РЕ” з наступними двома байтами нулів.
Заголовок РЕ файла можно розбити на такі частини:
в) Додатковий заголовок РЕ-файла (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 системний файл; 2000h dll-бібліотека; |
в) Додатковий заголовок.
За основним заголовком РЕ файла іде додатковий заголовок. В таблиці 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 підсистема не потрібна (драйвер пристрою); 0002h Windows GUI; 0003h консольна програма Windows; 0005h OS/2; 0007h Posix. |
+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].
При підготовці зверніть увагу на такі питання:
Уважно проаналізуйте текст консольної Windows програми read_pe.asm (додаток), за допомогою якої в отладчику td32.exe можна проаналізувати значення усіх полів заголовків виконуваного файла, імя якого задається в сегменті даних.
Потік (Thread) визначає послідовність виконання коду в процесі. При ініціалізації процесу система завжди створює первинний потік. Більшість процесів обходиться одним первинним потоком. Але ж процеси можуть створювати додаткові потоки, що дозволяє їм ефективніше виконувати свою роботу.
Кожен потік починає свою роботу з деякої вхідної функції. На мові асемблера це оформлюється у такий спосіб:
TreadProc proc
arg pvParam:DWORD
…
ret
ThreadProc endp
Функція потоку може виконувати будь які завдання. Раніше або пізніше вона закінчує свою роботу та повертає керування. У цей момент потік зупиняється, память, що була відведена під його стек, звільняється, а лічильник користувачів його обєкта ядра “потік” зменшується на 1. Коли лічильник обнулюється, цей обєкт ядра руйнується системою. Але цей обєкт ядра може жити значно довше, ніж потік, що зіставлений з ним.
При розробці функції потоку необхідно звертати увагу на такі основні питання:
Для створення потоків у середовищі Win32 призначена функція API CreateThread.
При кожному виклику цієї функції система створює обєкт ядра “потік”. Це не сам потік, а компактна структура даних, що використовується операційною системою для керування потоком та зберігає статистичну інформацію про потік.
Система виділяє память під стек потоку з адресного простору процесу. Новий потік виконується у контексті того ж процесу, що й батьківський потік. Тому він отримує доступ до всіх описувачів обєктів ядра, всієї памяті і стеків усіх потоків процесу. За рахунок цього потоки у рамках одного процесу можуть легко взаємодіяти один з одним.
Функції CreateThread передаються 6 параметрів:
Більш докладну інформацію про планування, створення та закриття потоків можна отримати, наприклад в [1].
а) в яких випадках створюються потоки;
б) побудова функції потоку;
в) функція CreateThread та її параметри;
г) завершення потоків;
д) призупинення та відновлення потоків.
Потоки повинні взаємодіяти один з одним у двох основних випадках:
Значна частина питань синхронізації потоків повязана з так званим атомарним доступом (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].
а) необхідність монопольного користування спільними даними в мнопоточній програмі;
б) основи спін-блокування, переваги та недоліки;
в) interlocked-функції API Win32;
г) поняття і організація критичних секцій;
д) правила використання функцій роботи з критичними секціями.
3.4 Порядок виконання роботи
Хоча механізми синхронізації в користувальницькому режимі забезпечують високу бистродію, їм властива низка обмежень, і в деяких випадках вони просто не будуть працювати.
Використання обєктів ядра для синхронізації дає значно більші можливості, ніж механізми синхронізації в користувальницькому режимі. Єдиний їх недолік менша швидкодія. Справа у тому, що при викликанні функцій, які забезпечують синхронізацію за допомогою обєктів ядра, потік повинен перейти з користувальницького режиму у режим ядра. Цей перехід займає понад 1000 процесорних тактів на платформі х86. Сюди також треба додати час на виконання коду цих функцій в режимі ядра.
Обєкти ядра “процес” та “потік” перебувають в зайнятому стані доки виконується відповідний процес або потік, та переходять у вільний стан коли процес або потік завершуються. Усередині цих обєктів підтримується булєва змінна, яка при створенні обєкта приймає значення FALSE (“зайнято”). По закінченні роботи відповідного процесу або потоку операційна система змінює значення цієї змінної на TRUE, що свідчить про те, що обєкт є вільним.
У вільному та зайнятому стані можуть перебувати такі обєкти ядра:
У складі API Win32 є функції, які дозволяють потоку в любий момент зупинитися та очікувати доки визначений обєкт ядра не перейде у вільний стан. З усього сімейства цих функцій частіше за всі використовується WaitForSingleObject.
Цій функції передаються два параметри.
Протягом очікування потоку не надається процесорний час, що економить ресурси системи. Значення, що повертається цією функцією характеризує обєкт, дескриптор якого передається у якості аргументу. Докладніше про можливі значення можна узнати з довідкової інформації (див., наприклад, Platform SDK documentation на сайті компанії Microsoft).
Самим простим обєктом ядра, який може використовуватись для синхронізації потоків, є подія. Ці обєкти місять лічильник (як усі обєкти ядра), та дві булєві змінні. Одна визначає тип даного обєкту-події, друга його стан (“зайнято” або “вільно”). Події просто повідомляють про закінчення деякої операції. Обєкти події бувають двох типів: зі скиданням вручну або з автоскиданням. Перші дозволяють після здійснення події виконуватися декільком потокам, другі тільки одному. Ініціалізуючий потік переводить обєкт подію у стан “зайнято” і переходить до своєї операції. Після закінчення він переводить обєкт подію у вільний стан, що дає можливість другим потокам, що очікували переходу події у такий стан, прокинутись і знов стати такими, що плануються.
Обєкт ядра “подія” створюється функцією CreateEvent.
Цієї функції передаються три параметри.
Обєкти ядра мютекси гарантують потокам взаємовиключаючий доступ до єдиного ресурсу. Вони містять лічильник числа користувачів, лічильник рекурсії, та змінну, в якої запамятовується ідентифікатор потоку, що володіє цим обєктом. Мютекси ведуть себе також, як критичні секції, але є обєктами ядра і дозволяють синхронізувати доступ до ресурсу декілька потоків з різних процесів. При цьому можна задавати максимальний час очікування доступу до ресурсу.
Ідентифікатор потоку визначає, який потік захопив мютекс, а лічильник рекурсій скільки разів. У мютексів багато застосувань і це найчастіше використовувані обєкти ядра. Як правило, за їх допомогою здійснюється захист блоків памяті, до яких звертаються багато потоків. Мютекси гарантують, що кожен потік отримає монопольний доступ до блоку памяті, і в такий спосіб захищають цілісність даних, що зберігаються у ньому.
Для мютексів визначені такі правила:
Для використання мютексу один з потоків повинен створити його за допомогою функції CreateMutex. Цієї функції передаються три параметра. Перший та останній з них ідентичні відповідним параметрам функції CreateEvent. Другий параметр fIniLialOwner визначає початковий стан мютекса. Якщо в ньому передається FALSE, то мютекс не належить жодному потоку і перебуває у вільному стані. Якщо значення параметру TRUE, ідентифікатор потоку мютекса, стає рівним ідентифікатору потоку, що викликає функцію CreateMutex, а лічильник рекурсії отримує значення 1. При цьому мютекс спочатку перебуває у зайнятому стані.
Потік отримує доступ до ресурсу, що поділяється, викликаючи одну з Wait функцій з переданням до неї описувача мютекса, що охороняє цей ресурс.
Більш докладне описання роботи з розглянутими та іншими обєктами ядра, що використовуються для синхронізації потоків, є, наприклад, в [1].
а) основні особливості використання обєктів ядра для синхронізації;
б) перелік обєктів ядра та їх створення;
в) використання Wait функцій;
г) організація доступу до ресурсів, що розділяються, за допомогою подій і мютексів.
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