10.19(금) 이론 - ( IPC, IOCP )

from Study/System 2007/10/21 22:07 view 53775
1. IPC( Inter Process Communication )

  1) 사용자 정의 메시지
    - SendMessage 로 메시지를 보낸다. 64비트 만 전달 할 수 있다.
    - 포인터는 가상주소간의 접근이 금지 되어 있으므로 불가능하다.

  2) WM_COPYDATA
    - 내부적으로 MMF 를 쓰기 때문에 결국 포인터가 아닌 MMF 공간의 주소를 사용한다.
 
  3) 공유 메모리
  4) MMF
  5) PIPE
    - 이름 없는 파이프는 상대방이 능동적으로 알 수 없다.
      - 만든놈이 알려줘야 한다. DuplicateHandle
      - 상속을 해주자. SetHandleInformation
   
    - 이름 있는 파이프 : 로컬 내에서만 지원한다.
      - '\\pc이름\PipeMailSlot\이름' 의 형식으로 파이프를 생성 해줘야 한다.
  6) 클립보드
  7) 메일 슬롯
  8) DDE, SOKET, RPC

2. IOCP ( 비동기 I/O : 중첩된 입출력, Overlapped I/O )
 
  - 큰파일을 쓰는 동안 다른 작업을 할 수 있도록 할 수 있다.
  - 작업의 취소가 가능해진다.
  - CreateIoCompletionPort 로 IOCP 를 생성한다.
사용자 삽입 이미지


2007/10/21 - [Study/System] - 10.19(금) 실습 - ( IPC 기법들, IOCP 기초 )
Tag | ,
1. IPC - 이름 없는 파이프

more..



2. IPC - 이름 있는 파이프

more..


3. IPC - 클립보드

more..


4. IPC - WM_COPYDATA

more..


5. APCQ 기본.

more..


6. IOCP 에 대한 단계별 기본 코드

  1) Device Ko 대기 - 쓰기 작업이 끝날 때까지 대기, 누가 끝나는지를 알수가 없다.

more..


  2) Event를 대기로 누가 끝나는지, Cancel 을 할수 있다.

more..



3) CALLBACK 함수를 사용한다.

more..



4) 비동기 요청 스레드와 비동기 대기 스레드를 분린한다.

more..


5) 입출력 완료포트 - 한개의 스레드가 여러개의 비동기 작업을 관리 해준다.!!

more..


Tag | ,
1. API Hooking

  - 직접 호출
    1) DLL내의 함수 위치는 항상 똑같을 수 없다. 주소가 달라진다면 호출이 제대로 되지 않는다.
    2) OS가 호출하는 코드를 바꿔줄 수는 있으나 100군데라면 전부 바꿔야 한다.

  - 간접호출
    1) .idta 에 자기가 사용하는 API의 모든 주소를 가지고 있다.
    2) 0x42,0000 에 메시지 박스 함수의 위치를 가지고 있다가 디레퍼런스 해주면 된다.
    3) 이 주소에 사용자가 만든 임의의 함수 f00() 의 주소를 복사해 넣는다면 재배치(Relocation)을 한다.
    4) 사용자가 만든 임의의 함수 foo() 의 주소를 복사해 넣는다면 API Hooking 을 할 수 있다.
    5) 유저레벨에서 Low 하게 함수를 가로 채는 방법이다.

2. 에러 처리

  - 엑셀의 셀 만들어 보기
    1) 한 셀당 3K를 보관 할 수 있다고 가정해보자.
    2) 먼저 넓은 영역을 통째로 잡고 예약만 한 다음에 쓸려고 할 때 확정을 하는 기법을 하고자 한다.
    3) 셀의 사용유무를 기억하는데만도 n의 수치 즉 셀의 수만큼 의 비트수가 필요하다.
    4) 셀에 무조건 문자열을 쓰고 예외가 발생(즉 셀이 비어있거나,더 쓰고자 할때) 의도적으로 exception을 발생시켜서 셀을 확정 짓는다. 그렇다면 효과적인 메모리 관리(?) 가 가능하다.

2007/10/17 - [Study/System] - 10.17(수) 실습 - ( API Hooking, 예외 )
Tag | ,
1.GetMessageBoxA 의 후킹~ 

more..


2. Debug Help API 를 이용한 후킹~

more..


3. __try, __except 기초

more..


4. C++ throw의 __except

more..


5. STH( Structure Termination Handling ) __try, __finally ( 구조화된 종료 처리 )
  1) 개념

more..


 2) 응용 ( 자원관리를 __finally 에서 담당해준다.!!! )

more..


6. SEH 를 응용해서 메모리 할당 해보기. ( 일명 : Big Shell )

more..


7. API Hooking ( SetTimer를 바꿔치기 해보자 - 미완성 ㅜ_ㅜ )
 1) DLL ( Inject 할 Dll )

more..


2) Inject ( DLL을 Inject 하기.. )

more..


Tag | ,

10.16(화) 이론-2 ( Hook )

