Maystyle :
Admin : New post
Guestbook
Local
media
Catergories
Recent Articles
Recent Comments
Recent Trackbacks
Calendar
Tag
Archive
Link
Search
 
해당되는 게시물 520건
  Windows Internals 다시보기 13 
작성일시 : 2010. 2. 19. 14:59 | 분류 : Windows Server/Kernel

* 아직은 많이 부족하기 때문에 제가 자신이 생길 때 까지 본 글은 제 블로그에 대한 링크만 허용합니다.

Thread 는 일을 하는 녀석입니다.

Thread 역시 Process 와 마찬가지로 쓰래드를 구성하고 있는 EThread 구조체 및 KThread, PEB 통해 그 내용을 확인 할 수 있습니다.  이 쓰래드에는 자신의 생성 및 소멸 시간, 프로세스의 ID 등의 정보 및 유저/커널 시간 CPU의 상태 레지스터 스케줄링 정보 및 다양한 대기열에 대한 정보와 유저 스택 및 커널 스택 정보로 이루어져 있습니다.

image

Windows Internals 에는 TEB 에 대한 설명이 빠져 있으나 위의 그림을 통해 우리는 대충 어떤 내용이 저장되어 있는지 확인 할 수 있습니다. 아래는 ETHRED 와 KTHRED의 각 블록 설명입니다.

image
image
image

위와 같이 Thread를 이루고 있는 블록들은 크게 ETHREAD 와 KTHRED 그리고 TEB로 이루어져 있습니다.

다만 이전의 운영체제와 다른 점은 User Level 쓰래드와 Kernel Level 쓰래드가 구분이 없다는 점입니다. 단순이 User Level에서 쓰래드가 실행 되다가 커널 레벨의 함수가 호출되어야 하는 경우 콜 게이트나 소프트웨어 인터럽트 등을 이용해서 단순하게 처리하게 됩니다.

아래의 예에서 보이는 것과 같이 유저 모드과 커널 모드가 동시에 존재 하는 것을 확인 할 수 있습니다. 이후 설명 드리겠지만 ChildEBP가 F로 시작하는 것은 커널 레벨임을 우리는 이전에 메모리 설명을 통해 알고 있습니다. Windows에서 말하는 커널 레벨 쓰래드는 디바이스 드라이버에 의해 생성된 쓰래드를 의미합니다.

kd> !thread
THREAD 81bddcb0 Cid 094c.0950 Teb: 7ffdf000 Win32Thread: e19a7970 RUNNING on processor 0
Owning Process 81e5db78 Image: notepad.exe
ChildEBP RetAddr Args to Child
f7f8ad4c 808234cb 0007fefc 00000000 00000000 win32k!NtUserGetMessage (FPO: [SEH])
f7f8ad4c 7c8285ec 0007fefc 00000000 00000000 nt!KiFastCallEntry+0xf8 (FPO: [0,0] TrapFrame @ f7f8ad64)
WARNING: Stack unwind information not available. Following frames may be wrong.
0007fed8 01002a3b 0007fefc 00000000 00000000 ntdll!KiFastSystemCallRet
0007ff1c 01007527 01000000 00000000 000a24b2 notepad!WinMain+0xe5 (FPO: [4,8,0])
0007ffc0 77e6f23b 00000000 00000000 7ffda000 notepad!WinMainCRTStartup+0x182 (FPO: [SEH])
0007fff0 00000000 010073a5 00000000 78746341 kernel32!ProcessIdToSessionId+0x209

아래와 같이 해당 notepad는 5개의 Thread가 있고, 이중 1번째인 THREAD 81bddcb0 가 CPU 0 번에서 동작하는 것을 확인할 수 있습니다.

THREAD 81bddcb0 Cid 094c.0950 Teb: 7ffdf000 Win32Thread: e19a7970 RUNNING on processor 0
THREAD 817a2db0 Cid 094c.0954 Teb: 7ffde000 Win32Thread: 00000000 WAIT: (Unknown) UserMode Alertable
81b22d50 SynchronizationEvent
81729f58 SynchronizationEvent
817a2e28 NotificationTimer
THREAD 81b6edb0 Cid 094c.0958 Teb: 7ffdd000 Win32Thread: 00000000 WAIT: (Unknown) UserMode Alertable
81b6ee28 NotificationTimer
THREAD 8174d3c8 Cid 094c.095c Teb: 7ffdc000 Win32Thread: e13bdbf0 WAIT: (Unknown) UserMode Non-Alertable
81b2e970 QueueObject
8174d440 NotificationTimer
THREAD 81b849e8 Cid 094c.0960 Teb: 7ffdb000 Win32Thread: e1628008 WAIT: (Unknown) UserMode Non-Alertable
81b2e970 QueueObject
81b84a60 NotificationTimer

