Является ли каждый регион на диаграмме сегментом?
Это 2 практически совершенно разных использования слова «сегмент»
- Сегментация / регистры сегментов x86 : современные операционные системы x86 используют модель с плоской памятью, в которой все сегменты имеют одинаковую базу = 0 и предел = макс. в 32-битном режиме, так же, как аппаратное обеспечение применяет его в 64-битном режиме , что делает сегментацию немного устаревшей , (За исключением FS или GS, используется для локального хранения потоков даже в 64-битном режиме.)
- Линкер / программа-загрузчик разделов / сегментов. (В чем разница раздела и сегмента в формате файла ELF )
Обычаи имеют общее происхождение: если вы были с использованием сегментированной модели памяти (особенно без страничной виртуальной памяти), вы можете иметь данные и BSS адрес быть относительно сегмента базы DS, стек по отношению к основанию SS и кода по отношению к Базовый адрес CS.
Таким образом, несколько разных программ могут быть загружены на разные линейные адреса или даже перемещены после запуска без изменения 16- или 32-битных смещений относительно баз сегментов.
Но тогда вы должны знать, к какому сегменту относится указатель, поэтому у вас есть «дальние указатели» и так далее. (Фактическим 16-битным x86-программам часто не требуется доступ к своему коду в качестве данных, поэтому они могут где-то использовать сегмент кода в 64 КБ и, возможно, еще один блок размером 64 КБ с DS = SS, с ростом стека из-за высоких смещений и данными в дно. Или крошечная модель кода с равными основами всех сегментов).
Как сегментация x86 взаимодействует с подкачкой
Отображение адреса в 32/64-битном режиме:
- сегмент: смещение (база сегмента подразумевается регистром, содержащим смещение, или переопределяется префиксом инструкции)
- 32 или 64-битный линейный виртуальный адрес = база + смещение. (В модели с плоской памятью, которую использует Linux, указатели / смещения = также линейные адреса. За исключением случаев доступа к TLS относительно FS или GS.)
Таблицы страниц (кэшируемые с помощью TLB) отображаются на линейный физический адрес 32 (устаревший режим), 36 (устаревший PAE) или 52-битный (x86-64). ( /programming/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the ).
Этот шаг не является обязательным: подкачка должна быть включена во время загрузки, установив бит в регистре управления. Без пейджинга линейные адреса являются физическими адресами.
Обратите внимание, что сегментация не позволяет использовать более 32 или 64 бит виртуального адресного пространства в одном процессе (или потоке) , потому что плоское (линейное) адресное пространство, на которое все отображается, имеет столько же битов, сколько и сами смещения. (Это не относится к 16-разрядным версиям x86, где сегментация действительно полезна для использования более 64 КБ памяти с 16-разрядными регистрами и смещениями.)
CPU кэширует дескрипторы сегментов, загруженные из GDT (или LDT), включая базу сегментов. Когда вы разыменовываете указатель, в зависимости от того, в каком регистре он находится, в качестве сегмента по умолчанию используется либо DS, либо SS. Значение регистра (указатель) рассматривается как смещение от базы сегмента.
Поскольку база сегмента обычно равна нулю, процессоры делают это в особом случае. Или с другой стороны, если вы делаете имеете ненулевую базу сегмента, нагрузки имеют дополнительную задержку , потому что «специальный» (нормальный) случай обхода добавления базовый адрес не применяется.
Как Linux настраивает регистры сегментов x86:
База и предел CS / DS / ES / SS равны 0 / -1 в 32- и 64-битном режиме. Это называется плоской моделью памяти, поскольку все указатели указывают на одно и то же адресное пространство.
(Архитекторы процессоров AMD стерилизовали сегментацию, применяя модель плоской памяти для 64-битного режима, потому что основные операционные системы все равно не использовали ее, за исключением защиты без exec, которая была обеспечена гораздо лучшим способом с помощью подкачки с помощью PAE или x86- 64 таблицы формата таблицы.)
TLS (Thread Local Storage): FS и GS не фиксируются на базе = 0 в длинном режиме. (Они были новыми с 386, и не использовались неявно никакими инструкциями, даже rep
инструкциями -string, которые используют ES). x86-64 Linux устанавливает базовый адрес FS для каждого потока в адрес блока TLS.
Например, mov eax, [fs: 16]
загружает 32-битное значение из 16 байтов в блок TLS для этого потока.
дескриптор сегмента CS выбирает, в каком режиме находится CPU (16/32/64-битный защищенный режим / длинный режим). Linux использует одну запись GDT для всех 64-битных процессов в пользовательском пространстве и другую запись GDT для всех 32-битных процессов в пользовательском пространстве. (Чтобы процессор работал правильно, DS / ES также должен быть настроен на допустимые записи, как и SS). Он также выбирает уровень привилегий (ядро (кольцо 0) по сравнению с пользователем (кольцо 3)), поэтому даже при возврате в 64-битное пространство пользователя ядру все равно приходится организовывать изменение CS, используя iret
или sysret
вместо обычного прыжок или повтор инструкции.
В x86-64 syscall
точка входа использует swapgs
для переключения GS из GS пользовательского пространства в ядро, которое она использует, чтобы найти стек ядра для этого потока. (Специализированный случай локального хранилища потоков). syscall
Инструкция не меняет указатель стека на точку в стеке ядра; он все еще указывает на стек пользователя, когда ядро достигает точки входа 1 .
DS / ES / SS также должны быть настроены на допустимые дескрипторы сегментов, чтобы ЦП мог работать в защищенном режиме / длинном режиме, даже если база / лимит этих дескрипторов игнорируются в длинном режиме.
Таким образом, в основном сегментация x86 используется для TLS и для обязательного x86 osdev, что требует от вас аппаратное обеспечение.
Сноска 1: Забавная история: есть архивы списков рассылки сообщений между разработчиками ядра и архитекторами AMD за пару лет до выпуска кремния AMD64, что привело к изменениям в дизайне, syscall
чтобы его можно было использовать. Смотрите ссылки в этом ответе для деталей.