RU/2: Обратная сторона VirtualAddressLimit.

Обратная сторона VirtualAddressLimit.


Предполагаемая аудитория.

Статья предназначена для широкого круга читателей. Начиная с людей, уже знающих о полуосевом ядре довольно много и желающих углубить свои знания и заканчивая слегка продвинутыми пользователями, задающимися вопросами типа:
- я увеличил кэш JFS и система грузиться перестала совсем. Почему?
- мне BIOS рассказал, что у меня стоит много гигабайт памяти, а программы не хотят запускаться с ошибкой "не хватает памяти". Как такое может быть?

Зачем ещё раз про ЭТО?

Действительно, про организацию адресного пространства в полуоси написано очень много, начиная с оригинальной документации в IBM OS/2 Developer Toolkit, который давно входит в дистрибутив ОС. В ряду этого материала хотелось бы отметить отличную публикацию Virtual Memory Problems under OS/2, после которой добавить вроде бы почти и нечего. Тем не менее, все эти статьи отличаются определённой узостью: они смотрят на адресное пространство с точки зрения приложений. Но это же только половина картины. А вот про вторую сторону, ядерную, в лучшем случае только слегка упоминается. Поэтому возникла идея эту тёмную сторону слегка осветить. Хотя бы немножко, фонариком :)

Небольшой ликбез.

Теоретически полуось существует под две процессорных архитектуры: IA-32 и PowerPC. Но поскольку вторая так и не стала даже релизом, то забудем о ней и поговорим о "просто OS/2", т.е. только для IA-32. В железном исполнении это соответствует процессору Intel 80386 и массе более поздних моделей, поддерживающих с ним совместимость. Редкий случай в нашем мире маркетинга, но число 32 в названии взято не с потолка, а имеет прямое отношение к важнейшим техническим возможностям и ограничениям этой архитектуры. Попробуем разобраться к каким.

IA-32 построена на принципах фон Неймана. Эти принципы предполагают память в виде множества однородных ячеек (в дальнейшем, байт), имеющими идентификатор в виде числа, обычно целого. Это число называется адресом ячейки в памяти. Сама ячейка при этом называется байтом. Сразу нужно уточнить, что однозначность преобразования здесь работает только вниз по уровням адресации - от адреса к ячейке. А вот вверх уже нет. Ячейка может иметь несколько адресов (address aliasing, совмещение адресов). И даже не иметь ни одного, тогда память является не адресуемой и для её использования надо каким-то образом адреса присвоить.

Итак, зачем вообще вводится понятие адреса? Для того, чтобы формализовать и ограничить отношения между процессором и памятью. Процессор машины фон Неймана может использовать память ровно двумя операциями: прочитать/записать ячейку с адресом, указанным в команде. Таким образом, исполняющиеся на процессоре команды (они же программа) от физической реализации памяти логически отвязаны. Важно только адресное пространство. В абстрактной машине фон Неймана его размер не ограничен. На реальных машинах для эффективности работы адрес может меняться только в определённых пределах. Обычно адрес - это целое число с фиксированным числом бит, допустим N. Что даёт ограничение адресного пространства в 2N байт.

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

А теперь рассмотрим в каком виде все эти идеи реализованы в IA-32. Байт на этой платформе является группой из восьми бит или октетом. К сожалению, это служит поводом широко распространённого заблуждения, что байт - это всегда октет. На самом деле информационная ёмкость байта фиксирована исключительно в пределах одной платформы. Преобразований адреса сделано два. Что даёт нам три вида адресов и их пространств. Пойдём снизу вверх:

