Techwiki:Memory management in the Windows XP kernel

From ReactOS Wiki
Revision as of 21:02, 30 November 2009 by ThePhysicist (talk | contribs)
Jump to: navigation, search

This is a google translation of "http://www.wasm.ru/article.php?article=mem_management" (WIP) ---

To start a sort of entry. This article is intended for those who have already worked with the memory in kernel mode, and is distinguished from MmProbeAndLockPages MmMapLockedPagesSpecifyCache, as well as familiarity with the device's memory management in a processor - page directory (PDE), page table entries (PTE), an exception error pages (# PF) . To correct the first omission is recommended that you read the relevant articles of the Four-F (http://www.wasm.ru/series.php?sid=9, Part 6 and 9), to correct the second - Article cycle BrokenSword "Intel processors in protected mode "(http://www.wasm.ru/series.php?sid=20, Part 6 and 7, by the way, in Part 7 has a bug in the picture - instead of pictures for PDE 4M pages of the image for PDE 4K pages).

I. PTE Device PDE / PTE, PTE invalid

Consider first how Windows is using a field PTE, which are marked as available Intel software operating system (Avail.) The three-bit Windows operating system uses as follows (the structure at turn off and turn PAE, respectively):

    typedef struct _MMPTE_HARDWARE
       ULONG Valid : 1;
       ULONG Write : 1;
       ULONG Owner : 1;
       ULONG WriteThrough : 1;
       ULONG CacheDisable : 1;
       ULONG Accessed : 1;
       ULONG Dirty : 1;
       ULONG LargePage : 1;
       ULONG Global : 1;
       ULONG CopyOnWrite : 1; // software field
       ULONG Prototype : 1;   // software field
       ULONG reserved : 1;    // software field
       ULONG PageFrameNumber : 20;
   } MMPTE_HARDWARE, *PMMPTE_HARDWARE;
   typedef struct _MMPTE_HARDWARE_PAE {
       ULONGLONG Valid : 1;
       ULONGLONG Write : 1;
       ULONGLONG Owner : 1;
       ULONGLONG WriteThrough : 1;
       ULONGLONG CacheDisable : 1;
       ULONGLONG Accessed : 1; 
       ULONGLONG Dirty : 1;
       ULONGLONG LargePage : 1;
       ULONGLONG Global : 1;
       ULONGLONG CopyOnWrite : 1; // software field
       ULONGLONG Prototype : 1; // software field 
       ULONGLONG reserved0 : 1; // software field
       ULONGLONG PageFrameNumber : 24;
       ULONGLONG reserved1 : 28; // software field
   } MMPTE_HARDWARE_PAE, *PMMPTE_HARDWARE_PAE;

Commentaries labeled such fields.

Golf CopyOnWrite indicates whether the page is copied in the record. These pages are specified by the user attribute or PAGE_WRITECOPY PAGE_EXECUTE_WRITECOPY and means that the process will be allocated a personal copy of the page when you try to write to it. Others will use public unmodified copy. Field Prototype for a valid PTE means that this so-called prototype PTE, used for shared memory between processes through the mechanism of projecting into the memory files (Memory Mapped Files, MMF, see the documentation on Win32 API CreateFileMapping, OpenFileMapping, MapViewOfFile (Ex)) field is reserved for a valid PTE is not used for the invalid PTE this bits called Transition and installed when PTE is considered transitional.

I'm not going to talk about the hardware memory management and other fields of structures PDE / PTE: about bad writing for more than a dozen times. Subsequent narration goes about the format of the PTE, which are used by Windows when the flag Valid = 0, or about invalid (invalid) PTE.

  • Paged out PTE (выгруженные PTE) - невалидный PTE, описывающий страницу, выгруженную в файл подкачки. Paged out PTE (unloaded PTE) - an invalid PTE, describing the options that has been uploaded to the paging file. По первому требованию она вновь будет считана и включена в рабочий набор. On the first demand, it will again be considered and included in the working set. PTE описывается структурой PTE describes the structure
          typedef struct _MMPTE_SOFTWARE {
             ULONG Valid : 1;
             ULONG PageFileLow : 4;
             ULONG Protection : 5;
             ULONG Prototype : 1;
             ULONG Transition : 1;
             ULONG PageFileHigh : 20;
         } MMPTE_SOFTWARE;
     And when the PAE is:
          typedef struct _MMPTE_SOFTWARE_PAE {
             ULONGLONG Valid : 1;
             ULONGLONG PageFileLow : 4;
             ULONGLONG Protection : 5;
             ULONGLONG Prototype : 1;
             ULONGLONG Transition : 1;
             ULONGLONG Unused : 20;
             ULONGLONG PageFileHigh : 32;
         } MMPTE_SOFTWARE_PAE;

При этом Valid = 0 PageFileLow содержит номер страничного файла (которых, как нетрудно догадаться, максимально может быть 16 штук) Protection, соответственно, атрибуты доступа на страницу, задается в виде констант MM_*: Thus Valid = 0 PageFileLow contains the number of paging file (which, you guessed it the most can be 16 pieces) Protection, respectively, the attributes of access to the page specified as constants MM_ *:

         #define MM_ZERO_ACCESS         0  // this value is not used. 
         #define MM_READONLY            1 
         #define MM_EXECUTE             2 
         #define MM_EXECUTE_READ        3 
         #define MM_READWRITE           4  // bit 2 is set if this is writable. 
         #define MM_WRITECOPY           5 
         #define MM_EXECUTE_READWRITE   6 
         #define MM_EXECUTE_WRITECOPY   7 
         #define MM_NOCACHE             8 
         #define MM_DECOMMIT         0x10 
         #define MM_NOACCESS         MM_DECOMMIT|MM_NOCACHE

Prototype = 0 Transition = 0 PageFileHigh - page number in the paging file (the paging file and pumping more)

  • Demand zero PTE (nullable demand PTE) - an invalid PTE, describing the options, which are not in the working set, but the treatment she should be on either list zeroed pages, or from the list of free pages, reset and added to the working set. Described similar unloaded PTE except that PageFileHigh = 0.
  • Prototype PTE (prototype PTE) - invalid PTE, which describe the page, shared by several processes, such as projected in the memory files. More precisely, such PTE is a single instance and are not included in the lists of PDE, and PDE in the lists of the process are the following invalid PTE, referring to the prototype PTE, respectively, of their version for systems without PAE and PAE:
          typedef struct _MMPTE_PROTOTYPE
             ULONG Valid : 1;
             ULONG ProtoAddressLow : 7;
             ULONG ReadOnly : 1;
             ULONG WhichPool : 1;
             ULONG Prototype : 1;
             ULONG ProtoAddressHigh : 21;
         } MMPTE_PROTOTYPE;
         typedef struct _MMPTE_PROTOTYPE_PAE {
             ULONGLONG Valid : 1;
             ULONGLONG Unused0: 7;
             ULONGLONG ReadOnly : 1;
             ULONGLONG Unused1: 1;
             ULONGLONG Prototype : 1;
             ULONGLONG Protection : 5;
             ULONGLONG Unused: 16;
             ULONGLONG ProtoAddress: 32;
         } MMPTE_PROTOTYPE_PAE;

In this case: Valid = 0 ProtoAddress (ProtoAddressLow / ProtoAddressHigh) contain a reference to the prototype PTE, describing the shared page. Prototype = 1 Protection provides security attributes page (MM_ *) ReadOnly is set, if the page should be read only. Ignored when loading images in the space of a session - the loader is allowed to write in these pages in order to handle the import or placed relokov. WhichPool purpose of this field is unknown to me ..

  • Transition PTE (transitional PTE) - invalid PTE, describing the page that is listed Standby, Modified or ModifiedNoWrite pages (these lists, etc.). At the reference page is returned to the working set. Describe the following structures:
          typedef struct _MMPTE_TRANSITION {
             ULONG Valid : 1;
             ULONG Write : 1;
             ULONG Owner : 1;
             ULONG WriteThrough : 1;
             ULONG CacheDisable : 1;
             ULONG Protection : 5;
             ULONG Prototype : 1;
             ULONG Transition : 1;
             ULONG PageFrameNumber : 20;
         } MMPTE_TRANSITION;
         typedef struct _MMPTE_TRANSITION_PAE {
             ULONGLONG Valid : 1;
             ULONGLONG Write : 1;
             ULONGLONG Owner : 1;
             ULONGLONG WriteThrough : 1; 
             ULONGLONG CacheDisable : 1;
             ULONGLONG Protection : 5;
             ULONGLONG Prototype : 1;
             ULONGLONG Transition : 1;
             ULONGLONG PageFrameNumber : 24;
             ULONGLONG Unused : 28;
         } MMPTE_TRANSITION_PAE;
     In this case: Valid = 0 Prototype = 0 Transition = 1 Appointment of other fields like valid PTE 

