Почему (не) сегментация?


42

Я изучаю операционные системы и архитектуру x86, и пока я читал о сегментации и разбиении на страницы, мне, естественно, было любопытно, как современные ОС управляют управлением памятью. Из того, что я обнаружил, Linux и большинство других операционных систем по существу избегают сегментации в пользу подкачки. Несколько причин, которые я нашел, были простота и портативность.

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

Теперь я знаю, что это загруженный вопрос, но мне любопытно, как будет обрабатываться сегментация в недавно разработанных операционных системах. Имеет ли смысл отдавать предпочтение пейджингу, чтобы никто не рассматривал более «сегментированный» подход? Если так, то почему?


И когда я говорю «избегать» сегментации, я подразумеваю, что Linux использует его только настолько, насколько это необходимо. Только 4 сегмента для пользовательского и ядра кода / сегментов данных. Читая документацию Intel, я только почувствовал, что сегментация была разработана с учетом более надежных решений. С другой стороны, мне много раз говорили, насколько сложным может быть x86.


Я нашел этот интересный анекдот после того, как связался с оригинальным «анонсом» Linux Торвальда для Linux. Он сказал это несколькими постами позже:

Просто я бы сказал, что портирование невозможно. Это в основном на C, но большинство людей не стали бы называть то, что я пишу C. Он использует все мыслимые возможности 386, которые я смог найти, так как это был также проект, чтобы рассказать мне о 386. Как уже упоминалось, он использует MMU , как для подкачки (пока не на диск), так и для сегментации. Именно сегментация делает ее по-настоящему зависимой от 386 (каждая задача имеет сегмент размером 64 МБ для кода и данных - максимум 64 задачи в 4 ГБ. Любой, кому нужно более 64 МБ / задача - жесткие файлы cookie).

Я полагаю, что мои собственные эксперименты с x86 заставили меня задать этот вопрос. У Линуса не было StackOverflow, поэтому он просто реализовал его, чтобы попробовать.


Какую книгу ты прочитал?

1
Я читаю несколько книг. Я начал задавать себе этот вопрос, читая руководство по системному программированию Intel (том 3), но немного прочитал об управлении памятью в Linux в «Понимании ядра Linux» и других источниках в Интернете.
Мистер Шикаданс

В частности, я читал раздел «Таблицы локальных дескрипторов», и мне было любопытно, как их используют операционные системы.
Мистер Шикаданс

1
OpenBSD сочетает в себе сегментацию x86 и разбиение на страницы для получения имитации битов NX (функция безопасности, запрещающая выполнение страниц данных). Может быть, PaX тоже это использовал.

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

Ответы:


31

Например, при сегментации можно было бы поместить каждый динамически выделенный объект (malloc) в отдельный сегмент памяти. Аппаратные средства будут автоматически проверять границы сегментов, и весь класс ошибок безопасности (переполнения буфера) будет устранен.

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

С 4 кольцами защиты можно разработать более детальный контроль доступа (с подкачкой у вас есть только 2 уровня защиты: пользователь и супервизор) и более надежные ядра ОС. Например, только кольцо 0 имеет полный доступ к оборудованию. Разделив ядро ​​ядра ОС и драйверы устройств на кольца 0 и 1, вы можете сделать более надежную и очень быструю микроядерную ОС, в которой HW выполнит большую часть соответствующих проверок доступа. (Драйверы устройств могут получить доступ к оборудованию через битовый массив доступа ввода / вывода в TSS.)

Однако .. x86 немного ограничен. Он имеет только 4 «свободных» регистра сегмента данных; их перезагрузка довольно дорогая, и одновременно можно получить доступ только к 8192 сегментам. (Предполагая, что вы хотите максимизировать количество доступных объектов, поэтому GDT содержит только системные дескрипторы и дескрипторы LDT.)

Теперь в 64-битном режиме сегментация описывается как «устаревшая», а проверка аппаратных ограничений выполняется только в ограниченных случаях. ИМХО, БОЛЬШАЯ ошибка. На самом деле я не виню Intel, я в основном виню разработчиков, большинство из которых думали, что сегментация была «слишком сложной» и жаждала плоского адресного пространства. Я также обвиняю авторов ОС, которым не хватило воображения, чтобы использовать сегментацию с пользой. (AFAIK, OS / 2 была единственной операционной системой, которая в полной мере использовала функции сегментации.)


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