다시 말해 일을 하는 녀석은 쓰래드입니다.
프로그램 내에서 어떠한 일을 한다 함은 바로 프로그래밍 된 함수가 동작한다 와 동일한 의미 입니다.

|
  Windows Internals 다시보기 12 
작성일시 : 2010. 2. 19. 14:54 | 분류 : Windows Server/Kernel

* 아직은 많이 부족하기 때문에 제가 자신이 생길 때 까지 본 글은 제 블로그에 대한 링크만 허용합니다.

객체의 생성과 종료

객체 매니저는 객체의 핸들 카운터와 참조 카운터를 통해 객체를 관리합니다.

아래 그림과 같이 커널에 의해 참조되는 객체의 경우 참조 카운터가 3이기 때문에 프로세스 A와 프로세스 B가 종료되더라도 해당 객체는 사라지지 않고 남게 됩니다. 하지만 두 번째 객체와 같이 프로세스 B에 의해서만 사용되는 객체의 경우 프로세스 B가 핸들이 반환하거나 종료되게 되면 해당 객체 역시 종료되게 됩니다.
대부분의 객체는 프로세스의 필요에 의해서 임시적으로 만들어지고 사라지지만, 일부 객체의 경우 OS 서비스를 위하여 필수적인 경우가 있습니다. 그리고 객체 매니저는 이런 참조 카운터를 이용하여 이러한 객체들을 효과적으로 관라할 수 있습니다.

image

|
  Windows Internals 다시보기 11 
작성일시 : 2010. 2. 19. 14:53 | 분류 : Windows Server/Kernel

* 아직은 많이 부족하기 때문에 제가 자신이 생길 때 까지 본 글은 제 블로그에 대한 링크만 허용합니다.

Object Method

마지막 구성요소는 표 3-12에 나온 것과 메소드입니다. 객체 메니저는 타입 객체에서 제공하는 메소드를 이용하여 생성 및 종료에서부터 권한 관리 등의 여러 가지 일을 할 수 있습니다. 물론 이 객체 관리자가 이 메소드들을 이용하여 객체를 관리하기 위해서는 모든 타입 객체의 메소드들의 디자인이 유사해야 합니다. 하지만 일부 ( Win32k.sys )의 경우 WindowsStation 과 Desktop 객체에 대한 메소드를 차제적으로 정의하고 있기도 합니다.

image

|
  Windows Internals 다시보기 10 
작성일시 : 2010. 2. 19. 14:51 | 분류 : Windows Server/Kernel

* 아직은 많이 부족하기 때문에 제가 자신이 생길 때 까지 본 글은 제 블로그에 대한 링크만 허용합니다.

Type 객체

동일한 종류의 객체를 위한 하나의 틀이라고 볼 수 있습니다. 이 Type 객체는 동일한 종류의 여러 객체들의 어트리뷰트 (내부 변수) 중 동일하게 가져가는 값들을 책임지고 있습니다. 예를 들어 프로세스 객체에 대하여 디버그 플래그를 설정한다면 아래 그림과 같이 다른 모든 프로세스 객체 역시 해당 플래그가 설정되게 되는 것이죠.

image

우리가 이전에 봤던 !process 커맨드를 통해 우리는 쉽게 프로세스 객체에 대해 들여다 볼 수 있습니다.

1. lkd> !process 0 0
2. **** NT ACTIVE PROCESS DUMP ****
3. PROCESS 860f1ab0 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
4. DirBase: 00122000 ObjectTable: 83000118 HandleCount: 484.
5. Image: System

일단 프로세스 객체의 주소를 확인했으면 !object를 실행 합니다.
1. lkd> !object 860f1ab0
2. Object: 860f1ab0 Type: (860f1ed0) Process
3. ObjectHeader: 860f1a98 (old version)
4. HandleCount: 4 PointerCount: 139

