Опрос
Вы участвуете в программе Windows Insider?
Популярные новости
Обсуждаемые новости

Сага о Windows. Глава вторая. Часть вторая

Напечатать страницу
12.09.2011 03:01 | Raiker

Рассказ наш, охвативший почти десятилетие, остановился на Intel 80386. Новый процессор появился на рынке в октябре 1985 года, то есть всего за месяц до выхода Windows 1.01. Индустрия программного обеспечения не слишком быстро адаптировалась к новому оборудованию. Так, поддержка защищенного режима начала внедряться в Windows только в середине 1987 году с выпуском Windows 2.0.


Intel 80386


Как известно, 80386 стал первым 32-битным процессором Intel. В связи с этим до 32-разрядных было расширено большинство архитектурных компонентов. Рассмотрим те из них, которые затрагивают вопрос адресации.

Во-первых, следует сказать, что шина данных в 80386 была расширена до 32 бит, что позволило адресацию 232 байт. Это не что иное, как тот самый пресловутый лимит в 4 Гб оперативной памяти, накладываемый классической 32-разрадной архитектурой.

Структура дескриптора в Intel 80386 выглядит следующим образом:


В первую очередь бросается в глаза использование 8 старших бит (байт 7) для хранения второй части базового адреса сегмента. Здесь хранятся биты 31-24 базового адреса (то есть старшая часть), что обеспечивает совместимость с защищенным режимом Intel 80286.

Аналогично поступили и с расширенным до 20 бит пределом. В частности, байт 6 дескриптора теперь хранит биты 19-16 предела. Напомню, предел используется для указания размера сегмента. 20-битный предел позволяет указать сегмент размером 1048576 байт (224, 1 Мб).

Но и это не предел. Обратим внимание на флаги, также появившимся в 6 байте дескриптора, и, в частности, к флагу G, называемому гранулярностью сегмента. Флаг G определяет, в чем измеряется поле предела. Если G=0, то в пределе указывается размер сегмента в байтах. Тогда максимальный размер сегмента составляет 1 Мб. Если G=1, то предел указывается в блоках памяти по 4 Кб, также называемых страницами. Очевидно, тогда возможный размер сегмента становится равен 220*4 Кб, а это 4194304 Кб, то есть 4 Гб. Таким образом, все адресное пространство может быть отдано одному сегменту.

Флаг D служит для обеспечения совместимости с защищенным режимом Intel 80286. Если D=1, то сегмент содержит 32-разрядные данные, если D=0, то 16-ти разрядные. Данный флаг по-разному работает для сегментов кода и сегментов стека (для сегментов кода указывает на использование 32-разрядных (D=1) или 16-разрядных (D=0) адресов в коде, для сегментов стека – на 32-разрядный (D=1) или 16-разрядный (D=0) указатель стека). Но данный нюанс нас сейчас вовсе не заботит, поэтому, не останавливаясь на нем, пойдем дальше.

Соседние два бита являются зарезервированными. Флаг X зарезервирован Intel для следующих моделей процессоров и при работе с Intel 80386 должен быть равен 0.

Флаг U (также иногда обозначается как AVL от англ. Available - доступен) называется пользовательским битом и доступен для использования операционной системой по своему усмотрению.

Заметьте, что размер дескрипторов не изменился и по-прежнему составляет 64 бита (8 Кб). Соответственно не изменился и размер теневых регистров в процессоре.

Также и сегментные регистры, хранящие селекторы, остались 16-битными, то есть для каждой дескрипторной таблицы по-прежнему можно указать только 8192 дескриптора. Соответственно и структура регистра LDTR, хранящего селектор таблицы LDT, не претерпела изменений.

Что же касается регистра GDTR, то он, как легко догадаться, теперь содержит не 24-битный, а 32-битный базовый адрес таблицы GDT.


