Використання Resource Manager бази даних RDM Server, Інші СУБД, Бази даних, статті

Зміст



Передмова


У статті розглядаються питання, пов’язані з роботою менеджера ресурсів (Resource Manager) бази даних RDM Server. Міститься пояснення спеціальних функцій Resource Manager, категорій API (таких як, функції управління потоками і функції організації черг) і надає докладний приклад, який ілюструє використання конкретних функцій Resource Manager. Призначена програмістам, потенційно зацікавленим у придбанні бази даних RDM Server.


Короткий огляд


Розробка багатопотокових додатків передбачає використання функцій операційних систем для управління потоками і забезпечення межпотоковой синхронізації і комунікації. Багато сучасні операційні системи підтримують такі функції, але оскільки доступ до них залежить від конкретної платформи, нерідко доводиться стикатися зі значними труднощами при створенні складних, переносних незалежних багатопотокових прикладних програм. Якщо розробляється вами програма не є переносних незалежним, ви можете вільно застосовувати відповідні функції вашої операційної системи. Однак, якщо ви створюєте додаток, масштабоване для великої кількості різних платформ, вам слід скористатися інтерфейсом Resource Manager (RM) бази даних RDM Server. Многопотоковой ядро ​​RDM Server використовує всі функції інтерфейсу RM. Це гарантує їх надійність і доступність для всіх платформ, підтримуваних RDM Server.


Resource Manager надає переносних незалежний інтерфейс з великою кількістю функцій, що дозволяють вирішувати такі складні завдання для операційних систем, як:



Всі функції RM призначені для роботи в однопроцессной середовищі багатопотокових додатків і не працюють для багатопроцесорний додатків. Вони можуть використовуватися в додаткових модулях RDM Server або спеціалізованих серверах бази даних з ядром RDM Server.


Повний список функцій інтерфейсу RM наводиться в наступній таблиці.


Таблиця 1 – Список функцій Resource Manager
















































































































































































































Функція Клас Опис
rm_startup загального призначення Запуск менеджера ресурсів.
rm_cleanup загального призначення Зупинка і очищення менеджера ресурсів.
rm_sleep загального призначення Призупинення потоку на вказане число мілісекунд.
rm_log загального призначення Запис повідомлення в журнал повідомлень RDM Server.
rm_getenv загального призначення Отримання рядки опису середовища операційної системи.
rm_interrupt загального призначення Дозвіл обробки переривань роботи користувачів.
rm_threadBegin управління потоками Початок виконання нового потоку.
rm_threadEnd управління потоками Завершення виконання потоку.
rm_syncCreate синхронізації Створення об’єкта синхронізації.
rm_syncDelete синхронізації Видалення об’єкта синхронізації.
rm_syncEnterExcl синхронізації Вхід в ділянку програми з монопольним доступом (семафор взаємного виключення).
rm_syncExitExcl синхронізації Вихід з дільниці програми з монопольним доступом (семафор взаємного виключення).
rm_syncStart синхронізації Запуск події (установка семафора подій в несигнальному (зайняте) стан).
rm_syncResume синхронізації Відновлення події (семафор подій в сигнальному стані).
rm_syncWait синхронізації Очікування події, яка має поновиться (сигнальне стан).
rm_syncEnableQ синхронізації Включення семафора взаємного очікування (допуск зчитувачів до загальних даними).
rm_syncDisableQ синхронізації Вимкнення семафора взаємного очікування (блокування зчитувачів).
rm_syncSendQ синхронізації Відправка повідомлення про завершення зчитування на семафор взаємного очікування.
rm_syncReceiveQ синхронізації Отримання повідомлення про дозвіл зчитування на семафорі взаємного очікування.
rm_syncWaitQ синхронізації Очікування на семафорі взаємного очікування закінчення роботи всіх зчитувачів.
rm_queueCreate організації черг Створення черги повідомлень.
rm_queueDelete організації черг Видалення черги повідомлень.
rm_queuePurge організації черг Видалення повідомлень з черги.
rm_queueWrite організації черг Запис повідомлень в чергу.
rm_queueRead організації черг Зчитування повідомлень з черги.
rm_createTag динамічної пам’яті Створення тега динамічно виділеної пам’яті.
rm_resetTag динамічної пам’яті Скидання буфера нелокального переходу вказаного тега.
rm_freeTagMemory динамічної пам’яті Звільнення всієї пам’яті, виділеної для тега.
rm_getMemory динамічної пам’яті Виділення блоку пам’яті.
rm_cGetMemory динамічної пам’яті Виділення та очистка блоку пам’яті.
rm_freeMemory динамічної пам’яті Звільнення виділеного блоку пам’яті.
rm_extendMemory динамічної пам’яті Розширення (або перерозподіл) блоку пам’яті.
rm_growMemory динамічної пам’яті Збільшення блоку пам’яті (якщо необхідно).
rm_allocPool динамічної пам’яті Виділення пулу пам’яті.
rm_getFromPool динамічної пам’яті Отримання буфера з пулу.
rm_putInPool динамічної пам’яті Внесення буфера в пул.
rm_zFreePool динамічної пам’яті Звільнення пулу пам’яті.
rm_fileOpen управління файлами Відкриття / Створення файлу.
rm_fileClose управління файлами Закриття файлу.
rm_filePrintf управління файлами Запис у файл форматованого виводу.
rm_fileSeek управління файлами Пошук абсолютної позиції у файлі.
rm_fileRead управління файлами Зчитування з файлу.
rm_fileWrite управління файлами Запис у файл.
rm_fileSeekRead управління файлами Зчитування від заданого місця у файлі.
rm_fileSeekWrite управління файлами Запис до заданого місця у файлі.
rm_fileLength управління файлами Визначення поточної довжини файлу.
rm_fileSync управління файлами Скидання файлу на диск.
rm_fileRemove управління файлами Видалення файлу (переміщення в інше місце).
rm_fileRename управління файлами Перейменування файлу.
rm_fileTempName управління файлами Створення унікального тимчасового імені файлу.