여러분도 알고 있는것과 같이 오브젝트의 해더는 오브젝트 바디 보다 0x18 비트 앞에 위치하고 있습니다.
자 우리는 아래 커멘드를 통해 해당 오브젝트의 해더를 확인 할 수 있습니다.
1. lkd> dt nt!_OBJECT_HEADER 860f1a98
2. +0x000 PointerCount : 139
3. +0x004 HandleCount : 4
4. +0x004 NextToFree : 0x00000004
5. +0x008 Type : 0x860f1ed0 _OBJECT_TYPE
6. +0x00c NameInfoOffset : 0 ''
7. +0x00d HandleInfoOffset : 0 '' 146
8. +0x00e QuotaInfoOffset : 0 ''
9. +0x00f Flags : 0x22 '"'
10. +0x010 ObjectCreateInfo : 0x82109380 _OBJECT_CREATE_INFORMATION
11. +0x010 QuotaBlockCharged : 0x82109380
12. +0x014 SecurityDescriptor : 0x83003482
13. +0x018 Body : _QUAD

마지막으로 Type 필드를 통해서 우리는 실제 Type 객체를 확인 할 수 있습니다.
1. lkd> dt nt!_OBJECT_TYPE 0x860f1ed0
2. +0x000 Mutex : _ERESOURCE
3. +0x038 TypeList : _LIST_ENTRY [ 0x860f1f08 - 0x860f1f08 ]
4. +0x040 Name : _UNICODE_STRING "Process"
5. +0x048 DefaultObject : (null)
6. +0x04c Index : 6
7. +0x050 TotalNumberOfObjects : 0x4f
8. +0x054 TotalNumberOfHandles : 0x12d
9. +0x058 HighWaterNumberOfObjects : 0x52
10. +0x05c HighWaterNumberOfHandles : 0x141
11. +0x060 TypeInfo : _OBJECT_TYPE_INITIALIZER
12. +0x0ac Key : 0x636f7250
13. +0x0b0 ObjectLocks : [32] _EX_PUSH_LOCK

이 정보를 통해 우리는 객체 타입의 이름이나 객체 및 핸들에 대한 현재 개수 및 피크치에서의 개수 등을 확인 할 수 있습니다. 그리고 TypeInfo 필드는 데이터 구조체에 대한 포인터를 저장하고 있습니다. 이 데이터 구조체에는 일반적으로 모든 객체에 공통적으로 가지고 있는 객체 타입이나 객체 타입의 메소드에 대한 포인터가 저장되어 있습니다.
1. lkd> dt nt!_OBJECT_TYPE_INITIALIZER 0x860f1ed0+60
2. +0x000 Length : 0x4c
3. +0x002 ObjectTypeFlags : 0xa ''
4. +0x002 CaseInsensitive : 0y0
5. +0x002 UnnamedObjectsOnly : 0y1
6. +0x002 UseDefaultObject : 0y0
7. +0x002 SecurityRequired : 0y1
8. +0x002 MaintainHandleCount : 0y0
9. +0x002 MaintainTypeList : 0y0
10. +0x004 ObjectTypeCode : 0
11. +0x008 InvalidAttributes : 0
12. +0x00c GenericMapping : _GENERIC_MAPPING
13. +0x01c ValidAccessMask : 0x1fffff
14. +0x020 PoolType : 0 ( NonPagedPool )
15. +0x024 DefaultPagedPoolCharge : 0x1000
16. +0x028 DefaultNonPagedPoolCharge : 0x2a0
17. +0x02c DumpProcedure : (null) 147
18. +0x030 OpenProcedure : 0x822137d3 long nt!PspProcessOpen+0
19. +0x034 CloseProcedure : 0x8221c3d4 void nt!PspProcessClose+0
20. +0x038 DeleteProcedure : 0x8221c1e2 void nt!PspProcessDelete+0
21. +0x03c ParseProcedure : (null)
22. +0x040 SecurityProcedure : 0x822502bb long nt!SeDefaultObjectMethod+0
23. +0x044 QueryNameProcedure : (null)
24. +0x048 OkayToCloseProcedure : (null)

image

|
  Windows Internals 다시보기 09 
작성일시 : 2010. 2. 19. 14:25 | 분류 : Windows Server/Kernel

* 아직은 많이 부족하기 때문에 제가 자신이 생길 때 까지 본 글은 제 블로그에 대한 링크만 허용합니다.

객체