Но если структура сегментных регистров не изменилось, то смещение, которое складывается с базовым адресом сегмента для получения адреса конечного сегмента, было расширено до 32 бит. Очевидно, что при помощи смещения, равного по длине физическому адресу, можно адресовать любой байт памяти, то есть все 4 Гб. Вспомним, что и максимальный размер сегмента, задающийся пределом при G=0, также может быть равен 4 Гб. Таким образом, появилась возможность забыть о сегментных регистрах как таковых – для всех дескрипторов можно указать начало сегмента равное началу памяти, и далее оперировать только смещением.

Но следует помнить, что 32-разрядное смещение возможно только в режиме страничной адресации, к обзору которого мы приступим далее. Если же используется реальный режим или защищенный режим без страничной адресации, то смещение остается 16-разрядным (для совместимости с предыдущими моделями).

Итак, перейдем к страничной адресации. Заметим, что она может быть включена, а может быть и отключена.

Страничная адресация – по сути, следующий шаг в абстрагировании конечного программного обеспечения от реальной организации памяти.

Представьте, что для каждой задачи существует своя виртуальная память объемом 4 Гб. Она адресуется посредством рассмотренной нами сегментной схемы. То есть используются те же дескрипторные таблицы, селекторы и т.д. Отличие от Intel 80286 здесь заключается только в возможности использования сегментов больших размеров (становится доступно 32-битное смещение, которое вкупе с флагом G в дескрипторе позволяет указать дескриптор объемом до 4 Гб).

Итак, при страничной адресации используется прежняя схема адресации. Только работает она не с физической, а с виртуальной памятью. Вычисленный таким образом линейный адрес не оказывается физическим. Это адреса сегментов виртуальной памяти (4 Гб), которую “видит” приложение, но они могут быть отличны от физических адресов, то есть реального размещения сегментов в памяти. Таким образом, совпадающие для сегментной адресации линейный и физический адреса расходятся. Линейный адрес – это адрес на выходе устройства сегментации. Физический адрес определяется следующим уровнем – страничной адресацией.

Страничную адресацию можно представить себе как “надстройку” над сегментной организацией памяти.

Другими словами, линейный адрес – это адрес в “мирке” адресного пространства, с которым работает приложение. Физический адрес – тот, который процессор должен передать по шине адреса, то есть реальный адрес.

Если сегментная адресация работает в своем собственном “мирке”, то необходим механизм, который вычисленный линейный адрес переведет в физический. Такой механизм называется трансляцией страниц.

Все пространство памяти условно делится на участки фиксированной длины – страницы. Для Intel 80386 размер страницы равен 4 Кб.

Рассмотрим, как происходит адресация страниц. Структура страничной адресации состоит из трех основных элементов. Во-первых, это сами страницы – блоки памяти по 4 Кб. Для того чтобы их адресовать, в свою очередь используется схема из двух уровней – таблиц страниц и каталога страниц.

Таблица страниц занимает одну страницу памяти (то есть 4 Кб) и содержит записи (так называемые PTE – Page Table Entry), указывающие на сами страницы. Каждая запись занимает 32 бита, то есть всего в таблице может храниться 1024 записи. Другими словами, в одной PTE хранятся записи о 1024 страницах по 4 Кб каждая.

Каталог страниц, как и таблица страниц, занимает одну страницу (4 Кб) и содержит 32-разрадяные записи PDE (Page Directory Entry), указывающие на таблицы страниц. Следовательно, каталог страниц может хранить 1024 записей PDE (то есть указывать на 1024 таблиц страниц).

Другими словами, адресация происходит по типу матрешки – каталог страниц указывает на таблицу страниц, а таблица страниц – на конкретные страницы. Легко подсчитать, что если в таблице и каталоге хранится по 1024 записей, а страница представляет собой память объемом 4 Кб, то всего при такой схеме адресуется 1024*1024*4 Кб = 4194304 Кб. В конечном итоге, это и есть те самые 4 Гб, доступные при 32-разрядной шине адреса и полностью покрываемые страничной адресацией.

