Microsoft MVP성태의 닷넷 이야기
디버깅 기술: 209. Windbg로 알아보는 Prototype PTE [링크 복사], [링크+제목 복사],
조회: 2476
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일

(시리즈 글이 8개 있습니다.)
디버깅 기술: 194. Windbg - x64 가상 주소를 물리 주소로 변환
; https://www.sysnet.pe.kr/2/0/13500

디버깅 기술: 203. Windbg - x64 가상 주소를 물리 주소로 변환 (페이지 크기가 2MB인 경우)
; https://www.sysnet.pe.kr/2/0/13836

디버깅 기술: 206. Windbg로 알아보는 PFN (_MMPFN)
; https://www.sysnet.pe.kr/2/0/13844

디버깅 기술: 207. Windbg로 알아보는 PTE (_MMPTE)
; https://www.sysnet.pe.kr/2/0/13845

디버깅 기술: 208. Windbg로 알아보는 Trans/Soft PTE와 2가지 Page Fault 유형
; https://www.sysnet.pe.kr/2/0/13846

디버깅 기술: 209. Windbg로 알아보는 Prototype PTE
; https://www.sysnet.pe.kr/2/0/13848

디버깅 기술: 210. Windbg - 논리(가상) 주소를 Segmentation을 거쳐 선형 주소로 변경
; https://www.sysnet.pe.kr/2/0/13849

디버깅 기술: 212. Windbg - (Ring 3 사용자 모드의) FS, GS Segment 레지스터
; https://www.sysnet.pe.kr/2/0/13852




Windbg로 알아보는 Prototype PTE

지금까지 PTE를 설명하면서 Hard, Soft, Trans 유형을 알아봤는데요,

Windbg로 알아보는 PTE (_MMPTE)
; https://www.sysnet.pe.kr/2/0/13845

Windbg로 알아보는 Trans/Soft PTE와 2가지 Page Fault 유형
; https://www.sysnet.pe.kr/2/0/13846

이번에는 Prototype PTE에 대해 다음의 글을 기반으로 베껴 정리해 보겠습니다. ^^

Prototype PTEs
; https://codemachine.com/articles/prototype_ptes.html




공유 메모리인 경우, 예를 들어 2개의 프로세스가 memory mapped file을 열어 같은 메모리를 바라보게 되면 각각의 프로세스에 있는 PTE는 동일한 PFN을 가리키게 됩니다.

[A Process PTE]              [PFN a9d33]                               
+------------------+         +-------------------------------+   
| Valid            |         |                               |   
| PFN = a9d33      | ----->  | Active Shared == 2            |   
+------------------+         |                               |   
                             |                               |
[B Process PTE]              |                               |
+------------------+         |                               |
| Valid            |         |                               |
| PFN = a9d33      | ----->  |                               |
+------------------+         +-------------------------------+

물론, 저렇게만 구성하고 있으면 문제가 발생할 수 있는 시나리오가 있습니다.

1. A 프로세스가 Working set trimmer에 의해 PTE 연결이 PFN과 끊긴 경우,
2. B 프로세스도 Working set trimmer에 의해 PTE 연결이 PFN과 끊긴 경우,
3. 이후 A 프로세스가 해당 메모리를 참조해 달라진 PFN이 로드되었다면,
4. B 프로세스는 이후 어떻게 그 달라진 PFN을 찾아서 참조해야 할까?

개인적인 생각으로는, 위와 같은 상황에서 윈도우 커널은 PFN을 참조하는 PTE에 대한 연결 리스트를 유지하고 있을 것이라고 생각했습니다. 하지만 ^^ 역시 머리 좋은 개발자들이 작성한 거라 전혀 다른 방식으로 해결하고 있었는데요, 이를 위해 "Prototype PTE"라는 것이 등장합니다.