메모리 메니저나 프로세스 메니저, I/O 메니저 등의 커널의 Excutive 에 해당하는 요소들과 윈도우 서브시스템의 경우 빠른 실행이 필요하기 때문에 커널 에서 실행 됩니다. 그리고 이러한 객체들은 객체 메니저 (Object Manager)를 통해 정의 되고 외부에 노출 되게 됩니다.

이 객체의 종류를 3가지로 나눈다면 커널의 실행부 객체와 커널 객체 그리고 GDI/User 객체로 나눌 수 있습니다. 실행부 객체는 실제 커널 실행부의 여러가지 요소로 이루어져 있습니다. 그리고 이 실행부 객체 중 윈도우 커널를 위해 서비스를 제공하는 객체들의 집합을 커널 오브젝트라고 부릅니다. 그리고 GDI/User 객체의 경우 커널과는 관계 없이 빠른 GUI 화면 처리를 위한 객체입니다.

image

이렇게 객체들은 실행부의 여러 부분들을 의미하며, 이중 일부가 커널 객체가 됩니다. 그리고 이 객체는 바로 객체 관리자를 통해서 이루어 지게 됩니다.

image

Windows 7의 경우 42개의 객체가 정의되어 있으며, 아래 그림은 윈도우 API를 통해 외부에 오픈되어 있는 실행부 객체에 대한 설명입니다.

image
image

저는 개인적으로 이 객체가 자바나 C#의 클래스와 비슷하다고 생각하는데, 실제 이 객체는 여러 변수로 이루어진 구조체와 해당 객체의 서비스에 따른 여러 함수들로 이루어져 일관성 있는 서비스 메커니즘을 제공하고 있습니다.

image

위의 그림에서와 같이 객체 관리자는 객체의 해더를 통해 객체에 대한 일괄적인 관리를 하게 되고 실제 실행부의 각 객체들은 이 객체의 바디 부분을 통해 실제 객체를 컨트롤 합니다.

그리고 객체 해더의 Type 객체에 대한 포인터는 특정 개체들에 대한 일반적인 정보들이 들어 있으며, 추가적으로 Object Subheader 를 통해 객체에 따라 특수한 설정들을 표현하기 하기도 하고 해당 특정 상황 (시스템 부팅 이나 객체 생성 등)에서의 객체의 특성을 표현하기도 합니다

image
image

그리고 이런 해더와 함께 객체 관리자는 모든 객체에 공통적으로 적용되는 서비스도 제공하고 있습니다.

image
image

아래와 같은 서비스를 통해 객체 관리자에게 요구되는 일반적인 관리 프로세스가 가능하게 됩니다.

|
  Windows Internals 다시보기 08 
작성일시 : 2010. 2. 18. 17:35 | 분류 : Windows Server/Kernel

* 아직은 많이 부족하기 때문에 제가 자신이 생길 때 까지 본 글은 제 블로그에 대한 링크만 허용합니다.

핸들

핸들은 프로세스에서 커널 객체를 조작하기 위해 사용하는 말 그대로 운전대 입니다.

쉬운 예로 파일을 조작한다면 프로세스는 객체 관리자로부터 파일을 조작하기 위해 해당 오브젝트에 대한 핸들을 넘겨 받아야 합니다. 다시 말해 Process 에서 파일 이나 레지스트리 세마포등의 자원을 이용하기 위해서는 해당 파일을 컨트롤 할 수 있는 API를 이용한다는 것은 핸들을 사용하는 것으로 표현 할 수 있습니다.. 예를 들어 파일을 오픈 하거나 만들기 위해서 CreateFile 함수를 사용하게 된다면 윈도우에서는 해당 객체들을 조정할 수 있도록 해당 프로세스의 핸들 테이블에 요청된 객체의 핸들을 추가해 줍니다. 실제로 프로세스 주소 공간 내에 해당 핸들 테이블이 있는 건 아닙니다. 실제로는 커널의 Paged Pool에 핸들 테이블이 저장되고, 프로세스에서는 해당 핸들에 대한 포인터만 가지고 있는 거죠.

(커널의 Paged Pool에 대해서는 이후에 설명하도록 하겠습니다.)

핸들은 객체에 대한 포인터 입니다.

image