Как же происходит обращение к данным конкретной страницы?

Каждый байт в странице обладает собственным 32-битным линейным адресом, который делится на три части.


Адресация каталога страниц и таблицы страниц занимает по 10 бит, под смещение отведено 12 бит. 12 бит смещения позволяют адресовать 4096 байт (212), то есть данное поле указывает конкретный байт внутри страницы. А вот с определением таблицы страниц и конкретной страницы дело обстоит сложнее.

Поле «Каталог» содержит 10 бит, которые указывают номер записи PDE в каталоге, к которой необходимо обратиться для получения информации о нужной таблице. 10 бит могут принимать 1024 значений (210), то есть данное поле покрывает все записи PDE, содержащиеся в каталоге. Заметьте, что здесь, по сути, содержится только порядковый номер PDE, а не смещение в байтах. О том, как поле “Каталог” пересчитывается в смещение, будет сказано ниже.

Аналогичным образом работает и поле “Таблица” – 10 бит указывают номер одной из 1024 записей PTE в таблице страниц. PTE, как вы помните, уже содержит данные о конкретной странице.

При помощи этих трех компонент и формируется физический адрес.

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

Но взгляните. У нас имеется 32-битный линейный адрес. Для удобства, запишем его в формате ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXX.

Условно говоря, этот адрес указывает на некий сегмент виртуальной памяти, определенный при помощи дескриптора. На самом деле, эти данные могут располагаться в любой точке физической памяти (и, забегая вперед, скажем, что они могут быть вовсе выгружены на жесткий диск). Как же по линейному адресу узнать, где именно в физической памяти располагаются эти данные?

На самом деле, это очень просто. Здесь-то и задействуется описанная нами структура из каталога страниц, таблиц страниц и самих страниц (объемом 4 Кб каждая).

Последние 12 бит (XXXXXXXXXXX) адресует 4 Кб памяти, то есть смещение внутри страницы. Вы можете представить это, как 12-битное смещение по отношению к предшествующей группе ZZZZZZZZZZYYYYYYYYYY. По сути, поле XXXXXXXXXX адресует одну страницу.

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

Физический адрес начала каталога заведомо известен процессору. Внутри же каталога содержатся записи длинною 32 бита, что является минимальным “шагом” внутри него. При этом каталог занимает 4 Кб (одну страницу) и может содержать 1024 записи. Но каждая из записей – и это важно помнить – указывает на таблицу страниц, и лишь таблица страниц указывает на 1024 страницы. Таким образом, одна таблица страниц в сумме адресует 4096 Кб (1024 записей PTE * 4 Кб). Таким образом, каждая запись PDE внутри каталога адресует 4 Мб.

За определение таблицы, то есть выбор соответствующей записи PDE в каталоге, отвечает поле “Каталог” (ZZZZZZZZZZ). Если оставаться в рамках линейного виртуального пространства, то какой “шаг” в памяти будет производить изменение этой части линейного адреса? Подсчитать это достаточно легко:

0000 0000 0100 0000 0000 0000 0000 0000 = 4194304-й байт. То есть увеличение поля ZZZZZZZZZ на 1 будет увеличивать текущий адрес на 4194304 байт, что равняется 4 Мб.

Таким образом, поле ZZZZZZZZZZ, примененное к страничной адресации, позволяет указать необходимую запись PDE, точнее ее порядковый номер. Повторю еще раз, так происходит потому, что данная часть адреса, взятая изолированно, позволяет “шагать” по памяти 4 мегабайтными блоками. Сколько значений может принять ZZZZZZZZZ, столько 4 мегабайтных блоков доступно.

Итак, запись в каталоге страниц, отсылающая к таблице, в конечном итоге адресует блоки по 4 Мб (поскольку каждая запись указывает на таблицу, содержащую информацию о 1024 страницах размером 4 Кб). Так и получается, что часть ZZZZZZZZZ линейного адреса виртуального пространства одновременно оказывается и порядковым номером записи PDE в каталоге.