Теперь о преобразованиях. Пересчёт из виртуального в линейный называется сегментным. Вышеупомянутые старшие 16 бит называются селектором сегмента. 13 бит из них содержат индекс в одной из дескрипторных таблиц, тоже лежащих в памяти. Элемент такой таблицы называется дескриптором сегмента. Помимо всего прочего, он содержит базовый линейный адрес сегмента и его размер. Младшие 32 бита виртуального адреса называются смещением в сегменте. Преобразование в линейный адрес таким образом состоит из двух действий: проверки, что смещение не больше размера сегмента (если не прошла, то процессор сам себе генерирует исключение общей защиты (general protection fault, GPF)) и добавления смещения к базовому линейному адресу сегмента. Нужно сказать, что в полном виде это преобразование практически не используется. Оно важно для кода, выполняющемся в режиме совместимости с процессором 80286 (т.н. 16 бит код). В этом режиме виртуальный адрес не 16:32, а 16:16. И, чтобы покрыть пространство, больше 64 KiB, обязательно задание множества сегментов с разными базами. В случае 16:32 адреса уже одного смещения достаточно, чтобы покрыть все 4 GiB линейных адресов. Поэтому для 32 бит кода обычно делают только один селектор, ставят ему базу в ноль, размер побольше, вплоть до 4 GiB и пользуются в коде уже только этим селектором. Таким образом, если в коде требуется передать указатель, то передают только 32 бита, а не 48. Это называется 0:32 адресом или плоской (flat) адресацией. Само преобразование при этом становится тривиальным и никаких сложностей и ограничений не добавляет. Затраты памяти на дескрипторные таблицы сравнительно небольшие: 64 KiB на общесистемную (GDT) и ещё столько же в каждом процессе на локальную (LDT). Поэтому в данной статье оно интереса не представляет.

Преобразование из линейного адреса в физический называется страничным (paging). При нём как линейное, так и физическое пространство представляется последовательностью блоков по 4 KiB. Такой блок называется страницей (page). Преобразование отображает каждую линейную страницу на произвольную физическую. Для этого преобразования в памяти приходится дополнительно держать структуру, называемую таблицей страниц (page table, PT), состоящую из однородных элементов (PTE). Математически преобразование заключается во взятии старших 20ти бит линейного адреса, выборке PTE с этим индексом и формировании физадреса, как 20ти бит из PTE и 12ти младших бит линейного адреса (смещение в странице размера, как уже сказано, 4 KiB).

Страничное преобразование виртуализует адресное пространство значительно гибче, чем сегментное, поэтому все 32 бит ОС используют для изоляции адресных пространств процессов именно его. При переключении процесса процессору просто указывается новое место PT. Ценой гибкости является размер PT. Подсчитаем его. Каждый PTE имеет размер 4 байта. 20 бит индекс означает 20 Mi элементов, значит общий размер PT составляет 4 MiB. И это на каждый процесс. Довольно много. Во времена выхода процессора 80386 далеко не каждая машина имела столько даже общей физической памяти. С другой стороны, далеко не каждому процессу требуются все 4 GiB адресного пространства и, если хранить только реально используемые PTE, то можно расход памяти сильно сократить. Почти так Интел и сделал. PT имеет надстройку, называемую каталог страниц (page directory, PD), состоящий из, соответственно, PDE, почти не отличающихся от PTE. 20 бит число внутри PDE - это также старшие 20 бит физадреса страницы, но вся страница считается заполненной PTE, соответствующим данному PDE. Преобразование несколько усложняется. Старшие 20 бит линейного адреса делятся пополам: 10 бит на выборку PDE и 10 бит на выборку PTE из страницы, на которую указывает PDE. Размер PD при этом тоже получается равен странице, и всё выглядит очень красиво. Экономия физпамяти достигается спецбитом в PTE/PDE, показывающим, что данный элемент не указывает никуда. При обращении к данному элементу процессор генерирует GPF и обработчик ОС может завершить процесс или сделать ещё что-то. Например, отвести память и продолжить выполнение дальше. Разумеется, память можно отводить и раньше, когда приложение об этом просит.

Менеджер страниц OS/2.

Теперь у нас есть достаточно знаний, чтобы начать смотреть, как реализована работа с памятью и адресными пространствами в ядре OS/2. Отвечающий за это компонент ядра называется менеджером виртуальной памяти (virtual memory manager, VMM). Да, именно виртуальной памяти вообще, т.е. всех её видов. Это крупный компонент, состоящий из двух семейств функций. Имена первых начинаются с букв VM, вторых - с PG. Первые реализуют работу собственно с объектами VMM: отведение под них адресных пространств и отображение объектов через все эти пространства. PG - это более низкоуровневая прослойка для управления таблицами страниц. Она условно образует отдельный компонент pager. Условность здесь в том, что VM и PG связаны между собой перекрёстно. VM для хранения данных нужен сервис от PG, а PG для работы с таблицами нужно, как минимум, отобразить их на виртуальное адресное пространство, что может сделать только VM.