Використання функцій RM загального призначення


Для забезпечення платформної незалежності Resource Manager бази даних RDM Server визначає деякі вбудовані типи даних і константи. Типи даних використовуються для атрибутів оголошення функцій і визначень дескрипторів; константи для кодів повернення і керуючих параметрів функцій. Визначення дескрипторів і керуючих параметрів задаються в описах функцій. Атрибути оголошення функцій наводяться нижче. Ці атрибути вказуються в оголошенні функції між типом даних і ім’ям функції. Зауважимо, що оголошення функцій використовуються для платформ, які мають спеціальні архітектурні функції, які не стільки вимагають переваги швидкодії, скільки самі надають такі переваги при своєму виконанні. Таким чином, ми інкапсулювати ці платформенно-орієнтовані оголошення в переносних незалежні визначення. Для багатьох платформ ці оголошення фактично порожні.


Таблиця 2 – Атрибути функцій Resource Manager



















Атрибут Опис
REXTERNAL Задає загальнодоступну функцію в загальній бібліотеці (DLL).
RINTERNAL Задає приватну функцію користувача, що викликається з окремої загальної бібліотеки (DLL).
REXTVARARG Визначає функцію формування списків змінних аргументів.
RTHREAD Визначає функцію управління потоками, яка буде викликана функцією rm_threadBegin.

Список кодів стану, що повертаються функціями RM, наводиться в наступній таблиці. Вони оголошені як тип short. Зауважимо, що деякі функції RM повертають значення -1 при невдалому виконанні і значення 0 (RM_OKAY) при успішному виконанні.


Таблиця 3 – Коди повернення Resource Manager






















Код повернення Опис
RM_OKAY Функція успішно виконана.
RM_TIMEOUT Операція не може завершитися до закінчення зазначеного часу очікування.
RM_QUEUE_EMPTY Немає повідомлень в черзі.
RM_NOCATPATH Вказано невірний шлях до каталогу. Необхідно вказати повне ім’я директорії, що містить каталог RDM Server. Повертається при виконанні функції rm_startup.
RM_INVCATALOG Недійсний каталог RDM Server. Або неправильно вказано повне ім’я файлу, або потрібний файл не знайдений у вказаному каталозі. Повертається при виконанні функції rm_startup.

Інтерфейс RM API надає ряд функцій загального призначення. Функції rm_startup і rm_cleanup є альтернативами функцій s_startup і s_terminate. Якщо ви збираєтеся використовувати тільки функції інтерфейсу RM API, Ігноруючи функції бази даннихRDM Server (тобто s_ , d_ або функції SQL), то слід мати на увазі, що виклик функції rm_startup замість s_startup вимагає менше пам’яті і виконується швидше. Це пояснюється тим, що s_startup запускає керуючі потоки бази даних RDM Server. У той час як функція rm_startup ініціалізує тільки підсистему бази даних Resource Manager.


Функція rm_getenv витягує стандартні рядки, що описують середовище операційної системи. Функція rm_interrupt надає доступ до обробки переривань роботи користувачів. Функція rm_sleep дозволяє призупинити потік на вказане число мілісекунд.


Функції управління потоками


