http://blog.daum.net/lks0018/3430801
커널 모드 VS 유저 모드
최신 CPU는 특권 레벨을 제공한다. 예를 들어 인텔 x86 CPU는 Ring0~Ring3(R0가 가장 높은 특권)의 4개의 특권 레벨을 제공하고 알파나 MIPS CPU의 경우 2개의 특권 레벨을 제공한다. 이들 특권 레벨은 코드가 실행할 수 있는 명령 셋을 결정한다. 윈도우는 인텔 프로세서의 Ring0(커널 모드)와 Ring3(유저 모드)의 두 가지 프로세서 접근 모드만을 사용한다. 이 이유는 알파와 같은 프로세서에서 사용되는 특권 레벨과 그 수를 맞추기 위해서이다. 커널 모드와 유저 모드를 사용하는 가장 근본적인 이유는 그림 1.5(가상 메모리 포스트 참조)에서와 같이 유저 애플리케이션을 운영체제로부터 격리되도록 하여 운영체제의 중요한 데이터를 손상시키지 않도록 하기 위함이다.
윈도우에서 실행되는 스레드는 어떤 모드에서 실행될까? 이것을 알아보기 전에 먼저 유저 모드와 커널 모드의 특징을 간략히 살펴보자.
유저 모드는 다음과 같은 특징을 갖는다.
n 사용자 애플리케이션 코드가 실행한다.
n 시스템 데이터에 제한된 접근만이 허용되며 하드웨어를 직접 접근할 수 없다. 예를 들어 x86 32비트 윈도우의 경우 2GB 이상의 메모리에 접근할 수 없으며 포트 접근 명령인 IN, OUT을 사용할 수 없고 또한 SIDT(CPU 내의 IDTR 레지스터 값을 읽는다) 같은 명령을 사용할 수 없다. 만약 이런 명령을 유저 모드에서 사용한다면 예외가 발생한다.
n 유저 애플리케이션은 시스템 서비스 호출을 하면 유저 모드에서 커널 모드로 전환된다.
n CPU는 유저 모드 특권 수준으로 코드를 실행한다.
n 유저 모드에서 실행하는 스레드는 자신만의 유저 모드 스택을 가진다.
커널 모드는 다음과 같은 특징을 갖는다.
n 시스템의 모든 메모리에 접근할 수 있고 모든 CPU 명령을 실행할 수 있다.
n 운영체제 코드나 디바이스 드라이버 같은 커널 모드 코드를 실행한다.
n CPU는 커널 모드 특권 수준에서 코드를 실행한다.
코드는 다음과 같은 이유로 인하여 커널 모드에서 실행한다.
1. 유저 모드의 시스템 서비스 호출 요청
n 이 요청은 시스템 서비스 디스패치 메커니즘에 의해 커널로 전달된다.
n 커널 모드 코드는 요청 스레드의 컨텍스트에서 실행한다.
2. 외부 디바이스의 인터럽트
n 인터럽트 서비스 루틴(ISR)과 ISR 루틴에서 종종 요청하는 DPC 루틴은 항상 커널 모드에서 실행한다.
3. 커널 모드 전용 스레드
n 디바이스 드라이버나 메모리 관리자, 캐시 관리자 등이 생성한 스레드는 시스템 프로세스에서 항상 커널 모드로 실행한다.
n 이들 스레드는 그 밖의 다른 스레드와 동일한 방식으로 스케줄링되고 선점된다.
오브젝트와 핸들
프로세스에서 사용되는 모든 리소스는 오브젝트로서 관리되며 핸들을 사용하여 접근한다. 그렇다면 오브젝트의 실체와 핸들이 무엇인지 알아보자.
윈도우에서 오브젝트는 윈도우가 정의한 오브젝트 유형의 런타임 인스턴스로서 오브젝트 관리자에 의해 관리된다. 오브젝트 유형은 시스템이 정의한 데이터 유형과 이 유형의 오브젝트 조작에 사용되는 함수(오브젝트 메소드), 이 유형의 오브젝트 상태를 정의하는 오브젝트 속성으로 이루어져 있다.
윈도우에서는 두 종류의 오브젝트를 사용한다.
n 익스큐티브 오브젝트 : 여러 익스큐티브 구성요소에 의해 구현되는 오브젝트로서 윈도우 API에 의해 노출된다. 애플리케이션에서 흔히 사용하는 이벤트, 파일, 프로세스, 스레드, 타이머 오브젝트가 이에 해당한다. 이들 오브젝트는 커널 오브젝트(보다 세부적으로는 다음에 설명하는 커널 디스패처 오브젝트)를 근간으로 하여 만들어 진다.
n 커널 오브젝트 : 윈도우 커널에 의해 구현되는 보다 기본적인 오브젝트로서 유저 모드 코드에서는 접근할 수 없으며 주로 익스큐티브 구성요소나 디바이스 드라이버에 의해 사용된다. 커널 모드 오브젝트는 그 용도에 따라 다시 두 가지 유형으로 구분할 수 있다.
n 인터럽트 오브젝트와 APC, DPC 오브젝트 같이 운영체제의 함수를 제어하기 위한 컨트롤 오브젝트
n 프로세스 간의 동기화 및 스레드 스케줄링에 사용되는 커널 스레드와 뮤텍스, 이벤트와 같은 디스패처(dispatcher) 오브젝트
프로세스는 CreateFile 또는 CreateEvent와 같은 함수를 사용하여 오브젝트를 생성하거나 오픈할 때에 오브젝트 핸들을 받는다. 오브젝트에 대한 프로세스의 접근 권한 검사는 핸들이 생성될 때 이루어진다. 따라서 성공적으로 핸들을 반환 받았다는 것은 접근 권한이 허용되었다는 것을 의미한다. 핸들은 DuplicateHandle API함수에 의해 또한 한 프로세스에서 또 다른 프로세스로 복사될 수도 있다. 이때 접근 권한을 축소할 수는 있지만 확장할 수는 없다. 한 가지 유의할 사항은 핸들은 프로세스 특정적(그림 1.2에서 각 프로세스는 자신만의 핸들 테이블을 유지한다)이라는 점이다. 이 사실은 한 프로세스 내에 존재하는 모든 스레드는 동일 프로세스 내의 다른 스레드가 오픈한 핸들을 사용할 수 있지만 다른 프로세스에 속하는 스레드가 오픈한 핸들을 사용할 수는 없다는 것을 나타낸다.
핸들은 유저 모드에서 오브젝트를 접근할 때 사용하는 일종의 패스포트(passport)이지 오브젝트 자체는 아니다. 따라서 시스템은 핸들을 접근하고자 하는 오브젝트의 주소로 변환하여야 한다. 이 변환 과정은 윈도우 2000과 그 상위 버전(윈도우 XP, 윈도우 2003 서버)에서 다소 다르게 이루어진다. 먼저 그림 1.6을 통해 윈도우 2000에서 이루어지는 핸들/오브젝트 변환 과정을 32비트 시스템이라는 가정하에서 살펴보자.
시스템은 EPROCESS 구조체(그림 1.2 참조) 내의 핸들 테이블 필드로부터 HANDLE_LAYER1테이블의 베이스 주소를 구하고 32비트 핸들의 8비트(비트25:18)를 HANDLE_LAYER1테이블의 인덱스로 사용하여 HANDLE_LAYER2테이블의 베이스 주소를 구한 다음 핸들의 8비트(비트 17:10)를 사용하여 HANDLE_LAYER3테이블의 베이스 주소를 구하고 핸들의 8비트(비트 9:2)를 HANDLE_LAYER3의 인덱스로 사용하여 오브젝트 헤더의 주소로 변환한다. 보다시피 이 메커니즘을 구현하기 위해서는 프로세스가 극소수의 핸들을 사용하더라도 핸들 테이블을 위한 많은 메모리 공간을 필요로 한다.
윈도우 2000의 이런 단점을 극복하기 위해 윈도우 XP와 그 상위 운영체제에서는 향상된 핸들/오브젝트 변환 메커니즘을 사용한다. 이 메커니즘은 한 프로세스가 사용하는 핸들 수에 따라서 핸들 테이블이 동적으로 구축되는 것에 기반한다. 예를 들어 프로세스에서 사용하는 핸들의 개수가 512개 미만이라면 그림 1.7과 같은 1단계(One-Level) 변환 핸들 테이블 엔트리만 사용된다. 사용 핸들의 수가 512 개를 초과하고 512,000 개 보다 작다면 그림 1.8과 같은 2 단계 변환 메커니즘을 사용한다. 만약 사용 핸들의 수가 512,000 개를 초과하고 16,000,000 개 보다 작다면 그림 1.9와 같은 3 단계 변환 메커니즘을 사용하도록 핸들 테이블이 동적으로 구축된다.
오브젝트 관리자는 오브젝트의 수명 관리를 구현하기 위해 각 오브젝트마다 HandleCount와 ReferenceCount를 관리한다. 프로세스가 오브젝트를 오픈하고 클로즈할 때 HandleCount는 각각 1 증가하고 1 감소한다. ReferenceCount는 오브젝트의 참조가 발생할 때 1증가하고 참조가 해제될 때 1감소한다. 그림 1.10에서 이벤트 오브젝트 A는 프로세스 A와 프로세스 B가 공유(프로세스 B는 DuplicateHandle API를 통해 핸들을 복사한다)하고 있다. 또한 커널의 구성요소 또는 드라이버도 커널 함수 ObReferenceObjectByPointer를 사용하여 이벤트 오브젝트 A를 참조하고 있다. 따라서 이벤트 오브젝트 A의 핸들 카운트는 2, 참조 카운트는 3이 된다. 반면에 이벤트 오브젝트 B는 프로세스 B에서만 오픈되므로 핸들 카운트, 참조 카운트 둘 다 1이다. 오브젝트 관리자는 오브젝트의 핸들 카운트와 참조 카운트 모두 0이 될 때에만 해당 오브젝트를 삭제한다. 오브젝트 누수를 방지하기 위해서는 반드시 오픈한 핸들을 닫아야 하며 또한 오브젝트를 직접 참조한 경우는 반드시 해제를 해야 한다.
ObReferenceObjectByPointer 같은 함수를 사용하여 커널 모드 컴포넌트가 생성한 오브젝트 포인터는 모든 실행 컨텍스트에서 유효한 데 그 이유는 이 포인터(오브젝트를 참조하는 가상 주소)는 커널 가상 주소 범위에 속하기 때문이다. 하지만 오브젝트 핸들은 핸들이 구해진 실행 컨텍스트에 한정적이어서 해당 실행 컨텍스트에서만 유효하다는 점에 유의해야 한다.
다음 포스트의 주제는 스레드 우선순위와 스케줄링에 관하여 알아보기로 한다.
'Windows > 윈도우 공통' 카테고리의 다른 글
시스템 파일 검사기 sfc.exe (0) | 2014.04.01 |
---|---|
[스크랩] 윈도우 운영체제의 개념 - 가상 메모리 (0) | 2014.04.01 |
[스크랩] Handle Leak 찾기 (0) | 2014.04.01 |
event id 1450 시스템 리소스가 부족하기 때문에 요청한 서비스를 완성할 수 없습니다. (0) | 2014.03.14 |
[스크랩] 빠른 포맷과 일반 포맷의 차이점 (0) | 2014.03.13 |