1
@zvrba: какое превосходное объяснение !!! Спасибо за это. Теперь у меня есть сомнение: не думаете ли вы, что INTEL мог бы выиграть большой приз, сделав сегменты непересекающимися и 4 ГБ способными с помощью подкачки? Я имею в виду, как я понял, «сегментация с подкачкой страниц» способна адресовать только до 4 ГБ адресного пространства виртуальной памяти. И это «арахис» !!! Представьте себе, что у вас есть сегменты кода, стека, данных размером до 4 ГБ каждый и не перекрывающиеся или перекрывающиеся, как вам бы хотелось! И это было бы большим успехом в то время, не требуя полной 64-битной архитектуры, как сегодня.
Fante

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

1
Не удивительно, что я любил OS / 2! Какая печальная потеря действительно ценной технологии благодаря невежеству и маркетингу.
Иллюминат

Любой, кто считает сегментацию хорошей идеей, не должен быть достаточно взрослым, чтобы помнить, насколько ужасна сегментация. Это ужасно. Практически весь когда-либо написанный C-код ожидает плоское адресное пространство. Удобно иметь возможность смотреть на указатель и просто видеть его адрес, не нужно копаться в базе сегментов, предполагая, что это даже возможно, чего нет в сегментации защищенного режима x86, если только ядро ​​не позволяет увидеть это как-то, скорее всего, с очень дорогим системным вызовом. Обмен с сегментами невозможен, если вы не поменяете местами целые сегменты. Пейджинг намного, намного лучше.
doug65536

25

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

В случае с 8086 на чипе было 20 адресных строк, что означало, что он мог физически получить доступ к 1 МБ памяти. Однако внутренняя архитектура была основана на 16-битной адресации, вероятно, из-за желания сохранить согласованность с 8080. Таким образом, в набор инструкций были включены регистры сегментов, которые будут объединены с 16-битными индексами, чтобы разрешить адресацию всего 1 МБ памяти , Модель 80286 дополнила эту модель настоящим MMU для поддержки сегментной защиты и адресации большего объема памяти (iirc, 16 МБ).

В случае PDP-11 более поздние модели процессора обеспечивали сегментацию на пространства команд и данных, опять же, для поддержки ограничений 16-битного адресного пространства.

Проблема с сегментацией проста: ваша программа должна явно обходить ограничения архитектуры. В случае 8086 это означало, что самый большой непрерывный блок памяти, к которому вы могли получить доступ, был 64 КБ. если вам нужно было получить доступ к большему, вы должны изменить свои регистры сегментов. Для программиста на Си это означало, что вы должны указать компилятору Си, какие указатели он должен генерировать.

Было намного проще программировать MC68k, который имел 32-битную внутреннюю архитектуру и 24-битное физическое адресное пространство.


5
Хорошо, это все имеет смысл. Однако, читая документы Intel, можно подумать, что сегменты могут быть использованы для большей защиты аппаратного уровня от программных ошибок. В частности, раздел 3.2.3 Руководства по системному программированию - есть ли преимущества для многосегментной модели? Было бы правильно сказать, что Linux использует защищенную плоскую модель? (раздел 3.2.2)
г-н Шикаданс

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

1
Спасибо, вы вернули все подавленные воспоминания об обработке изображений на сегментированной памяти - это будет означать больше терапии!
Мартин Беккет

10
Вы совершенно неправильно поняли сегментацию. В 8086 году это могло быть взломом; 80286 ввел защищенный режим, где это было важно для защиты; в 80386 это было еще более расширено, и сегменты могут быть больше, чем 64 КБ, все еще с преимуществом аппаратных проверок. (Кстати, 80286 НЕ имел MMU.)
zvrba

2
Еще в 1985 году, когда был представлен 386, адресное пространство 4 ГиБ считалось огромным. Помните, что жесткий диск емкостью 20 МБ в то время был довольно большим, и системы по-прежнему нередко выпускались только с дисководами гибких дисков. 3,5-дюймовые FDD были представлены в 1983 году. Они имели отформатированную емкость колоссальных 360 КБ. (3,56-дюймовые 3,5-дюймовые FDD стали доступны в 1986 году). С точностью до экспериментальной ошибки все тогда думали о 32-битном адресном пространстве, как мы сейчас думаем. 64 бита: физически доступный, но настолько большой, что становится практически бесконечным.
CVn

