2013년 1월 31일 목요일

object namespace


이벤트, 뮤텍스, 세마포, 공유 메모리등은 모두 커널오브젝트입니다.
윈도우 API 중에 무언가 생성할 때 이름을 파라미터로 받아들이는 녀석이 있다면 모두 커널오브젝트를 생성하는 API 라고 생각하면 됩니다.
다들 잘 아시는 것처럼 OS 는 이 커널오브젝트들을 관리하기 계층구조로 관리합니다. WinObj 같은 툴을 이용하면 쉽게 확인이 가능하죠.




이렇게 계층 구조로 커널오브젝트를 관리하기 때문에 오브젝트를 처리할 때는 네임스페이스처리를 잘 해주어야하는데요. Windows xp 까지는 콘솔세션은 세션 0, 원격데스크탑 세션은 세션 1, 세션 2 이렇게 세션이 생성되었습니다. ( 대부분의 사용자가 사용하는 ) 콘솔세션은 서비스와 어플리케이션이 동일한 세션에서 실행되었기때문에 별 문제가 없었습니다.

보통 이름있는 이벤트를 생성해서 서비스/응용프로그램간에 공유하는 경우 아래와 같은 코드가 문제없이 실행되었을 것입니다.
HANDLE event_handle = CreateEventW(
                          NULL, 
                          FALSE, 
                          FALSE, 
                          L"my_nmaed_event"
                          );
하지만 vista 이후에는 이런 코드가 정상작동하지 않을 것입니다.
vista 이후부터는 서비스 세션(세션 0)과 응용프로그램 세션(세션 1)이 분리되었기 때문이지요.



위의 코드는 \Sessions\응용프로그램이 실행된 세션번호\BaseNamedObjects 네임스페이스에 객체를 생성합니다.
아마 \Sessions\1\BaseNamedObjects\my_named_event 가 될 것입니다.
이 이벤트를 서비스에서 오픈해서 공유하려면 아래와 같은 코드를 사용하면 되겠죠.
HANDLE event_handle = OpenEventW(
                            EVENT_ALL_ACCESS,
                            FALSE, 
                            L"\\Sessions\\1\\BaseNamedObjects\\my_nmaed_event"
                            );
그런데 어플리케이션이 세션 2 에서 실행중이라면 어쩌죠? (원격데스크탑 등으로 접속해서 실행한 경우)
반대로 서비스에서 객체를 생성하면 항상 \Sessions\0\BaseNamedObjects\ 네임스페이스를 사용하니 되지 않겠느냐 라고 생각할 수도 있겠지만 그다지 좋은 해결책은 아니죠.
그래서 만들어진것이 Global 네임스페이스입니다.

Global 네임스페이스를 사용하면 세션에 구애 받지 않고, \BaseNamedObjects 네임스페이스를 사용하게 됩니다.
즉 아래 코드는 모두 \BaseNamedObject\my_named_event 객체를 생성하고, 오픈하게 됩니다.
따라서 서로 다른 세션에 있어도 오브젝트에 대한 접근이 가능하게 됩니다.
HANDLE event_handle = CreateEventW(
                          NULL, 
                          FALSE, 
                          FALSE, 
                          L"Global\\my_nmaed_event"
                          );
이렇게 오브젝트를 생성하고,
HANDLE event_handle = OpenEventW(
                          EVENT_ALL_ACCESS,
                          FALSE, 
                          L"Global\\my_nmaed_event"
                          );
이렇게 오픈해서 사용하면 됩니다.
만일 디바이스 드라이버의 경우 \BaseNamedObjects\my_named_object 에 접근하면 됩니다.
UNICODE_STRING us;
HANDLE   r0_handle;
PKEVENT   r0_object;
RtlInitUnicodeString(&us, L"\\BaseNamedObjects\\my_named_object");
r0_object = IoCreateNotificationEvent(&us, &r0_handle);


결론!
공유가 필요한 커널오브젝트를 사용하는 경우 오브젝트 네임스페이스 문제를 고려하자.
일반적으로 Global 네임스페이스를 이용하는게 편리하다.

댓글 2개:

  1. 제가 ProcMon등으로 보면 BaseNamedObjects가 Directory에도 보고이고 Mutant에도 보이고 하던데, 게다가 특정 게임하시는 분들은 Section에 있는 BaseNamedObjects을 Close Handle하여 멀티 실행을 하고요. 보여주신 소스를 보면 BaseNamedObjects가 임의로 지정한 이름의 일부분으로 보입니다. BaseNamedObjects는 사용자정의입니까 아니면 고정된 이름입니까? 질문을 한마디로 하면 이렇습니다.

    BaseNamedObjects의 정체는 뭡니까?

    답글삭제
    답글
    1. 윈도우즈 커널은 매우 많은 오브젝트를 관리하는데요, 대표적인게 프로세스, 스레드, 파일, 뮤텍스 , 이벤트 같은 것들 입니다. WinObj (http://technet.microsoft.com/ko-kr/sysinternals/bb896657.aspx ) 라는 툴을 이용하면 다양한 종류의 오브젝트들을 확인할 수 있습니다.
      이 오브젝트라는건 결국 다양한 객체(다른 표현할 단어가 마땅치 않네요) 관리를 위한 공통인터페이스를 정의한 것이구요, 각 객체별로 상이한 내용은 별도로 정의하는거죠.
      이 공통된 관리인터페이스에는 열기/닫기/읽기/쓰기 같은 공통 오퍼레이션과 궁금해 하시는 '이름' 정보도 포함됩니다.
      결국 BaseNamedObjects 는 말 그대로 '이름을 가지는 오브젝트' 입니다.
      파일도 이름이 있고, 뮤텍스도 이름을 가질 수 있고, 이벤트도 이름을 가질 수 있죠. 따라서 파일/뮤텍트(뮤턴트)/이벤트 객체는 BaseNamedObject 이고, 각자 자신들의 이름 (파일명 같은)을 가지고 있습니다.

      삭제