Как ОС определяет доступ к нулевому указателю без проверки всех адресов указателя?

Известно, что адрес 0 (который помечен как макрос "NULL") недопустим для доступа. Мне было интересно, как получается, что операционная система (скажем, Linux) может определить, когда есть доступ к нулевому адресу, где-то в коде, без необходимости доступа к каждому адресу указателя в коде? Я предполагаю, что это как-то связано с сигналом и, в частности, с сигналом "sigsegv".

Но я не уверен, как это делается.

5 ответов

Решение

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

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

NULL-указатель проверяется так же, как и все остальные адреса памяти: через преобразование логического адреса ЦП.

Каждый раз, когда процессор обращается к памяти (игнорируя кэширование), он ищет адрес в таблице страниц процесса. Если соответствующей записи нет, процессор вызывает ошибку доступа (которая в вариантах Unix преобразуется в сигнал).

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

Это довольно длинная тема. Вы должны понимать логическую трансляцию памяти (иногда ошибочно называемую виртуальной памятью), если хотите узнать больше по теме.

Указатели ссылаются на виртуальное адресное пространство. В виртуальном адресном пространстве каждая страница памяти может быть сопоставлена ​​с реальной физической памятью. Операционная система заботится об этом отображении отдельно для каждого процесса.

Когда вы обращаетесь к памяти через указатель, ЦП просматривает сопоставление для виртуального адреса, который указывает ваш указатель, и проверяет, есть ли за ним реальная физическая память. Дополнительные проверки выполняются для проверки того, что у вас есть доступ на чтение или запись к этому фрагменту памяти, в зависимости от выполняемой вами операции.

Если для этого адреса не сопоставлена ​​память, ЦП генерирует аппаратное прерывание. ОС ловит это прерывание и - обычно - сигнализирует sigsegv для вызывающего процесса.

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

... как получается, что операционная система (скажем, linux) может определить, когда есть доступ к нулевому адресу, где-то в коде, без необходимости доступа к каждому адресу указателя в коде?

Что ж, ОС не может определить разыменование NULL без обращения к указателю. Из вики по ошибке сегментации:

В вычислительной технике ошибка сегментации (часто сокращенная до segfault) или нарушение доступа - это ошибка, возникшая у оборудования с защитой памяти, уведомляющего операционную систему (ОС) о нарушении доступа к памяти; на компьютерах с архитектурой x86 это общая ошибка защиты. В ответ ядро ​​ОС обычно выполняет некоторые корректирующие действия, обычно передавая ошибку неисправному процессу, посылая процессу сигнал....

Нарушение доступа к памяти - это инцидент во время выполнения, и, если нет недопустимого доступа, ОС не подаст сигнал процессу.

FWIW, процессу разрешен доступ к выделенной для него памяти (в виртуальном адресном пространстве). Любой адрес, находящийся за пределами выделенного виртуального адресного пространства, при обращении к нему генерирует ошибку (через MMU), которая, в свою очередь, генерирует ошибку сегментации.

TL; DR - SIGSEV генерируется при обнаружении разыменования NULL-указателя, а не до этого. Кроме того, ОС не обнаруживает сам ошибочный доступ, а сообщает об этом в модуль управления памятью через сообщение об ошибке.

Linux получает эту поддержку от оборудования. Процессор информируется о назначении отдельных областей памяти и их доступности. При обращении к "недоступной" области памяти процессор сообщает операционной системе о проблеме, а операционная система сообщает приложению.

Это означает две вещи:

  • Нет никаких программных издержек, связанных с проверкой всех указателей на значение NULL.
  • Нет точной проверки допустимых значений указателя.

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

Memory Management Unit играет ключевую роль в возникновении исключения, когда NULL указатель разыменован или неверный адрес получен.

Во время обычного процесса отображения виртуальной памяти в физическую MMU на каждой RAM доступ, неопределенный адрес просто не найден в диапазоне виртуальных адресов, определенных в MMU descriptors, Это может иметь катастрофические последствия, если они произошли в пространстве ядра ОС, или просто выполнить уничтожение и очистку в области пользовательского пространства.

Другие вопросы по тегам