Ситуация с полем “Таблица” (YYYYYYYYYY) полностью аналогична, за тем исключением, что речь идет уже о самих страницах (блоках размером 4 Кб):

0000 0000 0100 0000 0001 0000 0000 0000 = 4096-й байт.

Как сказано выше, последние 12 бит (XXXXXXXXXXXX) выступают в качестве смещения внутри страницы. Они адресуют 4 Кб, что равно одной странице.

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

Поскольку 210 дает 1024 возможных значений, то каждое из полей ZZZZZZZZ и YYYYYYYY позволяет определить 1024 “шагов”. Это и есть таблицы и страницы соответственно, покрывающие, как было показано выше, все виртуальной адресное пространство.

Но узнать номера PDE и PTE еще недостаточно – нужно определить, в каком месте каталога и таблицы соответственно номера с этими записями начинаются. Для этого 10-битные значения необходимо перевести в смещения относительно начала каталога и начала таблицы соответственно.

Но как это сделать? На самом деле, очень просто – или, что то же самое, приписать два нуля справа, то есть преобразовать ZZZZZZZZZ и YYYYYYYYY в ZZZZZZZZ00 и YYYYYYYY00 соответственно. В десятичной системе счисления данная операция соответствует умножению на 4.

Все дело в том, что процессору нет необходимости в адресации каждого байта этих двух смещений – минимальный необходимый “шаг” составляет 32 бита, то есть 4 байта (от начала одной записи до начала другой). Но в то же время он должен перевести порядковые номера записей в смещения в байтах. Это и достигается путем приписывания двух нулей справа. Тогда максимальное значение каждого смещения будет равно 1111 1111 1100, то есть 4092 байта. Минимальный шаг при этом будет определяться третьим разрядом справа. Поскольку 0100 = 4, то и минимальный шаг будет равен 4 байтам.

Так линейный адрес, существующий только в виртуальном пространстве, транслируется в адрес соответствующей страницы в физической памяти.

Из сказанного выше понятно, что линейный и физический адреса страницы могут как совпадать, так и различаться. Совпадение возможно, когда определенная страница (то есть блок памяти в 4 Кб) как в виртуальном, так и в физическом пространстве находится в одном и том же месте.

Структура записей PDE и PTE почти полностью совпадает.


Структура записи PDE


Структура записи PDE

В поле Адрес указывается физический адрес таблицы (для записи PDE) или страницы (для записи PTE).

Здесь мы вновь сталкиваемся с особенностями двоичной системы счисления. Дело в том, что под поле адреса выделено лишь 20 бит, в то время как адрес должен быть 32-разрядным. Но на практике указание последних 12 бит и не нужно, поскольку при адресации начала таблиц или страниц они будут равны нулю.

Дело в том, что в двоичной системе число 4096 записывается следующим образом:

0001 0000 0000 0000

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

Три бита AVL (от англ. Available – доступны) не используются процессором и могут быть использованы операционной системой по своему усмотрению.

Биты 8 и 7, а также 4 и 3 не используются в Intel 80386 и зарезервированы Intel для последующих моделей процессоров. Для Intel 80386 они должны быть выставлены в ноль.

Флаг D используется только в записях PTE (описание страниц), в PDE должен быть равен нулю. Данный бит устанавливается процессором в “1” после того, как в страницу вносятся изменения. Впоследствии он может быть обнулен операционной системой. Используется для более гибкого управления подкачкой страниц, чтобы избежать повторного сохранения страницы на жесткий диск, если ее содержимое не изменилось.

Флаг A нам уже встречался в структуре дескрипторов. В страничной адресации он определен как для записей PTE, так и PDE. Напомню, процессор устанавливает его в “1” при каждом обращении к элементу, сбрасывается операционной системой для отслеживания обращений. Как и флаг D, он используется для управления подкачкой страниц.

