ddd
물리적 저장소가 할당된 페이지들은 보호 특성을 가질 수 있습니다.
이런 보호 특성이 필요한 이유는 가령 malware 애플리케이션들은 데이터를 저장하기 위한 메모리 공간에 코드를 저장한 후 악의적인 코드를 수행하려고 시도하기 때문입니다.
이러한 경우를 방지하기 위해서 물리적 저장소가 할당 된 페이지들은 서로 다른 보호 특성을 가질 필요가 있습니다.
2. 보호 특성은 총 8가지가 존재합니다.
이 중 PAGE_NOACCESS 부터 PAGE_EXECUTE_READWRITE는 이름만으로도 각 플래그의 특성들을 파악할 수 있습니다.
그러나 마지막에 위치하고 있는 PAGE_WRITECOPY와 PAGE_EXECUTE_WRITECOPY는 실행하거나, 읽고, 쓰는 특성 외에 해당 Page에 접근이 일어나면 복사본을 생성하는 특징을 가집니다.
이에 대해서는 뒤에 자세히 알아보도록 하겠습니다.
3. 그렇다면 Windows는 이러한 보호특성을 어떻게 이용할까요?
크게는 DEP, 카피 온 라이트, 특수 접근 보호 특성 플래그로 나누어 볼 수 있습니다.
하나씩 알아보도록 하겠습니다.
4. 처음으로 살펴볼 DEP는 Data Execution Prevention의 약자로서 운영체제가 수행할 코드와 데이터가 저장된 페이지를 구분하여 적용될 수 있는 보호특성을 제한하는 것입니다.
우선 코드가 저장되어 있는 페이지는 실행이 가능한 속성을 가진 보호특성들을 사용할 수 있습니다.
그와는 반대로 데이터가 저장되어 있는 페이지는 실행이 불가능한 속성들을 할당할 수 있습니다.
일반적으로는 코드가 있는 페이지는 PAGE_EXECUTE_READ 속성이 데이터가 있는 페이지는 PAGE_READWRITE 속성을 부여하여서
실행 가능한 페이지에 쓰기를 불가능하게 하고 데이터가 있는 페이지들은 실행이 불가능하게 함으로서 시스템을 malware같은 애플리케이션으로부터 보호할 수 있습니다.
5. PAGE_EXECUTE_READ나 PAGE_READWRITE 같은 일반적인 보호 특성들은 램과 페이징 파일 모두에서 할당이 해지될 때 까지 특성 값이 유지됩니다.
하지만 PAGE_WRITECOPY와 PAGE_EXECUTE_WRITECOPY는 조금 다른 특성을 가지고 있습니다.
일반적으로 데이터가 변경 될 일이 없는 읽기 전용 데이터나 실행 전용데이터를 동일한 작업을 하는 여러 프로세스가 공유한다면 상당한 시스템을 개선시킬 수 있습니다.
하지만 데이터를 변경될 수 있는 Page를 공유한다면 어떨까요?
여기 그림처럼 한 100이라는 데이터를 가진 페이지를 Process1과 Process2가 공유하고 있다고 생각해 봅시다.
이 때 Process 2가 Page의 데이터를 200으로 변경시켰습니다.
이 때 Process1은 정상적인 값을 가지고 연산을 수행할 수 없습니다. 비록 여기서는 2개의 프로세스로 예를 들었지만 많은 프로세스들이 한 Page를 공유하는데 이와 같은 일이 발생한다면 큰 혼란이 생길 것 입니다.
이러한 문제점을 해결하기 위한 기능이 “Copy On Write”입니다.
시스템의 성능을 상당히 개선 시킬 수 있기에 windows는 단일 저장소를 공유하는 기능을 제공합니다.
이러한 기능을 지원하는 것이 “카피 온 라이트”입니다.
이 기능을 지원하기 위해서 앞에서 살펴본 PAGE_WRITECOPY와 PAGE_EXECUTE_WRITECOPY라는 보호특성이 존재합니다.
6. 보호특성의 종류 설명 시 마지막에 언급된 PAGE_WRITECOPY와 PAGE_EXECUTE_WRITECOPY 특성은 변경이 가능한 데이터의 공유를 가능하게 하는 Copy On Write 기능을 지원하기 위해서 존재합니다.
기본적인 접근은 여러 프로세스들이 공유하는 페이지를 한 프로세스가 쓰기 시도 시 프로세스의 고유의 페이지로 새로 구성한다는 것입니다.
카피 온 라이트가 수행되는 순서를 살펴보면,
우선 RAM으로 부터 프리 페이지를 찾습니다. 시스템은 프로세스의 주소 공간에 모듈들이 처음으로 매핑될 때 변경될 가능성이 있는 페이지의 크기만큼 충분한 저장소를 확보해 두기 때문에 이 작업은 항상 성공적으로 수행됩니다.
그리고 첫번째 단계에서 발견할 프리페이지에 쓰기 작업을 수행할 페이지의 내용을 복사합니다. 이 때 프리 페이지에 부여 가능한 보호특성은 PAGE_READWRITE와 PAGE_EXECUTE_READWRITE로 제한됩니다. 그리고 원래 페이지의 보호 특성과 데이터는 갱신되지 않으므로서 기존의 데이터를 보호합니다.
마지막으로 프로세스의 페이지 테이블을 갱신함으로서 동일 가상 주소를 이용할 경우 복사된 페이지로 접근하게 합니다.
이런 과정을 통해서 데이터를 변경한 프로세스는 해당 페이지에 대해 별개의 페이지를 가지게 되고 windows가 변경 가능한 데이터를 여러 프로세스간 공유하는 것을 됩니다.
참고로 PAGE_WRTIECOPY와 PAGE_EXECUTE_WRITECOPY 보호특성은 VIrtualAlloc 함수를 사용시에는 사용할 수 없습니다. 이 보호 특성은 운영체제가 .exe나 DLL 파일 이미지를 매핑할 때에만 사용합니다.
7. 앞에서 살펴본 일반적인 보호특성 외에 특수한 목적을 위한 3가지의 특수 접근 보호 특성 플래그가 존재합니다.
PAGE_NOCACHE는 Commit된 페이지에 대해 캐싱을 수행하지 않게 하는 것인데 이것은 메모리 버퍼 관리가 필요한 하드웨어 디바이스 드라아버 개발자에 의해서 주로 사용됩니다.
PAGE_WRITECOMBINE은 성능개선을 목적으로 단일 장치에 대한 여러 번의 쓰기 작업을 하나로 결합하게 해 줍니다.
이 또한 일반적인 상황이 아닌 하드웨어 디바이스 드라이버 개발자에 의해서 주로 사용됩니다.
마지막으로 PAGE_GUARD는 페이지에 내용이 작성되었을 때 애플리케이션이 사실을 인지하도록 하는 플래그로서 대표적으로는 Stack에서 Overflow를 검출하기 위해서 사용됩니다.
8. 일반적으로 애플리케이션이 파일에 대한 I/O작업을 수행함에 있어서 어떻게 파일을 열고, 읽고, 닫는 것이 좋은 것과 파일을 열고나서 내용을 읽고 쓸 때 얼마만큼의 내용을 버퍼링 하는 것이 좋은가라는 고민을 항상 합니다.
이러한 질문에 대해서 windows는 메모리 맵 파일이라는 해결책을 제시합니다.
Virtual Memory와 비교해 보면 프로세스가 주소 공간을 예약하고 예약된 영역에 물리적 저장소를 Commit하는 과정은 똑같지만 Virtual Memory가 시스템의 페이징 파일을 이용한다면 Memory-mapped File은 디스크 상에 존재하는 파일을 물리적 저장소로 사용한다는 점입니다.
이로 인해서 파일이 일단 영역에 매핑되면 마치 메모리에 파일의 내용이 모두 로드된 것처럼 사용할 수 있습니다.
9. 메모리 맵 파일은 크게 3가지 목적을 위해서 사용됩니다.
첫번째로 .exe나 DLL 파일을 읽고 수행하기 위해서 입니다.
메모리 맵 파일을 사용함으로서 시스템은 페이징 파일을 일정하게 유지할 수 있고, [애플리케이션의 시작 시간도 일정하게 유지 할 수 있습니다.]<- 정확히 이해가 안됨.
두번째로 메모리 맵 파일을 사용하면 파일에 대한 I/O작업이나 파일의 내용에 대한 버퍼링을 자동적으로 수행해 줍니다.
마지막으로 다수의 프로세스 간에 데이터를 공유하기 위해서 사용됩니다. 윈도우는 프로세스들 사이에 데이터를 전달하는 다양한 방법들을 제공하고 있지만 내부적으로는 모두 메모리 맵 파일을 사용하여 구현되었으면, 실제로 메모리 맵 파일을 사용하는 것이 단일의 머신에서 프로세스 간에 데이터를 전달하는 가장 효과적인 방법입니다.
각각에 대해서 좀 더 자세히 알아보겠습니다.
10. 프로세스 생성 시 메모리 맵 파일을 이용할 경우 시스템은 .exe 파일들을 위해 할당된 주소 공간들의 물리적 저장소로 시스템의 페이징 파일 대신 .exe 파일 자체를 지정합니다.
이러한 작업은 DLL에 대해서도 일어납니다.
이러한 작업을 동해서 이미 수행 중인 애플리케이션을 다시 한 번 수행하면, 시스템은 앞서 수행되었던 실행 파일 이미지를 가리키는 파일 매핑 오브젝트를 투영하는 새로운 메모리 맵 뷰에 대한 열기 작업을 수행합니다.
이 것은 이미 로드된 코드와 데이터를 공유하게 합니다. 이를 통한 장점은 한 번 실행된 프로그램을 2, 3번째 실행할 때는 빠른 수행이 가능합니다.
간단한 예로 처음 우리가 MS-WORD 등을 실행할 때는 로딩시간이 필요하지만, 2, 3번 째 창을 띄울 때는 시간이 거의 소모되지 않는 것과 같은 것입니다.
하지만 정적 데이터는 실행 파일과 DLL의 여러 인스턴스들 사이에서 공유됨으로서 문제점이 발생할 수 있습니다. 왜냐하면 프로그램을 컴파일하고 링크하게 되면 코드와 데이터는 단일의 파일로 구성되기 때문입니다.
디스크 상의 실행 파일은 왼쪽의 그림과 같이 코드 섹션과 데이터 섹션으로 분리되어서 저장됩니다. 그리고 프로세스 생성시 페이지 단위로 페이지와 매핑되게 됩니다.