from Study/System 2007/10/16 19:28 view 27441
1. Hook
사용자 삽입 이미지

  - CPU는 INT이 1개 이므로 8259를 이용하여 외부신호를 받아온다. 15개의 신호를 받아 올수 있다.
  - 마스터 8259중 하나는 IRQ# : Slave신호를 받아온다.
  - IDT는 OS가 구축 해 놓아서 디바이스 드라이버에 신호를 보내준다.
  - 키보드 핸들러는 PORT상에서 키보드 상태를 얻어서 SHIQ에 넣어준다.

  - USER 영역에서는 SHIQ=>RIT로 보낼때 Low Level Hook 를 하거나
  - RIT => MSGQ에 보낼때 가로 챌수 있다. ( 제일 쉬움 )
  - NProtected 와 같은 보안 프로그램은 커널쪽인 IDT에서 암호화를 해서 내보낸다.
 g_hHook = SetWindowsHookEx( WH_GETMESSAGE,    // 훅의 종료(15가지)
                                               GetMsgProc,            // 훅함수
                                               g_hDll,                     // 훅함수를 가진 DLL핸들(주소)
                                                tid );                       // 훅을 설치할 스레드( 0:Global hook )


  - 훅은 '전역'(모든 윈도우) 의 성격을 지닌다. 또한 GetMessage 단계에서 메시지를 가로 챌 수 있다.
  - 서브클래싱은 특정윈도우에 메시지를 보내는 DispatchMessage 에서 가로 챈다.

GetMessage()
{
   1. SendQ, PostQ, 가상입력 Q순으로 메세지를 조사한다.
   2. 먼저 WH_GETMESSAGE 훅이 설치 되어 있다면 DLL을 Load하고 hook 함수를 호출한다.
   3. 메세지를 가져간다.
}

///////////////////////////////////////////////////////////////////////
// 모든 스레드는 Kernel32.dll에 있는 아래 함수 부터 실행된다.
////////////////////////////////////////////////////////////////////////

BaseThreadStart()
{
 __try
 {
   스레드도 dll을 가지고 있으므로 ThreadEntryPoint를 이함수로 해준다.
   가상주소에 있는 모든 Dll에 대해서 DllMain을 호출해준다.
   그리고 사용자 함수(foo) 호출을 한다.
   해지를 위해선 foo가 종료 되었을때 DllMain을 또 불러준다.
 }
 __except( 1 ) // 예외처리
 {
 }
}

///////////////////////////////////////////////////////////////////////
// FreeLibrary 도 MMF를 열고 해지를 한다.
// LoadLibrary의 원리
//////////////////////////////////////////////////////////////////////

HMODULE WINAPI LoadLibrary( LPCTSTR name )
{
  1. CreateFile 로 파일 생성
  2. MMF를 사용해서 가상주소와 연결( MapViewOfFileEx - PE헤더의 주소로 연결)
  3. if ( DllMain 호출( 주소, DLL_PROCESS_ATTACH, 0(명시적) ) == FALSE )
        MMF 해지, 화일 닫고 return 0;  // 로드를 다시 해지한다. TRUE리턴이 아니라면..
  4. return 주소!!!
}

2007/10/16 - [Study/System] - 10.16(화) 실습 ( 스택,힙, 훅, DLL )
Tag | ,

10.16(화) 이론-1 ( 스택, 힙, DLL)

from Study/System 2007/10/16 16:49 view 28534
1. 스택의 원리
사용자 삽입 이미지


- Page-Guard : 접근시 예외를 발생시키는 속성을 가진다.

- 2번째 페이지 에 접근하면 Page-Guard는 Commit 이 되고 3번째 페이지가 Page-Guard 상태가 된다.

- 마지막 페이지에 도달하면 Reserve 상태가 된다. 이를 벗어 나면 오버플로우 가 발생한다.

- 메모리가 필요할 때 Commit 상태가 되는것이다.

- 메모리의 상태를 알고 싶다면 VirtualQuery, VirtualQueryEx를 쓴다.



char* addr = (char*)0x0;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery( addr, &mbi, sizeof(mbi) );

2. Heap
사용자 삽입 이미지

- 힙은 프로세스당 1개씩 있지만 새롭게 힙공간을 만들어도 된다.
- HeapAlloc, HeapFree 는 기본힙을 생성하고 소멸 해준다.
  1) 확보된 영역을 조금씩 쓰기 위해선 OS가 관리해주는 Heap메모리는
 Heap매니저가 담당해준다.( 소형 메모리 할당 )

  2) 좌측 그림처럼 메모리가 조각화 되어서 메모리 낭비가 발생한다.


- HeapCreate, HeapDestroy 는 새롭게 힙공간을 만들어 준다.
  1) 메모리 조각화 현상을 막을 수 있다. 새로운 힙공간에는 똑같은 크기의 메모리만 써서 메모리 낭비를 막는다.

2) 기본힙은 내부적으로 동기화를 수행하므로 멀티스레드에는 안전하지만 수행속도가 그만큼 느려진다. 단일 스레드 프로그램에선 힙을 하나 생성해서 동기화를 수행치 않게 되면 빨라진다.

사용자 삽입 이미지