방법 자체는 단순합니다. 윈도우 커널에서 공유 메모리는 Memory Manager 측에서 "Section Object"를 생성해 관리를 합니다. 이때 Section Object는 가상 메모리 페이징에 사용되는 Hard PTE 항목에 준하는 필드를 Paged Pool 영역에 할당하고 연결시켜 두는데요, 바로 그것을 가리켜 Prototype PTE라고 합니다. (다시 말해, 가상 메모리의 페이징에 사용되는 페이지 테이블과는 무관하게 커널이 임의로 만든 PTE라고 보면 됩니다.)

실제로 이 Prototype PTE가 하는 역할은 Hard PTE처럼 PFN을 가리키게 됩니다. 그래서, 공유 메모리를 사용하는 경우의 구성은 다음과 같이 이뤄집니다.

[A Process PTE]              [PFN a9d33]                               
+------------------+         +-------------------------------+   
| Valid            |         | PteAddress == (Prototype PTE) |   
| PFN = a9d33      | ----->  | Active Shared == 2            |   
+------------------+         |                               |   
                             |                               |
                             |                               |
[Prototype PTE]              |                               |
+------------------+         |                               |
| Valid            | ------> |                               |
| PFN = a9d33      | <------ |                               |
+------------------+         |                               |
                             |                               |
                             |                               |
[B Process PTE]              |                               |
+------------------+         |                               |
| Valid            |         |                               |
| PFN = a9d33      | ----->  |                               |
+------------------+         +-------------------------------+

게다가 PFN 측에서도 실제 페이징에 연관된 프로세스들의 PTE를 가리키지 않고 Memory Manager가 만든 Section Object의 Prototype PTE를 가리키게 됩니다.

자, 그럼 저 상태에서 A 프로세스의 PTE가 PFN과의 연결이 끊기면 이제 해당 PTE는 Prototype PTE를 가리키도록 바뀝니다.

[A Process PTE]              [PFN a9d33]                               
+-------------------------+  +-------------------------------+   
| Not Valid               |  | PteAddress == (Prototype PTE) |   
| Proto = (addr of proto) |  | Active Shared == 1            |   
+-------------------------+  |                               |   
                 |           |                               |
                 |           |                               |
                 |           |                               |
[Prototype PTE]  v           |                               |
+------------------+         |                               |
| Valid            | ------> |                               |
| PFN = a9d33      | <------ |                               |
+------------------+         |                               |
                             |                               |
                             |                               |   
                             |                               |
[B Process PTE]              |                               |
+------------------+         |                               |
| Valid            |         |                               |
| PFN = a9d33      | ----->  |                               |
+------------------+         +-------------------------------+

마찬가지로 B 프로세스의 PTE마저 PFN과의 연결이 끊기면 이제 PFN a9d33의 Shared == 0이 되므로 해당 PFN은 Standby 또는 Modified 연결 리스트로 반환되고 다른 프로세스에 의해 사용 가능한 상태가 됩니다.

이후, A 또는 B가 다시 공유 메모리를 참조하게 되면 Prototype PTE부터 PFN 연결을 하게 되고, 프로세스 측의 PTE는 Prototype PTE가 가리키는 PFN을 다시 바라보는 식으로 동작합니다. 오호~~~ 저것만으로도 아무런 문제 없이 공유 메모리가 유지되는군요. ^^

그런데, 위의 시나리오는 커널에서 공유 메모리를 생성해 사용하는 경우에 해당합니다. User Mode에서 공유 메모리를 사용하는 경우에는 좀 더 복잡한 구조가 나오는데요, 이유는 알 수 없지만 사용자 응용 프로그램의 경우에는 EPROCESS가 가지고 있는 VAD를 거쳐서 Prototype PTE를 참조하는 식으로 바뀝니다.

따라서, 위에서 "A Process PTE"가 끊긴 경우라고 설명하면서 그린 다이어그램은, 실제로는 A Process가 사용자 모드 프로그램이므로 다음과 같은 식으로 바뀝니다.

