Дескриптор (англ. Handle) — це структура даних, яка представляє відкритий екземпляр базового об'єкта операційної системи, наприклад, файл, ключ реєстру, об’єкт синхронізації тощо[1]. Дескриптор однозначно ідентифікує створений об'єкт і надає доступ до об'єкта, дозволяючи читати та змінювати його стан.

Дескриптори об'єктів операційної системи ред.

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

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

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

Робота з дескриптором ред.

Програма отримує дескриптор під час створення нового або відкривання існуючого об'єкта. Наприклад, при створенні потоку за допомогою функції CreateThread вона повертає дескриптор створеного потоку. Незалежно від того, яким чином отримано дескриптор, при закінченні роботи його потрібно закрити викликом функції CloseHandle:

function CloseHandle(hObject: THandle): BOOL;

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

Якщо дескриптор неправильний, функція CloseHandle повертає значення false.

Перед поверненням у викликаючу програму CloseHandle видаляє відповідний запис із таблиці дескрипторів: після цього дескриптор вже недійсний у процесі й використовувати його не можна. При цьому запис видаляється незалежно від того, знищений об'єкт ядра чи ні. Після виклику CloseHandle процес більше не отримає доступу до цього об'єкта ядра (але якщо його лічильник не обнулено, об'єкт залишається в пам'яті).

Після завершення процесу операційна система автоматично звільняє всі ресурси, які йому належали, і у випадку об'єктів ядра діє так: у момент завершення процесу переглядає його таблицю дескрипторів і закриває всі відкриті дескриптори. Дескриптори є процесо-залежними, проте часто виникає необхідність їх сумісного використання кількома процесами. У Windows реалізовано такі механізми сумісного доступу до об’єктів ядра кількома процесами: спадкування дескрипторів, їх іменування та дублювання. Спадкування стосується лише споріднених процесів (батьківський-дочірній). Батьківський процес при створенні може передати права доступу до своїх об’єктів ядра дочірньому процесу. Для цього поле bInheritHandle структури SECURITY_ATTRIBUTES слід встановити у true.

При такому наслідуванні система, крім копіювання записів з таблиці дескрипторів, збільшує значення лічильників відповідних об'єктів ядра, оскільки вони тепер використовуються обома процесами. Щоб знищити якийсь об'єкт ядра, його дескриптор повинні закрити (викликом CloseHandle) обидва процеси.

Успадковуються тільки дескриптори об'єктів, що існують на момент створення дочірнього процесу. Якщо батьківський процес створить після цього нові об'єкти ядра з успадковуваними дескрипторами, вони будуть недоступні дочірньому процесу.

При спадкуванні дочірній процес «не знає», що він успадкував якісь дескриптори, тому йому окремо передають значення очікуваного ним дескриптора об'єкта ядра (наприклад, як аргумент у командному рядку чи змінну оточення). Дескриптори дозволяють різним процесам використовувати системні об'єкти. Інший спосіб, який дозволяє кільком процесам спільно використовувати об'єкти ядра, пов'язаний з іменуванням цих об'єктів. Іменувати можна багато (але не всі) об'єкти ядра. Наприклад, іменовані об'єкти створюють такі функції: CreateMutex, CreateEvent, CreateSemaphore, CreateFileMapping тощо.

За відомим іменем об’єкта інші процеси можуть відкрити його дескриптор і отримати таким чином до нього доступ. Ще один механізм спільного використання об'єктів ядра кількома процесами — це застосування функції DuplicateHandle, яка отримує запис у таблиці дескрипторів одного процесу й створює її копію в таблиці іншого[1]. Як і при спадкуванні, процес-приймач ніяк не повідомляється про те, що він одержав доступ до нового об'єкта ядра, тому його потрібно спеціально інформувати про це.

Дескриптор захисту ред.

Об'єкти ядра можна захистити дескриптором захисту (security descriptor), який описує, хто створив об'єкт і хто має право доступу до нього[2]. Майже всі функції, що створюють об'єкти ядра, приймають аргумент – вказівник на структуру SECURITY_ATTRIBUTES. Більшість програм замість цього аргументу передає NULL, що приводить до створення об'єкта із захистом за замовчуванням. Така ситуація передбачає, що творець об'єкта й будь-який користувач групи адміністраторів отримує до нього повний доступ, а всі інші не допускаються. Однак для зміни цієї поведінки можна самостійно створити й ініціалізувати структуру SECURITY_ATTRIBUTES, а потім передати її адресу функції створення об’єкта. Вона має такий вигляд (описано мовою Delphi)[1]:

  _SECURITY_ATTRIBUTES = record
    nLength: DWORD;                     // Розмір структури
    lpSecurityDescriptor: Pointer;      // Адреса дескриптора захисту
    bInheritHandle: BOOL;               // Чи успадковувати дескриптор
  end;
  SECURITY_ATTRIBUTES = _SECURITY_ATTRIBUTES;

Лише один елемент цієї структури має стосунок до захисту — lpSecurityDescriptor. Якщо слід обмежити доступ до об'єкта, потрібно створити дескриптор захисту й ініціалізувати структуру SECURITY_ATTRIBUTES. Проте специфічний захист об'єктів використовують рідко.

Інші види дескрипторів ред.

Дескриптори віртуальних адрес (virtual address descriptors, VAD) — це структури даних, які використовуються диспетчером пам'яті для обліку адрес віртуальної пам'яті, задіяних процесом.

Примітки ред.

  1. а б в Коноваленко, І. В.; Федорів, П. С. (2012). Системне програмування у Windows з прикладами на Delphi (українською) . Тернопіль: ТНТУ. с. 319. Архів оригіналу (DjVu) за 8 серпня 2016. Процитовано 3 червня 2016.
  2. Ріхтер, Джеффрі (2001). Windows для профессионалов: создание эффективных Win32 приложений с учетом специфики 64-разрядной версии Windows [Windows для професіоналів: створення ефективних Win32 застосунків з урахуванням специфіки 64-розрядної версії Windows] (російською) (вид. 4-е). Санкт-Петербург, Москва: Пітер, Издательско-торговый дом «Русская редакция».