Флаг U/S используется для контроля прав доступа. Данный флаг в записи PTE определяет доступ к странице, в PDE – ко всем страницам данной таблицы. В отличие от доступа, определяемого в дескрипторе, здесь права доступа определяются всего двумя режимами.

Режим супервизора (U/S=0) соответствует привилегиям на уровне сегментов CPL=0, CPL=1 и CPL=2, то есть в режиме супервизора доступны все страницы. Соответственно, страницы с US=0 доступны только из режима супервизора.

В свою очередь, режим пользователя (US=1) соответствует CPL=3, и в режиме пользователя доступны только страницы пользователя.

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

Флаг R/W определяет разрешение записи. При R/W=0 элемент (таблица или страница) доступен только для чтения. При R/W=1 – для чтения и для записи.

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

Наконец, флаг P выполняет ту же функцию, что и в структуре дескриптора при сегментной адресации. А именно, он указывает на то, находится ли страница в оперативной памяти или в файле подкачки. В случае, если происходит обращение к странице, отсутствующей в памяти, процессором вызывается исключение #PF (Page Fault), обработчик которого загружает нужную страницу.

Выше мы сказали о том, что процессору заведомо известен адрес начала каталога страниц. Он хранится в специальном 32-битном регистре CR3 (также называется PDBR – Page Directory Base Register). В процессоре Intel 80386 структура регистра CR3 предельно проста – в нем хранится 20 бит, определяющих адрес каталога, остальные 12 бит, зарезервированы для следующих версий процессора.


Формат регистра CR3 (PDBR)

Почему для указания адреса каталога страниц достаточно 20 бит, думаю, объяснять уже не требуется – каталог представляет собой страницу объемом 4 Кб, то есть должен быть выравнен по границе 4 Кб. Числа, кратные 4096, в двоичной системе имеют последние 12 разрядов равные нулю.

Итак, мы закончили рассмотрение защищенного режима Intel 80386. У вас мог возникнуть единственный вопрос: а зачем это нужно? Что позволяет страничная адресация сделать такого, что было невозможно в защищенном режиме Intel 80286?

На самом деле, у страничной организации имеется ряд преимуществ. И в первую очередь, это гибкое средство управление виртуальной памятью. Ведь страница может не совпадать с сегментом – быть больше или меньше его. Это позволяет выгружать в файл подкачки не целые сегменты, а лишь их части. Но это одновременно и недостаток, ведь если ОС работает со страницами, то и выделить приложению может минимум 4 Кб памяти, если же задаче требуется всего несколько байт, то остальное пространство пропадет всуе.

Нам требуется также сказать несколько слов о том, как изменилась поддержка мультизадачности, появившаяся в Intel 80286. Поскольку TSS хранит весь контекст задачи, включая содержимое всех регистров, то потребовалось расширить его поля до 32 бит.


Так, TSS теперь хранит содержимое 32-разрядных регистров, появившихся в Intel 80386 (EIP, EFLAGS, EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI). Также были добавлены 16-разрядные регистры FS и GS. Кроме этого, TSS теперь хранит и регистр CR3, который содержит указание на каталог таблиц страниц. Это позволяет изолировать задачи на уровне страничной адресации.

Среди других нововведений – появление карты разрешения ввода/вывода и бита отладки T.

Карта разрешения ввода/вывода позволяет дифференциально настроить доступ каждой отдельной задачи к портам ввода/вывода. Собственно говоря, сама карта располагается после основных полей TSS, но ограничена его пределами. Поле “База карты I/O” указывает смещение начала карты от начала TSS.

Каждый бит карты соответствует одному порту. Устанавливая биты карты в 0 или 1 можно либо запретить, либо разрешить обращение к соответствующему номеру бита порту.

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

Таковы основные нововведения защищенного режима Intel 80386.

