System2008. 11. 19. 11:33
1. Text (텍스트영역)

program코드 (cpu에 의해 수행되는 기계어 명령어들이 모여있는 곳)

 

2. Data(데이터영역)

전역변수와 정적(static)변수가 할당된 곳. 프로그램 시작과 동시에 할당되고 프로그램이 종료되어야 메모리에서 소멸된다.

(1) initialized data segment(초기화된 데이터영역) : initialized variables (초기화된 데이터들)

(2) uninitialized data segment(비초기화된 데이터영역) : uninitialized variables (비초기화된 데이터들) – BBS(block started by symbol)이라고도 함

 

3. Stack(스택영역)

automatically allocated variables(local variables) and other stack frame entries

지역변수와 매개변수가 저장되는 곳. 이 영역에 할당된 변수는 함수 호출이 끝나면 사라진다.

-컴파일 타임 크기 결정

 

4. Heap(힙영역)

dynamically allocated variables

동적 메모리 할당하는 곳. 프로그래머가 할당 및 해제를 해주어야 한다.

-런타임 크기 결정


구조도

Posted by skensita
System2008. 11. 19. 11:28

페이징을 위해 먼저 A20 게이트의 설명

- A20게이트를 0으로 클리어 하면 홀수 MB 메모리가 짝수 MB와 2진수 값이 같아진다 -> A20을 켜지 않으면 홀수 MB를 사용할 수 없다.

- A20게이트가 0으로 되어있다는것은 메모리 값의 2진수 표현이 1FFFFF라면 1 1111 1111 1111 1111 1111   이 0 1111 1111 1111 1111 1111되는 것이다. 이것은 과거 8086이 1MB의 주소를 사용한, 20개의 어드레스 라인을 가지고 있었기 때문이다.(즉 0부터 19번 비트까지) CPU가 발달하면서 08286이 24개의 어드레스 라인을 사용했지만 과거 프로그램과의 호환을 위해(1MB가 넘으면 다시 0부터 시작하는 메 모리 방식) 20번 비트를 키보드 컨트롤러의 특정 비트와 AND로 물려놓아 이 키보드 컨트롤러가 1로 셋트되어야만 20번 비트가 제대로 의미를 가질 수 있게 해놓았다.

- 따라서 주소지정을 올바르게 (홀수MB와 짝수 MB를 모두 사용하려면) 사용하려면 A20게이트를 열어놓아야 한다.(아마 요즘은 전혀 필요 없을듯)

예) 1MB는  16진수 -> 0x100000    2진수 -> 1 0000 0000 0000 0000 0000  A20 게이트가 켜져있지 않다면 이 값은 0
2MB는  16진수 -> 0x200000    2진수 -> 10 0000 0000 0000 0000 0000  A20게이트가 켜져있지 않아도 값은 같다.
3MB는 16진수 -> 0X300000    2진수 -> 11 0000 0000 0000 0000 0000   A20게이트가 켜져있지 않다면 이 값은 2MB와 같다.

○ 페이징은 논리주소 -> 선형주소 -> 물리주소의 과정을 거친다.

○ 32비트 시스템에서는 주소를 32비트로 표현한다. 이 32비트 주소는 페이징의 선형주소에 의해 10 비트 10 비트 12비트로 분할되어 각각 의미를 가진다.    

  페이지디렉토리 엔트리 번호(10비트)

  페이지 테이블 엔트리 번호(10비트)

      물리페이지의 offset(12비트)

○ 보호모드를 지원하는 CPU는 CR3 레지스터를 가지는데 이것이 페이지 '디렉토리'의 시작주소를 가진다.

○ 페이지 '디렉토리' 엔트리는...각 페이지 '테이블'의 시작주소의 포인터(주소)를 가진다
    '디렉토리'는 1024개(2의 10승->10비트)의 엔트리를 가지고 있다.
    마찬가지로 페이지 '테이블'은 각 물리페이지 시작주소를 포인터로 가지고 역시 1024개의 엔트리를 가진다.