Потік являє собою функцію, яка може бути виконана незалежно з інших потоків одного і того ж батьківського процесу. Будь потік батьківського процесу використовує разом з іншими потоками адресний простір, глобальні змінні і ресурси цього процесу. Кожен процес породжується одним потоком. Якщо основний потік батьківського процесу викликає якусь функцію управління потоками, то ця функція виконується паралельно з основним потоком. Теоретично може бути запущено будь-яке число потоків, але на практиці деякі платформи операційних систем обмежують кількість виконуваних потоків. Занадто велике число потоків може негативно позначитися на швидкодії операційної системи через втрати, які вона несе при управлінні і плануванні потоків. Втім, ці втрати можуть значно відрізнятися як для систем, що базуються на архітектурі комп’ютера, так і для конкретної операційної системи. Потоки дають значний виграш в швидкодії, якщо вони виконуються в системах із симетричною мультипроцесорної архітектурою (SMP), що використовують більше двох центральних процесорів.


За допомогою інтерфейсу RM API бази даних RDM Server ви можете викликати функцію rm_threadBegin, Яка ініціалізує виконання потоків. Потік складається зі стандартної функції мови C типу void, оголошеної з атрибутом RTHREAD. Викликаючи rm_threadBegin, Ви можете передати параметр покажчика в цю функцію. Потік виконується до тих пір, поки він не закінчить роботу або не викличе функцію rm_threadEnd.


Кожен потік має локальну пам’ять, яка виділяється з стека процесу. Призначуваний потоку розмір простору стека задається параметром функції rm_threadBegin. Використовуючи константу THREAD_STACK, можна задати системне значення за замовчуванням.


Всі потоки виконуються на одному з трьох пріоритетних рівнів. Як правило, потокам сеансів бази даних RDM Server (тобто, потокам, який виконує функцію s_login ) Повинен призначатися пріоритет RM_THREAD_HIGH, як і тим потокам, для виконання яких потрібно пріоритет, не менший ніж у системних потоків RDM Server. Часте використання пріоритету RM_THREAD_HIGH може негативно позначитися на системній продуктивності бази даних RDM Server.


Функції синхронізації


Кілька потоків не можуть безпечно маніпулювати глобальними змінними одночасно. Принаймні, один з двох потоків, які намагаються одночасно оновити одну і ту ж глобальну змінну, не зможе цього зробити. В гіршому випадку ця ситуація може призвести до фатального збою для деяких систем. Щоб забезпечити безпечне оновлення глобальних змінних декількома потоками, необхідно запустити один з трьох видів синхронізації. Синхронізація взаємного виключення впорядковує оновлення, виконувані різними потоками. Це означає, що ці оновлення будуть виконуватися одне за іншим в порядку надходження запитів. Функції rm_syncEnterExcl і rm_syncExitExcl інтерфейсу RM забезпечують виконання синхронізації цього типу за допомогою семафора взаємного виключення (mutex semaphore). Для захисту глобальних даних можна створювати будь-яку кількість семафорів взаємного винятки. Зауважимо, що доступ “тільки для читання” до будь-якої глобальної змінної безпечний і не вимагає впорядкування.


Два потоку, оновлюють одні й ті ж загальні дані, в деяких випадках можна синхронізувати за допомогою семафорів подій. При використанні семафора подій один із двох потоків отримує доступ до загальних даними, оновлює їх і сигналізує іншому потоку, що знаходиться в стані очікування, про те, що він закінчив свою роботу. Для підтримки семафора подій використовуються функції rm_syncStart, rm_syncResume і rm_syncWait.


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


Наприклад, RDM Server використовує семафори взаємного очікування для управління процесом контрольних точок. (Функції модифікації кешу бази даних знаходяться в очікуванні під час виконання контрольної точки, і навпаки, контрольна крапка не виконується, поки не завершаться всі запущені функції модифікації кеша.) Доступ до семафора взаємного очікування здійснюється за допомогою функцій rm_syncEnableQ, rm_syncDisableQ, rm_syncSendQ, rm_syncReceiveQ і rm_syncWaitQ.


Викликаючи функцію rm_syncCreate, Можна створювати будь-які перераховані вище семафори. Семафори звільняються при виконанні функції rm_syncDelete.


Функції організації черг


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


Викликавши функцію rm_queueWrite, Ви можете поставити повідомлення в чергу. Повідомлення складається з покажчика, розміру блоку, на який вказує цей покажчик, і ідентифікатора повідомлення (типу long). Простий механізм призначення пріоритетів дозволяє задавати повідомлення, яке повинно бути поставлено в початок черги. Зазвичай повідомлення в черзі обробляються в порядку надходження. Потік може прочитати наступне доступне в черзі повідомлення при виконанні функції rm_queueRead. Ця функція дозволяє вказати, скільки часу потік повинен чекати повідомлення, яке повинно бути відправлено, якщо черга пуста. Після закінчення часу очікування функція поверне стан RM_TIMEOUT. Якщо ви задали для часу очікування значення RM_INDEFINITE_WAIT, потік буде очікувати до тих пір, поки наступне за часом повідомлення не буде поставлено в чергу.