이런 핸들은 각 프로세스당 최대 16,777,216 개 까지 할당 받을 수 있습니다. (이는 OS 상에 하드 코딩되어 있습니다.) 하지만 실제로 이렇게 많은 수를 오픈하지는 않습니다. 실제로는 10,000 개 이상의 핸들을 동시에 오픈하는 프로세스의 경우 개발자가 “개”발 한 것이거나 핸들릭이 있다고 볼 수 있습니다.

핸들 테이블의 entry에는 실제 해당 오브젝트에 대한 포인터와 권한 정보를 포함하고 있습니다. 권한에 대한 정보는 32Bit의 길이를 갖으나 포인터의 경우 32비트는 8Byte의 길이를 64비트의 경우 12Byte의 길이를 갖습니다. 하지만 실제로는 64비트 윈도우의 경우 실제로 64비트 주소공간을 모두 이용하기 위하여 16Byte (64bit) 길이의 오브젝터 포인터를 갖게 됩니다.

아래 그림은 32비트 운영제체의 핸들 엔트리 입니다. 위에서 설명한 것과 같이 이 8Byte 길이의 핸들 엔트리는 객체에 대한 포인터와 권한 부로 나누어 집니다. 그리고 제가 64비트의 경우 12Byte 라고 위에서 이야기했는데 이는 핸들에 대한 포인터는 2배로 (8Byte)로 늘어나지만 Access mask는 여전이 4Byte만 사용하기 대문에 12Byte 가 됩니다. 하지만 할당해서 사용하는 것은 16Byte를 사용하고 있습니다.

image

실행부가 제공 하는 핸들 테이블은 Page 즉 4K Byte의 크기를 가지고 있습니다. 그러므로 32 비트 운영체제에서는 8 Byte의 객체 포인터이기 때문에 512가 저장될 수 있고, 64비트 운영체제의 경우 256개가 저장될 수 있습니다.

다시 위에서 언급한 16,777,216 개의 핸들을 표현 하기 위해서는 단일 프로세스가 소유 할 수 있는 핸들 테이블은 32비트 운영체제의 경우 총 32,768 개 64비트 운영체제의 경우 총 65,536개가 됩니다.

그리고 마지막으로 이 핸들 테이블이 위치하는 커널의 Paged Pool은 16,777,216가 할당되게 될 경우 최대 32비트의 경우 16,777,216*8Byte 즉 128MB가 64비트의 경우 최대 16,777,216*16Byte즉 256MB 정도가 됩니다.

프로세스가 생성되게 되면 먼저 가장 낮은 레벨의 핸들 테이블이 생성 됩니다. 그리고 필요에 따라 추가적으로 레벨들이 생성이 됩니다. 한 테이블의 크기는 4K 이기 때문에 512개의 핸들 엔트리가 위치할 수 있습니다. 하지만 이중 1개의 엔트리는 감사를 위해서 사용되게 됩니다. 즉 최초에 511개의 핸들 테이블이 생성된 이 후 필요에 따라 16,777,216 개의 핸들을 표현하기 위한 테이블들이 필요할 경우 추가로 생성되게 됩니다. 이 핸들 테이블의 각 엔트리를 이용하는 방식은 Virtual to physical memory transaction과 비슷합니다.

image

핸들 살펴 보기

!handle 구문 : !handle < handle index> < flags> < processid>
The handle index identifies the handle entry in the handle table. (Zero means display all handles.) The first handle is index 4, the second 8, and so on. For example, typing !handle 4 will show the first handle for the current process. The flags you can specify are a bitmask, where bit 0 means display only the information in the handle entry, bit 1 means display free handles (not just used handles), and bit 2 means display information about the object that the handle refers to.

1. lkd> !handle 0 7 acc
2. processor number 0, process 00000acc
3. Searching for Process with Cid == acc
4. PROCESS 89e1ead8 SessionId: 1 Cid: 0acc Peb: 7ffd3000 ParentCid: 0a28
5. DirBase: b25c8740 ObjectTable: f1a76c78 HandleCount: 246.
6. Image: windbg.exe
7. Handle table at f0aaa000 with 246 Entries in use
8. 0000: free handle, Entry address f0aaa000, Next Entry fffffffe
9. 0004: Object: 95d02d70 GrantedAccess: 00000003 Entry: f0aaa008
10. Object: 95d02d70 Type: (860f5d60) Directory
11. ObjectHeader: 95d02d58 (old version)
12. HandleCount: 74 PointerCount: 103
13. Directory Object: 83007470 Name: KnownDlls
14. 0008: Object: 89e1a468 GrantedAccess: 00100020 Entry: f0aaa010
15. Object: 89e1a468 Type: (8613f040) File
16. ObjectHeader: 89e1a450 (old version)
17. HandleCount: 1 PointerCount: 1
18. Directory Object: 00000000 Name: \Program Files\Debugging Tools for Windows
19. {HarddiskVolume3}