○ 페이지'테이블'의 엔트리와(물리페이지의 시작주소) 12비트 offset을 합치면 실제 물리주소가 나오게 된다.
    물리페이지의 offset만 12비트인 이유는 각 페이지는 4KB로 모두 잘라져있는데(페이징이선 크기를 모두 4KB로 자른다. 페이지 디렉토리도 4KB 이고 페이지 테이블도 4KB이며 각 물리페이지도 4KB로 램상에 차지한다.) 이 4KB가 4098byte 이고 이것은 2의 12승이다. 따라서 offset이 12비트면 4KB의 영역을 바이트 단위로 접근할 수 있는것이다.

    (역으로 생각해보면 모든 페이지가 4KB단위이므로 4098byte -> 2^12승(byte) -> 2^12승은 16진수로 3칸(?)을 차지한다. 즉 모든 물리페이지는 항상 하위 3자리가 000으로 끝난다. 0x00002000, 0x00008000 등등. 이것이 테이블 엔트리나 디렉토리 엔트리에서 주소지정을 할때 20비트만 사용하는 이유이다.뒷 주소가 0xffffff000 이므로 뒤의 세자리(즉 2의 12승)는 항상 0이라고 생각하고 나머지 5자리 즉 20비트만 필요한것이다. )

○ 결과적으로 디렉토리와 테이블 엔트리의 곱 1024*1024 = 1MB 이고 각각 4KB의 페이지를 가지고 있으므로 1MB*4KB = 4GB 즉 메모리 지정을 4GB까지 할 수 있다.

○ 프로그램에서 요구되는 32bit주소를 찾아가는 순서(페이지 디렉토리를 가리키는 첫번째 10비트를 A, 페이지 테이블을 가리키는 두번째 10비트를 B, 물리페이지 offset의 12비트를 C라 하면)
    - CR3의 주소를 참조해 페이지 디렉토리의 시작주소를 찾아간다.
    - 요구된 주소의 'A'와 디렉토리의 시작주소를 더하면 이 값이 1024개의 테이블중 하나를 가리키는 주소가 된다.(따라서 이 최상위 10비트는 디렉토리의 'offset'이다)
    - 테이블의 시작주소를 얻었으므로 이 주소와 'B'를더한다. 이 더한값은 물리페이지의 시작주소가 된다.(물리페이지는 4KB)
    - 이 물리페이지의 시작주소와 'C'를 결합하면 4KB 페이지 안의 실제 메모리 물리 주소가 나오게 된다.

○ 페이지 디렉토리는 각 프로세스마다 존재하고 페이지 디렉토리의 시작주소는 mm_struct 구조체의 pgd필드가 가 가진다...
여기에 적힌 주소는 가상 주소가 아니라 물리주소이다. 페이징을 해야하면(주소가 요청되면) 이 주소가 cr3에 들어간다..

Posted by skensita
System2008. 11. 19. 11:19



순서

 메모리 관리
    - 가상 메모리(Virual Memory) 개념 및 관련 함수
    - GlobalAlloc와 LocalAlloc 함수
    - Heap에 메모리 사용 함수
    - 배열에 메모리 할당 new
    - 메모리 공유 기법

 스레드
    - 스레드 구동 방법
    - 뮤텍스(Mutex), 세머포어(Semaphore), 이벤트(Event)
    - 멀티 스레드 예제


출처 : 충북대학교

Posted by skensita
Programming/Win32 API2008. 11. 19. 11:16
Question
  Win32 API 책(30장메모리 부분)을 보니까 메모리 할당 부분에 malloc와 new가 나오고
가상 메모리 부분에 VirtualAlloc이 나오든데
그럼 malloc하는 부분은 가상 메모리가 아니란 말씀이신가요?
malloc 하는 부분과 VirtualAlloc 하는 부분의 메모리가 다른 부분인가요?

그리고 그 뒤에 힙 부분에 HeapAlloc와 GlobalAlloc, LocalAlloc 가 나오든데
HeapAlloc는 Win32함수이고 다른 두 개는 Win16의 잔재라고 나와 있더군요.
그래서 이 차이점은 알겠는데
제가 듣기론 malloc도 힙 영역에 메모리를 할당한다고 들었는데
그런것 아닙니까? malloc는 힙 아닌 다른 영역에 할당 하나요?
힙 부분에 다른 함수들이 나오니까 다른 영역에 할당 하나 싶어서요.
그리고 HeapAlloc나 GlobalAlloc는 가상 메모리에 할당 하는게 아닌가요?
가상 메모리의 힙 영역에 할당하는 것인가요?

일단 제가 이해 하기로는 모두 실질적인 메모리에 할당 하는 것은 아니고
가상 메모리에 할당하고 이것을 페이징 해서 실제 메모리에 올리고 사용하고
내리고 하는 걸로 알고 있는데 맞나요?
그리고 가상 메모리의 VirtualAlloc가 할당하는 부분과 malloc가 할당하는 영역,
그리고 HeapAlloc가 할당 하는 영역은 다르다.
이런 건가요?
Answer
  가상메모리란 말그대로 실제 메모리가 아닌 가상의 메모리입니다. win32 환경에서
