- •1.Объекты ядра
- •2. Учет пользователей объектов ядра
- •3. Защита объектов ядра
- •4. Таблица описателей объектов ядра
- •5. Создание объекта ядра
- •6. Закрытие объекта ядра
- •7. Совместное использование объектов ядра несколькими процессами
- •8. Наследование описателя объекта
- •9. Изменение флагов описателя
- •10. Именованные объекты
- •11. Дублирование описателей объектов
- •12. Синхронизация с потоков с помощью объектов ядра
- •13. Wait-функции
- •13.1. Побочные эффекты успешного ожидания
- •14. События
- •15. Семафоры
- •16. Мьютексы
- •17. Отказ от объекта-мьютекса
- •18. Мьютексы и критические секции
- •19. Сводная таблица объектов, используемых для синхронизации потоков
- •20. Порядок выполнения работы
- •21. Контрольные вопросы
11. Дублирование описателей объектов
Последний механизм совместного использования объектов ядра несколькими процессами требует функции DuplicateHandle;
BOOL DuplicateHandle( HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, PHANDLE phTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions);
Говоря по-простому, эта функция берет запись в таблице описателей одного процесса и создает ее копию в таблице другого. DuplicateHandle принимает несколько параметров, но на самом деле весьма незамысловата. Обычно ее применение требует наличия в системе трех разных процессов.
Первый и третий параметры функции DuplicateHandle представляют собой описатели объектов ядра, специфичные для вызывающего процесса. Кроме того, эти параметры должны идентифицировать именно процессы — функция завершится с ошибкой, если будут переданы описатели на объекты ядра любого другого типа.
Второй параметр, hSourceHandle, — описатель объекта ядра любого типа. Однако его значение специфично не для процесса, вызывающего DuplicateHandle, а для того, на который указывает описатель hSourceProcessHandle. Параметр phTargetHandle — это адрес переменной типа HANDLE, в которой возвращается индекс записи с копией описателя из процесса-источника. Значение возвращаемого описателя специфично для процесса, определяемого параметром bTargetProcessHandle.
Предпоследние два параметра DuplicateHandle позволяют задать маску доступа и флаг наследования, устанавливаемые для данного описателя в процессе-приемнике. И, наконец, параметр dwOptions может быть 0 или любой комбинацией двух флагов. DUPLICATE_SAME_ACCESS и DUPLICATE_CLOSE_SOURCE.
Первый флаг подсказывает DuplicateHandle: у описателя, получаемого процессом-приемником, должна быть та же маска доступа, что и у описателя в процессе-источнике Этот флаг заставляет DuplicateHandle игнорировать параметр dwDesiredAccess.
Второй флаг приводит к закрытию описателя в процессе-источнике. Он позволяет процессам обмениваться объектом ядра как эстафетной палочкой. При этом счетчик объекта не меняется.
Представьте, что один процесс имеет доступ к объекту, к которому хочет обратиться другой процесс, или что один процесс хочет предоставить другому доступ к «своему» объекту ядра. Например, если процесс S имеет доступ к объекту ядра и нужно, чтобы к этому объекту мог обращаться процесс Т, используйте DuplicateHandle так:
// весь приведенный ниже код исполняется процессом S // создаем объект-мьютекс, доступный процессу S HANDLE hObjProcessS = CreateMutex(NULL, FALSE, NULL);
// открываем описатель объекта ядра "процесс Т" HANDLE hProcessT = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessIdT); HANDLE hObjProcessT; // неинициализированный описатель,
// связанный с процессом Т // предоставляем процессу Т доступ к объекту-мьютексу DuplicateHandle(GetCurrentProcess(), hObjProcessS, hProcessT, &hObjProcessT, 0, FALSE, DUPLICATE_SAME_ACCESS);
// используем какую-нибудь форму межпроцессной связи, чтобы передать // значение описателя из hObjProcessS в процесс Т
// связь с процессом Т больше не нужна CloseHandle(hProcessT),
// если процессу S не нужен объект-мьютекс, он должен закрыть его CloseHandle(hObjProcessS);
Вызов GetCurrentProcess возвращает псевдоописатель, который всегда идентифицирует вызывающий процесс, в данном случае — процесс S. Как только функция DuplicateHandle возвращает управление, bObjProcessT становится описателем, связанным с процессом Т и идентифицирующим тот же объект, что и описатель bObjProcessS (когда на него ссылается код процесса S). При этом процесс S ни в коем случае не должен исполнять следующий код:
// процесс S никогда не должен пытаться исполнять код, // закрывающий продублированный описатель CloseHandle(hObjProcessT);
Если процесс S выполнит этот код, вызов может дать (а может и не дать) ошибку. Он будет успешен, если у процесса S случайно окажется описатель с тем же значением, что и в hObjProcessT. При этом процесс S закроет неизвестно, какой объект, и что будет потом — остается только гадать.
Теперь о другом способе применения DuplicateHandle. Допустим, некий процесс имеет полный доступ (для чтения и записи) к объекту «проекция файла» и из этого процесса вызывается функция, которая должна обратиться к проекции файла и считать из нее какие-то данные. Так вот, если мы хотим повысить отказоустойчивость приложения, то могли бы с помощью DuplicateHandle создать новый описатель существующего объекта и разрешить доступ только для чтения. Потом мы передали бы этот описатель функции, и та уже не смогла бы случайно что-то записать в проекцию файла.
int WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE, PSTR pszCmdLine, int nCmdShow) {
// создаем объект "проекция файла", // его описатель разрешает доступ как для чтения, так и для записи HANDLE hFileMapRW = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGF_READWRITE, 0, 10240, NULL);
// создаем другой описатель на тот же обьект; // этого описатель разрешает дocтyп только для чтения HANDLE hFileMapRO;
DuplicateHandle(GetCurrentProcess(), hFileMapRW, GetCurrentProcess(), &hFileMdpRO, FILE_MAP_READ, FALSE, 0);
// вызываем функцию, которая не должна ничего записывать в проекцию файла ReadFromTheFileMapping(hFileMapRO);
// закрываем объект "проекция файла" , доступный только для чтения CloseHandle(hFileMapRO);
// проекция файла нам по-прежнему полностью доступна через hFileMapRW . . . // если проекция файла больше не нужна основному коду, закрываем ее CloseHandle(hFileMapRW); }
