Я изучаю операционные системы и архитектуру x86, и пока я читал о сегментации и разбиении на страницы, мне, естественно, было любопытно, как современные ОС управляют управлением памятью. Из того, что я обнаружил, Linux и большинство других операционных систем по существу избегают сегментации в пользу подкачки. Несколько причин, которые я нашел, были простота и портативность.
Какие практические применения существуют для сегментации (x86 или иным образом), и когда-нибудь мы увидим надежные операционные системы, использующие ее, или они будут продолжать отдавать предпочтение системе на основе подкачки.
Теперь я знаю, что это загруженный вопрос, но мне любопытно, как будет обрабатываться сегментация в недавно разработанных операционных системах. Имеет ли смысл отдавать предпочтение пейджингу, чтобы никто не рассматривал более «сегментированный» подход? Если так, то почему?
И когда я говорю «избегать» сегментации, я подразумеваю, что Linux использует его только настолько, насколько это необходимо. Только 4 сегмента для пользовательского и ядра кода / сегментов данных. Читая документацию Intel, я только почувствовал, что сегментация была разработана с учетом более надежных решений. С другой стороны, мне много раз говорили, насколько сложным может быть x86.
Я нашел этот интересный анекдот после того, как связался с оригинальным «анонсом» Linux Торвальда для Linux. Он сказал это несколькими постами позже:
Просто я бы сказал, что портирование невозможно. Это в основном на C, но большинство людей не стали бы называть то, что я пишу C. Он использует все мыслимые возможности 386, которые я смог найти, так как это был также проект, чтобы рассказать мне о 386. Как уже упоминалось, он использует MMU , как для подкачки (пока не на диск), так и для сегментации. Именно сегментация делает ее по-настоящему зависимой от 386 (каждая задача имеет сегмент размером 64 МБ для кода и данных - максимум 64 задачи в 4 ГБ. Любой, кому нужно более 64 МБ / задача - жесткие файлы cookie).
Я полагаю, что мои собственные эксперименты с x86 заставили меня задать этот вопрос. У Линуса не было StackOverflow, поэтому он просто реализовал его, чтобы попробовать.
источник
Ответы:
Например, при сегментации можно было бы поместить каждый динамически выделенный объект (malloc) в отдельный сегмент памяти. Аппаратные средства будут автоматически проверять границы сегментов, и весь класс ошибок безопасности (переполнения буфера) будет устранен.
Кроме того, поскольку все смещения сегмента начинаются с нуля, весь скомпилированный код автоматически будет независимым от позиции. Вызов в другую DLL сводится к удаленному вызову с постоянным смещением (в зависимости от вызываемой функции). Это значительно упростит компоновщики и загрузчики.
С 4 кольцами защиты можно разработать более детальный контроль доступа (с подкачкой у вас есть только 2 уровня защиты: пользователь и супервизор) и более надежные ядра ОС. Например, только кольцо 0 имеет полный доступ к оборудованию. Разделив ядро ядра ОС и драйверы устройств на кольца 0 и 1, вы можете сделать более надежную и очень быструю микроядерную ОС, в которой HW выполнит большую часть соответствующих проверок доступа. (Драйверы устройств могут получить доступ к оборудованию через битовый массив доступа ввода / вывода в TSS.)
Однако .. x86 немного ограничен. Он имеет только 4 «свободных» регистра сегмента данных; их перезагрузка довольно дорогая, и одновременно можно получить доступ только к 8192 сегментам. (Предполагая, что вы хотите максимизировать количество доступных объектов, поэтому GDT содержит только системные дескрипторы и дескрипторы LDT.)
Теперь в 64-битном режиме сегментация описывается как «устаревшая», а проверка аппаратных ограничений выполняется только в ограниченных случаях. ИМХО, БОЛЬШАЯ ошибка. На самом деле я не виню Intel, я в основном виню разработчиков, большинство из которых думали, что сегментация была «слишком сложной» и жаждала плоского адресного пространства. Я также обвиняю авторов ОС, которым не хватило воображения, чтобы использовать сегментацию с пользой. (AFAIK, OS / 2 была единственной операционной системой, которая в полной мере использовала функции сегментации.)
источник
Короткий ответ: сегментирование - это взлом, используемый для того, чтобы процессор с ограниченной способностью обращаться к памяти выходил за эти пределы.
В случае с 8086 на чипе было 20 адресных строк, что означало, что он мог физически получить доступ к 1 МБ памяти. Однако внутренняя архитектура была основана на 16-битной адресации, вероятно, из-за желания сохранить согласованность с 8080. Таким образом, в набор инструкций были включены регистры сегментов, которые будут объединены с 16-битными индексами, чтобы разрешить адресацию всего 1 МБ памяти , Модель 80286 дополнила эту модель настоящим MMU для поддержки сегментной защиты и адресации большего объема памяти (iirc, 16 МБ).
В случае PDP-11 более поздние модели процессора обеспечивали сегментацию на пространства команд и данных, опять же, для поддержки ограничений 16-битного адресного пространства.
Проблема с сегментацией проста: ваша программа должна явно обходить ограничения архитектуры. В случае 8086 это означало, что самый большой непрерывный блок памяти, к которому вы могли получить доступ, был 64 КБ. если вам нужно было получить доступ к большему, вы должны изменить свои регистры сегментов. Для программиста на Си это означало, что вы должны указать компилятору Си, какие указатели он должен генерировать.
Было намного проще программировать MC68k, который имел 32-битную внутреннюю архитектуру и 24-битное физическое адресное пространство.
источник
Для 80x86 есть 4 варианта: «ничего», только сегментация, только пейджинг, а также сегментация и пейджинг.
Для «ничего» (без сегментации или разбиения на страницы) у вас не будет простого способа защитить процесс от самого себя, нет простого способа защитить процессы друг от друга, нет способа справиться с такими вещами, как физическая фрагментация адресного пространства, нет способа избежать позиции независимый код и т. д. Несмотря на все эти проблемы, он может (теоретически) быть полезен в некоторых ситуациях (например, встроенное устройство, которое запускает только одно приложение, или, возможно, что-то, использующее JIT и все виртуализирующее все).
Только для сегментации; он почти решает проблему «защиты процесса от самого себя», но требуется много обходных путей, чтобы сделать его пригодным для использования, когда процессу требуется использовать более 8192 сегментов (с учетом одного LDT на процесс), что делает его в основном неработоспособным. Вы почти решаете проблему «защиты процессов друг от друга»; но разные части программного обеспечения, работающие на одном и том же уровне привилегий, могут загружать / использовать сегменты друг друга (есть способы обойти это - изменение записей GDT во время передачи управления и / или использование LDT). Он также в основном решает проблему «позиционно-независимого кода» (это может вызвать проблему «сегментно-зависимого кода», но это гораздо менее важно). Он ничего не делает для проблемы «фрагментации физического адресного пространства».
Только для подкачки; это не решает проблему «защиты процесса от самого себя» (но давайте будем честны, это действительно проблема только для отладки / тестирования кода, написанного на небезопасных языках, и в любом случае есть гораздо более мощные инструменты, такие как valgrind). Он полностью решает проблему «защиты процессов друг от друга», полностью решает проблему «кода, независимого от позиции», и полностью решает проблему «фрагментации физического адресного пространства». В качестве дополнительного бонуса он открывает некоторые очень мощные приемы, которые не так удобны без пейджинга; включая такие вещи, как «копировать при записи», отображенные в память файлы, эффективную обработку пространства подкачки и т. д.
Теперь вы думаете, что использование сегментации и разбиения на страницы даст вам преимущества обоих; и теоретически это возможно, за исключением того, что единственная выгода, которую вы получаете от сегментации (которая не улучшается при разбивке по страницам), - это решение проблемы «защитить процесс от себя», которая никого на самом деле не волнует. На практике то, что вы получаете, - это сложность обоих и накладные расходы обоих, что приносит очень мало пользы.
Вот почему почти все ОС, предназначенные для 80x86, не используют сегментацию для управления памятью (они используют ее для таких вещей, как для каждого процессора и для каждой задачи, но это в основном просто для удобства, чтобы не использовать более полезный регистр общего назначения для этих целей. вещи).
Конечно, производители процессоров не глупы - они не собираются тратить время и деньги на оптимизацию того, что, как они знают, никто не использует (они собираются оптимизировать то, что вместо этого используют почти все). По этой причине производители ЦП не оптимизируют сегментацию, что делает сегментацию медленнее, чем могло бы быть, что заставляет разработчиков ОС хотеть избегать ее еще больше. В основном они сохраняли сегментацию только для обратной совместимости (что важно).
В конце концов, AMD разработала длинный режим. Не нужно было беспокоиться о старом / существующем 64-битном коде, поэтому (для 64-битного кода) AMD избавилась от такой большой сегментации, как могла. Это дало разработчикам ОС еще одну причину (нелегкий способ портировать код, предназначенный для сегментации на 64-битные), чтобы продолжать избегать сегментации.
источник
Я довольно ошеломлен тем, что за все время, прошедшее с момента публикации этого вопроса, никто не упомянул о происхождении сегментированных архитектур памяти и об истинной силе, которую они могут себе позволить.
Первоначальной системой, которая либо изобрела, либо усовершенствовала в полезной форме все функции, связанные с проектированием и использованием сегментированных систем виртуальной памяти с постраничной загрузкой (наряду с симметричной мультипроцессорной и иерархической файловыми системами), была Multics (см. Также сайт Multicians ). Сегментированная память позволяет Multics предоставлять пользователю представление о том, что все находится в (виртуальной) памяти, и обеспечивает максимальный уровень совместного использования всегов прямой форме (то есть непосредственно адресуемой в памяти). Файловая система становится просто картой для всех сегментов в памяти. При правильном систематическом использовании (как в Multics) сегментированная память освобождает пользователя от многих трудностей, связанных с управлением вторичным хранилищем, обменом данными и взаимодействием между процессами. Другие ответы сделали некоторые волнообразные заявления о том, что сегментированная память труднее использовать, но это просто не соответствует действительности, и Multics доказала это с ошеломительным успехом десятилетия назад.
Intel создала зашумленную версию сегментированной памяти 80286, которая, хотя и является достаточно мощной, из-за ее ограничений не позволяла использовать ее для чего-то действительно полезного. 80386 улучшил эти ограничения, но рыночные силы в то время в значительной степени препятствовали успеху любой системы, которая могла бы по-настоящему воспользоваться этими улучшениями. За прошедшие годы кажется, что слишком много людей научились игнорировать уроки прошлого.
На раннем этапе Intel также попыталась создать более мощный супер-микро под названием iAPX 432 , который намного превзошел бы все остальное в то время, и имел архитектуру сегментированной памяти и другие функции, сильно ориентированные на объектно-ориентированное программирование. Первоначальная реализация была слишком медленной, и никаких дальнейших попыток ее исправить не было.
Более подробное обсуждение того, как Multics использовал сегментацию и разбиение на страницы, можно найти в статье Пола Грина « Виртуальная память Multics» - руководство и размышления.
источник
Сегментация была хакерским решением, позволяющим 16-битному процессору обрабатывать до 1 МБ памяти - обычно было доступно только 64 КБ памяти.
Когда появились 32-битные процессоры, вы могли адресовать до 4 ГБ памяти с моделью плоской памяти, и больше не было необходимости в сегментации - регистры сегментов были переопределены как селекторы для GDT / подкачки в защищенном режиме (хотя вы можете есть защищенный режим 16-битный).
Кроме того, режим плоской памяти гораздо удобнее для компиляторов - вы можете писать 16-битные сегментированные программы на C , но это немного громоздко. Плоская модель памяти делает все проще.
источник
Некоторые архитектуры (например, ARM) вообще не поддерживают сегменты памяти. Если бы Linux зависел от исходного кода сегментов, его нельзя было бы легко перенести на эти архитектуры.
Глядя на более широкую картину, отказ сегментов памяти связан с продолжающейся популярностью C и арифметики указателей. Разработка на C более практична для архитектуры с плоской памятью; и если вы хотите ровную память, вы выбираете подкачку памяти.
На рубеже 80-х годов было время, когда Intel, как организация, ожидала будущей популярности Ada и других языков программирования более высокого уровня. Это в основном то, откуда произошли некоторые из их более впечатляющих сбоев, такие как ужасная сегментация памяти APX432 и 286. С 386 они капитулировали перед программистами с плоской памятью; был добавлен пейджинг и TLB, а сегменты были изменены до 4 ГБ. А потом AMD в основном удалила сегменты с x86_64, сделав базу reg dont-care / implied-0 (кроме fs? Для TLS, я думаю?)
Сказав это, преимущества сегментов памяти очевидны - переключение адресных пространств без необходимости переполнения TLB. Возможно, когда-нибудь кто-нибудь сделает конкурентоспособный по производительности процессор, поддерживающий сегментацию, мы можем запрограммировать для него операционную систему, ориентированную на сегментацию, и программисты могут сделать Ada / Pascal / D / Rust / еще один язык, который не требует плоских данных. программы для него.
источник
Сегментация является огромным бременем для разработчиков приложений. Вот откуда пришел большой толчок для устранения сегментации.
Интересно, что я часто задаюсь вопросом, насколько лучше мог бы быть i86, если бы Intel убрала всю устаревшую поддержку этих старых режимов. Здесь лучше будет означать более низкую мощность и, возможно, более быструю работу.
Я думаю, можно утверждать, что Intel испортила молоко 16-битными сегментами, что привело к восстанию разработчиков. Но давайте посмотрим правде в глаза, 64-адресное пространство - это ничто, особенно если взглянуть на современное приложение. В конце концов, они должны были что-то сделать, потому что конкуренция могла и действительно эффективно выходила на рынок с проблемами адресного пространства i86.
источник
Сегментация приводит к более медленным переводам страниц и обмену
По этим причинам сегментация была в основном отброшена на x86-64.
Основное различие между ними заключается в том, что:
Хотя может показаться разумнее иметь настраиваемую ширину сегментов, но при увеличении объема памяти для процесса фрагментация неизбежна, например:
в конечном итоге станет по мере роста процесса 1:
пока раскол не станет неизбежным:
На данном этапе:
Однако для страниц фиксированного размера:
Фрагменты памяти фиксированного размера просто более управляемы и доминируют в современном дизайне ОС.
Смотрите также: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work
источник