결론 : 커널 오브젝트는 핸들 테이블이란 자료구조를 생성해서 핸들을 관리한다. 핸들을 인자로 취하는 함수를 호출할 땐 Create류 함수를 호출하며, 핸들을 삭제할 경우 CloseHandle 함수를 이용한다.
OS는 프로세스가 초기화되면 프로세스를 위해서 커널 오브젝트 핸들 테이블을 할당한다. 이러한 핸들 테이블은 유일하게 커널 오브젝트를 의해서만 사용된다. 그리고 이 핸들 테이블의 개발 방식이나, 관리 방법 등은 어떤 구체적인 문서화가 되어있지 않다. 간단하게 이런 식으로 있겠구나 하고 넘어가셔도 된다.
이 테이블은 아래와 같은 식으로 커널 오브젝트에 대한 포인터, 액세스 마스크, 플래스로 구성된다.
인덱스 | 커널 오브젝트의 메모리 블록을 가리키는 포인터 | 액세스 마스크(각 비트별 플래그 값을 가지는 DWORD) | 플래그 |
1 | 0x???????? | 0x???????? | 0x???????? |
2 | 0x???????? | 0x???????? | 0x???????? |
... | ... | ... | ... |
프로세스가 최초로 초기화되면, 이 테이블은 비어 있다. 프로세스 내 스레드가 예로 들어 CreateFileMapping과 같이 커널 오브젝트를 생성하는 함수를 호출한다 하면, 커널은 커널 오브젝트를 위한 메모리 블럭을 할당하고, 이후 핸들 테이블을 조사하여 비어있는 공간을 찾아낸 뒤 초기화를 수행한다. 이 때 포인터 멤버는 커널 오브젝트의 자료 구조를 가리키는 내부적인 메모리 주소로 할당되며, 액세스 마스크는 "풀 액세스"로 플레는 "설정" 상태로 초기화 된다.
커널 오브젝트를 생성하는 모든 함수는 프로세스 별로 고유한 핸들 값을 반환하며, 이 값은 프로세스 내 모든 스레드들이 사용할 수 있다. 그리고 이러한 핸들 값을 4로 나누면 커널 오브젝트를 저장하고 있는 핸들 테이블의 인덱스 값을 알 수 있다.
커널 오브젝트를 핸들을 인자로 취하는 함수를 호출할 땐 앞이 Create로 시작하는 류의 함수 중 하나를 호출해서 반환된 핸들 값을 전달해야 한다. 그리고 내부적으로 이러한 함수들은 프로세스 핸들 테이블로부터 사용하고자 하는 커널 오브젝트의 실제 주소를 얻어낸 후 잘 정의된 방식으로 커널 오브젝트의 자료 구조를 변경한다.
만약 유효하지 않는 핸들값을 전달하게 되면 이러한 함수들은 실패하며, GetLastError 호출 결과로 6(ERROR_INVALID_HANDLE)을 반환받는다. 핸들 값은 실제로는 프로세스 핸들 테이블의 인덱스 값으로 활용될 수 있으며, 이는 프로세스 별로 고유한 값이며 동시에 다른 프로세스에 의해 사용될 수 없는 값이다. 만약 다른 프로세스와 공유를 시도하게 된다면, 다른 프로세스 테이블의 완전히 다른 커널 오브젝트를 잠조하기 때문에, 이 오브젝트가 어떤 것인지 알 수 없게 된다.
커널 오브젝트를 생성하는 함수가 실패하면 위와 다르게 아예 0(NULL)값을 반환한다. 이러한 이유 때문에 유효한 커널 오브젝트의 값은 4부터 시작한다.(위에 핸들 값을 4로 나누면 인덱스가 나온단건 기억할 것!) 시스템 가용 메모리가 작거나 보안 문제가 발생한 경우 아예 -1(INVALID_HANDLE_VALUE, WinBase.h에 정의됨)을 반환한다.
커널 오브젝트를 삭제해야 할 경우는 다음과 같은 함수를 실행한다.
BOOL CloseHandle(HANDLE hobject);
이 함수를 내부적으로 살펴보면, 먼저 핸들 테이블을 검사하고 전달받은 핸들 값이 실제 커널 오브젝트에 접근 가능한 지 조사한다, 핸들이 유효한 값이고 시스템이 커널 오브젝트의 자료 구조를 획득하게 된다면 구조체 내의 사용 카운트 멤버를 감소 시킨다. 만약 0이 되었다면 커널 오브젝트를 파괴하고 메모리로부터 제거한다.
유효하지 않은 핸들을 전달한다면, 프로세스는 정상적으로 수행되지만 CloseHandle은 FALSE를 반환한다. GetLastError를 호출하게 된다면, ERROR_INVALID_HANDLE을 반환한다. 또 다른 경우로는 프로세스가 디버깅 상태라면 디버깅을 할 수 있도록 0xC0000008값을 반환한다.
CloseHandle함수는 반환 직전에 프로세스 핸들 테이블에서 해당 항목을 삭제한다. 주의해야 할 점은, 내가 CloseHandle을 했다 해도 커널 오브젝트에 대한 접근이 안 될 뿐이지, 사용 카운트가 0이 되지 않는 이상 커널 오브젝트는 삭제되지 않는다는 점은 주의 해야 한다. 그리고 이런 상황이 오래가면, 오브젝트 누수 상황이 발생한다.
다행인 점은(그러면서 위와 같은 상황인 경우 아닌 점은) 프로세스가 종료되면 OS는 모든 리소스를 반환한다. (이러한 매커니즘은 GDI에서도 동일하다.)
참고자료
『제프리 리처의 Windows Vis C/C++』, 제프리 리처 저, 2008
'Operating System' 카테고리의 다른 글
[Windows] 커널 오브젝트란? (0) | 2022.07.04 |
---|---|
[OS/Linux] 파일 디스크립터 (0) | 2022.04.24 |
[OS/네트워크] 네트워크 프로그래밍 (0) | 2022.04.24 |
[OS] CPU 스케쥴링 (0) | 2022.02.08 |
[C#, OS] Monitor (0) | 2022.02.08 |