Итак, линейное адресное пространство процесса. С точки зрения VM - это совокупность объектов. Но не только. В первую очередь - это совокупность областей, внутри которых уже можно отводить адреса под объекты. Типов этих областей довольно много. От полностью приватной для процесса до системно глобальной. Вдаваться в подробности не буду, в вышеупомянутой статье всё это написано и нарисовано. Сейчас интересно посмотреть на это же пространство с точки зрения pager. Тут всё предельно просто: всего две области. От начала адресов и до определённой границы - локальная для процесса. Выше этой границы - глобальная, которая во всех процессах одинакова. Эту же глобальную область называют ядерной или системной, потому что по соображениям защиты процессов друг от друга доступ в эту область разрешён только в режиме исполнения ядра. Но для pager более важно в этом делении другое: для глобальной области достаточно иметь один набор PTE на всю систему и просто на него ссылаться в PD процесса. Для локальной же в каждом процессе PTE должны быть свои.

Граница между этими областями определяется очень просто. До ядер 14.x она была фиксированной и равнялась 512 MiB. Потом IBM осознало, что одному процессу уже маловато будет и ввело в config.sys специальный параметр VIRTUALADDRESSLIMIT (в дальнейшем просто VAL), как раз и задающий значение этой границы. Допустимое его значение от 512 MiB до 3 GiB. Казалось бы, надо его делать побольше, чтобы любому приложению было где себя развернуть. К увеличенному расходу памяти под PTE это не приводит, потому что иерархическая структура PD/PT позволяет не отводить память под неиспользуемые области. Тем не менее, цена у подъёма VAL есть и существенная: на сколько байт увеличивается локальная область, на столько же уменьшается системная. Максимальное значение VAL оставляет от неё 1 GiB. Много это для практического использования ОС или мало? Надо разбираться.

Для подобных разборок IBM любезно выпустило очень мощный инструмент Theseus, который обращается в ядро через специальное недокументированное отверстие и вытаскивает много полезной информации о внутренних структурах. В частности отчёт System -> Kernel Information -> System Object Summary нам как раз и покажет объекты VM в системной области. Для теста возьмём свежепоставленную eComstation 2.1. В config.sys у неё сразу прописано VIRTUALADDRESSLIMIT=2048. Значит и локальная и глобальные области в этой системе имеют размер 2 GiB. Смотрим отчёт.

 Object  Allocated Committed  Present   Swapped
address    memory    memory    memory    memory      Description
80000000  00010000  00000000  00000000  00000000  PG misc owner (owner)
80010000  16DA0000  00126000  00126000  00000000  PG Compat. region page table (owner)
96DB0000  49200000  00027000  00027000  00000000  PG high page tables (owner)
DFFB0000  11F57000                                Free
F1F07000  00010000  00008000  00008000  00000000  SEL LDT (owner)
...

Analysis of 'Free' areas:
There are 353 free blocks which total 132FA000 (314344K or 306.977M)
The largest 10 free areas are:
address      size
DFFB0000  11F57000 (294236K or 287.340M)
F3AD4000  0000F000 (60K or 0.059M)
...

Итог поразительный. Из двух гибибайт свободно всего 307 MiB. А если отбросить мелкие блоки, которые всё равно мало кто может использовать, то реально остаётся 287 MiB. Для компьютера выпуска последней пятилетки, в котором размер физпамяти измеряется гибибайтами, выглядит маловато. Виртуальный диск, кэш файловой системы, Virtual PC - все эти потребители больших объёмов отводят себе пространство в системной области, а с таким остатком развернуться им особенно негде. Напрашивающийся способ решения этой проблемы - уменьшать VAL. Но VAL специально был введён для того, чтобы дать больше адресного пространства приложениям, поэтому делать его меньше половины адресного пространства - это уже довольно сильно их ограничивать. И ещё одно соображение. VAL вроде как заявлен рабочим и на 3 GiB. Если провести эксперимент и его туда поставить, то в отчёте размер свободного места практически не изменится. Значит с резервированием системной области всё не так просто.