15

Для 80x86 есть 4 варианта: «ничего», только сегментация, только пейджинг, а также сегментация и пейджинг.

Для «ничего» (без сегментации или разбиения на страницы) у вас не будет простого способа защитить процесс от самого себя, нет простого способа защитить процессы друг от друга, нет способа справиться с такими вещами, как физическая фрагментация адресного пространства, нет способа избежать позиции независимый код и т. д. Несмотря на все эти проблемы, он может (теоретически) быть полезен в некоторых ситуациях (например, встроенное устройство, которое запускает только одно приложение, или, возможно, что-то, использующее JIT и все виртуализирующее все).

Только для сегментации; он почти решает проблему «защиты процесса от самого себя», но требуется много обходных путей, чтобы сделать его пригодным для использования, когда процессу требуется использовать более 8192 сегментов (с учетом одного LDT на процесс), что делает его в основном неработоспособным. Вы почти решаете проблему «защиты процессов друг от друга»; но разные части программного обеспечения, работающие на одном и том же уровне привилегий, могут загружать / использовать сегменты друг друга (есть способы обойти это - изменение записей GDT во время передачи управления и / или использование LDT). Он также в основном решает проблему «позиционно-независимого кода» (это может вызвать проблему «сегментно-зависимого кода», но это гораздо менее важно). Он ничего не делает для проблемы «фрагментации физического адресного пространства».

Только для подкачки; это не решает проблему «защиты процесса от самого себя» (но давайте будем честны, это действительно проблема только для отладки / тестирования кода, написанного на небезопасных языках, и в любом случае есть гораздо более мощные инструменты, такие как valgrind). Он полностью решает проблему «защиты процессов друг от друга», полностью решает проблему «кода, независимого от позиции», и полностью решает проблему «фрагментации физического адресного пространства». В качестве дополнительного бонуса он открывает некоторые очень мощные приемы, которые не так удобны без пейджинга; включая такие вещи, как «копировать при записи», отображенные в память файлы, эффективную обработку пространства подкачки и т. д.

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

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

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

В конце концов, AMD разработала длинный режим. Не нужно было беспокоиться о старом / существующем 64-битном коде, поэтому (для 64-битного кода) AMD избавилась от такой большой сегментации, как могла. Это дало разработчикам ОС еще одну причину (нелегкий способ портировать код, предназначенный для сегментации на 64-битные), чтобы продолжать избегать сегментации.


13

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

Первоначальной системой, которая либо изобрела, либо усовершенствовала в полезной форме все функции, связанные с проектированием и использованием сегментированных систем виртуальной памяти с постраничной загрузкой (наряду с симметричной мультипроцессорной и иерархической файловыми системами), была Multics (см. Также сайт Multicians ). Сегментированная память позволяет Multics предоставлять пользователю представление о том, что все находится в (виртуальной) памяти, и обеспечивает максимальный уровень совместного использования всегов прямой форме (то есть непосредственно адресуемой в памяти). Файловая система становится просто картой для всех сегментов в памяти. При правильном систематическом использовании (как в Multics) сегментированная память освобождает пользователя от многих трудностей, связанных с управлением вторичным хранилищем, обменом данными и взаимодействием между процессами. Другие ответы сделали некоторые волнообразные заявления о том, что сегментированная память труднее использовать, но это просто не соответствует действительности, и Multics доказала это с ошеломительным успехом десятилетия назад.

Intel создала зашумленную версию сегментированной памяти 80286, которая, хотя и является достаточно мощной, из-за ее ограничений не позволяла использовать ее для чего-то действительно полезного. 80386 улучшил эти ограничения, но рыночные силы в то время в значительной степени препятствовали успеху любой системы, которая могла бы по-настоящему воспользоваться этими улучшениями. За прошедшие годы кажется, что слишком много людей научились игнорировать уроки прошлого.

На раннем этапе Intel также попыталась создать более мощный супер-микро под названием iAPX 432 , который намного превзошел бы все остальное в то время, и имел архитектуру сегментированной памяти и другие функции, сильно ориентированные на объектно-ориентированное программирование. Первоначальная реализация была слишком медленной, и никаких дальнейших попыток ее исправить не было.