모든 프로세스는 독립적인 4G 공간의 메모리 주소 공간을 가지게 됩니다. 그런데
실제로 4G의 메모리를 탑재하고 있는 컴퓨터는 없으며, 4G * 프로세스의 갯수
만큼의 메모리를 탑재한 컴퓨터는 더더욱 없습니다. 따라서 없는 것을 실제로
눈에 보이는 것처럼 만들기 위해서 몇가지 트릭이 사용됩니다. 가장 큰 트릭이
스와핑과 페이징이죠.

스와핑은 현재 실제 사용중이지 않은 메모리를 디스크로 옮기고, 실제로 필요한
페이지가 없을 경우 디스크로 부터 로딩해서 사용하는 기술입니다. 페이징은 좀
더 복잡한 개념인데, 가상 메모리가 실제 어떠한 물리 메모리로 맵핑 되는지의
정보를 가지고 있고 그것들을 변환하는 기술이라고 보시면 됩니다.

이것과 별개로 힙메모리라는 것이 나오는데, 이것은 프로세스에서 빈번히
사용하는 서로다른 크기의 작은 메모리 할당/해제시의 단편화가 적고 빠르게
되도록 만든 구조의 메모리 입니다. 이러한 메모리가 별도로 존재하는 것은
아니고 소프트웨어적으로 그렇게 관리하는 것을 말합니다. 보통은 특정한 크기의
풀을 만들어 두고 해당 풀의 메모리를 효율적으로 분할해서 쓰는 방식을 많이
사용합니다.

그럼 각각 이것들을 조작하는 API를 살펴보도록 하죠. 가장 저수준의
VirtualAlloc이라는 함수는 실제 가상 메모리를 할당받고 해제하는 함수입니다.
VirtualAlloc함수를 통해서는 실제 메모리 할당 뿐만 아니라 해당 페이지를
예약하는 등의 저수준 제어도 할 수 있습니다.

HeapAlloc이라는 함수는 힙에 메모리를 할당하는 함수입니다. HeapAlloc의 첫번째
인자로 넘어가는 것이 힙 핸들이죠. 힙은 HeapCreate함수를 통해 생성할 수
있으며, win32에서는 프로세스별로 하나의 기본 힙이 제공됩니다.
GetProcessHeap을 통해서 구할 수 있습니다.

malloc, free, new, delete 함수는 위의 API 보다 좀 더 상위의 계층에서 이러한
일들을 하게 됩니다. C 런타임 라이브러리 초기화 코드에서 해당 라이브러리의 힙
관리자 들이 별도의 힙 메모리를 만들고 관리하게 됩니다.

단순하게 이해하기 위해서는 그냥 위의 세가지 종류가 계층 구조라고 생각하면
편합니다. VirtualAlloc이 가장 하부의 계층, 그것을 이용해 HeapAlloc등의 API가
구현되고, 그 상위에 CRT 힙 함수들이 존재하는 것이죠.

실제로 응용 프로그램 계층에서의 대부분의 작업은 CRT 함수로도 충분히
가능합니다. 별도의 자료 구조를 특정 힙에 할당하는 방법을 쓸때에 Heap함수들을
사용하나 그렇게 자주 있는 일도 아니죠. VirtualAlloc을 쓰는 일은 물론 더더욱
없겠죠. 실제로 대용량 메모리를 사용해야 하는 경우에도 Memory mapped file을
통해서 처리하는 것이 일반적입니다.

음... 쭈욱 적고 밑에 질문글을 다시 보니 동문서답한 느낌도 드네용... ㅋㅋㅋ~

결론적으로 말하면 메모리는 하나입니다. 컴퓨터 뜯어보면 하나죠. ㅋㅋ~ 당연히
프로그램에서 사용하는 메모리도 그거 하나입니다. 하지만 편의상 메모리를
나눠서 쓰겠죠. 우리가 용돈을 받았을때 이건 차비, 식비,... 등으로 쪼개
쓰는것과 같다고 보면 됩니다. 컴퓨터도 그런식으로 용도에 맞게 메모리를
효율적으로 사용하기 위해서 가상메모리, 힙, 스택메모리 같은걸로 분리해서
사용합니다~

--
신영진 (Shin, YoungJin)
http://www.jiniya.net


Posted by skensita
Programming/Win32 API2008. 11. 19. 11:13

From Devpia

각 함수의 정의는 다음과 같다.
 
LPVOID HeapAlloc(HANDLE hHeap, DWORD dwFlags, DWORD dwBytes);
HLOCAL LocalAlloc(UINT uFlags, UINT uBytes);
HGLOBAL GlobalAlloc(UINT uFlags, DWORD dwBytes);