Что ж, будем изучать список объектов. Что там самое большое? Да вот же, две строки в самом начале списка с общим объёмом: 16DA0000h + 49200000h = 5FFA0000h = 1535 MiB. Три четверти системной области. Тут же читаем что это такое - таблицы страниц под приложения. Два блока - это понятно, потому что по историческим причинам локальная область делится на область совместимости с 16 бит кодом (до 512 MiB) и высокую область (более ограниченного применения). Кроме того, между ними находится глобальный кусочек, под названием GSR. Но почему такой общий объём? Здесь не обойтись без исходников ядра. Отведение этих блоков обнаруживается в части старта pager:

   ac.ac_va = VirtualAddressLimit + _64K;
   if (VMAllocMem(cbCRPageTables * maxProc,    /* reserve size */
                  0,                           /* commit size */
                  VMAC_ARENASYS+VMAC_LOCSPECIFIC+PG_SWAPPABLE+PG_R+PG_W,
                  HOBNULL,                     /* current task */
                  PGCRPTEOWNER,                /* owner */
                  HOBNULL,                     /* mte */
                  0,                           /* sel flags */
                  0,                           /* block no */
                  SSToDS(&ac)) != NO_ERROR)
    ...
    ac.ac_va = VirtualAddressLimit + _64K + (cbCRPageTables * maxProc);
    if (VMAllocMem(cbhPageTables * maxProc,    /* reserve size */
                   0,                           /* commit size */
                   VMAC_ARENASYS+VMAC_LOCSPECIFIC+PG_SWAPPABLE+PG_R+PG_W,
                   HOBNULL,                     /* current task */
                   PGHPTEOWNER,                 /* owner */
                   HOBNULL,                     /* mte */
                   0,                           /* sel flags */
                   0,                           /* block no */
                   SSToDS(&ac)) != NO_ERROR)

Если перевести эту массу специальных слов с языка C на человеческий, то смысл следующий: блоки отводятся по указаному здесь же адресу (флаг VMAC_LOCSPECIFIC) в начале системной области (ac.ac_va рассчитывается, как VirtualAddressLimit + _64K) и размера равного потенциально необходимому для PT в пользовательской области, помноженному на число процессов, в будущем доступных в системе. Или чуть другими словами: ядро сразу резервирует место под все PT, которые теоретически могут быть использованы при дальнейшей работе системы и делает это в системной области. Очевидно, что так "на будущее" можно нахватать сколько угодно, а значит ядро как-то себя ограничивает. Смотрим по исходникам выше расчёт размера:

   cbSysArena = _4G - VirtualAddressLimit;
   cbUserPageTables = cbSysArena - (512 * _1MEG);
   cbCRPageTables = MAXCR_PDE * PAGESIZE;
   pgchPageTables = (VirtualAddressLimit - MINHIGHLADDR) / PAGEDIRSIZE;
   cbhPageTables = pgchPageTables * PAGESIZE;
   maxProc = cbUserPageTables / (cbCRPageTables + cbhPageTables);
Ключевое тут начало и конец. Стартовый расчётный размер области пользовательских PTE - это общий размер системной области (он уже зафиксирован через VAL) минус 512 MiB. Этот размер делится на размер PTE одного процесса (опять таки, зависящий от VAL) и таким образом получается максимальное число процессов, которые можно запустить в системе. Если нет других ограничений на число процессов, то так память и отводится. Вот и прояснилось: и почему места так мало (ещё бы, всего полгига на все прочие ядерные надобности) и почему оно не сильно зависит от VAL.

Пользовательский тюнинг.

Что интересного в этой картине может изменить пользователь? Раз VAL уже зафиксирован, то остаётся только количество процессов. Для начала не плохо бы узнать под сколько их отводится пространства по умолчанию. Применим знания этого расчёта на практике и повторим его, исходя из списка выше. Размер объекта верхних страниц 49200000h. Размер PTE под одну верхнюю область: (VAL - 512 MiB) / 4096 * 4 (размер области в байтах, делённый на размер страницы и умноженный на размер PTE). Если всё записать в шестнадцатеричном виде: 49200000h / ((80000000h - 20000000h) / 1000h * 4) = 30Ch = 780.

Похоже на правду, но можно себя проверить по другому отчёту в Theseus: System -> General System -> General System Information:

29. QSV_MAXPROCESSES         = 780.
30. QSV_VIRTUALADDRESSLIMIT  = 80000000

Всё верно. Теперь каждый для себя должен решить сколько ему надо для практических задач. Лично у меня на слегка забитой всякими демонами машине go показывает всего 76 запущенных. Значит за 128 я вряд ли выйду. Ограничить количество процессов можно двумя способами.

Вписываем в config.sys PROCESSES=128 и смотрим что получилось:

 Object  Allocated Committed  Present   Swapped