[A Process PTE]              [PFN a9d33]                               
+-------------------------+  +-------------------------------+   
| Not Valid               |  | PteAddress == (Prototype PTE) |   
| Status: Proto           |  | Active Shared == 1            |   
+-------------------------+  |                               |   
                             |                               |
                             |                               |
[A Process VAD]              |                               |
+-------------------------+  |                               |
| VAD                     |  |                               |
| Proto = (addr of proto) |  |                               |
+-------------------------+  |                               |
                 |           |                               |
                 |           |                               |
                 |           |                               |
[Prototype PTE]  v           |                               |
+------------------+         |                               |
| Valid            | ------> |                               |
| PFN = a9d33      | <------ |                               |
+------------------+         |                               |
                             |                               |
                             |                               |   
                             |                               |
[B Process PTE]              |                               |
+------------------+         |                               |
| Valid            |         |                               |
| PFN = a9d33      | ----->  |                               |
+------------------+         +-------------------------------+




자, 그럼 실제로 Windbg를 통해 확인해 볼까요? ^^

확인 방법은 1) 커널에서 생성한 공유 메모리의 Prototype PTE, 2) 사용자 모드에서 생성한 공유 메모리의 Prototype PTE를 확인하는 것으로 나뉘는데요, 아쉽게도 1번 방법은 Examining Prototype PTEs (Case #1) 내용을 참고하시고, 2번 방법에 대해서만 실습을 해보겠습니다.

이를 위해 공유 메모리를 사용하는 C# 프로그램을 하나 준비하고,

internal class Program
{
    [DllImport("kernel32.dll")]
    static extern bool SetProcessWorkingSetSize(IntPtr hProcess, nint dwMinimumWorkingSetSize, nint dwMaximumWorkingSetSize);

    static unsafe void Main(string[] args)
    {
        int pid = Environment.ProcessId;
        Console.WriteLine($"Process ID: {pid} (0x{pid:x})");
        long size = 1024 * 1024 * 20; // 20MB 크기

        {
            MemoryMappedFile? mmf = null;

            try
            {
                mmf = MemoryMappedFile.OpenExisting("my_map", MemoryMappedFileRights.ReadWrite);
            }
            catch (FileNotFoundException)
            {
            }

            if (mmf == null)
            {
                mmf = MemoryMappedFile.CreateFromFile("c:\\temp\\test.txt",
                    FileMode.OpenOrCreate, "my_map", size,
                    MemoryMappedFileAccess.ReadWrite);
            }

            if (mmf == null)
            {
                return;
            }

            using (MemoryMappedViewAccessor mmva = mmf.CreateViewAccessor(0, size, MemoryMappedFileAccess.ReadWrite))
            {
                for (long i = 0; i < size / 2; i++)
                {
                    char ch = (char)(0x30 + (i % (0x7e - 0x30)));
                    mmva.Write(i, ch);
                }

                IntPtr ptrAddress = GetBufferAddress(mmva);

                Console.WriteLine($"mm-virtual address: 0x{ptrAddress:x}");

                while (true)
                {
                    string? text = Console.ReadLine();
                    if (text == "q")
                    {
                        break;
                    }

                    if (text == "m")
                    {
                        SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
                    }
                }
            }
        }
    }

    private static unsafe IntPtr GetBufferAddress(MemoryMappedViewAccessor mmva)
    {
        SafeMemoryMappedViewHandle handle = mmva.SafeMemoryMappedViewHandle;

        byte* ptrAddress = null;
        handle.AcquirePointer(ref ptrAddress);

        return new nint(ptrAddress);
    }
}

두 번 프로세스를 실행하면 이런 결과를 얻게 됩니다.

// 같은 물리 메모리를 가리키는 서로 다른 프로세스의 가상 주소

Process ID: 12996 (0x32c4)
mm-virtual address: 0x20d05130000

Process ID: 106836 (0x1a154)
mm-virtual address: 0x1d483620000

이 상태에서 라이브 커널 디버깅을 windbg로 연결해 각각의 PTE를 확인해 봅니다.

7: kd> !process 0 0 mem_map.exe
PROCESS ffff868231f8f0c0
    SessionId: 2  Cid: 32c4    Peb: f5c9a45000  ParentCid: 2278
    DirBase: 1700f0000  ObjectTable: ffff978b10b52b00  HandleCount: 177.
    Image: mem_map.exe

PROCESS ffff868231a290c0
    SessionId: 2  Cid: 1a154    Peb: 942874c000  ParentCid: 2278
    DirBase: 07309000  ObjectTable: ffff978ae0044b00  HandleCount: 171.
    Image: mem_map.exe

// [생략: ffff868231f8f0c0 프로세스 문맥 전환]

4: kd> !pte 0x20d05130000
                                           VA 0000020d05130000
PXE at FFFFFA7D3E9F4020    PPE at FFFFFA7D3E8041A0    PDE at FFFFFA7D00834140    PTE at FFFFFA0106828980
contains 0A000001D520F867  contains 0A000001DF210867  contains 0A00000164E50867  contains C0000001CC012867
pfn 1d520f    ---DA--UWEV  pfn 1df210    ---DA--UWEV  pfn 164e50    ---DA--UWEV  pfn 1cc012    ---DA--UW-V


// [생략: ffff868231f8f0c0 프로세스 문맥 전환]

1: kd> !pte 0x1d483620000
                                           VA 000001d483620000
PXE at FFFFFA7D3E9F4018    PPE at FFFFFA7D3E803A90    PDE at FFFFFA7D007520D8    PTE at FFFFFA00EA41B100
contains 0A000001F3418867  contains 0A000001E1619867  contains 0A0000016B271867  contains C0000001CC012867
pfn 1f3418    ---DA--UWEV  pfn 1e1619    ---DA--UWEV  pfn 16b271    ---DA--UWEV  pfn 1cc012    ---DA--UW-V

보는 바와 같이 동일한 PFN 1cc012를 가리키고 있지만,

4: kd> !pfn 1cc012
    PFN 001CC012 at address FFFFC40005640360
    flink       00000001  blink / share count 00000002  pteaddress FFFF978AE23F4000
    reference count 0001    used entry count  0150      Cached    color 0   Priority 5
    restore pte 8682312ACD5004C0  containing page 04B81D  Active      P     
     Shared        

정작 PFN 측에서는 "Prototype PTE"를 가리키고, 이때의 Shared Count는 2라고 나옵니다. 해당 Prototype PTE는 페이징 테이블에 없어 !pte 명령의 출력 과정에는 없지만 직접 _MMPTE 구조체로 값을 덤프해 보면,

1: kd> dt _MMPTE FFFF978AE23F4000 u.Hard.
nt!_MMPTE
   +0x000 u       : 
      +0x000 Hard    : 
         +0x000 Valid   : 0y1
         +0x000 Dirty1  : 0y0
         +0x000 Owner   : 0y0
         +0x000 WriteThrough : 0y0
         +0x000 CacheDisable : 0y0
         +0x000 Accessed : 0y1
         +0x000 Dirty   : 0y0
         +0x000 LargePage : 0y0
         +0x000 Global  : 0y1
         +0x000 CopyOnWrite : 0y0
         +0x000 Unused  : 0y0
         +0x000 Write   : 0y1
         +0x000 PageFrameNumber : 0y0000000000000000000111001100000000010010 (0x1cc012)
         +0x000 ReservedForSoftware : 0y0000
         +0x000 WsleAge : 0y1010
         +0x000 WsleProtection : 0y000
         +0x000 NoExecute : 0y0

이렇게 Prototype PTE가 (마치 Hard PTE처럼) 공유 메모리에 해당하는 PFN을 가리키고 있음을 확인할 수 있습니다. 또한, "Prototype PTEs" 글에서 설명했듯이 Prototype PTE가 paged pool에 위치하고 있다는 것과 "MmSt" 태그를 달고 있다는 것도 볼 수 있습니다.

1: kd> !pool FFFF978AE23F4000
Pool page ffff978ae23f4000 region is Paged pool
*ffff978ae23f4000 : large page allocation, tag is MmSt, size is 0xa000 bytes
        Pooltag MmSt : Mm section object prototype ptes, Binary : nt!mm

자, 그럼 이 상태에서 ffff868231f8f0c0 프로세스의 working set을 줄이면, 해당 공유 메모리의 PFN을 참조하는 share count가 1로 줄어듭니다.

2: kd> !pfn 1cc012
    PFN 001CC012 at address FFFFC40005640360
    flink       00000001  blink / share count 00000001  pteaddress FFFF978AE23F4000
    reference count 0001    used entry count  0150      Cached    color 0   Priority 5
    restore pte 8682312ACD5004C0  containing page 04B81D  Active     MP     
    Modified Shared              

그리고 프로세스의 PTE를 확인해 보면,

// [생략: ffff868231f8f0c0 프로세스 문맥 전환]

2: kd> !pte 0x20d05130000
                                           VA 0000020d05130000
PXE at FFFFFA7D3E9F4020    PPE at FFFFFA7D3E8041A0    PDE at FFFFFA7D00834140    PTE at FFFFFA0106828980
contains 0A000001D520F867  contains 0A000001DF210867  contains 0000000164E50880
pfn 1d520f    ---DA--UWEV  pfn 1df210    ---DA--UWEV  contains 0000000164E50880
not valid
 Transition: 164e50
 Protect: 4 - ReadWrite

하필 ^^; PTE를 담고 있는 164e50 페이지까지 Working set trimmer에 의해 Not Valid 상태로 바뀌어 PTE 내용 자체가 출력이 안 됩니다. 그 와중에 다행인 점은, 해당 영역이 아직 메모리에는 그대로 남아 있어 PTE 주소를 직접 덤프해 "Prototype" 필드가 1로 바뀐 것까지는 확인이 됩니다.

2: kd> dt _MMPTE FFFFFA0106828980 u.Proto.
nt!_MMPTE
   +0x000 u        : 
      +0x000 Proto    : 
         +0x000 Valid    : 0y0
         +0x000 DemandFillProto : 0y0
         +0x000 HiberVerifyConverted : 0y0
         +0x000 ReadOnly : 0y0
         +0x000 SwizzleBit : 0y0
         +0x000 Protection : 0y00100 (0x4)
         +0x000 Prototype : 0y1
         +0x000 Combined : 0y0
         +0x000 Unused1  : 0y0000
         +0x000 ProtoAddress : 0y111111111111111111111111111111110000000000000000 (0xffffffff0000)

여기서 혼동하지 말아야 할 것이 있는데요, _MMPTE의 Proto 구조체가 Prototype PTE의 내부를 나타내는 것이 아니고, 해당 PTE 자체가 Prototype PTE의 도움을 받아야 한다는 것만을 알려주는 것에 불과하다는 점입니다.

그리고 위에서 ProtoAddress는 커널에서 생성한 공유 메모리라면 Prototype PTE의 주소를 가리키겠지만, 사용자 모드 프로그램의 경우에는 VAD를 거쳐서 Prototype PTE를 참조하게 되므로 아무런 값도 가지지 않습니다.

어쩌면 위의 경우는 운이 좋아 Trans 상태의 PTE를 본 것이었을 수도 있습니다. 만약 아예 페이징이 돼 아래와 같은 식으로 Soft PTE로 바뀌면 메모리에 대한 덤프도 불가능해집니다.

6: kd> !pte 0x20d05130000
                                           VA 0000020d05130000
PXE at FFFFFA7D3E9F4020    PPE at FFFFFA7D3E8041A0    PDE at FFFFFA7D00834140    PTE at FFFFFA0106828980
contains 0A000001D520F867  contains 0A000001DF210867  contains 0026941D00D02084
pfn 1d520f    ---DA--UWEV  pfn 1df210    ---DA--UWEV  contains 0026941D00D02084
not valid
 PageFile:  2
 Offset: 26941d
 Protect: 4 - ReadWrite

6: kd> dq FFFFFA0106828980 L1
fffffa01`06828980  ????????`????????

어쨌든 뭐 중요한 것은 프로세스 측의 PTE는 그냥 내려가도 상관이 없다는 것만 기억해 두시면 되겠습니다. ^^




그나저나 위의 실습까지 하셨다면, 혹시나 저 상황에서 어떻게 Prototype PTE를 찾을 수 있는지 궁금하지 않으신가요? ^^

왜냐하면 공유 메모리를 가리키던 프로세스의 PTE 자체에는 "Prototype : 0y1"이라는 정보를 제외하고는 "Prototype PTE"의 위치에 대해서는 아무런 정보도 없기 때문에 이상할 수 있습니다.

물론, 그래도 괜찮으니까 ^^ 그런 식으로 만들었을 텐데요, 다행히 User 모드 프로그램의 경우 사용자의 가상 주소가 Prototype PTE를 찾은 Key로서 동작하게 됩니다. 정말 그런지 windbg로 확인을 해볼 텐데요, 이를 위해 우선 대상 프로세스의 VAD 주소를 먼저 알아야 합니다.

2: kd> !process ffff868231f8f0c0 1
PROCESS ffff868231f8f0c0
    SessionId: 2  Cid: 32c4    Peb: f5c9a45000  ParentCid: 2278
    DirBase: 1700f0000  ObjectTable: ffff978b10b52b00  HandleCount: 192.
    Image: mem_map.exe
    VadRoot ffff868231956660 Vads 133 Clone 0 Private 13071. Modified 13874. Locked 2.
    ...[생략]...

그리고 그 VAD 내부에 공유 메모리를 가리키던 가상 주소에 해당하는 항목을 찾을 수 있고,

// mm-virtual address: 0x20d05130000

2: kd> !vad ffff868231956660
VAD             Level         Start             End              Commit
ffff868215b8bdb0  6           7ffe0           7ffe0               1 Private      READONLY           
ffff868215b8f230  5           7ffed           7ffed               1 Private      READONLY           
...[생략]...       
ffff8682190f1980  4        20d00400        20d005ff             176 Private      READWRITE          
ffff86821e4e0220  6        20d00600        20d03605           12289 Private      READWRITE          
ffff868227020440  5        20d03610        20d0361f               0 Mapped       READWRITE          Pagefile section, shared commit 0x2b3
ffff8682270206c0  2        20d03620        20d05121               0 Mapped       READONLY           \Windows\Globalization\ICU\icudtl.dat
ffff868226328f00  5        20d05130        20d0652f               0 Mapped       READWRITE          \temp\test.txt
...[생략]...       

0x20d05130000 주소에 해당하는 VAD 항목의 주소는 ffff868226328f00인데요,

ffff868226328f00  5        20d05130        20d0652f               0 Mapped       READWRITE          \temp\test.txt

해당 VAD를 덤프해 보면,

2: kd> dt _MMVAD ffff868226328f00
nt!_MMVAD
   +0x000 Core             : _MMVAD_SHORT
   +0x040 u2               : <unnamed-tag>
   +0x048 Subsection       : 0xffff8682`312acd50 _SUBSECTION
   +0x050 FirstPrototypePte : 0xffff978a`e23f4000 _MMPTE
   +0x058 LastContiguousPte : 0xffff978a`e23fdff8 _MMPTE
   +0x060 ViewLinks        : _LIST_ENTRY [ 0xffff8682`312accd8 - 0xffff8682`31969720 ]
   +0x070 VadsProcess      : 0xffff8682`31f8f0c1 _EPROCESS
   +0x078 u4               : <unnamed-tag>
   +0x080 FileObject       : (null) 

저렇게 FirstPrototypePte 필드로 Prototype PTE의 가상 주소를 찾을 수 있습니다. 결국, Page fault handler는 저런 식으로 Prototype PTE를 찾아 공유 메모리를 각각의 프로세스에 연결하는 작업을 수행하게 될 것입니다.




자, 그럼 이 상태에서 다른 프로세스 1개도 working set을 줄여볼까요? ^^ 그 상태가 되면 이제 공유 메모리로 할당돼 있던 PFN은 Standby (또는 Modified) 상태로 바뀌게 되고,

7: kd> !pfn 1cc012
    PFN 001CC012 at address FFFFC40005640360
    flink       001B5711  blink / share count 00052AC2  pteaddress FFFF978AE23F4000
    reference count 0000    used entry count  0150      Cached    color 0   Priority 5
    restore pte 8682312ACD5004C0  containing page 04B81D  Standby     P     
     Shared              

해당 프로세스의 PTE는 (이전 프로세스와는 달리) PTE 영역이 해제되지 않아 "Proto: VAD"라고 나오는 출력을 볼 수 있게 됐습니다.

7: kd> !pte 0x1d483620000
                                           VA 000001d483620000
PXE at FFFFFA7D3E9F4018    PPE at FFFFFA7D3E803A90    PDE at FFFFFA7D007520D8    PTE at FFFFFA00EA41B100
contains 0A000001F3418867  contains 0A000001E1619867  contains 0A0000016B271867  contains FFFFFFFF00000480
pfn 1f3418    ---DA--UWEV  pfn 1e1619    ---DA--UWEV  pfn 16b271    ---DA--UWEV  not valid
                                                                                  Proto: VAD
                                                                                  Protect: 4 - ReadWrite

그렇다고 해서 별다른 정보를 더 갖고 있는 것은 아니고, 그냥 "Prototype" 필드가 1을 담고 있다는 정도에 불과합니다.

7: kd> dt _MMPTE FFFFFA00EA41B100 u.Proto.
nt!_MMPTE
   +0x000 u        : 
      +0x000 Proto    : 
         +0x000 Valid    : 0y0
         +0x000 DemandFillProto : 0y0
         +0x000 HiberVerifyConverted : 0y0
         +0x000 ReadOnly : 0y0
         +0x000 SwizzleBit : 0y0
         +0x000 Protection : 0y00100 (0x4)
         +0x000 Prototype : 0y1
         +0x000 Combined : 0y0
         +0x000 Unused1  : 0y0000
         +0x000 ProtoAddress : 0y111111111111111111111111111111110000000000000000 (0xffffffff0000)

기타, Prototype PTE의 주소를 찾는 방법은 이전과 동일하게 VAD를 찾아서 FirstPrototypePte 필드를 찾아가 보는 실습을 반복하시면 됩니다.




가상 메모리 관리에서의 페이징 시스템은 커널 측에서 소프트웨어로도 구현을 해야 하지만, CPU도 이와 직접적으로 연동이 되는 구조입니다. 엄밀히 말하면, CPU 제조사가 규칙을 만들어 놓으면 운영체제 개발자들은 그에 맞춰서 소프트웨어를 구현하는 식이라고 봐야 합니다. (아마도 현실적으로는, 서로 협의를 통해 만들지 않을까 싶습니다.)

Prototype PTE가 재미있는 것은, CPU 제조사 입장에서 봤을 때 이것은 전혀 고려하지 않았던 기능이라고 봐야 할 것입니다. 즉, 순전히 소프트웨어만으로 운영의 묘미를 살린 사례인데요, 공유 메모리를 이런 식으로 구현했다는 발상 자체가 재미있는 것 같습니다. ^^ 제가 리눅스는 잘 몰라서 확신할 수는 없지만 아마도 리눅스의 경우에는 공유 메모리를 또 다른 방식으로 구현하지 않았을까... 예상해 봅니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 12/18/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13881정성태2/7/2025408닷넷: 2316. C# - Port I/O를 이용한 PCI Configuration Space 정보 열람파일 다운로드1
13880정성태2/5/2025439오류 유형: 947. sshd - Failed to start OpenSSH server daemon.
13879정성태2/5/2025503오류 유형: 946. Ubuntu - N: Updating from such a repository can't be done securely, and is therefore disabled by default.
13878정성태2/3/2025704오류 유형: 945. Windows - 최대 절전 모드 시 DRIVER_POWER_STATE_FAILURE 발생 (pacer.sys)
13877정성태1/25/20251023닷넷: 2315. C# - PCI 장치 열거 (레지스트리, SetupAPI)파일 다운로드1
13876정성태1/25/20251170닷넷: 2314. C# - ProcessStartInfo 타입의 Arguments와 ArgumentList파일 다운로드1
13875정성태1/24/20251162스크립트: 69. 파이썬 - multiprocessing 패키지의 spawn 모드로 동작하는 uvicorn의 workers
13874정성태1/24/20251132스크립트: 68. 파이썬 - multiprocessing Pool의 기본 프로세스 시작 모드(spawn, fork)
13873정성태1/23/20251059디버깅 기술: 217. WinDbg - PCI 장치 열거
13872정성태1/23/20251045오류 유형: 944. WinDbg - 원격 커널 디버깅이 연결은 되지만 Break (Ctrl + Break) 키를 눌러도 멈추지 않는 현상
13871정성태1/22/20251159Windows: 278. Windows - 윈도우를 다른 모니터 화면으로 이동시키는 단축키 (Window + Shift + 화살표)
13870정성태1/18/20251236개발 환경 구성: 741. WinDbg - 네트워크 커널 디버깅이 가능한 NIC 카드 지원 확대
13869정성태1/18/20251256개발 환경 구성: 740. WinDbg - _NT_SYMBOL_PATH 환경 변수에 설정한 경로로 심벌 파일을 다운로드하지 않는 경우
13868정성태1/17/20251196Windows: 277. Hyper-V - Windows 11 VM의 Enhanced Session 모드로 로그인을 할 수 없는 문제
13867정성태1/17/20251303오류 유형: 943. Hyper-V에 Windows 11 설치 시 "This PC doesn't currently meet Windows 11 system requirements" 오류
13866정성태1/16/20251309개발 환경 구성: 739. Windows 10부터 바뀐 device driver 서명 방법
13865정성태1/15/20251394오류 유형: 942. C# - .NET Framework 4.5.2 이하의 버전에서 HttpWebRequest로 https 호출 시 "System.Net.WebException" 예외 발생
13864정성태1/15/20251345Linux: 114. eBPF를 위해 필요한 SELinux 보안 정책
13863정성태1/14/20251285Linux: 113. Linux - 프로세스를 위한 전용 SELinux 보안 문맥 지정
13862정성태1/13/20251229Linux: 112. Linux - 데몬을 위한 SELinux 보안 정책 설정
13861정성태1/11/20251346Windows: 276. 명령행에서 원격 서비스를 동기/비동기로 시작/중지
13860정성태1/10/20251269디버깅 기술: 216. WinDbg - 2가지 유형의 식 평가 방법(MASM, C++)
13859정성태1/9/20251337디버깅 기술: 215. Windbg - syscall 이후 실행되는 KiSystemCall64 함수 및 SSDT 디버깅
13858정성태1/8/20251410개발 환경 구성: 738. PowerShell - 원격 호출 시 "powershell.exe"가 아닌 "pwsh.exe" 환경으로 명령어를 실행하는 방법
13857정성태1/7/20251820C/C++: 187. Golang - 콘솔 응용 프로그램을 Linux 데몬 서비스를 지원하도록 변경파일 다운로드1
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...