- 윈도우즈 환경에서의 메모리 관리 함수들의 계층은 위 그림과 같다.
- 이외에도 GlobalAlloc ( 전역 힙 ), LocalAlloc ( 지역 힙 ) 이 있지만 이는 16bit시절에만 의미가 있고
- 지금은 HeapAlloc 이나 다름이 없다. 하지만 GlobalAlloc 은 유일하게 클립보드에 써줘야 한다.
The global functions are slower than other memory management functions and do not provide as many features. Therefore, new applications should use the heap functions. However, the global functions are still used with DDE, the clipboard functions, and OLE data objects.


3. DLL

  - 프로세스에 로드될때 ( DllMain() 생성,초기를 해준다. )    DLL_PROCESS_ATTACH
  - 프로세스에서 해지될때 ( DllMain() 소멸자 역할을 한다. ) DLL_PROCESS_DETACH
  - 스레드가 생성시 ( DllMain() TLS공간을 만들어 준다. )     DLL_THREAD_ATTACH
  - 스레드가 파괴시 ( DllMain() TLS공간을 없애준다. )         DLL_THREAD_DETACH

   - DllMain은 Serialize 가 된다. 스레드 두개가 동시에 접근 못하도록 구현되어 있다.( 한스레드만 접근 )
BOOL WINAPI DllMain( HANDLE hDll,    // DLL 핸들, 결국 주소
                     DWORD  r,                    // DllMain 이 호출된 이유
                     LPVOID how                 // DLL이 Load된 방식( 0이면 LoadLibrary 사용 )
                    )

  - 동적TLS( 스레드에서 dll을 로드 했을 때 전역변수 문제를 해결한다. strtok 의 static변수 )
사용자 삽입 이미지
  - 멀티스레드 환경을 고려할 때 dll의 전역변수는 동적 TLS를 사용해야 한다.
  - 동적TLS는 사용하지 않는 Index에 메모리를 할당하는 기법으로 모든 스레드는 인덱스를 가지고 메모리에 접근한다. 이때 Index는 프로세스에 할당되어 있다.~
DWORD index = 0; // 동적 TLS index로 사용.
//////////////////////////////////////////////////////////////////////////////////

index = TlsAlloc();    // 동적 TLS에 빈슬롯 할당
buf = (char*)HeapAlloc( GetProcessHeap(), 0, 1000 );
// 주소를 TLS에 보관
TlsSetValue( index, (void*)buf );

TlsFree( index );
////////////////////////////////////////////////////////////////////////////////
// 새로운 스레드가 생성 될 때마다 메모리를 할당해서 Thread-Safe를 보장!!
buf = (char*)HeapAlloc( GetProcessHeap(), 0, 1000 );
TlsSetValue( index, (void*)buf );

buf = (char*) TlsGetValue( index );
HeapFree( GetProcessHeap(), 0, buf );
/////////////////////////////////////////////////////////////////////////////////


2007/10/16 - [Study/System] - 10.16(화) 실습 ( 스택,힙, 훅, DLL )
동적TLS MS문서
Tag | ,

10.16(화) 실습 ( 스택,힙, 훅, DLL )

from Study/System 2007/10/16 15:12 view 33107
1. 스택의 원리

more..


2. 기본힙과 새로운 힙 만들기

more..


more..


3. 동적 TLS를 사용한 DLL ( strtok 문제 해결 )

more..


4. DLL이 로드되는 과정 보기(위예제랑 연동)

more..


5. Hook Dll 만들기

more..


6. Hook 한 메시지 읽어오기( Friend)

more..


Tag | ,

메모리에 관한 용어들

from Study/System 2007/10/16 14:55 view 24298
가상메모리 (Vitrual Memory)

 Vitrual Memory로 말그대로 실재 존재하지는 않지만 메모리 역할을 하는 것을 말하는데 윈도우는 하드 디스크의 일정 영역을 '가상 메모리'로 사용합니다.

스왑 (swap)

 시분할 운영체제에서 어떤 작업의 주기억장치 영역의 이미지를 보조기억장치에 기록하고, 다른 작업의 이미지를 주기억장치에 읽어 넣는 방식

* 주메모리의 한도를 넘는 프로세서를 실행하면 가상메모리(하드디스크의 일부분)에 주메모리가 로드할 데이터들중 일부 이미지들이 보내지어 작업처리량을 높이며 효율적으로 한다는것이 바로 가상메모리와 스왑의 메카니즘이긴하나

 하드디스크와 램사이의 전송속도자체의 차이와 주메모리가 넘겨주었던 데이터이미지들을 하드디스크로부터 다시 받는 이 과정에서 연산제어가 딱 맞아떨어지면 그횟수가 합리적 경제적으로 적어지겠지만 그렇지 못하는 경우가 생길때도 있어 흔히 이럴때 우리가 하드스왑현상으로 작업대기및 지연시간이 생겼다라고 말하는것같습니다.


메모리의 구조와 설명 

사용자 삽입 이미지
  메모리의 구조

* Null-pointer Assignment

  이 지역은 프로그램상의 메모리 접근 오류를 잡아내기 위하여 할당된 영역입니다. 어떤 프로그램이든 이 영역에 읽고 쓰기가 금지되어 있는데 이 지역에 read/write가 행해지면 시스템은 access viloation을 발생시킵니다.
시스템으로부터 메모리를 정상적으로 할당 받지 못할 경우 NULL(0x0)을 리턴받게 되는데 바로 이런 NULL pointer assignment를 방지하기 위한 공간입니다.