address    memory    memory    memory    memory      Description
80000000  00010000  00000000  00000000  00000000  PG misc owner (owner)
80010000  03C00000  00124000  00124000  00000000  PG Compat. region page table (owner)
83C10000  0C000000  00027000  00027000  00000000  PG high page tables (owner)
8FC10000  62407000                                 Free
F2017000  00010000  00006000  00006000  00000000  SEL LDT (owner)
...

Analysis of 'Free' areas:
There are 355 free blocks which total 637D8000 (1630048K or 1591.844M)
The largest 10 free areas are:
address      size
8FC10000  62407000 (1609756K or 1572.027M)
F2037000  00010000 (64K or 0.063M)
...

Отлично. Ничего не потеряв в полезности системы, свободное место в системной области удалось увеличить до 1.5 GiB. Этого уже достаточно и для больших потребителей этой области.

Возможные доработки драйверов и ядра.

Для драйверов можно дать только один банальный совет: помнить, что системная область - это очень ограниченный ресурс и использовать его минимально. Следовать ему не так просто. Виртуальные машины ещё можно перевести в приватные области и QEmu это отлично делает. Будем надеяться, что когда-нибудь VirtualBox сможет полностью заменить уже мёртвый VirtualPC. Для виртуальных дисков и кэша JFS ситуация сложнее. Это объекты, доступ к которым потенциально нужен всем процессам и в полном объёме. Принципиальным решением тут может быть только замена статического страничного отображения на динамическое, когда каждый процесс отображает себе в относительно небольшую область только необходимые ему в данный момент страницы, а не постоянно весь объект целиком. К сожалению, на данный момент полуосевой VMM достаточного сервиса для такой реализации не предоставляет.

Если говорить о ядре, то очевидным объектом на вынос в приватную область являются пользовательские таблицы страниц. Текущий дизайн очень жёстко ограничивает число процессов в системе. Если их требуется, скажем 1024, то их объём не сочетается даже с VAL=2048, не говоря уж о более высоком. Но это опять таки не просто. Хотя эти PTE сами по себе обслуживают локальные области, но работа с ними происходит глобально. Пример: произошла нехватка физпамяти и нужно освободить какую-то страницу, переместив её в файл подкачки. Т.е. нужно прочитать содержимое этой страницы, а потом отметить в её PTE, что она больше не в памяти. Такая страница определяется в глобальном контексте, а значит скорее всего будет принадлежать другому процессу. Сейчас эта задача решается чтением содержимого через новое локальное отображение на эту физпамять (оно нужно по любому, потому что именно в процессе его отведения и произошла нехватка), а PTE модифицируются через глобальную область. Если вынести PTE локально, то придётся создавать временное локальное отображение и на PTE другого процесса. Это усложняет VMM и может заметно повлиять на скорость его работы.

Достаточно тесно проблема глобальной области под все PTE примыкает и к другой возможной доработке VMM: поддержке physical address extension (PAE). Это расширение изначальной архитектуры IA-32 в минимальном объёме присутствующее в процессорах, начиная с Pentium Pro. Заключается оно в увеличении физадреса с 32 бит до 36 бит (оригинальное PAE) или даже до 52 бит (расширенное PAE, есть только в совсем новых процессорах, как часть EM64T). Поддержка PAE позволяет использовать, как минимум, 64 GiB физической памяти. Задача такой поддержки поднимает очень много проблем потому, что она затрагивает не только ядро, но и все драйверы. Здесь упомяну только одно следствие. 32 бит размера обычного PTE полностью использованы, поэтому дополнительные 4 бит физадреса в него класть просто некуда. Поэтому в PAE используется другой формат PTE, уже размером в 64 бита. Это означает, что уже при том же объёме отображаемого адресного пространства на PTE потребуется в два раза больше памяти. Если предполагать, что таким ядром будут пользоваться на системах, где от PAE есть польза, т.е. имеющих больше 4 GiB физпамяти, то лишний расход этой физпамяти можно считать не очень существенным потому, что PAE открывает доступ к значительно бо́льшему её объёму, чем увеличение PTE. Более важно, что так же в два раза возрастёт линейный размер отображения PTE в системную область. Это означает, что придётся или уменьшать максимальное число процессов ещё в два раза или жертвовать какими-то другими потребителями.


Примечания.

О разделяемой памяти.