셋 다 힙에서 메모리 블록을 할당받는 동일한 기능을 한다.

LocalAlloc과 GalobalAlloc은 Win32에서는 완전히 동일하다. Win16에서는 어플리케이션마다 가지는 Local 힙과 힙 매니저가 가지는 Global 힙이 따로 존재했지만 Win32에서는 따로 존재하지 않는다. 따라서 Win32에서 Local 힙과 Global 힙은 동일하고 함수 자체는 하위 호환을 위해 남아있다.

LocalAlloc과 GlobalAlloc은 동일하므로 LocalAlloc만을 언급하겠다.

HeapAlloc과 LocalAlloc의 차이점을 살펴보자.
첫째로 메모리 블록을 할당하는 힙 자체가 다르다는 것이다. LocalAlloc은 항상 디폴트 힙에서 할당한다. 디폴트 힙은 하나의 어플리케이션이 하나씩 가지는 메인 힙이다. 이는 프로세스가 생성되고 가상 주소 공간이 생겨날 때 같이 생성되는 힙이다. 그 크기는 링크시 실행파일 헤더에 기록이 된다. 링크 옵션으로 그 크기를 변경할 수도 있다. 기본 값은 1MB이다. 반면에 HeapAlloc은 함수의 첫 번째 인자에서 보듯이 임의의 힙에서 할당한다. HeapCreate로 만든 힙일 수도 있고 GetProcessHeap으로 얻는 디폴트 힙일 수도 있다.

둘째로 리턴값을 살펴보면 HeapAlloc은 포인터를 리턴하지만 LocalAlloc은 핸들을 리턴한다. 따라서 LocalAlloc으로 할당받은 메모리 블록을 실제로 사용할 때는 LocalLock 함수로 포인터를 얻어와야 한다. LocalAlloc의 사용이 좀 더 불편하지만 핸들을 사용하므로써 동기화와 같은 기능을 제공한다. 그리고 포인터를 직접 사용하지 않고 포인터를 사용하는 또 다른 (결과적으로) 이유는 다음과 같다.
LocalAlloc의 첫번째 인자는 할당받을 메모리 블록의 특성을 지정하는데, GMEM_MOVEABLE 이라는 특성을 지정할 수 있다. 이는 힙 메모리 단편화 제거를 위해 또는 힙 공간이 부족할 때 힙 관리자가 메모리 블록을 이동할 수 있도록 하게 하는데 이렇게 되면 포인터로 리턴했을 경우 이동된 후 잘못된 참조를 하게 되므로 핸들로서 관리되는 것이다. 실제로 GMEM_MOVEABLE 이 아닌 GMEM_FIXED로 지정하게 되면 리턴값은 그 자체가 포인터이다.

※ 참고로 C run-time library인 malloc은 위의 API 함수들과 같은 기능을 하지만 디폴트 힙이 아닌 자신만의 힙을 사용하고 자신만의 방법으로 힙을 관리한다.

+ VirtualAlloc() 함수는 뭐냐. 이것도 생각해 봐야.

LocalAlloc()과 같은 heap function 은 memory exception 을 가져 올 수
있다는 것과 대용량의 memory 할당에는 적합하지 않다는 것입니다.



=======  From MSDN : "Heap Functions" ============================================
Windows NT 4.0: External factors may cause accesses to heap memory to generate access violations.
One possible cause of an access violation is very limited space in the paging file. Therefore, all
accesses to heap memory should be protected with structured exception handling. This is not
necessary on later versions of Windows.

Windows 95/98/Me: The heap managers are designed for memory blocks smaller than four megabytes. If
you expect your memory blocks to be larger than one or two megabytes, you can avoid significant
performance degradation by using the VirtualAlloc or VirtualAllocEx function instead.

======= From MSDN : "Comparing Memory Allocation Methods" =========================
This topic provides a brief comparison of the following memory allocation methods:

CoTaskMemAlloc
GlobalAlloc
HeapAlloc
LocalAlloc
malloc
new
VirtualAlloc

The following functions are equivalent: GlobalAlloc, LocalAlloc, and HeapAlloc with the handle
returned by the GetProcessHeap function.

The VirtualAlloc function allows you to specify additional options for memory allocation. However,
its allocations use a page granularity, so using VirtualAlloc can result in higher memory usage.

The malloc function has the disadvantage of being run-time dependent. The new operator has the
disadvantage of being compiler dependent and language dependent.