* MS-DOS 16-bit Compatibility(win98 only)

 win9x에만 존재하는 4MB의 공간으로 MS-DOS와 win16과의 하위 호환성을 유지하기 위한 공간입니다.
이 공간 또한 read/write가 금지되어야 하는데 MS사에서 기술적인 몇 가지 이유로 이 공간을 보호하지 못했다고 하군요.
 win2000에서는 DOS나 win16 어플리케이션을 자신의 유저 주소 공간에서 실행하기 때문이 이 영역이 없다고합니다.

* User area

 프로세스의 사용자 공간입니다. 다른 프로세스에 의해 간섭받지 않는 고유의 공간으로서 이 공간에 exe와 dll 모듈등 자신들만의 데이타를 담는거라고 합니다.
 특기할 만한 것은 win9x에서는 처음 4MB를 제외한 0x400000부터 0x7FFFFFFF인 반면, win NT는 0x10000부터 0x7FFEFFFF로 역시 처음 2GB에서 64KB를 제외한 부분입니다.

 이는 어떤 프로그램이 실행될 때 메모리 상의 베이스 주소가 다를 수 있다는 것인데, 때문에 프로그램을 만들 때 win NT는 win 9x와 호환성에 주의해야 하는것이더군요.

 win NT에 맞추어 프로그램의 베이스 메모리를 0x10000로 맞추었다가는 win9x에서는 접근이 금지된 지역이므로 실행이 불가능하게 됩니다. (물론 요즘엔 win 9x가 사라져가는 추세라서 그리 큰 영향은 없겠지만..)

* 64KB Off-Limits(win2000 only)

 0x7FFF0000부터 0x7FFFFFFF까지는 win NT에서 처음 64KB의 영역과 마찬가지로 메모리 경계(off-limit)를 나타내는 지역입니다. 커널 영역의 접근을 방지하기 위한 일종의 완충지역이라고 보면 된다고 하는데 어렵군요.

* Shared Memory Mapped File(win98 only)

 win9x에만 존재하는 이 1GB의 영역은 모든 프로세스들이 공통적으로 접근하고 공유할 수 있는 시스템 dll들과 MMF(memory-mapped files)가 적재되는 곳입니다. 3대 시스템 dll이라 불리는 kernel32.dll, user32.dll, gdi32.dll에서 advapi32.dll도 이 영역에 로드되며, win NT에서는 이런 시스템 dll조차 비공유 메모리 영역에 따로 적재하여 사용합니다.
 (이런한 이유로인하여 2000의 보안성이 더 높아 금융권에서도 아직 널리 사용되는건 아닐련지 생각해봅니다.)

* Kernel area

win9x에서는 1GB, win NT에서는 2GB로 예약되어 있는 커널 공간입니다.
스케쥴러, 메모리 관리자, 파일 시스템 코드, 네트워크 코드등의 OS 코드와 디바이스 드라이버 레벨의 모듈이 적재되는 곳이며, 원칙적으로는 이 영역에 대한 read/write가 금지되어 있지만,  win98에서는 이 영역에 대해 접근이 가능합니다.
 유저 어플리케이션에서 이 영역을 손상시킬 수 있다는 점 때문에 win98이 자주 다운되고 불안하다는 비난을 받게 된 이유입니다.




●물리적 메모리와 가상 메모리의 매핑 관계

사용자 삽입 이미지
 
물리적메모리와 가상메모리의 매핑관계

* Page table에는 페이지가 RAM에 존재하는지 paging file에 존재하는지에 대한 정보를 가지고 있습니다.
 옛날 DOS및 윈3.1 시절에는 시스템의 메모리는 그 시스템에 설치된 RAM이 전부였습니다. 만약 시스템에 1MB의 RAM이 설치되어 있다면 커밋할 수 있는 메모리는 1MB가 전부였던 것이지요 .
(예를들어 Dos경험자 분들의 공통적인 눈물겨운 사투 EMM386등을 이용한 메모리 확장(Extened) 노가다)

 그러나 오늘날의 현대적 운영체제들은 대부분 시스템에 설치된 RAM 이외에 디스크 파일을 메모리로 간주할 수 있는 메커니즘을 제공하고 있으며, 이 때 이 메모리로 간주되는 파일을 페이징 파일(paging file)이라고 합니다.
 간단히 말해 자신의 시스템에 256MB의 메모리와 256MB의 페이징 파일을 가지고 있다면 물리적 메모리는 512MB가 되는 것입니다.

● 쓰레드 (thread)

 thread는 process 안에서 실행되는 코드의 실행 흐름이라고 할 수 있습니다.

 쓰레드는 프로세스가 할당한 메모리 영역에서 실행되며 프로세스에 할당된 시스템 자원을 사용하게 되며, 프로세스가 생성되고 초기화될 때마다 운영체제는 기본쓰레드(primary thread)를 생성하게됩니다.

  각 쓰레드는 명령 코드와 함께 CPU 레지스터 상태를 저장하는 context와 2개의 스택 영역이 포함되고, 2개의 스택 영역은 특권 프로세스 모드와 사용자 모드를 위한 스택입니다.