|
  Windows Internals 다시보기 07 
작성일시 : 2010. 2. 18. 17:33 | 분류 : Windows Server/Kernel

* 아직은 많이 부족하기 때문에 제가 자신이 생길 때 까지 본 글은 제 블로그에 대한 링크만 허용합니다.

Working Set 과 Committed Memory

실제 프로세스가 동작에 필요한 총 메모리는 Committed memory 라 하는데, 이 Committed memory는 실제 Physical Memory 상에 상주 하는 부분과 Paging 된 부분으로 나눠 집니다. 그리고 이 중 Physical RAM에 상주하는 메모리를 Working Set이라 합니다. 그리고 그 중에서도 해당 프로세스만이 사용하는 메모리를 Private Memory 라 합니다. 우리는 PTE의 내용을 확인 하여 이 VAD가 페이징 여부 등을 확인 할 수 있습니다.

페이지 목록 다이나믹

앞서 살펴본 notepad.exe 의 경우 가상 메모리 사용량과 Working Set의 크기가 상당히 차이가 나는 것을 볼 수 있습니다. 이는 Working Set 이 해당 프로세스의 메모리 중 실제 물리 메모리에 로딩된 페이지들을 의미하기 때문입니다.
Working Set Sizes (now,min,max) (1554, 50, 345) (6216KB, 200KB, 1380KB)
PeakVirtualSize 48 Mb
다시 말해 Committed Memory. 즉 프로세스가 사용하겠다고 선언한 메모리는 물리 메모리에 있을 수 도 있고, 하드 디스크에 있을 수 도 있습니다. 그리고 우리는 그 중 물리 메모리에 있는 페이지들을 Working Set이라 부르며, 이 중 다른 프로세스와 공유하지 않는 해당 프로세스만을 위한 메모리를 Private 메모리라 합니다. 페이지 프레임들은 아래의 그림과 같은 방법으로 페이지 목록 사이에서 이동하게 됩니다.

image

작업 세트가 가득 찼거나 (시스템 초기화때에 계산되어 MmMaximumWorkingSetSize – 512 한 값 / x86의 Windows 2003 의 경우 1984MB가 최대 값) 메모리 관리자가 작업 세트를 정리하는 등의 활동으로 인해 Working Set 중 일부 페이지가 해제 되어야 하는 경우 수정되지 않았다면 스탠바이 페이지 리스트로 가고 수정되었다면 모디파이드 페이지 목록으로 이동합니다. 만약 프로세스가 종료되게 되면 모든 프라이빗 페이지들은 프리 리스트로 이동합니다. 또한 페이지 파일에 저장되는 섹션에 대한 마지막 참조가 종료되면 이 페이지들 역시 프리 리스트로 이동합니다.

제로 페이지 폴트가 발생 하면 메모리 메니저는 0으로 초기화된 메모리를 요청합니다. 만약 0 으로 초기화된 페이지가 없는 경우에는 프리 페이지 리스트의 페이지 중 하나를 0으로 초기화 한 후 사용합니다. 물론 이 프리 페이지 역시 비어 있다면 스텐드 바이 페이지 리스트 중 하나를 0으로 초기화 한 후 사용하게 됩니다.

- 제로 페이지 폴트 : 모두 0으로 초기화된 페이지에 대한 참조 및 한번도 참조 되지 않은 메모리에 대한 유저 모드 Committed Private 페이지에 대한 참조
- 제로 페이지 폴트 발생 > 메모리 메니저 : 0으로 초기화된 메모리 요청
n 1, 0으로 초기화된 페이지 요청
n 2, 프리 페이지 리스트중 하나를 0으로 초기화 한 이후 사용
n 3, 스텐바이 페이지 리스트 중 하나를 0으로 초기화 한 후 사용
- 유저 모드 프로세스의 사용 메모리를 0으로 초기화 하는 이유 : C2 보안 규정을 충족하기 위해
n 디스크로부터 읽는 경우 0으로 초기화 하지 않고 사용하는 것을 선호함
n 디스크나 원격 저장소로부터 데이터를 가져오는 과정에서 초기화 함 (이해 않됨)

