|
Навигация
|
Главная » Delphi Доступ к переменным Thread local storage (TLS) любого тредаИсточник: habrahabr DCa Данная статья иллюстрирует, как получить доступ к переменным из блока Thread Local Storage в Delphi. Однако принципы нахождения "чужого" блока TLS одинаковы для всех компиляторов Windows и применимы для любых языков программирования, поддерживающих TLS в том виде, как это определяет Windows. В Delphi, в отличии от глобальных переменных, переменные, объявленные в блоке threadvar, создаются для каждого потока (thread) с возможностью хранить независимые значения. Каждый поток читает и записывает свою копию значений.Но иногда необходимо прочесть или даже изменить переменные, соответствующие другому треду. Конечно, лучше изменить алгоритм, чтобы избежать такой необходимости, но решение этой задачи есть. Все блоки данных (Thread local storage, TLS) находятся в памяти одновременно, но по разным адресам, каждый тред хранит указатель на свою область памяти, поэтому есть возможность найти блок переменных и конкретное значение, принадлежащее любому треду, созданному в пределах текущего процесса. Область Thread local storage, в которой хранятся значения, определяется по значению из блока данных TEB. Адрес массива находится по смещению tlsArray (объявлено в модуле SysInit.pas). При каждом обращении к переменной, объявленной как threadvar, происходит неявный вызов функции _GetTls, которая возвращает указатель на область данных текущего треда. Добавив смещение переменной, можно получить ее адрес. Чтобы получить адрес переменной из другого треда, нужно вычесть адрес текущего блока и добавить адрес блока целевого треда. Просто вызвать служебную функцию _GetTls невозможно, нужно вызывать ее из ассемблерного кода, добавив символ @ перед именем и название модуля System. Этот же способ подходит для вызова большинства служебных функций, название которых начинается с подчеркивания, которые не вызываются обычным способом в коде Delphi: Для начала напишем такую функцию: Функция принимает в качестве аргументов дескриптор (не идентификатор!) нужного нам треда и адрес переменной в текущем блоке переменных треда. Вернет функция адрес той же переменной, но относящейся уже к другому треду. В первой строке мы получили смещение переменной относительно начала блока TLS.Теперь нужно добавить это смещение к указателю области локальных переменных целевого треда, которое хранится в блоке данных TEB. Смещение TEB треда в общем виртуальном пространстве процесса можно получить, вызвав функцию NtQueryInformationThread. Эта функция относится к числу Native-функций Windows, которые находятся в библиотеке ntdll.dll Для ее использования можно подключить модуль JwaNative.pas из набора JEDI Win32 API или поместить непосредственно в текущий модуль объявление внешней функции с таким прототипом (необходимо подключение стандартного модуля Windows.pas): Вместе с получением адреса TEB, функция теперь будет выглядеть так: Теперь остается найти блок TLS в структуре PEB. Смотрим исходные коды SysInit, конкретно функцию _GetTls. В 32-битной ОС адрес TLS массива (в котором под индексом TlsIndex находится адрес области данных треда) определяется таким кодом: Для 64-битной таким: Несложная проверка может показать, что код для 64-битной версии так же работает и в 32-битной, если взять во внимание другое значение tlsArray, а также то, что TEB находится по адресу GS:[0], а не FS[0], как в 32-битной Windows. Поскольку у нас уже есть адрес TEB (поле TebBaseAddress из структуры basic), который равен началу сегмента GS для Win64 и сегмента FS для Win32, то мы можем заменить значение @GSSegBase на полученный нами указатель TEB Полная функция с некоторой оптимизацией может выглядеть так: Для удобства использования данной функции в коде создадим класс с несколькими статичными методами: Тогда мы можем объявить следующие два метода: При использовании параметризованных типов бывают трудности в объявлении указателя на тип T.В таких случаях можно воспользоваться конструкциями такого типа: Delphi разрешает разыменование нетипизированного указателя, если сразу происходит преобразование типа или если такое значения без типа передается в функции напободие FillChar и Move (в которых аргументы объявлены так же без типа). Теперь для доступа к переменной "чужого" треда можно использовать такой код: А добавив такое объявление после класса TThreadLocalStorage: Можно еще сократить код: В качестве завершения, нужно отметить, что, при доступе к переменной из другого треда, надо помнить о синхронизации, как при доступе к глобальным переменным, доступным всем тредам. Это особенно относится к операциям Inc, Dec, а также к составным типам данных. Отсутствие необходимости синхронизировать доступ к threadvar данным было обусловлено только тем, что все прочие треды не имели доступа к данным текущего треда. Лицензионная политика линейки продуктов Embarcadero. Delphi. Работаем с файлами. Компания Borland приобретает компанию Segue Software; объявлено о планах отказаться от семейства программных продуктов IDE. WordPress. Работа с XML-RPC в Delphi. Комментарии (продолжение). Создание собственной кнопки в Delphi. Главная » Delphi |
© 2024 Team.Furia.Ru.
Частичное копирование материалов разрешено. |