● 스레싱( thrashing)

 한 프로세스의 어떤 쓰레드thread 가 프로세스 주소 공간에 있는 데이타에 접근하려고 할 때 그 데이타가 RAM에 존재하고 있다면 CPU는 프로세스의 가상 주소를 해당 물리 주소에 맵핑하고 데이타에 접근하게 됩니다.  그러나 데이타가 RAM에 없다면 그 데이타가 페이징 파일에 있는지 확인하게 되겠지요.

이 상황을 Page fault라고 합니다. page fault가 발생하면 시스템은 페이징 파일내에 데이타가 존재하는지 확인하고 없다면 access violation을 발생시킵니다.

 
 접근하려는 데이타가 페이징 파일에 있다면 시스템은 현재 RAM에 비어 있는 페이지가 존재하는지 확인하고 존재하지 않는다면 RAM상의 어떤 페이지 중에 한 페이지를 선택해서 이를 해제free하게 됩니다.

 만일 해제하려는 페이지의 데이터가 RAM에서 변경된 적이 있다면 (이를 데이터가 더럽혀졌다(dirty)라고 합니다.)

 해당 페이지의 내용을 페이징 파일에 갱신한 후 해당 RAM 페이지를 해제하는데, 새로 비워진 페이지에 원래 접근하려고 했던 데이터가 존재하는 페이지를 페이징 파일로부터 로딩하게 되고 CPU가 가상 주소를 물리 주소에 맵핑한 후에야 비로소 데이터에 접근하게 되는 것입니다.

 만일 시스템에 RAM이 부족하게 되면 page fault가 빈번하게 발생하게 되고 운영체제는 계속해서 RAM과 하드 디스크의 페이징 파일 사이에서 페이즈를 바꾸는 작업에 대부분의 시간을 소모하게 되겠지요.

  이런 현상을 바로 thrasing이라고 부르는데 가끔 win2000/Xp계열을 사용하다보면 어느 순간에 이유없이 하드 디스크가 버버벅 거리며 돌아가면서 성능이 저하되는 모습을 본적이 있을 것입니다.

 이때가 바로 thrashing이 발생한 경우이며 이쓰레드는 인텔계열로 치자면 CPU자체의 L2캐쉬의 크기와 듀얼코어이냐 싱글코어이냐에 따라 발생하는 쓰레씽이 작업지연시간이 줄어들고 여러개의 쓰레씽들도 처리하는 속도가 차이가 나게됩니다.

여기서 한 시스템에 수많은 프로그램들이 동시에 실행된다면 그 많은 프로그램들의 실행 코드나 데이타들을 페이징 파일에서 할당해야 할 테니 페이징 파일이 대책없이 커지지 않을까 하는 의문을 가질 수 있는데.

 이것을 방지하기 위해 시스템은 프로그램이 실행될 때 실행 파일을 열고 코드와 데이타의 크기를 먼저 확인한 뒤에 실행에 필요한 만큼의 영역을 확보한 후 실행 파일의 이미지 자체를 영역으로 할당합니다. 프로그램의 .exe 실행 파일이나 DLL등의 파일 이미지가 주소 공간의 확보된 영역으로 사용될 때 이를 memory mapped file이라고 합니다.

 (예를들어 윈도우즈에서 실행중인 파일이나 dll을 삭제하려고 할 때 "실행중이므로 삭제할 수 없다"라는 메시지가 뜨면서 삭제가 안되는 경우를 많이 보았을 것입니다. 이는 실행한 파일이나 dll을 전부 메모리에 올리지 않고 필요한 부분만 메모리에 올려지고 나머지는 memory mapped file로 묶여져 있기 때문인 것입니다.)

● 가상메모리 설정법

 가상메모리는 실제 물리적메모리의 1.5~2배정도를 설정해주는것이 좋다는 MS사의 비공식? 사항이 있었던 것으로 기억하며 설정방법은 최소와 최대를 모두 같은값(실제 물리적메모리의 1.5~2배 의 값)으로 설정하는 것을 추천합니다.
그리고 가상메모리공간을 확보할 하드디스크는 운영체제가 설치되어있는 드라이브가 아닌 다른 드라이브로 함으로써 스왑현상을 어느정도 해소하게 하는것이 좋습니다.

예를 들어 비스타 얼티메이트 64비트 사용합니다. 파티션은 100g를 둘로 나눠서 d에 가상메모리 시스템이 관리시키고 위에 모든드라이브에서 윈도우에서 자동관리해놓고 그냥 사용합니다.
1.5배,2배 굳이 지정하지 않구요.

Tag |

메모리에 대한 이야기

from Study/System 2007/10/15 20:05 view 23639
윈도우의 모든 프로세스는 가상메모리 기반으로 되어있습니다.
프로세스가 실행될때, 먼저 가상 공간을 확보후 (페이징파일과는 분명히 다릅니다.) 실제 메모리에 매핑을 하게되는 과정을 거칩니다.

작업관리자에 보면 메모리 사용량과 가상메모리 사용량이 나오는데, (디폴트는 아닙니다. 보기->칼럼선택 에서 선택해 줘야 나옵니다.) 페이징파일을 날렸는데 VM Size는 표시가 되지요. 어찌된 일일까요?