제로 페이지 쓰래드 (시스템 프로세스의 0번 쓰래드)는 프리 리스트에서 제로 페이지 리스트로 페이지를 이동시킵니다. 이 제로 페이지 쓰래드는 게이트 객체의 시작 신호를 받은 후 동작하게 됩니다. 프리 리스트에 8개 이상의 페이지가 있는 경우 게이트 객체는 이 시그널을 보냅니다. 하지만 제로 페이지 쓰래드의 경우 쓰래드 우선 순위가 0이기 때문에 다른 유저 쓰래드가 동작하지 않을 경우에만 동작할 수 있습니다 .(일반적인 유저 쓰래드의 최하 우선 순위는 1입니다. 스래드 우선 순위에 대해서는 이후 쓰래드에서 설명하도록 하겠습니다. )

- 프리 리스트에서 패이지가 8개 이상, 유휴 상태일 때 제로 페이지 쓰래드를 통해 프리 리스트의 페이지의 데이터가 0으로 변경됨

메모리 관리자가 0으로 초기화된 페이지를 원하지 않는다면 먼저 프리 리스트에서 페이지를 요청하게 됩니다. 만약 프리 리스트가 비어 있다면 다시 제로로 초기화된 페이지 리스트에서 요청하게 되고, 다시 이 리스트 마저 비어 있다면 스텐드 바이리스트에 요청하게 됩니다. 메모리 메니저가 스텐드 바이 리스트의 메모리를 사용하기 위해서는 반드시 해당 메모리 를 지시하고 있는 PTE를 찾아 참조를 제거하는 등의 적절한 조치를 해줘야 합니다.

페이징 정책

페이징은 물리 메모리의 페이지를 페이징 파일 즉 하드 디스크에 저장하는 것을 의미합니다. 즉 페이징 정책은 어떤 페이지를 페이징 할지 결정하는 정책을 의미합니다. 윈도우는 페이징이 될 페이지를 선택할 때 지역성과 FIFO LRU 방식을 이용합니다.

- 지역성 : 프로세스가 메모리를 참조할 때 특정 부분을 주로 참조하고 있다는 경험적 사실을 토대로 한 페이징 선별 방식
- LRU (Least Recently Used) : 가장 오랫동안 사용하지 않는 페이지를 교체하는 선별 방식
- FIFO (First In First Out) : 가장 먼저 사용된 메모리를 페이징 시키는 선별 방식

|
  Windows Internals 다시보기 05 
작성일시 : 2010. 2. 17. 18:41 | 분류 : Windows Server/Kernel

* 아직은 많이 부족하기 때문에 제가 자신이 생길 때 까지 본 글은 제 블로그에 대한 링크만 허용합니다.

가상메모리 주소를 실제메모리 주소로 변환해 보겠습니다.

