Адреса переменных локального хранилища потока

Хорошо, скажи, что у меня есть

__thread int myVar;

И затем я передаю &myVar из одного потока в другой... Если данные действительно «локальные», то хранилище TLS 1 потока может не отображаться в адресное пространство других потоков, и на самом деле можно утверждать, что оно < em>не должно быть. Это приведет к SIGSEGV или что-то в этом роде. Однако система может просто сопоставить тот же адрес с другой страницей. Это то, что Linux делает с .tbss/.tdata? В этом случае передача адреса переменной даст вам адрес неправильной переменной! Вы получите свою собственную локальную копию, а не копию, которую пытались передать. Или все совместно используется и сопоставляется с разными виртуальными адресами, что позволяет вам передавать адреса переменных __thread?

Очевидно, что следует бить и выпороть за попытку передать локальное хранилище потока другому потоку, передав его адрес. Есть миллион других способов — например, копирование в любую другую переменную! Но мне было любопытно, знает ли кто-нибудь..

  1. Чиновник описал поведение в этой ситуации
  2. Детали текущей реализации GCC/Linux

-- Эван


person Evan Langlois    schedule 17.07.2014    source источник
comment
Потоки не имеют отдельных адресных пространств. Все они совместно используют адресное пространство процесса. Это одна из нескольких причин, почему они легче, чем процессы. Непонятно, что вы спрашиваете.   -  person user207421    schedule 05.09.2016
comment
См. определение __thread, которое является ЛОКАЛЬНЫМ хранилищем потоков. Это фрагмент данных, который является локальным для потока и не используется совместно.   -  person Evan Langlois    schedule 06.09.2016


Ответы (1)


По крайней мере, для x86 TLS выполняется с использованием сегментных регистров. Регистр сегмента по умолчанию %ds подразумевается в инструкциях, которые обращаются к памяти. При доступе к TLS поток использует другой регистр сегмента — %gs для i386 и %fs для x86-64 — который сохраняется/восстанавливается при планировании потока, точно так же, как другие регистры используются при переключении контекста.

Таким образом, к переменной процесса можно получить доступ с помощью чего-то вроде:

mov (ADDR) -> REG ; load memory `myVar` to REG.

что неявно:

mov %DS:(ADDR) -> REG

Для TLS компилятор генерирует:

mov %FS:(ADDR) -> REG ; load thread-local address `myVar` to REG.

По сути, даже если адрес переменной кажется одинаковым в разных потоках, например,

fprintf(stdout, "%p\n", & myVar); /* in separate threads... */

тот факт, что каждый поток использует другое значение для сегментного регистра, означает, что они отображаются в разные области физической памяти.

Та же схема используется Windows (она может поменять местами роли %fs и %gs - не уверен) и OS X. /drepper/tls.pdf" rel="nofollow noreferrer">техническое руководство по TLS для ELF ABI. В нем отсутствует обсуждение архитектуры ARM и есть подробности об IA-64 и Alpha, поэтому он показывает свой возраст.

person Brett Hale    schedule 17.07.2014
comment
Ух ты. Я даже не думал о сегментных регистрах (вырос на машинах, отличных от x86)! Таким образом, Linux обычно устанавливает все сегментные регистры в 0 для плоского адресного пространства и оставляет их неиспользуемыми, но когда в дело вступает TLS, компилятор может установить fs/gs в другом месте, и ранее неиспользованный регистр теперь отслеживает местоположение нашего локальные данные. Это правильно? Я удивлен, что ядро ​​удосужилось загрузить/сохранить неиспользуемые (насколько я понимаю, раньше они были неиспользуемыми) сегментные регистры. - person Evan Langlois; 17.07.2014
comment
Я люблю такие ответы. Спасибо. - person whoan; 27.11.2017
comment
Этот ответ был очень полезным! Вы скажете: «Одно небольшое замечание». Если бы вы передавали адрес из одного потока в другой, вы не могли бы получить доступ к памяти, которую он представляет в первом потоке. документы GCC противоречат этому, говоря, что полученный таким образом адрес может быть использован по любой нитке. - person AnOccasionalCashew; 23.02.2020
comment
@AnOccasionalCashew - я должен был уточнить, что исключение соответствующего регистра сегмента (в IA32 / x86-64) не даст значимого локального адреса потока - моя текущая формулировка вводит в заблуждение в этом контексте. - person Brett Hale; 24.02.2020