Но помимо описанных выше режимов, в 80386 появился и еще один, так называемый режим Virtual 8086 (здесь и далее V86). Этот режим интересен тем, что он обеспечивает совместимость с Intel 8086, сохраняя возможности защищенного режима, такие как многозадачность и страничную трансляцию. По сути, V86 – это режим задачи защищенного режима. То есть в защищенном режиме могут одновременно присутствовать как задачи в обычном состоянии, так и в состоянии V86.

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

По сути, для каждой задачи в режиме V86 создается отдельная виртуальная машина. За виртуализацию отвечает специальный компонент операционной системы, называемый монитором V86. Этот компонент работает с наивысшим уровнем привилегий (CPL=0) и содержит все средства виртуализации (обработчики прерываний и исключений, средства эмуляции операций вводы/вывода и т.п.).

Как правило, внутри виртуальной машины выполняется копия операционной системы, необходимой приложению. То есть в V86 запускается ОС реального режима (скажем, MS-DOS) и в ней исполняется конечное приложение. Разумеется, задачам, запущенным в режиме V86, доступен только 1 Мб памяти.

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

Режим V86 позволял решить проблему совместимости – появлялась возможность запуска приложений реального режима в ОС, ядро которой работает в защищенном режиме.

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

В Intel 80286 переход в защищенный режим осуществлялся при помощи команды LMSW, которая загружала 16-битный регистр MSW (Machine Status Word). Последний бит регистра отвечал за переход в защищенный режим (значение 0 соответствовало реальному режиму, значение 1 – защищенному). Но с помощью команды LMSW было невозможно вернуть процессор обратно в реальный режим, о чем уже говорилось выше. Единственным способом вернуться в реальный режим было выполнить сброс процессора.

В Intel 80386 регистр MSW был заменен регистром управления CR0. Точнее, он был расширен до 32 бит (младшие 16 бит регистра CR0 и есть MSW).


Формат регистра CR0

Непосредственно за переключение между режимами по-прежнему отвечает последний бит - PE (Protection Enable). При PE=1 защищенный режим включен, при PE=0 выключен (процессор работает в реальном режиме). В Intel 80386 появилась возможность свободного изменения флага PE, то есть процессор может свободно перемещаться между режимами.

Флаги MP, EM и TS присутствовали уже в регистре MSW процессора Intel 80286, флаг ET был добавлен в 80386. Эти флаги используются для управления сопроцессором.

Наконец, флаг PG (Paging) отвечает за страничную адресацию. Если PG=1, то страничная адресация включена. Если PG=0, то отключена.

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

Итак, мы завершили, пожалуй, одну из наиболее сложных теоретических частей всего цикла “Саги о Windows”. Но это было необходимо, потому что без всего вышесказанного (ну хорошо, без большей части вышесказанного) невозможно понять ни специфику Windows/286 и Windows/386, ни огромного значения ряда нововведений Windows 3.0. Более того, останется непонятной и революционность Windows NT.

В следующей статье цикла мы продолжим рассмотрение адресации памяти, но уже в связи со спецификой архитектуры IBM PC и Windows 2.0.


Райкер, TheVista.Ru Team,
Сентябрь 2011

Комментарии

Не в сети

у меня первый комп был с процессором Intel 486. Windows 98 SE вообще тормозила, а Windows 95 летала, но мне её функционал не нравился. Оперативки кажется стояло 16 Мегабайт

16.09.11 20:02
0
Не в сети

Продолжение будет когда-нибудь?

15.03.12 12:32
0
Для возможности комментировать войдите в 1 клик через

По теме

Акции MSFT
420.55 0.00
Акции торгуются с 17:30 до 00:00 по Москве
Все права принадлежат © ms insider @thevista.ru, 2022
Сайт является источником уникальной информации о семействе операционных систем Windows и других продуктах Microsoft. Перепечатка материалов возможна только с разрешения редакции.
Работает на WMS 2.34 (Страница создана за 0.053 секунд (Общее время SQL: 0.036 секунд - SQL запросов: 57 - Среднее время SQL: 0.00063 секунд))
Top.Mail.Ru