Вступ
Більшість функціональних можливостей операційної системи Microsoft Windows забезпечують динамічні бібліотеки (DLL), які представляють собою набір функцій, скомпонованих разом у вигляді бінарного файлу, який може бути завантажений в адресний простір процесу, що використовує ці функції.
Реалізація прикладних програмних функцій у вигляді бібліотек DLL дає такі переваги:
- сприяє модульній структурі коду програми, ефективному використанню оперативної пам'яті та дискового простору;
- внесення змін в DLL не вимагає перекомпіляції всього проекту;
- функції DLL можуть використовувати декілька процесів одночасно;
- управління динамічними бібліотеками покладається на операційну систему;
- бібліотеки DLL можуть використовувати програми, написаних на різних мовах.
Функції, оформлені у вигляді бібліотек DLL, називаються API-функціями. Система Windows використовує як системні, так і API-функції користувача.
Постановка задачі
Практично у всіх сучасних операційних систем реалізовані мультипрограмні середовища, що одночасно виконують багато потоків. Більшість системних бібліотек DLL містять процедури і функції, що використовуються всіма потоками, причому використовується єдиний завантажений в пам'ять екземпляр такої бібліотеки. Звичайно, таким ж властивостями повинні володіти бібліотеки DLL, що розробляються користувачами для своїх потреб.
Алгоритми розробки бібліотек DLL розглядався в роботі [1], але питанню роботи таких бібліотек в мультипрограмному середовищі увага не приділялася. В даній роботі розглядається алгоритму створення бібліотек DLL для роботи в мультипрограмному середовищі.
Алгоритм створення бібліотек DLL для мультипрограмного середовища
Бібліотеки DLL користувача містять процедури і функції, які використовуються усіма потоками, причому використовується єдиний завантажений в пам'ять екземпляр такої бібліотеки. Оскільки процесорний час у мультипрограмних системах розподіляється між потоками операційною системою на свій розсуд, заздалегідь неможливо передбачити, у якій точці виконання потоку перерветься і розпочнеться виконання іншого потоку. У такому випадку можлива ситуація коли виконання одного потоку перерветься після його входу в одну з процедур DLL, а потік, який одержав управління, також виконає вхід у ту ж саму процедуру DLL. Така ситуація називається повторним входом в процедуру. Для забезпечення працездатності процедур у разі повторного входу необхідно, щоб вони були реентерабельними. Для забезпечення реентерабельності процедур потрібно витримувати ряд вимог.
Код процедури не може бути самомодифікованим. Якщо при першому вході код модифікувався, то повторний вхід отримає модифікований код, що призведе до невірного результату роботи. Реентерабельна процедура не може викликати нереентерабельну процедуру і не може мати власних статичних областей пам'яті для зберігання змінних. Сегмент даних такої процедури може містити лише константи, а області пам'яті під змінні процедура повинна отримувати з модуля, що її викликає. Результати процедур повинні повертатися або через регістри процесора або через область даних, отриману при виклику.
Виконання перерахованих правил реалізується простими засобами стандартного механізму виклику підпрограм з передачею параметрів через стек та розміщенням локальних змінних у стеку.
Розглянемо алгоритм практичної реалізації динамічної бібліотеки на прикладі однієї процедури обчислення факторіалу числа.
.686
.model flat,stdcall
option casemap:none
include masm32includewindows.inc ; файли структур, констант …
.code
;****** функція точи входу в DLL *******
DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
mov eax,TRUE
ret
DllEntry Endp
;*****функція обчислення факторіалу числа n **********
Factorial proc
push ebp ;збереження базового вказівника для поточного кадра стека ebp
mov ebp,esp ; збереження поточного вказівника стека
mov ecx,dword ptr[ebp+8] ;загрузка лічильник циклів n
mov edi,dword ptr[ebp+12] ;загрузка алреси результата
mov ebx,1
ckl:
imul ebx,ecx ; обчислення факторіала
loop ckl
mov [edi],ebx ; збереження результату обчислення
pop ebp
ret 8
Factorial endp
end DllEntry
В даному коді містяться дві функції DllEntry та Factorial. Перша з них є функціє точки входу в DLL і повинна обов’язково бути присутньою в бібліотеці, при загрузці даної бібліотеки в оперативну пам'ять. Операційна система автоматично створить ще один потік в межах процесу що викликав дану функцію і передасть йому точку входу саме на цю функцію. Дана функція отримує три параметри від операційної системи: hInstDLL – хендл бібліотеки dll, параметр reason вказує причину виклику DLL. Коли reason дорівнює DLL_PROCESS_ATTACH, то DLL завантажується у віртуальний адресний простір поточного процесу в результаті операції запуску або в результаті виклику LoadLibrary. При значенні DLL_THREAD_ATTACH поточний процес створює новий потік. При значенні DLL_THREAD_DETACH потік вивільняє пам’ять, а при DLL_PROCESS_DETACH DLL вивантажується з віртуального адресного простору зайнятого процесу в результаті невдалого завантаження DLL, завершення роботи процесу або виклику функції FreeLibrary. По завершенню виконання цієї функції визначається чи продовжувати роботу цієї бібліотеки (TRUE або FALSE), результат повертається через регістр eax.
Процедура обчислення факторіалу числа Factorial є реентерабельною, оскільки механізм передачі параметрів відбувається через стек і результат обчислень записується в область пам’яті вказану модулем, що її визиває.
У випадку необхідності задання статичних областей пам’яті можна використовувати директиву LOCAL призначену для оголошення локальних змінних усередині процедури. Заданні цією директивою змінні поміщаються в стек і тим самим не порушують реентерабельності процедури.
Для того щоб сформувати інтерфейс бібліотеки, тобто задати функції які будуть входити в бібліотеку DLL треба описати їх у спеціальному файлі з розширення .def, який назвемо mydll.def, вміст якого наступний:
LIBRARRY mydll ; ім’я бібліотеки
EXPORTS Factorial ; ім’я процедури обчислення факторіалу
Тепер скомпілюємо нашу бібліотеку за допомогою командного файлу mydll.bat, який виконає наступні дії:
ml.exe /c /coff mydll.asm
link.exe /DLL /DEF:mydll.def mydll.obj
В результаті ми отримуємо бібліотеку mydll.lib, що містить функцію обчислення факторіалу.
Для того щоб динамічно використовувати бібліотеку DLL, необхідно завантажувати та вивантажувати її з оперативної пам’яті по ходу виконання програми. Для цього існують спеціальні функції:
• LoadLibrary – функція для завантаження бібліотеки в оперативну пам'ять;
• GetProcAddress – функція визначення адреси завантаженої в пам’ять бібліотечної функції;
• FreeLibrary – функція вивантаження бібліотеки з пам’яті.
Код виклику бібліотечної функції наступний:
push offset res ; заносимо в стек адрес комірки результату обчислень
push n ;записуємо в стек число, факторіал якого буде обчислено
Call [FactorialAddr] ; адреса процедури знаходиться в елементі пам’яті,
; адреса якої розміщується в FactorialAddr
Висновок
Реалізація програмних функцій у вигляді бібліотек DLL для роботи в мультипрограмному середовищі дає великі переваги програмісту у гнучкості, надійності та ефективності використання своїх програмних продуктів та скорочує час на розробку програмного забезпечення.
Література
1. Рисований О.М. Системне програмування [Текст]: підручник для студентів напрямку “Компютерна інженерія” вищих навчальних закладів в 2-х томах. Том 1. – Видання четверте: виправлено та доповнено – Х.: “Слово”, 2015.– 576 с.
|