이는 그냥 프로세스에 할당된 가상 메모리 양에 불과합니다. 페이징파일과 아무런 관련이 없지요.
하지만 대부분의 사람들은 이를 페이징파일 사용량이라 착각하곤 합니다. 페이징파일과 가상메모리는 분명히 다르며, 가상메모리의 종류도 페이징파일만이 아닙니다.

Q2. 램이 많으므로 페이징파일 삭제?

 결론부터 말씀드리자면 삭제하지 말기를 추천드립니다. 페이징파일을 날리면 메모리를 쓸데없는곳에 쓰게됩니다. 윈32에서 실제 메모리의 할당단위는 64K 입니다. 하지만 페이지의 단위는 플랫폼마다 다르지만 x86 환경에서는 4K입니다.

 예를들어 4KB짜리 데이터 100개를 램상주시킨다 할때, 모두 실제메모리에 올린다면 최소단위가 64K므로 6.4M의 메모리가 나갑니다만, 페이징파일로 처리시는 400K면 끝납니다.

 그리고 대부분 모르시는 이야기이지만, 가상 메모리에는 단순 페이징 파일 외에, 메모리 맵 파일(Memory-mapped file) 과 힙(Heap)이 존재합니다.

 메모리 맵 파일은 윈32 메모리 관리 매커니즘의 꽃이지만, 가상이 아닌 실제 메모리 부분이므로 넘어가도록 하겠습니다. 파일을 메모리처럼 사용하는 기술이라 생각하시면 됩니다.

힙은 프로세스 내부적으로 이미 확보된 공간을 말합니다. 프로세스마다 디폴트로 1M가 확보되는데 이건 100% 페이징파일로 들어갑니다. 페이지가 4KB라는 장점을 극대화시키기 위한 메모리로, 작은 데이터를 처리할때 최고의 경쟁력과 경세성을 보장해줍니다. 다만 페이징파일이다보니 속도가 느린 단점은 존재하겠지만요...어쨌든 당연히 페이징파일의 삭제는 힙이라는 훌륭한 매커니즘을 포기한다는 이야기가 되는겁니다.

 그 밖의 경우는 실제메모리로 올릴것인지, 페이징파일에 넣을지는 OS가 결정합니다. 사용빈도가 높다면 메모리에 올리고, 적다면 페이징파일에 심습니다. 알아서 최대의 효율을 낼수있도록 지속적으로 감시해주니,
이런 좋은 기능을 페이지파일 삭제로 없애버리는 오류는 만들지 마시길...

 특별히 손을 안대도 큰파일은 메모리로 작은파일은 페이징파일로, 알아서 관리해 준답니다. (Q3에서 관련내용 이어집니다.)

그리고 대표적으로 토토샵같이 페이징파일을 반드시 이용하게끔 제작된 소프트웨어도 상당수 있다는걸 염두해두시길...(아마도 힙사용을 위해 프로그램상에서 미리 체크하는거라 생각됩니다.)


Q3. 페이징파일은 자신의 메모리기준 얼마? 그리고 가변시킬까 고정시킬까?

'지금 자신의 시스템이 얼마만큼의 페이징파일을 사용하는지 알고는 계십니까?'

 사실 요즘같이 램이 빵빵환 환경에서는 페이징파일에 큰 데이터가 들어가지도 않습니다. 512M 시스템 기준으로 제가 측정해본 결과로는 간단한 업무등의 상황에서는 100M 미만, 풀작업환경시 (Eclipse, Toad..등 메모리사용량 536M 상황입니다.) 120~130M 사이를 왔다갔다했습니다.

 512M 기준 윈도우에서 디폴트로 잡아주는값이 768M 이긴 합니다만. 게임등을 돌려서 오버헤드시라 해도 200~300M 이상 안나옵니다. 크게잡아 하드 400M정도를 낭비하는 셈이 되는거죠.

 1.5배라는 속설은 메모리가 128, 256 하던 옛날 이야기입니다. 메모리가 1기가 이상라면 말할것도 없이 낭비율은 더 커질수밖에 없고요. 할당량 가변, 고정의 논쟁도 의미가 없습니다.

 할당량 가변시 트레이에 가상메모리 부족이란 말풍선이 뜨면서 증가작업에 들어가는데, 최근에 이거 보신분이 솔직히 몇명이나 될런지 의문이 듭니다. 잘 일어나지도 않는 증가작업 걱정에 괜한 하드 낭비하시는거라 생각드네요.

 그리고 솔직히 말풍선이 떠서 증가작업에 들어갔다 하더라도, 대부분 사용상의 부족이 아닌 특정 프로세스가 Hang이 걸려 무한루프에 빠지거나,메모리 누수가 발생한 경우가 대부분이지요.

제가 제안하는 최적의 세팅은, 자신의 가상메모리 사용량보다 살짝 위로해서 최저로 잡고, 최고치를 넉넉하게 잡아주는겁니다.

이렇게 사용시 부족메세지가 자주 보이면 최저를 올려줘야 한단 이야기가 되겠죠.



Q4. 메모리 1G->2G의 효용성은?

자신의 메모리 사용량을 안다면 답은 나옵니다. 2G만큼을 쓴다면 2G로 증설하면 됩니다. 당연히 2G만큼을 쓰지않으면서 증설하면 효과가 없겠죠. 참고로 일반적인 개인 컴퓨팅 환경에서 1G채울일도 거의 없습니다.
최근 고사양 게임에서나 1G 안팎으로 놀긴 하지만요.

