Является ли ((void *) -1) действительным адресом?
Дословно из Linux 'man shmat
:
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
[...] on error (void *) -1 возвращается, и errno устанавливается для указания причины ошибки.
(POSIX говорит то же самое, используя немного другую формулировку.)
Существует ли какое-либо обязательное правило или определение (стандарт?), Которое (void *) -1
не может быть действительным адресом?
3 ответа
Чтобы ответить на вопрос напрямую, нет, не существует обязательного правила, определения, стандарта или спецификации, которая гласит (void *) -1
не может быть действительным адресом.
(Конечно, никакие правила, определения, стандарты или спецификации, касающиеся адресов памяти, не являются обязательными. Я вижу людей, идущих по улице каждый день, например, не соответствующих стандарту C, но я никогда не видел, чтобы за это арестовывали никого. Но, даже если мы опускаем обязательную часть, используя (void *) -1
как адрес, как правило, не запрещены общими спецификациями.)
Тем не менее, это не обязательно для (void *) -1
не быть действительным адресом, чтобы шмат работал. Просто необходимо, чтобы успешный вызов shmat никогда не возвращался (void *) -1
и это (void *) -1
поддерживаться компилятором в целях проверки возвращаемого значения из shmat. Если эти два условия соблюдены, то программа всегда может отличить успешный вызов shmat от неудачного вызова shmat.
Что касается второго условия, то стандарт С не гарантирует, что (void *) -1
может использоваться, так что POSIX, указывая, что это ошибка возврата из shmat, неявно требует реализации C (или другого языка) для его поддержки. Так что это расширение языка, необходимого для POSIX, и, как правило, это простая вещь для поддержки компиляторов.
Что касается первого условия, подумайте, когда мы можем захотеть вернуть shmat (void *) -1
для успешного звонка. shmat может быть вызван с запрошенным пользователем адресом или без него, и в этом случае реализация выбирает адрес. В любой обычной компьютерной архитектуре существует несколько причин использовать адреса, кратные различным значениям. Для shmat наиболее очевидным является отображение памяти. На архитектурах с виртуальной памятью память отображается в единицах страниц, и shmat, когда он отображает память для сегмента, будет отображаться в начале страницы. Любой четный размер страницы не имеет кратных (void *) -1
, поскольку последнее нечетно, поэтому shmat никогда не выбирает сопоставить сегмент с (void *) -1
, Даже если shmat не использовал размер страницы, он обычно использовал бы другое выравнивание, такое как 4, 8 или 16 байтов, потому что предоставление выровненной памяти означает, что структуры, хранящиеся в начале этой памяти, будут выровнены, что приведет к более быстрой памяти доступ на многих процессорах.
Это оставляет случай, когда пользователь запрашивает (void *) -1
как адрес. Это было бы необычно, и это могло бы работать, только если сегмент памяти был одним байтом, или модель памяти позволяла обернуться вокруг (или компилятор представил очень странную модель памяти, в которой (void *) -1
были не последним байтом в адресном пространстве). Я не могу точно сказать, поддерживают ли какие-либо системы POSIX это или нет. Тем не менее, ясно, что это по сути бесполезно, и ни у кого нет причин делать это, кроме любопытства. Поэтому, это безопасно и разумно исключить этот случай из shmat, просто сказав, что не поддерживается, не делайте этого.
0xffffffff
технически является допустимым адресом в 32-битной среде, но в большинстве операционных систем (конечно, Linux/Windows) будет в зарезервированной части ядра адресного пространства. Это означает, что в процессах пользовательского режима его можно безопасно использовать в качестве кода ошибки, поскольку ни одна функция выделения пользовательского режима не вернет его в качестве используемого адреса.
Для простоты рассмотрим машину с 16-битным адресным пространством. Эта машина может обращаться к памяти от 0 до 65535. В этом случае (void*) -1
будет таким же, как 0xffff, который равен 65535. Хотя технически этот адрес действителен, существует немного систем, в которых было бы что-то, что они позволили бы получить доступ.
Еще одна вещь, которую следует учитывать, это то, что почти все системные вызовы POSIX возвращают -1
по ошибке.
Как отметил Бендж, на самом деле можно сопоставить адрес NULL
, Это может быть использовано, например, когда вы хотите увидеть, есть ли сопоставление с указанным shmid
в этом случае shmaddr
аргумент установлен в NULL
и функция возврата NULL
чтобы показать, что общая память существует.