실제로는 VAD 값을 뒤져 데이터를 확인 할 수도 있습니다. 하지만 본 예에서는 간단하게 !pte 를 통해 페이지 테이블 엔트리에서 저장하고 있는 값을 확인 하고 이를 통해 실제 주소를 확인 하도록 하겠습니다.
(출처 : http://blogs.technet.com/marcelofartura/archive/2008/11/20/how-to-manually-translate-virtual-addresses-into-physical-ones.aspx)

이제 부터 가상 메모리를 들여다 보도록 하겠습니다.
편의상 특정 가상 메모리 주소 b03b5000를 통해 확인 하겠습니다.

lkd> !pte b03b5000
VA b03b5000
PDE at 00000000C0602C08 PTE at 00000000C0581DA8
contains 000000000A6FD963 contains 00000000164B2963
pfn a6fd -G-DA--KWEV pfn 164b2 -G-DA—KWEV

페이지 테이블과 관련된 녀석들은 0xC0000000 정도에 몰려 있는 것을 볼 수 있습니다.
PDE at 00000000C0602C08 PTE at 00000000C0581DA8
여기서 잠깐 PDE는 C0602C08 에 위치하고 PTE C0581DA8 에 위치하는 것을 알 수 있습니다
내 실제로 커널에 페이지 테이블을 저장하는 공간은 가상 주소0xC0000000 정도에 위치하고 있습니다.

그림 윈도우즈 x86 시스템의 커널 메모리 공간 레이아웃
image

더불어 아래의 내용과 같이 실제 인덱스 위치 및 위에서 설명한 권한에 대한 부분이나 페이징 여부 외에 페이지에 대한 접속 여부 등등 많은 내용을 알 수 있습니다.
pfn a6fd -G-DA--KWEV pfn 164b2 -G-DA—KWEV

(참고 x86, x64 에 대한 페이지 디렉토리 엔트리와 페이지 테이블 엔트리에 대한 설정)

Bit

Display
when set

Display
when clear

Meaning

0x200

C

-

Copy on write.

0x100

G

-

Global.

0x80

L

-

Large page. This only occurs in PDEs, never in PTEs.

0x40

D

-

Dirty.

0x20

A

-

Accessed.

0x10

N

-

Cache disabled.

0x8

T

-

Write-through.

0x4

U

K

Owner (user mode or kernel mode).

0x2

W

R

Writeable or read-only. Only on multiprocessor computers and any computer running Windows Vista or later.

0x1

V

 

Valid.

 

E

-

Executable page. For platforms that do not support a hardware execute/noexecute bit, including many x86 systems, the E is always displayed.

(참고 IA64 에 대한 페이지 디렉토리 엔트리와 페이지 테이블 엔트리에 대한 설정)

Display
when set

Display
when clear

Meaning

V

 

Valid.

U

K

Owner (user mode or kernel mode).

D

-

Dirty.

A

-

Accessed.

W

R

Writeable or read-only. Only on multiprocessor computers and any computer running Windows Vista or later.

E

-

Execute.

C

-

Copy on write.

그렇다면 실제 가상 주소 b03b5000 의 내용과 이 가상 주소를 조회하여 얻은 실제 물리 주소를 직접 찾아가 데이터를 비교해 보겠습니다.
먼저 가상 주소 b03b5000 의 데이터를 확인해 봤습니다.

lkd> dc b03b5000
b03b5000 00905a4d 00000003 00000004 0000ffff MZ..............
b03b5010 000000b8 00000000 00000040 00000000 ........@.......
b03b5020 00000000 00000000 00000000 00000000 ................
b03b5030 00000000 00000000 00000000 000000d8 ................
b03b5040 0eba1f0e cd09b400 4c01b821 685421cd ........!..L.!Th
b03b5050 70207369 72676f72 63206d61 6f6e6e61 is program canno
b03b5060 65622074 6e757220 206e6920 20534f44 t be run in DOS
b03b5070 65646f6d 0a0d0d2e 00000024 00000000 mode....$.......

자 그러면 우리가 방금 확인 했던 PTE에 들어있는 실제 물리 주소의 내용을 확인해 보겠습니다.

lkd> !pte b03b5000
VA b03b5000
PDE at 00000000C0602C08 PTE at 00000000C0581DA8
contains 000000000A6FD963 contains 00000000164B2963

이전에 설명했던 것과 같이 PFN 00000000164B2963 의 첫 20 비트 값이 실제 물리 페이지의 위치 입니다.
한번 보겠습니다.

실제 물리주소는 PTE에 있다고 말씀 드렸습니다. 그리고 이 PTE의 내용 중 첫 20비트를 확인 해 보니 164B2000 임을 확인 했습니다.
그리고 이 164B2000 주소에 b03b5000 의 뒷 12 비트를 더해 주면 실제 물리 주소가 됩니다.
이 경우에는 000 이니깐 더해 줘도 동일하게 164B2000가 됩니다.

실제 물리 주소인 164B2000의 값을 확인 해 보니 가상 메모리 주소와 일치함을 확인 할 수 있습니다.

lkd> dc /p 164B2000

b03b5000 00905a4d 00000003 00000004 0000ffff MZ..............
b03b5010 000000b8 00000000 00000040 00000000 ........@.......
b03b5020 00000000 00000000 00000000 00000000 ................
b03b5030 00000000 00000000 00000000 000000d8 ................
b03b5040 0eba1f0e cd09b400 4c01b821 685421cd ........!..L.!Th
b03b5050 70207369 72676f72 63206d61 6f6e6e61 is program canno
b03b5060 65622074 6e757220 206e6920 20534f44 t be run in DOS
b03b5070 65646f6d 0a0d0d2e 00000024 00000000 mode....$....…
|
 Prev   1   ···   10   11   12   13   14   15   16   ···   65   Next