Q5. 32비트 환경에서 4G의 메모리가 잡히지 않는 이유와 그 효용성은?

이부분은 좀 어려운 내용입니다. 윈32(9x 포함)에서의 메모리 관리는 다음과같은 영역으로 되어있습니다.

0x00000000~0x0000FFFF : 널포인터 할당영역 (에러보고를 위한 구간)
0x00001000~0x7FFEFFFF : 사용자 영역 (사용자가 사용할수 있는 공간)
0x7FFF0000~0x7FFFFFFF : 64K 오프리밋 (구간간 경계선)
0x80000000~0xFFFFFFFF : 커널 영역 (시스템이 사용할수 있는 구간)

사실 위는 NT고 윈9x에서는 16비트영역, 공유영역 다른구간이 있습니다만, 베이스는 동일하니 NT기반으로 설명합니다.

어쨌든 위와 같이 윈32의 사용자가 사용할수 있는 메모리 공간은 2G - 128K 입니다. 우리같은 사용자는 사용자 영역서만 작업이 가능하기 때문이죠. 그렇기 때문에 4G의 램을 붙여봐야 2G밖에 사용을 할수 없습니다.

과거에 커널영역도 사용자가 사용할 수 있는 OS가 있었습니다. 하지만, 커널영역에 접근한 사용자의 응용프로그램이 문제를 일으키면서, 물귀신처럼 커널영역에 올라간 시스템데이터를 망가트리는 경우가 많았죠.
결과는 바로 블루스크린이 뜨는거였고, 이 OS는 너무나도 유명한 윈9x 계열이랍니다.

그렇기때문에 현재의 NT기반 OS는 커널영역의 접근이 철저히 금지되어 있습니다. 하지만 요새 괴기스러운 팁이 나도는데, /PAE를 비롯한 각종 옵션으로, 메모리를 2G 넘어서까지 사용하게끔 해주는 팁이죠. 별 대단한것은 아닙니다. 메모리 영역이 다음과같이 변화됩니다.

0x00001000~0xBFFEFFFF : 사용자 영역 (사용자가 사용할수 있는 공간)
0xBFFF0000~0xBFFFFFFF : 64K 오프리밋 (구간간 경계선)
0xC0000000~0xFFFFFFFF : 커널 영역 (시스템이 사용할수 있는 구간)

즉 커널영역을 뺏어쓰는거밖에 안됩니다. 그리고 MS기술문서에는 커널영역의 감소로 인한 문제점 발생 경고를 하고 있습니다. 또한 대형서버급 몇몇 OS는 제외하고는 이 설정은, '모양만 똑같이 갖춰줄뿐 사용자 영역 메모리는 여전히 2G로 제한된다.' 라고 명시되어 있습니다. 개발자의 64비트 드라이버 개발 등을 비롯한 테스트 목적으로만 사용하라고 나와있죠.

즉, 64비트환경이 아니라면 4G램은 아무런 의미가 없습니다. 여전히 2G이상 사용 불가능입니다.
(참고 :
http://support.microsoft.com/kb/291988)


 보너스. NT(2000,XP..) 계열이 윈9x계열보다 메모리를 많이먹는 이유.

위에서 잠깐 언급만 했습니다만, 9x에서는 공유영역이란게 있습니다. kernel32.dll, user32.dll, gdi32.dll 등의 시스템 모듈들은 공유영역에 올라가게 되고, 응용프로그램들이 공유영역에 올라간 모듈들을 참조해서 사용하는데, 이건 결과론적으로 최악의 구성이 되어버렸습니다.

응용프로그램이 오류로 인해 죽을시 공유된 모듈을 함께 물고 죽인다는 치명적인 문제가 있었죠.
kernel32가 내려가버린 OS는 도대체 뭘까요? ㅎㅎ 바로 블루스크린 등장하시게 되는겁니다.

이때문에 NT계열에서는 공유영역이 사라지고, 공유영역에 올라가던 핵심 모듈들이, 프로세스의 수많큼 매번 로드되어 메모리에 적재됩니다. 때문에 프로세스 수만큼 메모리가 * 되어 사용되는 단점이 있지요.

반면, 오류 발생시 자신이 로드한 녀석만 죽기때문에, 전체 시스템은 원활하게 굴러가게 되는 구조가 되었습니다. 메모리의 낭비가 있긴 하지만, 아무래도 블루스크린없는 후자가 좋겠죠.

1. 가상메모리

Kernel영역 2~4G 사이
PageTable 0xC000,0000
ntoskrnl.exe 0x8000,000
User영역 0~2G 사이
OS를 보호(완충지대) 0x7FFF,0000
기본적인 DLL 영역
kernel32.dll, user32.dll
GDI32.dll, ntdll.dll
0x7xxx,xxxx
사용자 추가 DLL 0x1000,0000
MS-DOS 시절
전용프로그램 공간(호환성)
0x40,0000
Heap ~ 0x13,0000
Stack 0x13,0000 ~ 0x3,0000
User영역의 시작 0x1,0000
NO_ACCESS(접근금지) 0~64k

  - 좀더 많은 주소를 얻고 싶을 때 boot.ini 에서 3G를 주면 된다.
  - malloc이 실패하면 0 번지가 리턴된다. 이영역은 사용하지못한다.
  - 2G-128K 가 사용자가 쓸수 있는 공간이라 할수 있다.
  - 하나의 프로세스에서 스레드 1000개 정도가 MAX라 할수 있다.( Thread는 1M의 스택영역을 갖고있다. )
  - 지금은 0x40,0000을 쓰지 않지만 과거와의 호환을 위해서 0x40,000 번지 부터 프로그램이 로드된다.

  - VirtualAlloc () => 비어있는 가상주소공간을 할당한다는 개념..
    1) 예약 : 주소만 확보해둔다.
    2) 확정 : 물리공간과의 연결을 해준다.
    3) 결국 예약을 함으로써 주소만 미리 확보 해둔다음 확정을 사용하여 쓴다는 개념!!
