Объединение указателя и целого числа в объединении - PullRequest
1 голос
/ 12 декабря 2011

Я определил объединение следующим образом:

union {
  uintptr_t refcount;
  struct slab_header *page;
} u;

Указатель page гарантированно будет выровнен по границе страницы (скорее всего, 4096) и никогда не будет NULL.Это означает, что наименьший возможный адрес будет 4096.

refcount будет в пределах 0 .. 4095.

После создания вмещающей структуры я могу либо иметь u.refcount = 0 или u.page = mmap(...).

Код этого объединения будет выглядеть примерно так:

if (u.refcount < 4096) {
  /* work with refcount, possibly increment it */
} else {
  /* work with page, possibly dereference it */
}

Всегда ли гарантируется, что это будет работать в полностью POSIX-совместимой реализации?Возможно ли когда-нибудь, чтобы uintptr_t и struct slab_header * имели разные представления, так что, например, когда u.page == 8192, u.refcount < 4096 дает значение true?

Ответы [ 5 ]

3 голосов
/ 12 декабря 2011

Я не думаю, что это "всегда гарантированно работает", потому что:

  1. uintptr_t необязательно (7.18.1.4).
  2. A void * можетпреобразовать в uintptr_t и обратно (7.18.1.4).Не гарантируется, что это так с struct slab_header*.void * имеет те же требования к представлению и выравниванию, что и указатель на тип символа.Указатели на структуры не обязательно должны иметь одинаковое представление или выравнивание (6.2.5 27).Даже если бы это было не так, ничто не гарантирует sizeof(uintptr_t) == sizeof(void *), оно, очевидно, могло бы быть больше и все же удовлетворять требованию преобразования в void * в типичном случае однородных указателей.
  3. Наконец, даже если ониимеют одинаковый размер и являются конвертируемыми, возможно, представление значений указателя странным образом отличается от представления целых чисел без знака.Представление целых чисел без знака относительно ограничено (6.2.6.2 1), но таких указателей для указателей не существует.

Поэтому я бы пришел к выводу, что наилучшим способом было бы иметь общие начальные элементы, которыеговорит государство.

1 голос
/ 12 декабря 2011

Я собираюсь ответить на другой вопрос - "это хорошая идея?" Более серьезное беспокойство, которое я вижу из вашего кода, - это проблемы с алиасами. Я был бы не удивлен (на самом деле, я бы слегка ожидал этого), если бы можно было написать кусок кода с эффектом

  • Напишите что-нибудь на u.refcount
  • Напишите что-нибудь на u.page
  • Чтение u.refcount
  • Узнайте, что значение, которое вы прочитали, совпадает с тем, что вы впервые написали

Вы можете насмехаться, но я видел, как это произошло - и если вы не понимаете опасности, потребуется очень много времени для отладки.

Вы можете быть в безопасности с этим конкретным союзом; Я оставлю это кому-то с большим опытом работы с подобными вещами (и / или удобной копией стандарта C), чтобы сделать такое суждение. Но я хочу подчеркнуть, что это то, о чем важно беспокоиться !!!

Существует другая стоимость. Использование объединения в сочетании с «магическим» тестом для обнаружения того, что в нем хранится - особенно тест с использованием специфических для системы фактов - сделает ваш код труднее для чтения, отладки и сопровождения. Вы можете предпринять шаги для смягчения этого факта, но это всегда будет проблемой.

И, конечно, стоимость вашего кода просто не работает, когда кто-то пытается использовать его на странной машине.

Правильное решение, вероятно, состоит в том, чтобы структурировать ваш код таким образом, чтобы единственным кодом, который заботится о компоновке данных, была пара крошечных inline d подпрограмм доступа, чтобы вы могли легко поменять, как хранить вещи. Организуйте свой код, чтобы использовать флаг времени компиляции, чтобы выбрать какой!

Теперь, когда я сказал все это, вопрос, который я действительно хочу задать (и хочу, чтобы вы привыкли размышлять): "Стоит ли это того?" Вы жертвуете временем, удобочитаемостью кода, простотой программирования и переносимостью. Что вы получаете?

Многие люди забывают задать этот вопрос, и они пишут сложный код ошибки, когда упрощенный, простой в написании и чтении код столь же хорош.

Если вы обнаружили, что это изменение удвоит производительность рутины, требующей много времени, то, вероятно, стоит бороться с этим хаком. Но если вы исследуете это просто потому, что это умный способ сэкономить 8 байтов, то вы должны рассматривать это только как интеллектуальное упражнение.

0 голосов
/ 12 декабря 2011

Всегда ли это гарантированно работает?

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

Возможно ли когда-нибудь, чтобы uintptr_t и struct slab_header * имели разные представления, так что, например, когда u.page == 8192, u.refcount <4096 выдает true? </p>

Это также теоретически возможно. Я не могу думать о текущей реализации, где это происходит.

0 голосов
/ 12 декабря 2011

должна быть какая-то проблема с этим кодом.Но в любом случае вы можете иметь в начале вашей программы проверку:

if (sizeof(uintptr_t) != sizeof (struct slab_header*)
{
    print error
}

, но что-то кажется отключенным (или неясным).Вы хотите, чтобы "refcount" был 0, когда у вас есть страница.а не ноль, когда страница пуста?

0 голосов
/ 12 декабря 2011

Я думаю, что <stdint.h> и стандарт C99 гарантируют, что uintptr_t и void* имеют одинаковый размер и могут быть брошены без потерь.

Выравнивание страницы для указателя является подробностью реализации.

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