2012-06-02 2 views
4

X86-64, Linux, Windows.Карта памяти на другой адрес

Учтите, что я хочу сделать какой-то «бесплатный запуск для указателей тегов». В принципе, я хочу иметь два указателя, которые указывают на один и тот же фактический блок памяти, но чьи биты разные. (Например, я хочу, чтобы один бит использовался коллекцией GC или по какой-то другой причине).

intptr_t ptr = malloc() 
intptr_t ptr2 = map(ptr | GC_FLAG_REACHABLE) //some magic call 

int* p = int*(ptr); 
int* p2 = int*(ptr2); 
*p = 10; 
*p2 = 20; 
assert(*p == 20) 
assert(p != p2) 

ответ

2

В Linux, mmap() тот же файл дважды. То же самое и в Windows, но для этого есть свой набор функций.

1

Однажды я работал над реализацией Пролога, который использовал следующую технику, чтобы иметь запасные биты в указателе:

  • выделить область памяти с известным выравниванием. malloc() обычно выделяет память с 4-байтным или 8-байтовым выравниванием. При необходимости используйте posix_memalign(), чтобы получить области с более высоким размером выравнивания.

  • Поскольку полученный указатель выровнен с интервалами нескольких байтов, но он представляет точные адреса байтов, у вас есть несколько запасных бит, которые по определению будут равны нулю в указателе области памяти. Например, 4-байтовое выравнивание дает вам два запасных бита на стороне LSB указателя.

  • Вы ИЛИ (|) свои флаги с этими битами и теперь имеют указатель с меткой.

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

2

Отображение той же памяти (mmap на POSIX, как упоминает Игнасио, MapViewOfFile на Windows) для нескольких виртуальных адресов может предоставить вам некоторые интересные головоломки когерентности (есть запись в один адрес виден при чтении по другому адресу?). А может и нет. Я не уверен, что все гарантии платформы.

Чаще всего, каждый просто резервирует несколько бит в указателе и перемещает по мере необходимости.

Если все ваши объекты выровнены с 8-байтовыми границами, обычно просто хранить теги в 3 наименее значимых битах указателя и маскировать их перед разыменованием (как упоминается thkala). Если вы выберете более высокое выравнивание, например 16-байтовое или 32-байтовое, то есть 3 или 5 наименее значимых бит, которые можно использовать для пометки. Эквивалентно, выберите несколько наиболее значимых бит для пометки и сдвиньте их до разыменования. (Иногда несмежные биты используются, например, при упаковке указателей в сигнализации NaNs из IEEE-754 поплавков (2 значения) или двойные (2 значения).)

Продолжая на высоком конце указателя, текущие реализации x86-64 используют не более 48 бит из 64-разрядного указателя (0x0000000000000000-0x00007fffffffffff + 0xffff800000000000-0xffffffffffffffff), а Linux и Windows только передают адреса в первом диапазоне в пользовательское пространство, оставляя 17 наиболее популярных, значимые биты, которые можно безопасно замаскировать. (Это не является ни переносимым, ни гарантированным, чтобы оставаться в силе в будущем.)

Другой подход заключается в том, чтобы перестать рассматривать «указатели» и просто использовать индексы в массив больших массивов, поскольку JVM работает с -XX:+UseCompressedOops.Если вы выделили пул размером 512 Мбайт и сохранили 8-байтовые выровненные объекты, есть 2 возможных мест размещения объектов, поэтому 32-значное значение дополнительно содержит 6 бит. Для разыменования потребуется добавить индекс времени выравнивания к базовому адресу массива, сохраненного в другом месте (для каждого «указателя» он одинаковый). Если вы внимательно посмотрите на вещи, это просто обобщение предыдущей техники (которая всегда имеет базу в 0, где вещи выстраиваются в линию с реальными указателями).

Смежные вопросы