char* p1 = (char*)VirtualAlloc( (void*)0, // 원하는 주소(64k배수), 자동으로 할당 0
                                         size*15,    // 원하는 크기(4k 단위)
                                         MEM_RESERVE,          // 예약만
                                         PAGE_NOACCESS );    // 보호 속성(어짜피 접근못하므로 NOACCESS)

void* p2 = VirtualAlloc( p1, size, MEM_COMMIT, PAGE_READWRITE );

  - VirtualAllocEx => 다른 프로세스의 가상메모리를 확보.
  - 주소 할당단위는 64k배수 뒷4자리가 0이어야 하고, 0일때는 자동으로 할당된다.
  - malloc은 결국 윈도우에선 VirtualAlloc 을 호출 한다는 것을 알 수 있다.

2. MMF( Memory Mapped File )

  - OS는 오래동안 쓰지 않는 프로그램의 물리메모리를 가상메모리에 백업해 놓는다.
  - 백업되 있던 가상메모리를 물리 메모리로 올리기 위해선 오버헤드가 발생된다.
  - VirtualLock 을 쓰면 항상 물리공간에 있게 할 수 있다. ( 물리메모리에 부담이 간다. )
  - Working Set : 물리 공간에 있는 Page 집합 SetProcessWorkingSet ( 1~2M를 쓸 수 있다. )

사용자 삽입 이미지

  - 메모리에 연결된 파일을 MMF 라 한다.!! 파일로 직접 접근하여 쓸 수 있다.
  - 보안을 설정해주면 접근을 제한 해줄 수 있다. 아무나 접근 할 수 있는 공유메모리와 다른 개념!!!
  - 파일을 Open해서 쓰는 것보다 효율적이다.
    1) 일반파일보다 빠르게 작업을 할 수 있다.
    2) 연결만 해놓으면 그자체가 buf가 되므로 버퍼 없는 작업이 가능하다.
    3) 동일한 파일을 가상주소로 연결 하며 프로세스간 통신(IPC)가 구현된다.

    4) PEVIEW 처럼 exe의 Header를 분석하기 위해선 MMF가 이상적이다. 메모리로 exe를 로드해서 읽기 보다는 가상주소를 exe로 바로 맵핑하면 해당 주소에 맞는 구조체만 만들어 주면 된다.
    5) exe, dll은 모두 MMF로 구성되어 있다. ( Demand Page )모두 Page단위로 필요할 때만 물리메모리에 올려진다.

  - WM_SETTEXT 의 MMF 사용
    1) 다른 프로세스의 윈도우 제목을 변경하고자 할때 문자열만 넘기면 왜 되는 것인가?
int main()
{
    HWND h = FindWindow( 0, "계산기" );

    // 계산기의 캡션바를 "Hello"로 변경한다. - 될까?
    SendMessage( h, WM_SETTEXT, 0, (LPARAM)"Hello" );
}
    2) WM_SETTEXT 메시지를 수행할 때 내부적으로 MMF 에 "Hello" 를 보관했다가 계산기에서 이 영역에서 문자열을 읽어 와서 제목을 변경 해 주는 것이다.

3. DLL Inject ( 참고 문서 :

  - 다른 프로세스에 내가 만든 DLL을 집어넣어 보자.!!! 핵심은 CreateRemoteThread
  - FindWindow 로 계산기 핸들을 얻은 다음에 pid => 프로세스 핸들 을 얻게 되면
  - SetWindowLong으로 WndProc 를 변경 할 수 있을까? (서브클래싱을 할 수 있을까?)
  - 되지 않는다.. 왜냐하면 프로세스간의 독립성을 보장하는 가상메모리를 생각하면 알 수 있다.
  - 계산기의 가상 메모리에는 내가 변경하고자 하는 함수가 올라와 있지 않기 때문이다.
  - 이를 해결하기 위해선 계산기의 가상메모리에 함수를 올려놓아야 한다.( DLL을 사용해서 올려보자!! )

사용자 삽입 이미지

  - 문자열을 써주기 위해선 VirtualAllocEx 로 공간을 할당한 후에 WriteProcessMemory로 써준다.

2007/10/15 - [Study/System] - 10.15(월) 실습-1( 가상메모리, MMF, DLL Inject )
참고문서 :
Tag | ,