Более подробное обсуждение того, как Multics использовал сегментацию и разбиение на страницы, можно найти в статье Пола Грина « Виртуальная память Multics» - руководство и размышления.


1
Отличная информация и превосходные аргументы. Спасибо за ссылки, они бесценны !!!
Fante

1
Спасибо за ссылку на Multics и за очень информативный ответ! Очевидно, что сегментация во многом превосходила то, что мы делаем сейчас.
GDP2

1
Ваш ответ - настоящая жемчужина. Большое спасибо за то, что поделились этими мыслями, что мы потеряли. Мне очень хочется увидеть возвращение к сегментации через разработку правильной ОС, которая могла бы ускорить усовершенствование оборудования. Поистине, с таким подходом можно решить очень много вопросов! Это даже звучит так, как если бы мы могли получить настоящие языки ООП с гораздо более высокими уровнями производительности и на голом металле с сегментацией.
Иллюминат

6

Сегментация была хакерским решением, позволяющим 16-битному процессору обрабатывать до 1 МБ памяти - обычно было доступно только 64 КБ памяти.

Когда появились 32-битные процессоры, вы могли адресовать до 4 ГБ памяти с моделью плоской памяти, и больше не было необходимости в сегментации - регистры сегментов были переопределены как селекторы для GDT / подкачки в защищенном режиме (хотя вы можете есть защищенный режим 16-битный).

Кроме того, режим плоской памяти гораздо удобнее для компиляторов - вы можете писать 16-битные сегментированные программы на C , но это немного громоздко. Плоская модель памяти делает все проще.


Много ли можно сказать о «защите», обеспечиваемой сегментацией, когда мы можем просто использовать вместо этого пейджинг?
Мистер Шикаданс

1
@Мистер. Shickadance Segmentation не обеспечивает какой- либо защиты памяти - для защиты памяти вам нужен защищенный режим, в котором вы можете защитить память с помощью GDT или подкачки.
Джастин

5

Некоторые архитектуры (например, 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 / еще один язык, который не требует плоских данных. программы для него.


1

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

Интересно, что я часто задаюсь вопросом, насколько лучше мог бы быть i86, если бы Intel убрала всю устаревшую поддержку этих старых режимов. Здесь лучше будет означать более низкую мощность и, возможно, более быструю работу.

Я думаю, можно утверждать, что Intel испортила молоко 16-битными сегментами, что привело к восстанию разработчиков. Но давайте посмотрим правде в глаза, 64-адресное пространство - это ничто, особенно если взглянуть на современное приложение. В конце концов, они должны были что-то сделать, потому что конкуренция могла и действительно эффективно выходила на рынок с проблемами адресного пространства i86.


1

Сегментация приводит к более медленным переводам страниц и обмену

По этим причинам сегментация была в основном отброшена на x86-64.

Основное различие между ними заключается в том, что:

  • подкачка разделяет память на фрагменты фиксированного размера
  • сегментация позволяет разной ширины для каждого куска

Хотя может показаться разумнее иметь настраиваемую ширину сегментов, но при увеличении объема памяти для процесса фрагментация неизбежна, например:

|   | process 1 |       | process 2 |                        |
     -----------         -----------
0                                                            max

в конечном итоге станет по мере роста процесса 1:

|   | process 1        || process 2 |                        |
     ------------------  -------------
0                                                            max

пока раскол не станет неизбежным:

|   | process 1 part 1 || process 2 |   | process 1 part 2 | |
     ------------------  -----------     ------------------
0                                                            max

На данном этапе:

  • единственный способ перевести страницы - это выполнить бинарный поиск по всем страницам процесса 1, что ведет к неприемлемому журналу (n)
  • своп из процесса 1 часть 1 может быть огромным, так как этот сегмент может быть огромным

Однако для страниц фиксированного размера:

  • каждый 32-битный перевод выполняет только 2 чтения памяти: просмотр каталога и таблицы страниц
  • каждый своп приемлемый 4KiB

Фрагменты памяти фиксированного размера просто более управляемы и доминируют в современном дизайне ОС.

Смотрите также: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.