II. Error handling pages

When the processor encounters invalid PTE, an exception error pages (# PF, Page Fault). In Windows, the handler calls _KiTrap0E MmAccessFault () to handle exceptions, which, after a certain number of checks is MiDispatchFault, if the page should be resolved successfully.

MiDispatchFault is one of the following options to resolve the error page:

  • MiResolveProtoPteFault called when an error page on PTE c flag Prototype = 1 She explores the prototype PTE, which indicates the failed PTE and: 1. If the prototype PTE has a flag too Prototype, then it's projected in the shared memory page file. Called MiResolveMappedFileFault. 2. If the prototype PTE has a flag Transition, it means that this transition PTE, his page is in the list of modified or idle pages. Popal is there as a result of truncation of the working set. Called MiResolveTransitionFault. 3. If the prototype PTE Transition == 0 & & Prototype == 0 & & PageFileHigh == 0, it is demand-zero PTE. Called MiResolveDemandZeroFault. 4. If the prototype PTE Transition == 0 & & Prototype == 0 & & PageFileHigh! = 0, then the page is swapped out to the swap. Called MiResolvePageFileFault.
  • MiResolveTransitionFault вызывается, когда сбойный PTE имеет флаг Transition=1, либо если он указывает на прототипный PTE, имеющий флаг Transition. Поскольку страницы в этом состоянии оказываются в результате усечения рабочего набора или других обстоятельств, когда понадобились физические страницы, то разрешение такой ошибки страницы должно заключаться в возвращении страницы в рабочий набор. MiResolveTransitionFault called when the failed PTE has a flag Transition = 1, or if it points to a prototype PTE, having the flag of Transition. Since the pages in this condition are a result of truncation of the working set, or other circumstances, when needed physical pages, the resolution of this error page must be in the return page in the working set. Поскольку страница еще не выгружена на диск, то сделать это очень просто - нужно лишь записать валидный PTE на место недействительного. Например, в состояние Transition как раз и переводит страницы функция MmTrimAllSystemPagableMemory(0), но о ней подробнее далее в части статьи, посвященную подкачке. Since the page is not swapped out to disk, then make it very easy - just need to record a valid PTE in place of the invalid. For example, in the state Transition precisely translates page feature MmTrimAllSystemPagableMemory (0), but more about it later in part of the article devoted to pumping .
  • MiResolveDemandZeroFault вызван при обработке ошибки страницы, обнуляемой по требованию. MiResolveDemandZeroFault caused by errors when processing the page zeroing on demand. Если запрос был из пользовательского режима, тогда идет попытка выделения физической страницы из списка обнуленных страниц (о поддерживаемых списках физических страниц далее). If the request came from user mode, then is an attempt to allocate physical page from the list of zeroed pages (on the supported list of physical pages, etc.). Если это не удается, выделяется свободная страница и обнуляется. If this fails, the allocated free pages, and reset. При запросе из режима ядра обнуление не форсируется при выделении страницы из списка свободных страниц. At the request of the kernel-mode does not reset is forced when allocating a page from the list of free pages. Для обнуления используются зарезервированные системные PTE или гиперпространство. To reset use reserved system PTE or hyperspace.
  • MiResolvePageFileFault вызывается при обработке ошибки страницы, которая была выгружена в файл подкачки. MiResolvePageFileFault call processing error page, which was unloaded in the paging file. Инициируется операция чтения файла подкачки за счет возвращения статуса STATUS_ISSUE_PAGING_IO, страницы читаются из файла подкачки кластерами для снижения числа ошибок страниц. Когда MiDispatchFault получает статус STATUS_ISSUE_PAGING_IO, она выполняет операцию чтения страниц с помощью функции IoPageRead, которая производит создание обычного IRP для операции IRP_MJ_READ, но ставит в нем специальный флаг IRP_PAGING_IO. Read operation is initiated by the paging file from the return status STATUS_ISSUE_PAGING_IO, pages are read from the paging file clusters to reduce the number of page faults. When MiDispatchFault receives the status STATUS_ISSUE_PAGING_IO, it performs an operation of reading pages using the IoPageRead, which makes the establishment of normal operations for the IRP IRP_MJ_READ, but raises it a special flag IRP_PAGING_IO. Страница выбирается из списка свободных или обнуленных страниц. Page is selected from the list of free or zeroed page.
  • MiResolveMappedFileFault вызывается из MiResolveProtoPteFault когда Prototype==1 у прототипного PTE. MiResolveMappedFileFault called from MiResolveProtoPteFault when Prototype == 1 in the prototype PTE. Тогда PTE трактуется следующим образом (варианты без PAE и с PAE): Then PTE treated as follows (options without PAE and PAE):
          typedef struct _MMPTE_SUBSECTION {
             ULONG Valid : 1;
             ULONG SubsectionAddressLow : 4;
             ULONG Protection : 5;
             ULONG Prototype : 1;
             ULONG SubsectionAddressHigh : 20;
             ULONG WhichPool : 1;
         } MMPTE_SUBSECTION;
         typedef struct _MMPTE_SUBSECTION { 
             ULONGLONG Valid : 1; 
             ULONGLONG Unused0 : 4; 
             ULONGLONG Protection : 5; 
             ULONGLONG Prototype : 1; 
             ULONGLONG Unused1 : 21; 
             ULONGLONG SubsectionAddress : 32; 
         } MMPTE_SUBSECTION;
     It contains the address of the object SUBSECTION, supporting the projected file. For example, in SUBSECTION:: ControlArea-> FilePointer stored FILE_OBJECT file.

III. Management of physical memory Physical memory in the system is described by certain structures of a kernel mode. They are necessary to maintain a list of vacant and occupied the pages to meet allocation and other memory operations. To start with, what are the main parts of the kernel responsible for the description and allocation of physical memory system. The first structure, which we consider to be MmPhysicalMemoryDescriptor, having a description:

   typedef struct _PHYSICAL_MEMORY_RUN {
       PFN_NUMBER BasePage;
       PFN_NUMBER PageCount;
   } PHYSICAL_MEMORY_RUN, *PPHYSICAL_MEMORY_RUN;
   typedef struct _PHYSICAL_MEMORY_DESCRIPTOR {
       ULONG NumberOfRuns;
       PFN_NUMBER NumberOfPages;
       PHYSICAL_MEMORY_RUN Run[1];
   } PHYSICAL_MEMORY_DESCRIPTOR, *PPHYSICAL_MEMORY_DESCRIPTOR;
   PPHYSICAL_MEMORY_DESCRIPTOR MmPhysicalMemoryDescriptor;

Variable kernel MmPhysicalMemoryDescriptor describes all of the available and suitable for the use of physical memory in the system and initialized at boot time.

The kernel maintains lists of six pages (out of eight possible states), which run almost all the physical page, except that with the exception of those used by the memory manager.Lists of pages supported pointers u1.Flink and u2.Blink structure MMPFN (about her later). It lists:

  • ZeroedPageList - Список обнуленных страниц, которые можно выдавать по запросу из пользовательского кода. ZeroedPageList - The list of zeroed pages, which can be issued on request from the user code. В фоновом режиме работает поток MmZeroPageThread (в него переходит первичный поток KiSystemStartup после всей инициализации) и занимается обнулением свободных страниц с перемещением их в этот список. In the background thread is running MmZeroPageThread (it becomes the primary flow KiSystemStartup after all initialization) and is zeroed free pages with the movement of them in this list. При запросе страницы пользовательским кодом это наиболее приоритетный список, откуда может быть вынута страница. If you request a page the user code is the most priority list, from which can be taken out page.
  • FreePageList - список свободных страниц. FreePageList - list of free pages. Они могут быть переданы пользоваелю после обнуления (поток либо сигналит событие MmZeroingPageEvent, тогда страницу обнуляет поток обнуления страниц MmZeroPageThread, либо в некоторых исключительных случаях обнуляет самостоятельно - например при обработке #PF на PTE типа demand-zero. В этом случае передача страницы потоку обнуления повлечет за собой дополнительные потери времени), в запросах от пользователя это второй по приоритету список после ZeroedPageList. They can be sent after clearing polzovaelyu (stream or flashes event MmZeroingPageEvent, then reset the page flow page zeroing MmZeroPageThread, or in some exceptional cases resets itself - for example when processing # PF PTE type of demand-zero. In this case, the flow channel page zeroing would for an additional loss of time), in the user's request is the second priority list after ZeroedPageList.
  • StandbyPageList - список простаивающих страниц. StandbyPageList - a list of idle pages. Эти страницы раньше входили в рабочий набор (процесса или системы), но в последствии были удалены из него. This page was previously part of the working set (process or system), but later were removed from it. Страница не была изменена с последней записи на диск, PTE, ссылающийся на такую страницу, находится в переходном (transition) состоянии и страница может быть использована для удовлетворения запроса на выделение памяти, но после просмотра списков обнуленных и свободных страниц. Page has not been changed since the last recording on the disc, PTE, referring to a page that is in transition (transition) state and the page can be used to satisfy the request for memory allocation, but after viewing the list zeroed and free pages. ( В Windows 2003 есть 8 подсписков, поддерживающих простаивающие страницы, по приоритетам, они описаны в массиве MmStandbyPageListByPriority[]. В Windows XP и ниже список один) (In Windows 2003 there are 8 sublists supporting idle page, on the priorities, they are described in the array MmStandbyPageListByPriority []. In Windows XP and below the list one)
  • ModifiedPageList - список модифицированных страниц, они тоже раньше входили в рабочий набор, но в последствии были удалены из него в результате сокращения рабочих наборов по какой-либо причине. ModifiedPageList - list of modified pages, they too had entered into a working set, but later were removed from it by reducing the working sets, for any reason. Страницы были изменены с момента последней записи на диск и должны быть записаны в файл подкачки. Pages have changed since the last recording on the disc and must be written in the paging file. PTE все еще ссылается на страницу, но недействителен и находится в состоянии transition. PTE still refers to the page, but is not valid and is in transition.
  • ModifiedNoWritePageList - список модифицированных, но не записываемых страниц. ModifiedNoWritePageList - list of modified but not written pages. Аналогично предыдущему, но страница не должна быть записана на диск. As above, but the page should not be written to disk.
  • BadPageList - список страниц, которые были помечены менеджером памяти плохими по каким-либо причинам. BadPageList - a list of pages that have been marked bad memory manager for any reason. Они не должны быть использованы. They should not be used. Например, поток обнуления страниц временно помечает страницы плохими, когда ищет область из страниц, ждущих обнуления, чтобы они не были вдруг переданы какому-нибудь процессу по запросу выделения продолжительного региона памяти (MmAllocateContiguousMemory). For example, the flow of zeroing pages temporarily mark pages bad when looking area of the pages, waiting for reset, so they were suddenly transferred to some process on request allocation of an extended region of memory (MmAllocateContiguousMemory). PTE не должны ссылаться на такую страницу. PTE should not rely on this page.

Status pages, not a list:

  • ActiveAndValid - страница активная и действительная, не входит ни в один список. ActiveAndValid - page is active and real, not included in any list. Такие страницы являются частью рабочего набора или не входят ни в один рабочий набор и являются частью неподкачиваемой памяти системы. These pages are part of the working set or not belong to any one of the working set and are part of the nepodkachivaemoy memory. На них ссылаются действительные PTE. They refer to the actual PTE.
  • TransitionPage - временное состояние страницы на время ожидания операции ввода-вывода. TransitionPage - a temporary state pages on the waiting time I / O operations.

Pointers to lists of stores core variable MmPageLocationList [], the contents of which declared as follows:

   PMMPFNLIST MmPageLocationList[8] =
   {
       &MmZeroedPageListHead,
       &MmFreePageListHead,
       &MmStandbyPageListHead,
       &MmModifiedPageListHead
       &MmModifiedNoWritePageListHead,
       &MmBadPageListHead,
       NULL,
       NULL 
   };

There are two important flow, operating lists of pages - page zeroing and stream flow records of modified pages.

  • Поток обнуления страниц. Flow zeroing pages. В него KiSystemStartup переходит после инициализации всех компонентов системы и запуска менеджера сессий smss. It KiSystemStartup goes after initialization of all components of the system and the manager run the sessions smss. Он занимается тем, что в цикле ожидает события MmZeroingPageEvent. It deals with the fact that in a loop waiting for an event MmZeroingPageEvent. Когда оно наступает (а наступает оно при наличии в системе достаточного количества свободных страниц, чтобы поток обнуления мог их стереть), захватывается спин-блокировка базы данных фреймов (PFN Database), выделяется страница из списка свободных страниц, проецируется в гиперпространство и обнуляется, после чего включается в список обнуленных страниц и цикл повторяется. When it comes (and it occurs if the system as a sufficient number of free pages that the flow of zeroing could erase them), is captured by the spin-locking database frames (PFN Database), is allocated a page from the list of free pages, is projected into hyperspace and is reset after What is included in the list of zeroed pages, and the cycle repeats.
  • Поток записи модифицированных страниц. Stream recording the modified pages. После старта подсистемы управления памятью MmInitSystem() создает через PsCreateSystemThread поток MiModifiedPageWriter, который стартует второй вспомогательный поток MiMappedPageWriter, а сам переходит в MiModifiedPageWriterWorker. Основной функцией выгрузки страниц в своп-файл является MiGatherMappedPages, о выгрузке будет рассказано далее в следующей части статьи. After starting the memory management subsystem MmInitSystem () creates a stream PsCreateSystemThread MiModifiedPageWriter, which kicks off the second subsidiary stream MiMappedPageWriter, while he himself goes into MiModifiedPageWriterWorker. The main function of the discharge of pages to the swap file is MiGatherMappedPages, on unloading will be discussed further in the next section.

MmPfnDatabase. MmPfnDatabase - is an array of structures MMPFN, describing each physical page in the system.This is perhaps the second most important object, after arrays PDE / PTE, which support low-level memory operations. The lists of PFN stored information about a particular physical page. MMPFN schematically presented as follows (full obyavleie attached to your sources to the article, including other versions of the OS - Windows 2000, Windows 2003 Server):

   typedef struct _MMPFN {
       union {
           PFN_NUMBER Flink;             // Used if (u3.e1.PageLocation < ActiveAndValid)
           WSLE_NUMBER WsIndex;          // Used if (u3.e1.PageLocation == ActiveAndValid)
           PKEVENT Event;                // Used if (u3.e1.PageLocation == TransitionPage)
           NTSTATUS ReadStatus;          // Used if (u4.InPageError == 1) }
       u1;
       PMMPTE PteAddress;
       union {
           PFN_NUMBER Blink;             // Used if (u3.e1.PageLocation < ActiveAndValid)
           ULONG ShareCount;             // Used if (u3.e1.PageLocation >= ActiveAndValid)
           ULONG SecondaryColorFlink;    // Used if (u3.e1.PageLocation == FreePageList or == ZeroedPageList)
       } u2;
       union {
           struct _MMPFNENTRY {
               ULONG Modified : 1;
               ULONG ReadInProgress : 1;
               ULONG WriteInProgress : 1;
               ULONG PrototypePte: 1;
               ULONG PageColor : 3;
               ULONG ParityError : 1;
               ULONG PageLocation : 3;
               ULONG RemovalRequested : 1;
               ULONG CacheAttribute : 2;
               ULONG Rom : 1;
               ULONG LockCharged : 1;
               ULONG ReferenceCount : 16;
           } e1;
           struct {
               USHORT ShortFlags;
               USHORT ReferenceCount;
           } e2;
       } u3;
       MMPTE OriginalPte;
       union {
           ULONG EntireFrame;
           struct {
               ULONG PteFrame : 26;
               ULONG InPageError : 1;
               ULONG VerifierAllocation : 1;
               ULONG AweAllocation : 1;
               ULONG LockCharged : 1;
               ULONG KernelStack : 1;
               ULONG Reserved : 1;
           };
       } u4;
   } MMPFN, *PMMPFN;

Элементы u1.Flink / u2.Blink поддерживают связанность шести списков страниц, про которые говорилось выше, используются, когда u3.e1.PageLocation < ActiveAndValid. Если u3.e1.PageLocation >= ActiveAndValid, тогда второе объединение трактуется как u2.ShareCount и содержит счетчик числа пользователей - количество PTE, ссылающихся на эту страницу. Elements u1.Flink / u2.Blink maintain connectivity of six lists of pages, about which mentioned above, are used when u3.e1.PageLocation <ActiveAndValid. If u3.e1.PageLocation> = ActiveAndValid, then the second association is treated as u2.ShareCount and contains count the number of users - the number of PTE, referring to this page. Для страниц, содержащих массивы PTE, содержит число действительных PTE на странице. Если u3.e1.PageLocation == ActiveAndValid, u1 трактуется как u1.WsIndex - индекс страницы в рабочем наборе (или 0 если страница в неподкачиваемой области памяти). Если u3.e1.PageLocation == TransitionPage, u1 трактуется как u1.Event - адрес объекта "событие", на котором будет ожидать менеджер памяти для разрешения доступа на страницу. Если u4.InPageError == 1, то u1 трактуется как ReadStatus и содержит статус ошибки чтения. For pages that contain arrays of PTE, contains a number of valid PTE in the page. If u3.e1.PageLocation == ActiveAndValid, u1 is treated as u1.WsIndex - index page in working set (or 0 if the page in nepodkachivaemoy memory area). If u3. e1.PageLocation == TransitionPage, u1 is treated as u1.Event - address of the facility "event", which will expect the memory manager to allow access to the page. If u4.InPageError == 1 then u1 is treated as ReadStatus and contains the status of read errors .

ReferenceCount содержит счетчик ссылок действительных PTE на эту страницу или использования ее внутри менеджера памяти (например, во время записи страницы на диск, счетчик ссылок увеличивается на единицу). ReferenceCount contains a reference count of the valid PTE on this page, or use it within the memory manager (for example, during the page write to the disk, the reference count is incremented). Он всегда >= ShareCount PteAddress содержит обратную ссылку на PTE, который указывает на эту физическую cтраницу. He is always> = ShareCount PteAddress contains a back link to the PTE, which indicates that the physical pages. Младший бит означает, что PFN удаляется. OriginalPte содержит оригинальный PTE, используемый для восстановления его в случае выгрузки страницы. u4.PteFrame - номер PTE, поддерживающего страницу, где находится текущая структура MMPFN. Кроме того объединение u4 содержит еще и следующие дополнительные флаги: Lower bits means that the PFN is removed. OriginalPte contains the original PTE, used to restore it to the unloading of pages. U4.PteFrame - Number PTE, supports a page where the current structure MMPFN. In addition the union has yet u4 and the following additional flags:

  • InPageError - показывает, что при чтении страницы с диска произошла ошибка. InPageError - shows that when reading a page from disk error occurred. u1.ReadStatus хранит статус этой ошибки. u1.ReadStatus keeps the status of this error.
  • VerifierAllocation устанавливается в единицу для аллокаций, защищаемых Driver Verifier. VerifierAllocation installed in the unit for allocation protected by Driver Verifier.
  • AweAllocation устанавливается в единицу для Address Windowing Extension AweAllocation installed in the unit for Address Windowing Extension
  • Назначение поля LockCharged и одноименного поля MMPFNENTRY мне, к сожалению, не известно. Appointment LockCharged fields and fields of the same name MMPFNENTRY me, unfortunately, is not known. Если кто знает - поделитесь. If anyone knows - share.
  • KernelStack, видимо, устанавливается в единицу для страниц, принадлежащих стеку ядра. KernelStack, apparently, is installed in the unit for the pages that belong to the kernel stack.

Если страница находится в списке обнуленных или простаивающих страниц, второе объединение трактуется как указатель, связывающий списки обнуленных или свободных страниц по вторичному цвету (т.н. Secondary Color). If the page is in the list of zero or idle pages, the second association is treated as a pointer linking the lists of zero or free pages for the secondary color (the so-called Secondary Color). Различие по цвету делается по следующей причине: количество цветов устанавливается в количество страниц, которые может вместить в себя кеш-память второго уровня процессора и различие делается, чтобы два соседних выделения памяти не использовали страницы одного цвета для правильного использования кеша. The difference in color is made for the following reason: the number of colors is set to the number of pages that can contain a cache memory of the second level of the processor and the difference is that the two neighboring memory allocation is not used the pages of one color for the correct use of cache.

Объединение u3, фактически, содержит флаги данного PFN. Combining u3, in fact, contains the flags of the PFN. Рассмотрим что же они означают: Consider what they mean:

  • Modified. Modified. Установлен для подкачиваемых или спроецированных с диска страниц, что ее содержимое было изменено и должно быть сброшено на диск. Set for booster or projected from the disk pages that its contents had been changed and must be flushed to disk.
  • ReadInProgress, он же StartOfAllocation ReadInProgress, he's StartOfAllocation
  • WriteInProgress, он же EndOfAllocation WriteInProgress, he's EndOfAllocation
  • PrototypePte, он же LargeSessionAllocation Для неподкачиваемых системных адресов эти три поля трактуется как StartOfAllocation, EndOfAllocation и LargeSessionAllocation и обозначают следующее: PrototypePte, he's LargeSessionAllocation For nepodkachivaemyh system addresses these three fields are treated as StartOfAllocation, EndOfAllocation and LargeSessionAllocation and indicate the following:
    • StartOfAllocation установлено в 1, если эта страница является началом неподкачиваемого пула. StartOfAllocation set to 1, if the page is the start nepodkachivaemogo pool.
    • EndOfAllocation установлено в 1, если эта страница является концом неподкачиваемого пула. EndOfAllocation set to 1, if the page is the end nepodkachivaemogo pool.
    • LargeSessionAllocation установлено в 1 для больших аллокаций в пространстве сессии. LargeSessionAllocation set to 1 for large allocation in the space of the session.
     Для подкачиваемых адресов эти поля означают следующее: For booster addresses these fields are as follows:
    • ReadInProgress установлен, пока страница находится в процессе чтения с диска ReadInProgress set until the page is in the process of reading from disk
    • WriteInProgress установлен, пока страница записывается на диск WriteInProgress set until the page is written to disk
    • PrototypePte установлен, когда PTE, который ссылается на эту PFN, является прототипным. PrototypePte installed when PTE, which refers to this PFN, is a prototype.
  • PageColor, он же иногда называемый Primary Page Color, или цвет страницы. PageColor, it is sometimes called the Primary Page Color, or the color of the page. Используется на некоторых платформах для равномерного распределения списков страниц (аллокации вида MiRemoveAnyPage выдаются страницу каждый раз из другого списка другого цвета и несколько списков, поддерживающих, например, свободные страницы, расходуются равномерно). Used on some platforms to distribute lists of pages (allocation type MiRemoveAnyPage options are issued each time a different list of a different color and a few lists, supporting, for example, free pages, spread evenly). В x86 и x64 используется всего один цвет страниц и это поле всегда равно нулю. Не путать с Secondary Color, который используется для равномерного распределения страниц по кешу второго уровня и используется в функциях MiRemoveZeroPage, MiRemoveAnyPage и др. Кроме простых списков свободных и обнуленных страниц так же поддерживаются списки свободных и обнуленных страниц по цвету - MmFreePagesByColor[список][SecondaryColor], где _список_ - это ZeroedPageList или FreePageList. In the x86 and x64 uses only one color pages, and this field is always zero. Not to be confused with the Secondary Color, which is used to distribute pages of the second-level cache and used in functions MiRemoveZeroPage, MiRemoveAnyPage, etc. In addition to simple lists of free and zeroed pages so is supported by lists of free and zeroed pages in color - MmFreePagesByColor [list] [SecondaryColor], where _spisok_ - it ZeroedPageList or FreePageList. Списки поддерживаются вместе с общими списками свободных и обнуленных страниц, при обнаружении несоответствия генерируется синий экран PFN_LIST_CORRUPT. Lists are maintained together with the common lists of free and zeroed pages if it detects a mismatch generated a blue screen PFN_LIST_CORRUPT.
  • PageLocation - тип страницы (как раз один из восьми вышеперечисленных от ZeroedPageList до TransitionPage) PageLocation - the page type (just one of eight of the above ZeroedPageList to TransitionPage)
  • RemovalRequested - этим битом помечаются страницы, запрошенные к удалению. RemovalRequested - this bit marked pages requested for deletion. После уменьшения их счетчика ссылок до нуля, PTE станет недействительным переходным, а страница попадет в список плохих (BadPageList) After reduction, the reference count to zero, PTE will invalidate the transition, but the page will get a list of bad (BadPageList)
  • CacheAttribute - атрибут кеширования страницы. CacheAttribute - attribute cache page. MmNonCached или MmCached. MmNonCached or MmCached.
  • Rom - новшество WinXP: физическая страница доступна только для чтения. Rom - innovation WinXP: physical page is read-only.
  • ParityError - на странице произошла ошибка честности ParityError - on the page the error occurred honesty

Лучше усвоить написанное поможет пример, содержащийся в приложении к статье. Better written to help assimilate the example contained in the annex to the article. В примере драйвер, который показывает доступные Memory Runs и демонстрирует обращение с PDE/PTE/PFN. In the example driver, which shows the available Memory Runs and demonstrates the treatment of PDE / PTE / PFN. Код примера хорошо откомментирован и, с учетом материала статьи, не должен вызвать вопросов. Code examples are well otkommentirovan and, given the material article, should not cause problems.

IV. Managing virtual memory - paging file

Однако размещать все данные постоянно в физической памяти невыгодно - к каким-то данным обращения происходят редко, к каким-то часто, к тому иногда требуются объемы памяти большие, чем доступно физической памяти в системе. However, placing all data permanently in the physical memory is unprofitable - to some this treatment are rare, the kind often, sometimes to a memory space larger than the available physical memory in the system. Поэтому во всех современных ОС реализован механизм подкачки страниц. Therefore, in all modern operating system has a mechanism to paging. Называется он по-разному - выгрузка, подкачка, своп. He called differently - unloading, swap, swap. В Windows этот механизм представляет собой часть менеджера памяти, управляющего подкачкой, и максимально до 16 различных страничных файлов (paging files в терминологии Windows). In Windows, this mechanism is part of the memory manager that manages the swap, and a maximum of 16 different paging file (paging files in the terminology of Windows). В Windows есть подкачиваемая и неподкачиваемая память, соответственно, они могут и не могут быть выгружены на диск. In Windows there is pumping and nepodkachivaemaya memory, respectively, they can and can not be unloaded in the disc. Подкачиваемую память в ядре можно выделить из пула подкачиваемой памяти, неподкачиваемую - соответственно из пула неподкачиваемой (для небольших аллокаций). Memory is pumping in the nucleus can be distinguished from the pool of memory is pumping, nepodkachivaemuyu - respectively from the pool nepodkachivaemoy (for small allocation). В пользовательском режиме память обычно подкачиваемая, если только она не была заблокирована в рабочем наборе с помощью вызова VirtualLock. Страничные файлы в ядре Windows представлены переменной ядра MmPagingFile[MAX_PAGE_FILES] (максималное число страничных файлов, как можно было догадаться еще в самом начале по размеру поля номера страницы в страничном файле в 4 бита, составляет 16 штук). In user mode memory is pumping normally, unless it was blocked in the working set by calling VirtualLock. Paging file in the Windows kernel before the kernel variable MmPagingFile [MAX_PAGE_FILES] (maksimalnoe number of paging files, as you might guess at the very beginning by the size of the field page number in the page file to 4 bits, makes 16 pieces). Каждый страничный файл в этом массиве представлен указателем на структуру вида: Each page file in this array represented a pointer to the structure of the form:

    typedef struct _MMPAGING_FILE {
       PFN_NUMBER Size;
       PFN_NUMBER MaximumSize;
       PFN_NUMBER MinimumSize;
       PFN_NUMBER FreeSpace;
       PFN_NUMBER CurrentUsage;
       PFN_NUMBER PeakUsage;
       PFN_NUMBER Hint;
       PFN_NUMBER HighestPage;
       PVOID Entry[MM_PAGING_FILE_MDLS];
       PRTL_BITMAP Bitmap;
       PFILE_OBJECT File;
       UNICODE_STRING PageFileName;
       ULONG PageFileNumber;
       BOOLEAN Extended;
       BOOLEAN HintSetToZero;
   	BOOLEAN BootPartition;
   	HANDLE FileHandle;
   } MMPAGING_FILE, *PMMPAGING_FILE;
  • Size - the current size of the swap file (page)
  • MaximumSize - the maximum size of the swap file (page)
  • MinimumSize - minimum size of the swap file (page)
  • FreeSpace - the number of free pages
  • CurrentUsage - employment pages. Always true to the formula Size = FreeSpace + CurrentUsage +1 (the first page is not used)
  • PeakUsage - peak load on the paging file
  • Hint, HighestPage, HintSetToZero - [unknown purpose]
  • Entry - an array of two pointers to blocks MMMOD_WRITER_MDL_ENTRY, used flow records of modified pages.
  • Bitmap - bitmap RTL_BITMAP employment pages in the paging file.
  • File - object file, the file system used to read / write the paging file
  • PageFileName - the name of the paging file, for example, \?? \ C: \ pagefile.sys
  • PageFileNumber - number pagefile
  • Extended - the flag, presumably indicating, whether the paging file expanded ever since the inception
  • BootPartition - a flag indicating whether the paging file on the boot partition. If no paging file located on the boot partition, then during the BSoD crash dump will not be recorded.
  • FileHandle - Hendl paging file.

The annex to the article is an example of the withdrawal otkommentirovanny field structure MmPagingFile [0] working system.

Когда системе нужна страница, а свободных страниц осталось мало, происходит усечение рабочих наборов процессов (оно происходит и по другим причинам, это лишь одна из них). When the system needs a page and free pages, there are few, there is a truncation of the working sets of processes (it occurs for other reasons, it is only one of them). Допустим, что усечение рабочих наборов было инициировано функцией MmTrimAllSystemPagableMemory(0). Assume that truncate the working sets was initiated function MmTrimAllSystemPagableMemory (0). Во время усечения рабочих наборов, PTE страниц переводятся в состояние Transition, счетчик ссылок Pfn->u3.e2.ReferenceCount уменьшеается на 1 (это выполняет функция MiDecrementReferenceCount). During truncation working sets, PTE pages are translated into a state of Transition, the reference count Pfn-> u3.e2.ReferenceCount umensheaetsya 1 (it performs the function MiDecrementReferenceCount). Если счетчик ссылок достиг нуля, сами страницы заносятся в списки StandbyPageList или ModifiedPageList, в зависимости от Pfn->u3.e1.Modified. If the reference count reached zero, the actual pages are entered in the lists or StandbyPageList ModifiedPageList, depending on Pfn-> u3.e1.Modified. Страницы из списка StandbyPageList могут быть использованы сразу, как только потребуется - для этого достаточно лишь перевести PTE в состояние Paged-Out. Pages on your watchlist StandbyPageList can be used as soon as they need - it is enough just to translate PTE in the state Paged-Out. Страницы из списка ModifiedPageList должны быть сперва записаны потоком записи модифицированных страниц на диск, а уж после чего они переводятся в StandbyPageList и могут быть использованы (за выгрузку отвечает функция MiGatherPagefilePages()). Псевдокод снятия страницы из рабочего набора (сильно обрезанный код MiEliminateWorkingSetEntry и вызываемых из нее функций): Pages on your watchlist ModifiedPageList must first be recorded flow records of modified pages to disk, and only then they are transferred to StandbyPageList and can be used (for the discharge meets function MiGatherPagefilePages ()). Pseudocode remove pages from the working set (strongly cut code MiEliminateWorkingSetEntry and caused of its functions):

   TempPte = *PointerPte;
   PageFrameNumber = PointerPte->u.Hard.PageFrameNumber;
   if( Pfn->u3.e1.PrototypePte == 0)
   {
       //
       // Privacy page, to make transition.
       //
   	
       MI_ZERO_WSINDEX (Pfn);  // Pfn->u1.WsIndex = 0;
   	
       //
       // The following macro does this:
       //
       // TempPte.u.Soft.Valid = 0;
       // TempPte.u.Soft.Transition = 1;
       // TempPte.u.Soft.Prototype = 0;
       // TempPte.u.Trans.Protection = PROTECT;
       //
       
       MI_MAKE_VALID_PTE_TRANSITION (TempPte, 
                                     Pfn->OriginalPte.u.Soft.Protection);
   								  
       //
       // This call is actually replaces the current PTE on TempPte and clears the buffer
       // Translation lookaside
       //
       // ( *PointerPte = TempPte );
       //
       
       PreviousPte.u.Flush = KeFlushSingleTb(Wsle[WorkingSetIndex].u1.VirtualAddress,
                                             TRUE,
                                             (Wsle == MmSystemCacheWsle),
                                             &PointerPte->u.Hard,
                                             TempPte.u.Flush);
       //
       // Decrement counter use. If he was equal to zero, the page is translated into a transition state
       // And decremented the reference count.
       //
       
       // MiDecrementShareCount()
       Pfn->u2.ShareCount -= 1;
       
       if( Pfn->u2.ShareCount == 0 )
       {
           if( Pfn->u3.e1.PrototypePte == 1 )
           {
               // ... Additional processing of the prototype PTE ...
           }
           
           Pfn->u3.e1.PageLocation = TransitionPage;
           
           //
           // Decreases by 1 the reference count. If he, too, became equal to zero, move
           //  Page in the list of modified pages, or idle, or completely remove
           // (Placing in the list of bad pages), depending on MI_IS_PFN_DELETED () and RemovalRequested.
           //
           
           // MiDecrementReferenceCount()
           Pfn->u3.e2.ReferenceCount -= 1;
           
           if( Pfn->u3.e2.ReferenceCount == 0 )
           {
               if( MI_IS_PFN_DELETED(Pfn) )
               {
                   // PTE no longer refer to this page. Move it to the list of free or delete, if necessary.
                   
                   MiReleasePageFileSpace (Pfn->OriginalPte); MiReleasePageFileSpace (Pfn-> OriginalPte);
                   
                   if( Pfn->u3.e1.RemovalRequested == 1 )
                   {
                       // Page is marked for deletion. Move it to the list of bad pages. It will not be used,
                       // Until someone does not remove it from the list.
                       
                       MiInsertPageInList (MmPageLocationList[BadPageList],
                                           PageFrameNumber);
                   }
                   else
                   {
                       // Put the page in the list of free
                       MiInsertPageInList (MmPageLocationList[FreePageList],
                                           PageFrameNumber);
                   }
                   return;
               }
               
               if( Pfn->u3.e1.Modified == 1 ) if (Pfn-> u3.e1.Modified == 1)
               {
                   // Page modified. We insert in the list of modified pages,
                   // Modified page writer thread writes it to disk.
                   MiInsertPageList (MmPageLocationList[ModfifiedPageList], PageFrameIndex);
               }
               else
               {
                   if (Pfn->u3.e1.RemovalRequested == 1)
                   {
                       // Remove page, but leave its status as idle.
                       Pfn->u3.e1.Location = StandbyPageList;
                       
                       MiRestoreTransitionPte (PageFrameIndex);
                       MiInsertPageInList (MmPageLocationList[BadPageList],
                                           PageFrameNumber);
                       return;
                   }
                   
                   // Put the page in the list of idle pages.
                   if (!MmFrontOfList) { if (! MmFrontOfList) (
                       MiInsertPageInList (MmPageLocationList[StandbyPageList],
                                           PageFrameNumber);
                   } else {
                       MiInsertStandbyListAtFront (PageFrameNumber);
                   }
               }
           }
       }
   }

In the annex to the article there is a program with source code to demonstrate the truncation of working sets of user mode by calling SetProcessWorkingSetSize (hProcess, -1, -1).

In contrast, when the flow turns to a page that was removed from the working set is an error page. К страничным файлам относятся два типа PTE: Transition и Paged-Out. By the paging file are two types of PTE: Transition and Paged-Out. Если страница была удалена из рабочего набора, но еще не была записана на диск или ей вообще не нужно быть записанной на диск и она ЕЩЕ НАХОДИТСЯ в физической памяти (состояние Transition PTE), то вызывается MiResolveTransitionFault() и PTE просто переводится в состояние Valid с соответствующей корректировкой MMPFN и удалением страницы из списка простаивающих или модифицированных страниц. If the page has been deleted from the working set, but have not yet been written to disk, or it does not need to be stored on disk, and it is still in physical memory (state Transition PTE), it is called MiResolveTransitionFault () and PTE simply translated into a state of Valid appropriate adjustment MMPFN and removal of pages from the list of idle or modified pages. Если страница уже была записана на диск, либо ей не нужно было быть записанной на диск и ее уже использовали для каких-то других целей (состояние Paged-Out PTE), то вызывается MiResolvePageFileFault() и инициируется операция чтения страницы из файла подкачки со снятием соответствующего бита в битовой карте. Псевдокод разрешения Transition Fault (обрезанный код MiResolveTransitionFault): If the page has been written to disk, or it did not need to be stored on disk and it is already used for some other purpose (state Paged-Out PTE), it is called MiResolvePageFileFault () and the read operation is initiated by a page from the paging file to the withdrawal of corresponding bit in the bitmap. Pseudocode permission Transition Fault (trimmed code MiResolveTransitionFault):

   if( Pfn->u4.InPageError )
   {
       return Pfn->u1.ReadStatus;  // # PF on the page, reading has not been successful.
   }
   if (Pfn->u3.e1.ReadInProgress)
   {
       // Re-error page. Если снова у того же потока, If you are back at the same flow,
       // Then returns STATUS_MULTIPLE_FAULT_VIOLATION;
       // If the other - then forward to the completion of reading.
   }
   MiUnlinkPageFromList (Pfn);
   Pfn->u3.e2.ReferenceCount += 1;
   Pfn->u2.ShareCount += 1;
   Pfn->u3.e1.PageLocation = ActiveAndValid;
   MI_MAKE_TRANSITION_PTE_VALID (TempPte, PointerPte);
   MI_WRITE_VALID_PTR (PointerPte, TempPte);
   MiAddPageToWorkingSet (...);

Pseudo load page from disk (cut code MiResolvePageFileFault):

    TempPte = *PointerPte; TempPte = * PointerPte;
   // Prepare the parameters for reading 
   PageFileNumber = TempPte.u.Soft.PageFileLow; 
   StartingOffset.QuadPart = TempPte.u.Soft.PageFileHigh << PAGE_SHIFT;
   FilePointer = MmPagingFile[PageFileNumber]->File;
   // Check empty page 
   PageColor = (PFN_NUMBER)((MmSystemPageColor++) & MmSecondaryColorMask);
   PageFrameIndex = MiRemoveAnyPage( PageColor );
   // build MDL...
   // Adjust its records in the database pages 
   Pfn = MI_PFN_ELEMENT (PageFrameIndex);
   Pfn->u1.Event = &Event;
   Pfn->PteAddress = PointerPte;
   Pfn->OriginalPte = *PointerPte;
   Pfn->u3.e2.ReferenceCount += 1;
   Pfn->u2.ShareCount = 0;
   Pfn->u3.e1.ReadInProgress = 1;
   Pfn->u4.InPageError = 0; 
   if( !MI_IS_PAGE_TABLE_ARRESS(PointerPte) ) Pfn->u3.e1.PrototypePte = 1;
   Pfn->u4.PteFrame = MiGetPteAddress(PointerPte)->PageFrameNumber;
   // Temporarily transfer options in Transition condition at the time of reading 
   MI_MAKE_TRANSITION_PTE ( TempPte, ... ); 
   MI_WRITE_INVALID_PTE (PointerPte, TempPte);
   // Read the page. 
   Status = IoPageRead (FilePointer,
                        Mdl,
                        StartingOffset,
                        &Event,
                        &IoStatus);
   if( Status == STATUS_SUCCESS )
   {
       MI_MAKE_TRANSITION_PTE_VALID (TempPte, PointerPte);
       MI_WRITE_VALID_PTE (PointerPte, TempPte);
       MiAddValidPageToWorkingSet (...);
   }

Working set Working set by definition - is a set of resident pages of the process (the system). There are three types of working sets:

  • Рабочий набор процесса содержит резидентные страницы, принадлежащие процессу - код, данные процесса и все последующие аллокации пользовательского режима. Work process contains a set of resident pages that belong to the process - code, data process and all subsequent allocation to user-mode. Хранится в EPROCESS::Vm Stored in EPROCESS:: Vm
  • Рабочий набор системы содержит резидентные подкачиваемые страницы системы. Operating system contains a set of memory-resident page is pumping system. В него входят страницы подкачиваемого кода, данных ядра и драйверов устройств, системного кеша и пула подкачиваемой памяти. It includes page is pumping code, kernel data, and device drivers, system cache and memory pool is pumping. Указатель на него хранится в переменной ядра MmSystemCacheWs. Pointer to it stored in the variable kernel MmSystemCacheWs.
  • Рабочий набор сеанса содержит резидентные страницы сеанса, например, графической подсистемы Windows (win32k.sys). Work session contains a set of resident pages a session, for example, graphics subsystem Windows (win32k.sys). Указатель хранится в MmSessionSpace->Vm. The pointer is stored in MmSessionSpace-> Vm.

Когда системе нужны свободные страницы, инициируется операция усечения рабочих наборов - страницы отправляются в списки Standby или Modified, в зависимости от того, была ли запись в них, а PTE переводятся в состояние Transition. When the system needs to free the page, initiated by the truncation of working sets - Pages are sent to the lists of Standby or Modified, depending on whether or write to them, and PTE converted into a state of Transition. Когда страницы окончательно отбираются, то PTE переводятся в состояние Paged-Out (если это были страницы, выгружаемые в файл подкачки) или в Invalid, если это были страницы проецируемого файла. Когда процесс обращается к странице, то страница либо удаляется из списков Standy/Modified и становится ActiveAndValid, либо инициируется операция загрузки страницы с диска, если она была окончательно выгружена. When the page was finally selected, then transferred to a state of PTE Paged-Out (if this page were discharged in the swap file), or Invalid, if it were projected page file. When a process accesses a page, the page or removed from the list Standy / Modified becomes ActiveAndValid, or initiating the transaction page is loaded from disk, if it was completely unloaded. Если памяти достаточно, процессу позволяется расширить свой рабочий набор и даже превысить максимум для загрузки страницы, иначе для загрузки страницы выгружается какая-то другая, то есть новая страница замещает старую. Имеется системный поток управления рабочими наборами или т.н. If the memory is sufficient, the process is allowed to increase its working set, and even exceed the maximum for the page is loaded, otherwise the download page is unloaded some other, that is, the new page replaces the old one. There is a systematic flow of control or so-called working sets диспетчер баланса. Manager balance. Он ожидает на двух объектах KEVENT, первое из которых срабатывает по таймеру раз в секунду, а второе срабатывает, когда нужно изменить рабочие наборы. He expects the two sites KEVENT, the first of which is triggered by a timer once per second, while the second is triggered when you need to change the working sets. Диспетчер настройки баланса так же проверяет ассоциативные списки, регулируя из глубину для оптимальной производительности. Configuration Manager also checks the balance of associative lists, adjusting depth of for optimum performance.

V. V. Ядерные функции управления памятью В этой части речь пойдет о некоторых полезных функциях управления памятью в режиме ядра. Слои функции управления памятью ядра можно разделить следующим образом от низшего уровня к высшему: Nuclear functions of memory management in this part will discuss some useful functions of memory management in kernel mode. Layers of kernel memory management functions can be divided as follows from the lowest to the highest level:

  • макросы MI_WRITE_VALID_PTE/MI_WRITE_INVALID_PTE macros MI_WRITE_VALID_PTE / MI_WRITE_INVALID_PTE
  • низкоуровневые функции MiResolve..Fault, MiDeletePte и другие функции работы с PDE/PTE, а так же функции работы с MMPFN и списками страниц - MiRemovePageByColor, MiRemoveAnyPage, MiRemoveZeroPage. low-level functions MiResolve .. Fault, MiDeletePte and other functions work with PDE / PTE, as well as the functions work with MMPFN and lists of pages - MiRemovePageByColor, MiRemoveAnyPage, MiRemoveZeroPage.
  • функции, предоставляемые драйверам для работы с физической памятью: MmAllocatePagesForMdl, MmFreePagesFromMdl, MmAllocateContiguousMemory. features provided drivers to work with physical memory: MmAllocatePagesForMdl, MmFreePagesFromMdl, MmAllocateContiguousMemory.
  • функции, предоставляемые драйверам для работы с пулом: ExAllocatePoolWith..., ExFreePoolWith..., MmAllocateContiguousMemory (относится и к предыдущему слою и к этому) features provided drivers for use with a pool: ExAllocatePoolWith ..., ExFreePoolWith ..., MmAllocateContiguousMemory (applies to the previous layer and so)

Для пользовательской памяти дело обстоит немного по-другому: For the user memory is the case a little differently:

  • макросы MI_WRITE_VALID_PTE/MI_WRITE_INVALID_PTE macros MI_WRITE_VALID_PTE / MI_WRITE_INVALID_PTE
  • функции работы с VAD и пользовательской памятью - MiAllocateVad, MiCheckForConflictingVad, и др. function with VAD and user memory - MiAllocateVad, MiCheckForConflictingVad, etc.
  • функции работы с виртуальной памятью - NtAllocateVirtualMemory, NtFreeVirtualMemory, NtProtectVirtualMemory. function of the virtual memory - NtAllocateVirtualMemory, NtFreeVirtualMemory, NtProtectVirtualMemory.

Описывать начнем их от низшего уровня к высшему, сначала для управления памятью ядра, затем пользовательской памятью. Describe them start from the lowest to the highest level, first for the memory management kernel, then the user memory.

Memory management kernel mode

1. 1. макросы MI_WRITE_VALID_PTE/MI_WRITE_INVALID_PTE Эти макросы используются во всех функциях, которые как-либо затрагивают выделение или освобождение физической (в конечном итоге) памяти. macros MI_WRITE_VALID_PTE / MI_WRITE_INVALID_PTE These macros are used in all functions, which in any way affect the allocation or release of physical (eventually) memory. Соответственно, они записывают действительный и недействительный PTE в таблицу страниц процесса. Accordingly, they write a valid and invalid PTE in the page table of the process.

2. 2. Низкоуровневые функции для работы с PDE/PTE и списками физических страниц Все это, вообщем то, я уже описывал, когда рассказывал про списки страниц, MMPFN и другое, поэтому приведу лишь прототипы функций с кратким описанием их действий: PFN_NUMBER FASTCALL MiRemoveAnyPage(IN ULONG PageColor) ; // Выделяет физическую страницу заданного цвета (SecondaryColor) из списков свободных, обнуленных или простаивающих страниц. PFN_NUMBER FASTCALL MiRemoveZeroPage(IN ULONG PageColor) ; // Выделяет физическую страницу заданного цвета (SecondaryColor) из списка свободных страниц. VOID MiRemovePageByColor (IN PFN_NUMBER Page, IN ULONG Color); // Выделяет указанную страницу, удаляя ее из списка свободных страниц указанного цвета. Low-level functions to work with PDE / PTE and lists the physical pages are all it's what I have described, when told about the listings pages, MMPFN and more, so bring only the function prototypes with brief descriptions of their actions: PFN_NUMBER FASTCALL MiRemoveAnyPage (IN ULONG PageColor ) / / Selects a physical page of a given color (SecondaryColor) from the lists of free, zero or idle pages. PFN_NUMBER FASTCALL MiRemoveZeroPage (IN ULONG PageColor); / / Selects a physical page of a given color (SecondaryColor) from the list of free pages. VOID MiRemovePageByColor (IN PFN_NUMBER Page, IN ULONG Color); / / Selects the specified web page, removing it from the list of free pages of this color.

3. 3. Функции, предоставляемые драйверам для работы с физической памятью PMDL MmAllocatePagesForMdl( IN PHYSICAL_ADDRESS LowAddress, IN PHYSICAL_ADDRESS HighAddress, IN PHYSICAL_ADDRESS SkipBytes, IN SIZE_T TotalBytes ); Эта функция выделяет физические страницы (не обязательно идущие подряд, как это делает, например, MmAllocateContiguousMemory), пробуя выделить страницы общим размером TotalBytes, начиная с физического адреса LowAddress и заканчивая HighAddress, "перешагивая" по SkipBytes. Просматриваются списки обнуленных, затем свободных страниц. The functions performed by drivers to work with the physical memory PMDL MmAllocatePagesForMdl (IN PHYSICAL_ADDRESS LowAddress, IN PHYSICAL_ADDRESS HighAddress, IN PHYSICAL_ADDRESS SkipBytes, IN SIZE_T TotalBytes); This function allocates physical pages (not necessarily consecutive, as does, for example, MmAllocateContiguousMemory), trying to provide a common page size TotalBytes, starting with the physical address LowAddress and ending HighAddress, "stepping" on SkipBytes. view lists zeroed, then the free pages. Разумеется, страницы неподкачиваемые. Certainly, Pages nepodkachivaemye. Если страниц не хватает, функция старается выделить столько страниц, сколько возможно. Возвращаемое значение - Memory Descriptor List (MDL), описывающий выделенные страницы. If pages are missing, the function tries to allocate as many pages as possible. Return value - Memory Descriptor List (MDL), describing the selected pages. Они должны быть освобождены соответствующим вызовом MmFreePagesFromMdl и ExFreePool для структуры MDL. Страницы НЕ спроецированы ни на какие виртуальные адреса, об этом должен позаботиться программист с помощью вызова MmMapLockedPages. They should be released and the corresponding call MmFreePagesFromMdl ExFreePool for the structure of MDL. The pages are not projected to any virtual address, this should take care of the programmer by calling MmMapLockedPages.

PVOID MmAllocateContiguousMemory( IN ULONG NumberOfBytes, IN PHYSICAL_ADDRESS HighestAcceptableAddress ); Функция выделяет физически непрерывную область физических страниц общим размером NumberOfBytes, не выше HighestAcceptableAddress, так же проецируя их в адресное пространство ядра. Сначала она пытается выделить страницы из неподкачиваемого пула, если его не хватает, она начинает просматривать списки свободных и обнуленных страниц, если и их не хватает, то она просматривает страницы из списка простаивающих страниц. Возвращает базовый адрес выделенного участка памяти. Память должна быть освобождена с помощью вызов MmFreeContiguousMemory.

4. 4. функции, предоставляемые драйверам для работы с пулом Они подробно описаны в статье Four-F, поэтому останавливаться на этом я не буду.

Дополнительные функции управления памятью режима ядра Из функций контроля ядерной памяти следует, наверное, упомянуть про MmIsAddressValid и MmIsNonPagedSystemAddressValid. Функция MmIsAddressValid проверяет страницу памяти на то, возникнет ли ошибка страницы при доступе к этому адресу. То есть, другими словами, она проверяет тот факт, что страница уже сейчас находится в физической памяти. Следует отметить, что состояние transition,paged-out,prototype ее не волнуют, поэтому она может использоваться лишь для контроля адресов при высоких IRQL (>=DPC/Dispatch), поскольку при этих IRQL не разрешены ошибки страниц (а если встретится ошибка страницы, будет выброшен синий экран IRQL_NOT_LESS_OR_EQUAL). Если нужно проверять ядерные адреса на доступ при низком IRQL, то, насколько мне известно, нет документированных способов это сделать. Видимо, считается, что драйвер должен знать, какие у него адреса правильные, а какие нет и не пробовать обращаться по неправильным адресам. В приложении к статье имеется написанная мной функция MmIsAddressValidEx, которая проверяет адрес на корректность доступа при низком IRQL, учитывая, что PTE может находиться в недействительном состоянии, но ошибка страницы не вызовет синего экрана или исключения (в программном смысле). С учетом рассказанных мною структур недействительных PTE, разобраться в ее исходном коде будет нетрудно. Функция MmIsNonPagedSystemAddressValid, почему-то незаслуженно "выброшенная" разработчиками Windows и обозначенная как obsolete, на самом деле тоже полезна. Она на порядок проще, чем MmIsAddressValid (которую, кстати, и рекомендует использовать Microsoft), и всего лишь проверяет то, что переданный ей адрес принадлежит подкачиваемой или неподкачиваемой областям памяти ядра. Адрес не проверяется на корректность, но результат функции вовсе не эквивалентен MmIsAddressValid (в том смысле, что память может быть в пуле подкачиваемой памяти, но может быть как выгружена на диск так и загружена, поэтому возвращенное значение FALSE еще ничего не говорит о том, можно ли обращаться к этой памяти), поэтому я совершенно не понимаю, почему Microsoft сочли ее "obsolete" и не рекомендуют использовать, подсовывая взамен MmIsAddressValid. Использовать MmIsNonPagedSystemAddressValid мы будем, например, в функции вывода MMPFN в приложении, когда потребуется определить, принадлежит ли адрес подкачиваемому пулу (поля MMPFN, как Вы помните, различаются для подкачиваемого и неподкачиваемого пулов).

Управление пользовательской памятью Для начала стоит заметить, что для управления пользовательской памятью используется дополнительный механизм - Virtual Address Descriptors (VAD), которые описывают проекции секций, а так же выделения памяти через NtAllocateVirtualMemory (VirtualAlloc в Win32 API). Представлены эти VAD в виде дерева, указатель на вершину содержится в поле EPROCESS->VadRoot. Секции можно создавать и проецировать на пользовательские адреса с помощью NtCreateSection, NtMapViewOfSection (Win32API-аналоги у них: CreateFileMapping, MapViewOfFileEx). Адреса памяти могут резервироваться (reserve) и в последствии память по этим адреса может передаваться (commit) процессу. Этим заведует NtAllocateVirtualMemory.

Функции работы с VAD и пользовательской памятью Элемент VAD представлен следующей структурой:

    typedef struct _MMVAD_FLAGS {
       ULONG_PTR CommitCharge : COMMIT_SIZE;
       ULONG_PTR PhysicalMapping : 1;
       ULONG_PTR ImageMap : 1;
       ULONG_PTR UserPhysicalPages : 1;
       ULONG_PTR NoChange : 1;
       ULONG_PTR WriteWatch : 1;
       ULONG_PTR Protection : 5;
       ULONG_PTR LargePages : 1;
       ULONG_PTR MemCommit: 1;
       ULONG_PTR PrivateMemory : 1;
   } MMVAD_FLAGS;
   typedef struct _MMVAD_SHORT {
       ULONG_PTR StartingVpn;
       ULONG_PTR EndingVpn;
       struct _MMVAD *Parent;
       struct _MMVAD *LeftChild;
       struct _MMVAD *RightChild;
       union {
           ULONG_PTR LongFlags;
           MMVAD_FLAGS VadFlags;
       } u;
   } MMVAD_SHORT, *PMMVAD_SHORT; 

Так же есть структура MMVAD, аналогичная MMVAD_SHORT, но содержащая больше полей и используемая для проецированных файлов (дополнительные поля это PCONTROL_AREA, необходимая для поддержания спроецированных файлов и содержащая такие важные указатели, как PFILE_OBJECT и др; о проекциях файлов, как-нибудь в следующий раз: и так уже 50 килобайт вышло =\), а MMVAD_SHORT используется для пользовательских выделений памяти. Чтобы отличить, какой именно VAD представлен указателем, используется флаг u.VadFlags.PrivateMemory: если он установлен, то это "частная память", то есть обычное выделение памяти. Если сброшен - проекция файла. Поля StartingVpn и EndingVpn, соответственно, обозначают начальную и конечную виртуальную страницу (Virtual Page Number) описываемой области (для конвертирования виртуального адреса в номер страницы используется MI_VA_TO_VPN, который просто сдвигает виртуальный адрес на PAGE_SHIFT бит вправо). Поля Parent, LeftChild, RightChild используются для связи дескрипторов виртуальных адресов в дерево. u.VadFlags содержит некоторые полезные флаги, а именно:

  • CommitCharge. Это поле содержит количество фактически выделенных и переданных страниц процессу, если VAD описывает переданную память, или 0 если описывается зарезервированная память.
  • PhysicalMapping. Этот флаг показывает, что память на самом деле является проекцией физических страниц, созданной с помощью MmMapLockedPages при AccessMode == UserMode.
  • Флаг ImageMap показывает, что VAD описывает загруженный исполняемый модуль (с помощью LoadLibrary и других, в конечном итоге сводящихся к NtCreateSection с SEC_IMAGE).
  • UserPhysicalPages устанавливается при вызове NtAllocateVirtualMemory с MEM_PHYSICAL|MEM_RESERVE, используемом для выделения окна для физических страниц при испольщовании AWE (Address Windowing Extensions).
  • NoChange установлен, когда запрещено менять атрибуты доступа области, описываемой этим VAD. Чтобы создать такую область, используется флаг SEC_NO_CHANGE у NtCreateSection.
  • WriteWatch устанавливается при выделении памяти с флагом MEM_WRITE_WATCH, при этом создается битовая карта страниц, где впоследствии отмечается, на какие страницы была произведена запись. Эту информацию можно получить в последствии через Win32 API GetWriteWatch() и сбросить карту через ResetWriteWatch()
  • Protection - изначальные атрибуты доступа к памяти.
  • LargePages содержит 1 при использовании больших страниц через MEM_LARGE_PAGES. В Windows XP/2000 не поддерживается.
  • MemCommit содержит 1 если память была передана процессу.
  • PrivateMemory, как уже было сказано, отличает MMVAD от MMVAD_SHORT.

Функция MiAllocateVad выделяет VAD для процесса, резервируя переданные ей адреса, а функция MiCheckForConflictingVad (на самом деле макрос, раскрывающийся в вызов функции MiCheckForConflictingNode) проверяет, существуют ли VAD у процесса такие, что описываемая ими область памяти перекрывается с указанными виртуальными адресами. Если это так, возвращается VAD первой конфликтной области, иначе NULL. Функция используется при передаче памяти процессу в NtAllocateVirtualMemory для поиска VAD, соответствующего указанному адресу. Функция MiInsertVad добавляет VAD в дерево виртуальных дескрипторов адресов процесса и реорганизует его, если это нужно. Функция MiRemoveVad, соответственно, удаляет и освобождает VAD. Перейдем теперь к функциям, доступным пользовательскому коду и драйверам устройств для управления памятью.

Функция NtAllocateVirtualMemory производит следующие действия: 1) для резервирования адресов вызывается MiCheckForConflictingVad для проверки, не была ли эта область или какая-либо ее часть зарезервированы или использованы другой функцией для работы с памятью (например, проецированием секции) ранее. Если так - возвращает STATUS_CONFLICTING_ADDRESSES. Далее выделяется VAD функцией MiAllocateVad, заполняются соответствующие поля и VAD добавляется в дерево с помощью MiInsertVad. Если он описывает AWE-вид или включен WriteWatch, тогда еще вызывается MiPhysicalViewInserter. 2) для передачи адресов вызывается MiCheckForConflictingVad, но уже с целью найти соответствующий VAD, созданный при резервировании. Потом соответствующие страницы выставляются в таблице страниц как обнуляемые по требованию, а так же меняются атрибуты защиты, если это необходимо. NtFreeVirtualMemory производит обратные действия.

На этом я думаю, наконец-то (!), что статью можно завершить.

В приложении к статье можно найти:

1. программу Working Sets для демонстрации усечения рабочих наборов. 2. Функции ручной загрузки и выгрузки страницы в файл подкачки. Примечание: очень сырые! Поскольку страница не добавляется в рабочий набор и не удаляется из него, может быть синий экран MEMORY_MANAGEMENT или PFN_LIST_CORRUPT (для выгрузки и загрузки соответственно), поэтому экспериментировать на реальной системе я не советую. Лучше запускать только изучающий и анализирующий код, который не изменяет никаких параметров системы. Это функции MiPageOut и MiLoadBack (префиксы Mi я сам добавил для красоты :)) 3. Функция вывода в DbgPrint содержимого MMPFN. Это MiPrintPfnForPage. 4. Функция MmIsAddressValidEx для расширенной проверки доступа к адресам при низком IRQL. Возвращает статус проверки - элемент перечисления

         enum VALIDITY_CHECK_STATUS {
         	VCS_INVALID = 0,     //  = 0 (FALSE)
         	VCS_VALID,           //-|
         	VCS_TRANSITION,      // |
         	VCS_PAGEDOUT,        // |-   > 0
         	VCS_DEMANDZERO,      // |
         	VCS_PROTOTYPE,       //-|
         }; ); 

Так же может трактоваться как BOOLEAN, поскольку статус невалидной страницы 0, а все остальные больше нуля. 5. Комплексный пример драйвера , демонстрирующий все эти функции (ручная загрузка и выгрузка закомментированы).