Читавшие более подробные статьи про VM могут удивиться: как это всё пространство ниже VAL локально? Ведь там кроме приватной памяти есть и разделяемая (shared), которая вроде как во всех процессах тоже одинакова? Ведь если её реализовать, как глобальную, то можно существенно сэкономить память под PTE. К сожалению, это не возможно. Да, объект в разделяемой памяти одинаков в разных процессах, но только в тех, которые получили к нему доступ. Всем остальным при обращении по адресам этого объекта надо давать отлуп. И реализовать это можно только локальными PTE с флагом отсутствия страниц. Впрочем, и тут есть исключение. Глобальная область внутри локальной всё таки существует. Называется она global shared region (GSR). Заканчивается она на фиксированной исторической границе 512 MiB, а вот размер её менялся в разных версиях ядер. В 14.x он был урезан до 32 MiB. Это узкоспециализированная область, доступная приложениям только для чтения (опять соображения безопасности). Это означает в том числе, что создать свой объект в этой области приложения не могут. Используется она только ядерным загрузчиком DLL, который может там сделать объект с кодом этой DLL. Локальные PTE под эту область не отводятся, поэтому на процесс в области глобального массива PTE нужно на 32 KiB меньше. Но поскольку размер PTE на процесс считается на мегабайты, на принципиальную экономию это не тянет.

О уникальности реализации VIRTUALADDRESSLIMIT.

Одним из любимейших человеческих занятий является сравнения себя любимого с остальными. А в идеале ещё и доказательство, что у себя больше. Потому полюбопытствуем, как с аналогами VAL на других платформах. Радостно можно отметить, что, хотя аналоги и есть, но не полные:

Это бросающиеся в глаза отличия. Но есть и ещё одно. В Windows и Linux параметр задаётся таким образом, что ядро его знает уже в момент своего старта. В OS/2 такой механизм произвольных параметров ядра не предусмотрен и VAL указывается в config.sys. Но для того, чтобы config.sys хотя бы загрузить, ядро должно стартовать miniFSD. А ему для работы требуется инициализированный VMM. Тут возникает неувязка: как стартовать VMM, если важнейший параметр его конфигурации ещё не доступен? IBM тут выкрутилось просто героически: VMM стартует в два прохода. В первый раз размер системной области ставится на фиксированную величину и в этой области miniFSD отводит, что ему нужно. Когда же прочитан VAL, системная область пересчитывается на указанный размер. И только после этого отводится пространство под локальные PTE, которое, как видно в отчётах, всегда находится в начале системной области.

Вопрос на засыпку: какое значение границы принято за начальное? 512 MiB (максимум для системы, да и просто совместимо с предыдущими версиями ядер)? 3 GiB (гарантирует, что всё отводимое на ранней стадии окажется выше VAL) ? Неет, у IBM своя логика. За начальное значение взята граница, которая останется, если VAL в config.sys не указан. А именно 2 GiB. Такой дизайн потребовал написания двух веток кода: и для уменьшения системной области и для увеличения. Кроме того он оставляет вопрос: а что делать, если miniFSD отведёт область ниже указанного VAL? Судя по коду, вопрос этот IBM не слишком беспокоил. Проверка на этот счёт, в принципе, написана, но исключительно для версии allstrict, т.е. совсем параноидально самопроверяющейся. Более обычные ядра считают, что такого выделения не может быть никогда. Определённые резоны в этом есть. Считается, что драйверам всё равно, где им ядро выделит область адресов, поэтому у них и нет возможности управлять местом. А VMM старается использовать в первую очередь высокие адреса. Таким образом, если заняты адреса ниже VAL, значит и выше уже всё забито. В нормальных условиях это действительно маловероятно.

О секретности.

Данная статья содержит выдержки из оригинальных исходников ядра OS/2, читать которые можно только по специальному разрешению IBM. Если у Вас случайно нет такого разрешения, то за Вами уже выехали.

 

Находками делился Slavik Gnatenko
19.06.2013


Новые статьи на нашем сайте:


Комментариев к странице: 2 | Добавить комментарий
Домой | Проект ядро Core/2 | Проект OS/4 Download | Новости | Гостевая книга | Подробно обо всем | Нужные программы | Проекты | OS/2 FAQ | Всячина | За и Против | Металлолом | #OS2Russian | RDM/2 | Весёлые картинки | Наша галерея | Доска объявлений | Карта сайта | ПОИСК | ФОРУМ