Функції динамічної пам’яті


Resource Manager бази даних RDM Server містить набір функцій розподілу тегованих пам’яті, які дозволяють розподіляти пам’ять, пов’язану з тегами. Для створення тега пам’яті викличте функцію rm_createTag. При виклику цієї функції ви можете передбачити буфер нелокального переходу (jump buffer) для функції setjmp. Якщо для створеного тега виявиться недостатньо доступної пам’яті, управління буде передано в цей буфер. Зазвичай в цьому випадку з функції верхнього рівня викликається стандартна функція setjmp мови C (коли вам потрібно передати управління цієї функції, зберігаючи стан стека). При використанні буфера нелокального переходу вам не потрібно перевіряти кожен виклик функцій rm_getMemory, rm_cGetMemory, rm_extendMemory або rm_growMemory на значення NULL. Вся пов’язана з тегом пам’ять може бути звільнена викликом однієї функції rm_freeTagMemory.


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


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


Функції управління файлами


Деякі операційні системи встановлюють межа на максимальне число файлів, які процес може одночасно відкрити. Функції управління файлами, передбачені інтерфейсом RM, відстежують цей межа, щоб динамічно відкривати і закривати файли операційної системи, не виходячи за його рамки.


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


Приклад програми


У цьому розділі наводиться приклад програми “fastdump”, яка ілюструє використання більшості функцій Resource Manager. Це програма являє собою утиліту багатопотокового шістнадцяткового дампа, в якій використовуються тільки функції інтерфейсу Resource Manager API, тобто в ній не виконуються звернення до бази даних RDM Server. Програма запускає до 9 потоків, що відповідають за виконання дампа. Потік основного процесу формує чергу для відправки повідомлень, що запитують дамп кожного блоку з вхідного файлу в форматований вихідний файл. Кожен потік зчитує наступне по черзі повідомлення, потім асинхронно зчитує відповідний блок з вхідного файлу, форматує вихідний блок і асинхронно записує цей відформатований блок у вихідний файл. Після відправлення повідомлення, яке запитує дамп останнього блоку, потік основного процесу посилає кожному потоку повідомлення про завершення дампа. Після прочитання цього повідомлення потік зупиняється і сигналізує про своє завершення семафору подій, який інформує про це потік основного процесу. Після того, як всі потоки відправлять сигнал про своє завершення, програма припиняє роботу.


Програма “fastdump” може бути виконана за допомогою наступної командного рядка:


fastdump [-b размер_блока] [-t чісло_потоков] вхідний_файл вихідний_файл


-B размер_блока Визначає розмір блоку вхідного файлу, дамп якого виконується всіма потоками.




Якщо розмір блоку не кратний 512, то він буде усічений до розміру кратного 512.
Значення за замовчуванням – 4096.


-T чісло_потоков Задає число потоків, що виконують дамп. Максимальне число потоків 9, за замовчуванням 3 потоку.




Нижче наводиться початковий код основної програми “fastdump”, який обробляє аргументи командного рядка.