The CoTaskMemAlloc function has the advantage of working well in either C, C++, or Visual Basic.
It is also the only way to share memory in a COM-based application, since MIDL uses CoTaskMemAlloc
and CoTaskMemFree to marshal memory.

GlobalAlloc, LocalAlloc, HeapAlloc 함수들은 궁극적으로 동일한 힙에 메모리를 할당하지만, 약간의 차이를 가지고 있다. HeapAlloc은 메모리가 할당될 수 없으면 예외를 발생시킬 수 있으며, LocalAlloc은 이런 기능이 없다. LocalAlloc은 핸들 할당을 지원함으로써 핸들값을 바꾸지않고 재할당을 통해 기초메모리 (underlying memory) 를 이동시킬 수 있으며, HeapAlloc은 이런 기능이 없다.

각각의 힙 할당자는 서로 다른 기법을 사용해 구현되었기에, 각 함수에 맞게 메모리를 해제해야 한다. 예를 들어, HeapAlloc으로 할당된 메모리는 LocalFree / GlobalFree이 아닌 HeapFree으로 해제되어야 한다.

VirtualAlloc 함수는 메모리 할당에 추가 옵션을 허용한다. 하지만 이 함수는 페이지 입도를 사용하므로, 더 많은 메모리가 소비될 수 있다.

malloc함수는 런타임 의존적, new연산자는 컴파일러/언어 의존적이라는 약점을 가지고 있다.

 

CoTaskMemAlloc함수는 C/C++/Visual Basic 모두에서 잘 작동하는 이점을 가지고 있다. 또한 MIDL (Microsoft Interface Definition Language) 이 메모리를 마샬링*하는데 CoTaskMemAlloc / CoTaskMemFree를 사용하므로, COM 기반 애플리케이션에서 메모리를 공유하는 유일한 방법이다.

 

*마샬링(marshalling) : 하나 이상의 프로그램 또는 연속적이지 않은 저장공간으로부터 데이터를 모은 다음, 이들을 메시지 버퍼에 집어넣고 특정 수신기나 프로그래밍 인터페이스에 맞도록 그 데이터를 조직화하거나, 미래 정해진 다른 형식으로 변환하는 과정.

주로 한 언어로 작성된 프로그램의 출력 매개변수를 다른 언어로 작성된 프로그램의 입력으로 전달해야 하는 경우 필요하다.

 

 VirtualAlloc은 힙을 사용하지는 않습니다.
그리고 힙처럼 연속된 메모리로 제한되지는 않으며
64KB의 조각들로 할당합니다...
그리고 또 하나...
새로운 사실인데...
new가 메모리 할당에 실패했는데 VirtualAlloc함수가 성공하는 경우가 있다는 군요...

VirtualAlloc이라는 함수가 하드에 잡아준다 이건 절대 아닙니다.
사람들이 가상메모리하면 바로 하드 스와핑을 떠올리시는데 그건 아닙니다.
Virtual* 메모리 API는 Win32에서 가장 원시적인(?) 함수라고 보시면됩니다.
(내가 쓸 메모리는 항상 램에 있어야한다 등등은 AWE라는 메모리 기술로
조절할 수 있습니다)

VirtualAlloc은 큰 메모리를 잡을 때 많이 씁니다.
왜냐면 얘들은 allocation granularity와 page size에 맞춰서
까다롭게 commit, decommit을 해줘야하기 때문이죠.

반면 HeapAlloc함수들은 이런 메모리 바운더리 및 얼라인먼트에대한
부담없이 언제든지 원하는 메모리를 뽑아 쓸 수 있도록 설계된 것이죠.
(결국 HeapAlloc도 VirtualAlloc으로 commit된 메모리에서 나오는 것이죠)
new, malloc 등 모든 함수들은 결국 HeapAlloc으로 귀결된답니다.

아무래도 메모리의 경계를 잘못 맞추어서 겪으신 어려움인것 같군요.
priority낮은 쓰레드가 뒤에서 돌면서 가비지 콜렉터 역할을 하며
페이지크기에 맞게 남은 페이지를 decommit해야하는걸로 알고 있습니다.
여튼, 속도는 잘나오겠지만 쓰는 법이 약간 까다롭죠.

결론은 작은 메모리를 빈번하게 할당, 해지해야할 땐
Heap메모리를 써야한다는 것, 큰 메모리를 비교적 적은 횟수에
쓰실 때엔 Virtual메모리를 쓰는게 정석인 듯 하옵니다..

윈도우 메모리 구조는 Programming Apps for Windows란
책에 가장 자세히 설명되어 있습니다^^

Posted by skensita
Hacking & Security/System2008. 11. 19. 10:06
Posted by skensita