/* ==========================================================================
Multithreaded hex dump program
*/
void main( int argc, char *argv[] )
{
int argi;
char *inFileName, *outFileName;
short *threadSems, tno, stat;
unsigned long filelen, pos;
jmp_buf noMoreMem;
DUMPMSG *pMsg;
/* process command line options */
for (argi = 1; argi < argc && argv[argi][0] == “-“; ++argi) {
char opt = argv[argi][1];
switch ( opt ) {
ase “b”: /* set size of input file block buffer */
if ( ++argi == argc ) usage();
buffSize = (size_t)atoi(argv[argi]);
if (buffSize % 512) {
/* make sure its a multiple of 512 */
buffSize = (buffSize/512)*512;
}
break;
case “t”: /* set # of dump threads (max. of 9) */
if ( ++argi == argc ) usage();
maxThreads = (short)atoi(argv[argi]);
if (maxThreads >= 10)
maxThreads = 9;
break;
default:
usage();
}
}
if ( argi < argc – 1 ) {
/* fetch file names */
inFileName = argv[argi++];
outFileName = argv[argi];
}
else
usage();


Функція usage дозволяє вивести на друк інформацію про використання програми і вийти з програми. Змінні buffSize і maxThreads є глобальними цілочисельними змінними (типу size_t і short, відповідно). Наступна частина основної програми запускає Resource Manager.


/* startup RDM Server resource manager */
stat = rm_startup(NULL, MessageConsole, LOG_ERROR/LOG_WARN/LOG_INFO);
if ( stat != RM_OKAY ) {
printf(“Unable to start up resource manager. Status = %d
“, stat);
exit(1);
}


Перший аргумент функції rm_startup вказує повний шлях до директорії каталогу RDM Server. В даному випадку передається значення NULL, яке дозволяє витягти шлях до каталогу з змінної середовища CATPATH. Директорія каталогу містить файл velocis.ini, З якого Resource Manager витягує інформацію про свою конфігурації. Якщо змінна середовища CATPATH ​​відсутній, то функція rm_startup повертає код помилки RM_NOCATPATH.


Другий аргумент функції rm_startup вказує адресу користувача функції журналу повідомлень RDM Server. Коли він заданий, база даних RDM Server викликає зазначену функцію, щоб обробити всі журнальні повідомлення, які генеруються самої базою даних RDM Server або при кожному виклику функції rm_log, Що виходить з користувальницького додатки.


Третій аргумент складається з бітової карти типів журнальних повідомлень. В даному випадку в ньому зазначені тільки реєструються повідомлення типу errors (повідомлення про помилку), warnings (попереджувальні повідомлення) і information (інформаційні повідомлення). Можна також включити і інші типи повідомлень, наприклад, повідомлення запуску RDM Server (LOG_STARTUP) та повідомлення реєстрації користувача (LOG_LOGIN).


Тепер наведемо код функції MessageConsole.


/* =========================================================================
Log console messages
*/
void REXTERNAL MessageConsole(
RDSLOG_CTRL fcn, / * тип виклику: open, close, message * /
RDSLOG_CTRL fcn, /* call type: open, close, message */
short type, /* message type */

char *msg) /* message to be logged */
{
if ( fcn == RDSLOG_MESSAGE ) {
switch ( type ) {
case LOG_ERROR: printf(“***ERROR: “); break;
case LOG_WARN: printf(“***WARINING: “); break;
}
printf(“%s
“, msg);
}
}


У цій функції немає нічого складного. Вона просто друкує повідомлення про помилку і попереджуючі повідомлення на консолі сервера. Звичайно, можна написати більш складні обробники повідомлень. Наприклад, в Windows NT/2000 ви могли б написати повідомлення для журналу подій.


Наступний розділ основної програми відкриває вхідний і вихідний файли.


/* open files */
hInFile = rm_fileOpen(inFileName, O_RDONLY/O_BINARY, RM_SHARE, 0, 0);
hOutFile = rm_fileOpen(outFileName, O_CREAT/O_RDWR/O_BINARY,
RM_SHARE/RM_DIRECTIO, 0, 0);
if ( ! hInFile // !hOutFile ) {
printf(“Unable to open files
“);
rm_cleanup();
exit(1);
}


Дескриптори файлів hInFile і hOutFile є глобальними змінними типу RM_PFILE. Вони використовуються всіма потоками, що виконують дамп. Обидва файлу відкриваються як спільно використовуються (RM_SHARED) виконавчі (O_BINARY) файли (не текстові). Вхідний файл доступний лише для читання (O_RDONLY). Вихідний файл доступний для читання / запису (O_RDWR) (незважаючи на те, що запис також застосовується). Якщо вихідного файлу немає, то він створюється (O_CREAT). Крім того, вихідний файл має прапор RM_DIRECTIO. Цей прапор вказує записи, які повинні записуватися прямо на диск.


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


Наступний розділ основної програми налаштовує управління динамічною пам’яттю.


/* set up memory management */
if ( setjmp(noMoreMem) ) {
rm_log(LOG_ERROR, “FATAL EXIT: out of memory”);
goto shutdown;
}
/* use C malloc function to allocate a reserve threshold amount */
threshold = malloc(2*buffSize);

/* allocate memory tag, RM_USESEM => multithread allocations are serialized */
mTag = rm_createTag(“fastdump”, 0, noMoreMem, FreeReserve, 0, RM_USESEM);

/* allocate pool for queue messages */
qPool = rm_allocPool(mTag, “qPool”);


Зауважимо, що оголошення локальних змінних, які були раніше продемонстровані для основної програми, включають змінну noMoreMem, Оголошену як тип jmp_buf (оголошення виходить з # include <jmpbuf.h> Під # include rds.h). Виклик стандартної функції setjmp мови С зберігає в noMoreMem інформацію про місце повернення, передаючи її у функцію rm_createTag. Таким чином, коли якийсь виклик функції розподілу пам’яті, пов’язаний з mTag, Не проходить через нестачу пам’яті, то буде виконуватися функція longjmp для зазначеного jmp_buf. В результаті, функція setjmp поверне ненульове значення з викликом функції rm_log для повідомлення про нестачу пам’яті, за яким послідує зупинка програми.


Threshold є глобальним покажчиком на об’єкт невідомого типу (global void pointer), який використовується для отримання зарезервованого фрагмента пам’яті (в даному випадку, рівного двом блокам). Функція FreeReserve звільняє цей фрагмент, якщо при запиті про виділення пам’яті для тега mTag не буде знайдено достатньої кількості пам’яті. Після звільнення цього буфера будь-які наступні звернення до FreeReserve не додають вільної пам’яті, а призводять до передачі управління до функції setjmp.


Виклик функції rm_createTag містить прапор RM_USESEM. Він необхідний, оскільки дозволяє впорядкувати виклики на виділення пам’яті для тега mTag, Що надходять відразу від декількох потоків. Якщо прапор RM_USESEM не буде зазначений, то ці виклики можуть призвести (і, швидше за все, приведуть) до пошкодження структури даних розподіляє пам’яті, що загрожує порушенням загального захисту або навіть нескінченним циклом. У будь-якому випадку, вам слід заздалегідь визначити, які потоки будуть виконувати вилучення та розподіл пам’яті з кожного тега пам’яті. Якщо ви впевнені, що всі операції по виділенню пам’яті для даного тега будуть виконуватися з одного і того ж потоку, то для цього прапора можна вказати значення RM_NOSEM. Це прискорить розподіл пам’яті.


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


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


Тепер наведемо код функції FreeReserve. Зауважимо, що аргументи цієї функції включають не тільки тег пам’яті, а й запитуваний розмір пам’яті.


/* =========================================================================
Insufficient memory reserve function
*/
void REXTERNAL FreeReserve(
RM_MEMTAG tag,
size_t size)
{
if ( threshold && size <= 2*buffSize ) {
/* free reserve if it has sufficient space */
free(threshold);
threshold = NULL; / * друк повідомляючого повідомлення * /
rm_log(LOG_WARN, “running low on memory”);
}
}


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


/* Create message queue for sending dump instructions to threads */
dumpQueue = rm_queueCreate(“dumpQueue”);
/* A global variable, blockCount, will be updated by the threads.
This mutex semaphore will be used to synchronize access to it.
*/
countSem = rm_syncCreate(“countSem”, RM_MUTEX_SEM, RM_EXCLUSIVE);
/* allocate array of event semaphores, 1 per thread */
threadSems = rm_getMemory(maxThreads*sizeof(short), mTag);
rm_log(LOG_INFO, “Launching %d dump threads”, maxThreads);
for ( tno = 0; tno < maxThreads; ++tno ) {
threadSems[tno] = rm_syncCreate(“doneSem”, RM_EVENT_SEM, RM_EXCLUSIVE);
rm_syncStart(threadSems[tno]);
rm_threadBegin(DumpThread, THREAD_STACK, &threadSems[tno], RM_THREAD_HIGH);
}


Черга dumpQueue використовується для відправки повідомлень з основного потоку в потоки, що виконують дамп. Семафор взаємного виключення countSem впорядковує доступ до глобальної змінної blockCount типу long для її оновлення. Ця змінна збільшується після виконання потоками кожного запиту на дамп. Наведений вище сегмент програми ілюструє спосіб використання семафорів взаємного виключення для захисту доступу до глобальних даних.


Масив threadSems містить для кожного потоку один семафор подій. Ці семафори створюються циклом for, Який і запускає потоки. Виклик функції rm_syncStart встановлює семафор подій в несигнальному стан, який призупиняє всі наступні виклики функції rm_syncWait для цього семафора, поки який-небудь інший потік або кілька потоків не викличуть функцію rm_syncResume. Виклик функції rm_threadBegin ініціалізує виконання зазначеної функції управління потоками. Другий аргумент задає розмір простору стека, що виділяється для цієї функції. Константа THREAD_STACK є розміром стека RDM Server за замовчуванням. Стек повинен вміщати аргументи і локальні змінні не тільки зазначеної функції управління потоками, а й усіх функцій, які будуть з неї викликатися. Якщо ви не визначили розмір стека, вам краще всього використовувати значення за замовчуванням THREAD_STACK (розмір цього значення визначається максимальною глибиною викликів RDM Server). Третій аргумент функції rm_threadBegin є змінною-покажчиком, який передається як аргумент функції управління потоками. Ви можете задати його на свій вибір. В даному випадку, ми передаємо адресу семафора завершального події. Останній аргумент функції rm_threadBegin задає пріоритет потоку. У даному прикладі ми вибрали пріоритет RM_THREAD_HIGH, оскільки ця програма використовує тільки функції Resource Manager. (Також можна вибрати значення RM_THREAD_LOW і RM_THREAD_NORMAL.) Для додатків, які будуть реєструватися і звертатися до бази даних RDM Server, цей параметр повинен встановлювати пріоритет NORMAL або LOW.


Після запуску потоків, що відповідають за виконання дампа, основна програма починає відправляти цим потокам повідомлення із запитами на дамп, використовуючи чергу dumpQueue. Наступний код виконує розбиття дампа, створюючи і налаштовуючи повідомлення із запитами на дамп, і потім викликає функцію rm_queueWrite, Організовуючи чергу з цих повідомлень.


/* get length of file */
filelen = rm_fileLength(hInFile);
/* send dump messages to threads */
for (pos = 0; filelen > 0; pos += buffSize) {
pMsg = rm_getFromPool(sizeof(DUMPMSG), qPool);
pMsg->startpos = pos;
pMsg->blocklen = filelen >= buffSize ? buffSize : filelen;
rm_queueWrite(dumpQueue, pMsg, sizeof(DUMPMSG), 0, 0);
filelen -= buffSize;
}


Функція rm_fileLength повертає розмір вхідного файла в байтах, зберігаючи його в локальній змінній filelen типу unsigned long (довге ціле без знака). Ця змінна використовується для управління циклом for, Який розбиває вхідний файл на повідомлення із запитами на дамп, довжиною buffSize (можливо, крім останнього повідомлення). Оголошення типу DUMPMSG виконується наступним чином.


typedef struct dumpmsg {
unsigned long startpos; /* byte offset from start of file */
size_t blocklen; /* length of block (= buffSize, 0 = end) */
} DUMPMSG;


Спочатку з пулу виділяється буфер повідомлення. Потім при виконанні функції rm_queueWrite це повідомлення ставиться в чергу dumpQueue. Три перших аргументу цієї функції не вимагають пояснень. Четвертий аргумент є значенням типу довге ціле, яке може використовуватися в якості ідентифікатора повідомлення. У даній програмі цей аргумент використовується як логічний прапор, який має значення TRUE (або 1) після відправлення останнього повідомлення. Останній аргумент – булевий. Він вказує, що дане повідомлення є пріоритетним і, отже, має бути поставлено в початок черги. У цьому прикладі, всі повідомлення мають пріоритет NORMAL і ставляться в чергу в порядку надходження.


З цього моменту ми залишаємо основну програму і переходимо до розгляду функції управління потоками.


/* =========================================================================
Dump thread – dumps 1 block at a time
*/
static void RTHREAD DumpThread(void *pDoneSem)
{
short doneSem;
char *inBuff;
char *outBuff;
unsigned int len;
DUMPMSG *pMsg = NULL;
long end_of_dump = 0;
/* end-of -dump event semaphore is thread argument */
doneSem = *(short *)pDoneSem;
/* allocate input buffer for 1 dump block */
inBuff = rm_getMemory(buffSize, mTag);
/* allocate output buffer for each formatted dump line */
outBuff = rm_getMemory(77*(buffSize/16)+1, mTag);
while ( ! end_of_dump ) {
rm_queueRead( dumpQueue, &pMsg, NULL, &end_of_dump, RM_INDEFINITE_WAIT );
if ( pMsg ) {
/* read block to be dumped */
len = rm_fileSeekRead(hInFile, pMsg->startpos, inBuff, pMsg->blocklen);
if ( len != pMsg->blocklen )
rm_log(LOG_ERROR, “error reading input file”);
else
DumpBlock(inBuff, pMsg->startpos, pMsg->blocklen, outBuff);
/* update count of dumped blocks */
rm_syncEnterExcl(countSem);
++blockCount;
rm_syncExitExcl(countSem);
/* free queue message */
rm_putInPool(pMsg, qPool);
}
}
/* signal foreground that we”re done */
rm_syncResume(doneSem);
/* terminate thread */
rm_threadEnd();
}


Як говорилося вище, аргумент функції управління потоками є покажчиком на семафор завершального події і витягується в змінну doneSem. Кожен потік повинен мати свій вхідний буфер inBuff. Виклик функції rm_getMemory виділяє певну кількість байтів buffSize в цьому буфері. Для кожного вхідного блоку форматований вивід записується в один вихідний буфер outBuff, який потім при одноразовому виклик записи записується в вихідний файл. Кожна Форматована вихідна рядок складається з 16 байтів дампа вхідного блоку. Всі такі рядки мають довжину, рівну 77 символам.


Основне тіло потоку виконується всередині циклу while. Цикл завершується, коли прапор end_of_dump, що повертається функцією rm_queueRead, Має значення TRUE. Аргументи функції rm_queueRead досить прості. Останній аргумент вказує, скільки часу (в мілісекундах) потік чекає повідомлення, яке буде записано в чергу. В даному випадку потік чекає повідомлення необмежений час (RM_INDEFINITE_WAIT).


Якщо аргумент очікування має значення 0, то функція або поверне значення RM_OKAY і доставить повідомлення, або поверне значення RM_QUEUE_EMPTY, яке вказує на відсутність повідомлень в черзі.


Повертаний покажчик повідомлення міститься в pMsg. Значення NULL для pMsg відправляється з останнім повідомленням (прапор end_of_dump дорівнює 1). Виклик функції rm_fileSeekRead запускає зчитування в inBuff вхідного блоку, на який вказує останнє повідомлення. Функція rm_fileSeekRead повертає фактичне число прочитаних байтів. Якщо це значення (len) не одно запрошенням числа байтів, значить сталася помилка (або ви намагаєтеся виконати зчитування за міткою кінця файла, що в даному випадку неможливо). Після успішно завершеного зчитування викликається функція DumpBlock для форматування вхідного блоку у вихідний буфер.


Мінлива blockCount являє собою глобальну змінну типу long, яка використовується для підрахунку блоків з виконаними дампами. Процес оновлення цієї змінної вимагає упорядкування, оскільки вона оновлюється одразу кількома потоками. Коли потік, оновлюючий цю змінну, чекає своєї черги до семафору взаємного виключення, що викликається ним функція rm_syncEnterExcl призупиняється. Після того, як він отримає управління над семафором взаємного виключення, функція rm_syncEnterExcl виконується і потік безпечно оновлює змінну blockCount. При цьому всі інші потоки, які звертаються до функції rm_syncEnterExcl для поновлення змінної countSem, ставляться в чергу. Коли керуючий потік, викликаючи функцію rm_syncExitExcl, Звільняє семафор взаємного виключення, управління передається наступному потоку, що стоїть першим у черзі.


Звільнивши семафор взаємного виключення, потік викликає функцію rm_putInPool, Щоб повернути буфер повідомлення в пул пам’яті.


Після отримання повідомлення про завершення дампа з основного (високопріоритетний) потоку цикл переривається, і потік викликає функцію rm_syncResume, Яка встановлює семафор завершального події для цього потоку в сигнальне стан. Зазвичай цей семафор залишається в сигнальному стані до свого перезапуску, який відбувається в результаті виклику функції rm_syncStart. Це означає, що до тих пір поки він знаходиться в сигнальному стані, будь-які виклики функції rm_syncWait для цього семафора будуть негайно повертатися.


Для завершення потоку викликається функція rm_threadEnd. Зауважимо, що при виконанні цієї функції потік автоматично завершується. Але оскільки деякі нові платформи RDM Server можуть вимагати виклику спеціальної функції, завершувати потік слід цим викликом.


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


/* send terminate thread messages */
for (tno = 0; tno < maxThreads; ++tno)
rm_queueWrite(dumpQueue, NULL, 0, 1, 0);

/* wait for each thread to signal they”re done */
for (tno = 0; tno < maxThreads; ++tno)
rm_syncWait(threadSems[tno], RM_INDEFINITE_WAIT);

rm_log(LOG_INFO, “Dump complete: %ld %d-byte blocks written to file %s.”,
blockCount, buffSize, outFileName);


Після відправки повідомлень про завершення дампа програма виконує цикл по всіх елементах масиву семафора завершального події і викликає rm_syncWait, очікуючи завершення всіх потоків. Потім, вона викликає функцію rm_log, Щоб зареєструвати завершення дампа і повідомити загальну кількість записаних блоків. Функція rm_log аналогічна функції printf, за винятком першого аргументу, який вказує тип повідомлення. Другий аргумент – специфікатор формату printf, за яким слід 0 або кілька змінних, перерахованих в цьому специфікатор. В даному випадку це повідомлення буде обробляти функція MessageConsole, Яка була задана при виконанні функції rm_startup.


Остання частина програми закриває файли і звільняє всі використовувані ресурси Resource Manager.


/* close files */
rm_fileClose(hInFile);
rm_fileClose(hOutFile);
/* delete the queue */
rm_queueDelete(dumpQueue);
/* free the message pool */
rm_zFreePool(&qPool);
/* free semaphores */
rm_syncDelete(countSem);
for (tno = 0; tno < maxThreads; ++tno)
rm_syncDelete(threadSems[tno]);
/* free allocated memory */
rm_freeTagMemory(mTag, 1);
if ( threshold )
free(threshold);

rm_cleanup();


Другий аргумент функції rm_freeTagMemory є прапором, який вказує на те, чи буде цей тег звільнятися. Оскільки тег можна багаторазово використовувати, його звільнення не завжди потрібно. Тег багаторазово використовується, коли при одноразовому виконання будь-якої функції виділяється і звільняється вся пам’ять. В цьому випадку, оскільки тег вже існує, потрібно вказати спосіб завдання нового буфера нелокального переходу, який необхідний при нестачі пам’яті. Це можна зробити, викликавши функцію rm_resetTag.


На цьому ми завершуємо розгляд утиліти багатопотокового шістнадцяткового дампа. Цей досить тривіальний приклад наочно показує, як можна використовувати більшість функції Resource Manager, які необхідні для створення складних прикладних програм або додаткових модулів для сервера бази даних RDM Server.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*