728x90

프로세스 별로 핸들정보를 저장하고 있는 핸들 테이블이 존재한다.

각각의 프로세스가 자신만의 핸들 테이블을 하나씩 구성하고 관리한다.

 

프로세스가 CreateProcess 함수나 CreateMailslot과 같은 함수 호출을 통해서 리소스 생성을 요구한 결과로 핸들 정보를 얻게 될 경우, 프로세스 자신에게 속해있는 핸들 테이블에 해당 정보가 등록된다.

 

 

핸들 테이블은 프로세스 별로 독립적이다.

 

 

핸들의 상속

CreateProcess 함수를 호출하면 새로운 자식 프로세스가 생성되고, 자식프로세스를 위한 핸들 테이블도 더불어 생성된다. CreateProcess 함수 호출시 전달되는 인자에 따라 부모프로세스의 핸들테이블에 등록되어 있는 핸들 정보들을 자식 프로세스에 상속시킬 수 있다.

 

핸들의 상속에 대한 이해

 

자식프로세스 핸들 테이블 상속

실제 핸들테이블에는 해당 핸들의 상속여부를 결정짓기 위한 컬럼이 존재한다. 또한 핸들테이블의 레코드가 상속될때 상속 여부에 대한 정보도 변경없이 그대로 상속된다. 따라서 자식 프로세스가 또 다른 자식 프로세스를 생성할 경우에도 이 핸들에 대한 정보도 계속해서 상속된다.

 

CreateProcess 함수중 bInheritHandle 전달인자는 자식 프로세스에게 핸들 테이블에 등록되어 있는 핸들 정보를 상속해 줄것인지, 말것인지를 결정하는 요소이다. TRUE를 인자로 전달할 경우 부모 프로세스의 핸들 테이블 정보는 자식 프로세스로 상속된다.

 

핸들의 상속과 커널 오브젝트의 Usage Count

프로세스의 핸들 테이블에 해당 핸들에 대한 정보가 갱신(추가)되었을 때 프로세스가 핸들을 얻게 되었다고 한다.

 

자식프로세스 생성 후 ->

 

자식 프로세스 핸들테이블 상속 후 Usage Count

핸들 테이블에 등록되어 있는 수만큼 Usage Count가 정해진다.

 

Mailslot 예제 핸들테이블

 

MailReceiver의 핸들 테이블의 127, 0x1200 번지에 있는 커널 오브젝트는 CreateMailSlot 에 의해 생성된 것이다.

MailSender의 핸들 테이블의 핸들 127은 0x1700번지에 있는 커널 오브젝트는 MailSlot에 데이터를 쓰기위한 CreateFile에 의해 생성된 것이다. 핸들 테이블은 프로세스 마다 독립적인 요소이기 때문에 핸들값이 같아도 가리키는 커널 오브젝트는 같을수도 있고, 다를 수도 있다.

 

Pseudo 핸들과 핸들의 중복(Duplicate)

GetCurrentProcess 함수를 통해서 얻은 핸들은 가짜 핸들(Pseudo 핸들) 이다.

 

GetCurrentProcess함수를 통해 얻은 핸들은 핸들 테이블에 등록되어 있지 않은 핸들이고, 실행중인 프로세스를 참조하기 위한 용도로 정의해놓은, 약속된 상수가 반환되는 것이기 때문이다. 그렇기 때문에 자식 프로세스에게 상속되지 않고, CloseHandle함수가 호출되어도 아무일도 발생하지 않는다.

 

실행중인 프로세스의 진짜 핸들을 얻기 위해서는 DuplicateHandle 이라는 함수가 필요하다.

DuplicateHandle()

BOOL DuplicateHandle(

       HANDLE hSourceProcessHandle,

       HANDLE hSourceHandle,

       HANDLE hTargetProcessHandle,

       LPHANDLE lpTargetHandle,

       DWORD dwDesiredAccess,

       BOOL bInheritHandle,

       DWORD dwOptions

);

1.hSourceProcessHandle : 복제할 핸들을 소유하는 프로세스 지정

2.hSourceHandle : 복제할 핸들 지정

3.hTargetProcessHandle : 복제된 핸들을 소유할 프로세스 지정

4.lpTargetHandle : 복제된 핸들값을 저장할 변수의 주소를 지정

5.dwDesiredAccess : 복제된 핸들의 접근권한 지정

6.bInheritHandle : 복제된 핸들 상속 여부

7.dwOptions : DUPLICATE_SAME_ACCESS를 전달하면 원본 핸들과 동일한 접근권한, DUPLICATE_CLOSE_SOURCE가 전달되면 원본 핸들 종료

 

ex)

DulpcateHandle(A핸들, 256, B핸들, &val, ...);

DuplicateHandle(

      프로세스 A 핸들,                     //프로세스 A에 존재하는

      256,                                    //핸들 256의 정보를

      프로세스 B 핸들,                     //프로세스 B의 핸들 테이블에 등록한다.

      &val,                                  //등록된 핸들의 값은 변수 val에 저장한다.

      ...

);

프로세스를 생성할 때 핸들 테이블을 상속시키면 핸들 값 그대로 상속되지만 Duplicate Handle함수를 사용하면 새로운 핸들 값을 지정하여 테이블에 등록한다.

 

 

DuplicateHandle 사용예시

DuplicateHandle(

       프로세스 A 핸들,

       256

       프로세스 A 핸들,

       &val,

       ...

);

위 사용은 프로세스 A핸들 테이블의 256에 해당하는 핸들을 A프로세스 핸들테이블에 저장하라는 것이다.

DuplicateHandle함수에 의해 핸들이 복사되고 나면, Usage Count 도 증가한다. 복사된 핸들에 대해서도 CloseHandle 함수를 통해 핸들을 반환해야한다.

 

DuplicateHandle(

      GetCurrentProcess(), GetCurrentProcess(),

      GetCurrentProcess, &hProcess, ...

);

위와 같이 호출하면 진짜 핸들이 생성되어 핸들 테이블에 등록된다.

 

위와같이 DuplicateHandle함수로 자기자신의 핸들을 테이블에 등록할 때 beInheritHandle인자를 TRUE로 주어 상속을 하게 설정하면 등록된 부모프로세스의 핸들을 자식에게 상속시켜 줄 수 있다. 이후 부모프로세스의 핸들을 통해 자식 프로세스가 부모프로세스의 정보를 참조하거나 상태를 알 수 있다.

728x90

'Programming > Windows System Programming' 카테고리의 다른 글

스케줄링 알고리즘과 우선순위  (0) 2020.07.13
파이프 방식의 IPC  (0) 2020.07.12
프로세스 환경변수  (0) 2020.07.11
Signaled vs Non-Signaled  (0) 2020.07.11
프로세스간 통신(IPC) 메일 슬롯  (0) 2020.07.10
728x90

환경변수 : 프로세스 별로 별도의 메모리 공간에 저장하고 관리하는 문자열 데이터

환경변수 구조 : Key = Value

 

[Key,value]의 형태를 가진다. 자식프로세스 생성시, 자식 프로세스의 환경변수를 등록할 수 있고, 부모 프로세스의 환경변수를 상속시킬수도 있다.

 

환경변수등록 함수 SetEnvironmentVariable

BOOL SetEnvironmentVariable(

       LPCTSTR     lpName,

       LPCTSTR     lpValue,

);

1.lpName : Key에 해당하는 값을 지정한다. 이후 Key를 통해 value값을 참조한다.

2.lpValue : value에 해당하는 값을 지정한다.

 

환경변수 참조 함수 GetEnvironmentVariable

DWORD GetEnvironmentVariable(

     LPCTSTR lpName,

     LPTSTR   lpBuffer,

     DWORD  nSize

);

1.lpName : key를 전달한다. key에 해당하는 value를 얻는다.

2.lpBuffer : value를 저장하기 위한 메모리 주소

3.nSize : lpBuffer가 가리키는 메모리 크기

728x90
728x90

커널 오브젝트의 두가지 상태

커널 오브젝트는 특정 상황에 따라 두가지 상태를 지닌다. Signaled 상태(신호를 받은 상태)와 Non-signaled 상태(신호를 받지 않은 상태)이다.

 

커널 오브젝트의 상태 정보는 커널오브젝트를 구성하는 멤버 변수중 커널 오브젝트의 상태정보를 담당하는 변수에 저장된다. Non-Signaled 상태라면 FALSE, Signaled 상태라면 TRUE 값을 지니게 된다.

 

프로세스 커널 오브젝트 상태

1.프로세스 커널 오브젝트는 오브젝트가 생성될 때 만들어진다.

2.처음 커널 오브젝트가 생성되면 커널오브젝트의 상태는 Non-Signaled 상태이다.

3.프로세스가 종료되면 프로세스 커널오브젝트의 상태는 Signaled 가 된다.

프로세스가 실행중일 때에는 프로세스 커널 오브젝트의 상태가 Non-Signaled 이고 프로세스가 종료되면 운영체제에 의해 Signaled 상태가 된다.

 

 

Signaled 상태에서  Non-Signaled 상태로의 전환은 일어날 수 없다.

종료된 프로세스가 다시 시행되면 Signaled 에서 Non-Signaled상태가 되겠지만 종료된 프로세스는 다시 실행을 재개하지 못한다. 즉 프로세스 커널 오브젝트의 상태는 일단 Signaled가 되면 절대로 다시 Non-Signaled 상태로 변경되지 않는다.

 

커널 오브젝트의 상태를 확인하는 용도의 함수

핸들을 인자로 전달해서 커널 오브젝트의 상태를 확인하는 함수 WaitForSingleObject

DWORD WaitForSingleObject(

      HANDLE hHandle,

      DWORD dwMilliseconds

);

 

1.hHandle : 상태 확인을 원하는 커널 오브젝트의 핸들을 인자로 전달한다.

2.dwMilliseconds : hHandle이 가리키는 커널오브젝트가 Signaled 상태가 되었을 때 반환한다. 이 함수는 커널 오브젝트가 Signaled 상태가 될때까지 기다리는 함수이다. dwMilliseconds는 커널 오브젝트가 Signaled상태가 될때까지 기다릴 수 있는 최대 시간을 밀리 세컨드(Milliseconds)단위로 지정하는 용도로 사용되는 인자다. 상수 INFINITE를 인자로 전달하면, 커널 오브젝트가 Signaled 상태가 될때까지 반환하지 않고 무한정 기다린다.

 

WaitForSingleObject함수의 반환 결과

      vlaue                                      의미

WAIT_OBJECT_O                     커널 오브젝트가 Signaled 상태가 되었을 때 반환되는 값

WAIT_TIMEOUT                      커널 오브젝트가 Signaled 상태가 되지않고, dwMilliseconds인자를 통해서 설정된                                                  시간이 다 된 경우 반환되는 값

WAIT_ABANDONED                 오류 발생에 의해서 반환되는 경우에 반환되는 값

 

두번째 커널 오브젝트의 상태를 확인하는 함수

WaitForMultipleObject 함수

DWORD WaitForMultipleObjects(

           DWORD nCount,

           const HANDLE * lpHandles,

           BOOL bWaitAll,

           DWORD dwMilliseconds

);

1.nCount :  배열에 저장되어 있는 핸들 개수를 전달한다.

2.lpHandles : 핸들을 저장하고 있는 배열의 주소 정보를 전달한다. 이 주소를 시작으로 nCount개의 핸들이 대상이된다.

3.bWaitAll : TRUE 전달시 관찰대상 모두 Signaled 상태가 되기를 기다리고, FALSE 전달시 하나라도 Signaled 상태가                      되면 반환할 것인지를 전달한다.

4.dwMilliseconds : 타임 아웃을 설정하는 용도로 사용된다.

 

커널 오브젝트에 존재하는 종료코드(Exit Code)

프로세스가 종료되면서 전달하는 값을 종료코드(Exit Code)라 하고, 이 종료코드는 종료되는 프로세스의 커널 오브젝트에 저장된다. 커널 오브젝트에 저장되는 종료코드는 부모 프로세스가 GetExitCodeProcess함수 호출을 통해 자식 프로세스의 종료코드를 얻을 수 있다.

 

WaitForSingleObject 함수의 유용성

자식프로세스의 종료코드를 얻기 위해서는 자식프로세스들이 종료된 상태여야 한다. 부모 프로세스 입장에서 자식프로세스가 종료될때까지 기다렸다가 GetExitCodeProcess함수를 호출해야 한다. 부모프로세스가 자식프로세스 종료때까지 기다린느 것은 WaitForSingleObject 함수를 사용하면 충분히 가능하다.

 

CreateProcess 함수를 통해 얻게되는 핸들을 이용하여 WaitForSingleObject를 호출하여 INFINITE를 인자로 주면 부모프로세스는 자식프로세스가 종료될때 까지 블로킹(Blocking)상태에 놓인다. 이후 자식프로스세가 종료되면 자식 프로세스의 커널 오브젝트는 Signaled 상태가 되고 부모 프로세스는 블로킹 상태를 빠져 나와서 나머지 부분을 실행한다.

 

728x90
728x90

IPC : Inter-Process Communication 즉 프로세스 사이의 통신이다.

 

통신하고자 하는 프로세스가 만날 수 있는 여건

공유하는 메모리 영역이 허락되면 프로세스간 통신은 아주 쉬워진다. 그러나 프로세스들은 서로 만나서 데이터를 주고 받는것이 불가능하다. 각 프로세스는 자신에게 할당된 메모리 이외에는 접근이 불가능하기 때문이다.

 

 

운영체제는 프로세스가 자신에게 할당된 메모리 공간 이외의 영역에 접근하는것을 허용하지 않는다. 프로세스가 할당된 메모리 공간 이외의 다른 프로세스 영역에 접근할 수 있다면 여러 프로세스를 실행시켰을때 문제가 발생할 것이다. 이렇게 제한을 하는 이유는 안전성을 높이기 위해서 이다.

 

메일 슬롯 방식의 IPC

Windows에서는 다양한 IPC 기법을 제공한다. 그중 메일 슬롯 기반의 IPC도 있다.

 

메일슬롯(Mail Slot)원리

메일슬롯은 편지를 넣을 수 있는 가느다란 우체통의 입구를 의미한다. 메일 슬롯의 기본 원리는 "데이터를 주고받기 위해 프로세스가 우체통을 마련하는 것"이다.

 

데이터를 보내는 프로세스를 Sender라하고 데이터를 받는 프로세스를 Receiver라고 한다. Receiver는 밖에 메일슬롯이라고 하는 우체통을 하나 걸어둔다. Sender는 Receiver의 주소를 통해서 Receiver의 메일슬롯에 데이터를 날린다. 그러면 Receiver는 메일슬롯을 통해 데이터를 얻는다.

 

Receiver 프로세스는 CreateMailSlot 함수를 통해 우체통을 생성해야 한다.

CreateMailSlot함수

HANDLE CreateMailslot(

          LPCTSTR lpName,

          DWORD nMaxMessageSize,

          DWORD lReadTimeout,

          LPSECURITY_ATTRIBUTES lpSecurityAttributes

);

위 함수는 Mailslot의 핸들값을 반환한다.

 

첫번째 인자는 생성하는 메일 슬롯의 이름을 결정하는데 사용된다. 즉 주소(경로)를 지정하는 것이다.

ex)\\computername\mailslot\[path]name

 

두번째 인자는 메일 슬롯의 버퍼크기를 지정하는데 사용된다.

만약에 0이 전달될 경우 시스템이 허용하는 최대크기로 지정된다.

 

세번째인자인 lReadTimeout은 메일슬롯으로부터 읽어들일 데이터가 있다면 이 데이터들을 읽어들일때까지 ReadFile함수를 사용한다. 메일 슬롯이 비어있다면 데이터가 채워질 때까지 ReadFile함수는 반환하지 않고 블로킹 상태에 놓이게 된다.

lReadTimeout은 최대 블로킹시간을 밀리세컨드 단위로 지정하는데 사용된다.

 

lpSecurityAttributes : 핸들을 상속하기 위한 용도

 

메일슬롯을 통한 데이터 통신을 하기 위해서 Sender는 Receiver가 만들어 놓은 메일슬롯의 이름(경로)을 알아야 한다.

Sender는 CreateFile함수와 메일 슬롯 이름을 통해 데이터를 보내기 위한 데이터 스트림을 개방하고, 해당 스트림에 WriteFile함수를 통해 데이터를 전송한다.

 

Receiver는 ReadFile함수를 통해서 Mailslot에 전송된 데이터를 읽어들인다.

ReadFile 함수

BOOL ReadFile(

   HANDLE hFile,

   LPVOID lpBuffer,

   DWORD nNumberOfBytesToRead,

   LPDWORD lpNumberOfBytesRead,

   LPOVERLAPPED lpOverlapped

);

hFile : 메일슬롯의 핸들을 인자로 넣으면, 메일슬롯에 존재하는 데이터를 읽어들인다.

lpBuffer : 읽어들인 데이터를 저장할 버퍼를 지정하는 용도로 쓰인다.

nNumberOfBytesToread : 함수 호출이 완료된 이후에, 읽어들인 데이터 크기를 바이트 단위로 얻기위한 변수를 지정한다.

lpOverlapped : 일반적으로 NULL을 전달한다. 중첩된(overlapped) I/O 에서 사용됨

 

WriteFile 함수

BOOL WriteFile(

   HANDLE hFile.

   LPCVOID lpBuffer,

   DWORD nNumberOfBytesToWrite,

   LPDWORD lpNumberOfBytesWritten,

   LPOVERLAPPED lpOverlapped

);

hFile : 데이터를 읽어들일 파일을 설정한다.(데이터를 받을 파일)

lpBuffer : 전송할 데이터가 저장되어 있는 버퍼

nNumberOfBytesToWrite : 전송할 데이터 크기를 지정한다.

lpNumberOfBytesWritten : 함수호출 완료 후 전송된 실제 데이터의 크기를 바이트 단위로 얻기위한 변수의 주소 지정

 

Mailslot의 특징

Mailslot의 경우 Sender에서 Receiver로만 메시지를 전송하는 단방향 통신만 가능하다. 메일 슬롯은 한쪽 방향, 메일슬롯이 설치되어 있는 방향으로만 메시지를 전달할 수 있기 때문에 채팅 프로그램을 구현하기 위해서는 두개의 메일 슬롯을 생성해야만 한다.

또한 메일 슬롯은 브로드캐스팅(Broad casting)방식의 통신을 지원한다. 즉, 하나의 Sender는 한번의 메시지 전송으로  여러 Receiver에게 동일한 메시지를 동시에 전송하는 것이 가능하다.

 

메일 슬롯은 생성과 동시에 Usage Count가 1이다. 메일슬롯을 참조하는 프로세스는 메일슬롯을 생성한 프로세스 하나 뿐이기 때문이다. 메일 슬롯 뿐만아니라, 프로세스와 쓰레드를 제외한 다른 모든 커널 오브젝트는 생성과 동시에 Usage Count가 1이된다.

 

 

 

 

 

 

 

 

728x90
728x90

운영체제가 커널 오브젝트 소멸 시점을 결정하기 위해서는 일단 프로세스가 종료되어야 한다.

 

자식 프로세스의 종료코드는 자식 프로세스의 커널 오브젝트에 저장된다. 자식 프로세스가 종료될때 커널 오브젝트를 소멸시키면 문제가 발생할 수 있다.

 

GetExitCodeProcess 함수

GetExitCodeProcess 함수는 첫번째 인자로 전달된 핸들이 가리키는 프로세스가 반환하는 종료코드(Exit Code, 종료 상태를 알리는 값)을 얻기 위한 함수이다. 전달된 핸들의 프로세스가 종료되지 않고 실행중이라면, STILL_ACTIVE를 반환한다.

 

return에 의해 반환되는 값이나 exit 함수 호출시 전달되는 인자는 종료상황을 알리기 위해 사용된다. 일반적으로 종료코드 -1이나 0은 비정상적 종료를 알리고자 하는 경우에 많이 사용한다.

 

자식 프로세스의 종료코드는 자식 프로세스의 커널 오브젝트에 저장된다. 자식 프로세스가 종료될때 커널 오브젝트도 동시에 소멸된다면 부모 프로세스는 자식 프로세스의 종료코드를 얻을 수 없게 된다. 때문에 프로세스가 종료되었다고 해서 커널 오브젝트까지 동시에 소멸시키지는 않는다.

 

커널 오브젝트는 해당 커널 오브젝트를 참조하는 대상이 하나도 없을때 소멸시킨다.(windows 에서)

 

커널 오브젝트를 참조하는 프로세스가 하나라도 있을 시에는 커널 오브젝트는 소멸되지 않는다. Windows는 커널 오브젝트 소멸 시기를 결정하기 위해 UsageCount를 관리한다.

 

UsageCount는 커널 오브젝트에 접근가능한 대상의 수를 나타낸다.

프로세스는 생성과 동시에 커널오브젝트의 UsageCount가 1이된다. 그리고 자식 프로세스의 경우 UsageCount가 2가 된다. 왜냐하면 프로세스 본인과 부모 프로세스가 PROCESS_INFORMATION 구조체를 통해서 커널 오브젝트에 접근이 가능하기 때문이다.

 

프로세스는 GetCurrentProcess 함수 호출을 통해서 언제든 자신의 커널 오브젝트 참조를 위한 핸들을 얻을 수 있다. 그렇기 때문에 프로세스 생성시 Usage Count는 무조건 1 이상이다.

 

자식 프로세스가 종료 된 후 자식 커널 오브젝트

자식 프로세스가 종료되면 커널오브젝트에 접근하는 대상이 하나 줄기 때문에 Usage Count 값도 1 줄어든다.

 

CloseHandle 함수

CloseHandle 함수는 핸들을 반환하면서 커널 오브젝트의 Usage Count를 하나 감소시키는 기능을 지닌다.

프로세스의 경우 프로세스가 종료되는 시점에서도  UsageCount가 하나 감소한다.

 

CloseHandle 함수와 프로세스 종료는 별개이다. 프로세스 종료 요청시에 사용되는 함수는 TerminateProcess 이다. 이것은 강제 종료 요청함수이다.

 

바탕화면에 있는 아이콘을 더블 클릭해서 프로세스를 생성할 경우, 프로세스의 UsageCount는 2이다. 바탕화면도 일종의 프로세스이고 더블 클릭이라는 이벤트를 통해서 바탕화면 프로세스에게 프로세스 생성을 요청하는 것이기 때문이다.

728x90
728x90

커널 오브젝트의 종속 관계

커널 오브젝트는 Windows 운영체제에 종속적이다.

커널 오브젝트는 프로세스에 종속적인 것이 아니라, 운영체제에 종속적인 관계로 커널 오브젝트의 소멸 시점은 운영체제에 의해서 결정된다. 커널 오브젝트는 프로세스에 종속적인 것이 아니라 운영체제에 종속적인 관계로 여러 프로세스에 의해 접근 가능하다.

 

커널 오브젝트는 운영체제에 종속적이지만 핸들(핸들 테이블)은 프로세스에 종속적이다.

 

프로세스가 생성된 후 정보를 저장하는 PROCESS_INFORMATION pi; 에서 pi.hProcess를 통해 프로세스의 핸들을 받아온다.

 

PROCESS_INFORMATION 정의

typedef struct _PROCESS_INFORMATION

{

    HANDLE hProcess;                  //프로세스 핸들

    HANDLE hThread;                   //쓰레드 핸들

    DWORD dwProcessId;             //프로세스의 ID

    DWORD dwThreadId;              //쓰레드 ID

}PROCESS_INFORMATION;

 

위 정보들을 통해 자식 프로세스의 핸들을 얻을 수 있다.

핸들을 얻는 방법은 커널 오브젝트의 종류와 상황에 따라서 다양하다. 커널 오브젝트는 프로세스에 종속적인 것이 아니라 운영체제에 종속적인 관계로 여러 프로세스에 의해서 접근이(공유가) 가능하다.

 

dwProcessId는 새로 생성되는 프로세스 ID 정보로 채워지게 된다. 프로세스 핸들은 프로세스 커널 오브젝트를 가리키기 위한 것이고, 프로세스 ID는 커널 오브젝트가 아니라 프로세스 자체를 구분짓기 위한 것이다. hThread와 dwThreadId도 각각 핸들과 쓰레드 Id를 가리키는 것이다.

 

커널 오브젝트와 Usage Count

커널 오브젝트를 생성한 주체가 커널 오브젝트를 소멸시킬 권한을 가지고 있다. CreateProcess 함수 호출이 커널 오브젝트의 생성 원인이기는 하나 프로세스 생성에 대한 요청이며, 이 과정에서 운영체제가 프로세스를 관리하고자 커널 오브젝트를 생성한다. 즉 커널 오브젝트 생성 주체는 운영체제이다.

 

커널 오브젝트를 생성한 것은 프로세스가 아니고 프로세스에 종속적이지도 않기 때문에 프로세스가 소멸된다고 해서 커널 오브젝트가 소멸된다고 말할 수 없다.

 

CloseHandle 함수

BOOL Close Handle(

     HANDLE hObject;

);

위 함수는 핸들을 닫는다. 핸들을 반환한다고도 표현한다.

 

ex)

A프로세스는 실행과정에서 B프로세스를 생성한다. 그리고 나서 B프로세스의 핸들을 이용해서 CloseHandle 함수를 호출한다. 이는 B프로세스에 대해서 더이상 내가 관여할 바 아니니, B프로세스 핸들을 반환하라 라는 의미이다.

CloseHandle 함수가 호출되는 시점에서 B프로세스가 종료된다면, CloseHandle 함수는 프로세스 종료 및 커널 오브젝트 반환 기능이 있다고 결론 내릴 수 있다.

 

커널 오브젝트 소멸 예시

위 그림은 CloseHandle함수에 의해서 B프로세스 및 해당 커널 오브젝트가 소멸된다는 가정하에 진행되는 그림이다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90
728x90

커널 : 컴퓨터를 운영하는데 있어서 중심이 되는 운영체제 핵심 부분

커널 오브젝트 : 커널에서 관리하는 중요한 정보를 담아둔 데이터 블록

 

프로세스를 생성하는 실질적인 주체는 운영체제이다. 프로그래머가 아니다. CreateProcess 함수호출을 통해 프로세스 생성을 요구한 것이고, Windows 운영체제가 우리의 요구에 맞게 프로세스를 생성해 주는 것이다. 그러므로 프로세스를 생성하는 실질적인 주체는 운영체제이다.

 

프로세스 생성과 소멸, 프로세스 상태변화 등 생성된 프로세스를 관리하는 것도 운영체제가 하는 일이다.

 

동시에 여러개의 프로세스를 관리하기 위해선 운영체제 입장에서 고정적으로 저장하고 갱신해야 할 정보들이 생기기 마련이다. 프로세스 상태정보(Running, Blocked, Ready 상태)와 우선순위 정보등의 정보는 운영체제 내부에 저장되어야 한다. 그리고 프로세스 상태정보와 프로세스 우선순위 정보도 변경될때 마다 갱신되어야 한다. 이러한 정보 저장과 갱신을 통한 정보들로 프로세스 스케줄러가 프로세스를 관리할 수 있다.

 

운영체제가 프로세스를 관리하기 위해 프로세스에 관련된 몇몇 정보를 저장할 수 있어야 하고, 참조 및 변경도 가능해야 한다. 이러한 프로세스 정보를 저장하기 위해 구조체를 정의하고, 이 구조체를 커널 오브젝트 라고 부른다. 커널 오브젝트는 프로세스가 생성될때 마다 하나씩 생성된다.

프로세스의 커널 오브젝트는 프로그래머가 직접 생성하거나 조작할 수 없다.

 

프로세스가 생성될 때에만 커널 오브젝트가 생성되는 것은 아니다. 프로세스 내에서 흐름을 구성하는 쓰레드를 생성할 때에도, IPC를 위해 사용되는 파이프나 메일 슬롯을 생성할 때에도 커널 오브젝트를 생성해서 필요한 정보들을 채워야만 한다.

 

프로세스, 쓰레드, 파이프, 메일 슬롯 등 커널 오브젝트를 생성할 때 커널 오브젝트의 종류에 따라서 서로 다른 구조체를 기반으로 생성된다. 대상에 따라 관리되어야할 정보들이 다르기 때문에 커널 오브젝트의 형태(커널 오브젝트를 구성하는 멤버)도 다를 수 밖에 없다.

 

예를 들어 파일이 생성될때 파일 커널 오브젝트에는 파일 속성(Read 모드, Write 모드 or Read/Write 모드)정보가 저장되어야 한다. Read 모드로 개방된 파일에 데이터를 저장하려는 경우, Windows에 의해 접근이 허용되지 않는다. 반면에 프로세스 커널 오브젝트에 파일 속성 정보를 저장할 필요가 없고 프로세스 우선 순위를 저장하기 위한 멤버가 필요하다.

 

커널 오브젝트 : 프로세스, 쓰레드 혹은 파일과 같은 리소스들을 관리하기 위해 필요한 정보를 저장하는 메모리 블록

 

☆Windows 커널에 의해서 관리되는 리소스 수만큼 커널 오브젝트도 생성된다.

 

Kernel Object                                              Resource

파이프 커널 오브젝트 <--------------------------> 파이프

프로세스 커널 오브젝트 <------------------------> 프로세스

쓰레드 커널 오브젝트 <----------------------------> 쓰레드

 

프로그래머는 직접 커널 오브젝트를 조작할 수 없다. 그러나 시스템 함수 호출을 통해 간접적인 조작은 가능하다.

 

프로세스 우선순위 변경

BOOL SetPriorityClass(

          HANDLE hProcess,

          DWORD dwPriorityClass

);

hProcess : 우선순위를 변경할 프로세스의 핸들(Handle)

dwPriorityClass : 새로 적용할 우선순위 정보

 

"hProcess가 가리키는 프로세스의 우선순위를 dwPriorityClass로 변경한다."

 

SetPriorityClass는 특정 프로세스의 우선순위를 높이는 일이다. 그러나 프로세스의 우선순위 정보는 프로세스 커널 오브젝트에 존재한다. windows는 커널 오브젝트를 생성할 때마다 핸들이라는 정수값을 부여한다. 이 정수값만 알 수 있다면, 커널 오브젝트를 지시하는것은 쉬워진다.

 

Handle                                           Kernel Object                                                Resource

파이프 핸들 <------------------------>파이프 커널 오브젝트 <-----------------------------> 파이프

프로세스 핸들<---------------------->프로세스 커널 오브젝트 <-------------------------->프로세스

쓰레드 핸들 <---------------------->쓰레드 커널 오브젝트 <------------------------------->쓰레드

 

SetPriorityClass 함수의 첫번쨰 인자는 프로세스의 커널 오브젝트를 가리키는 핸들을 전달하는 것이다.

 

*****Sleep함수는 프로세스를 Blocked상태가 되게 한다.******

 

현재 실행되고 있는 프로세스의 핸들 얻기

GetCurrentProcess함수는 현재 실행중인 프로세스(GetCurrentProcess 함수를 호출한 프로세스)의 핸들을 얻을 때 사용되는 함수이다. GetCurrentProcess 함수의 반환값을 통해 핸들을 얻게된다.

HANDLE GetCurrentProcess(VOID);

 

현재 실행중인 프로세스의 우선순위를 높이는 방법

SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);

 

 

프로그램 실행중

출력 함수 호출이 완료되기도 전에, 함수가 호출되어 실행되는 중간에 CPU할당 시간을 다른 프로세스에게 넘겨 줄 수도 있다. CPU는 명령어 단위로 실행한다. 즉 출력함수의 명령어 수행중 다른 프로세스에게 CPU할당이 넘어갈 수 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90
728x90

프로그램 실행중에 또하나의 프로세스를 생성할 수 있다.

 

Create Process 함수의 이해

프로세스 생성을 위해 Create Process 함수를 제공한다. 이때 Create Process 함수를 호출하는 프로세스를 부모 프로세스(Parent Process)라 하고, Create Process함수 호출에 의해 생성된 프로세스를 가리켜 자식 프로세스(Child Process)라고 한다. 만약에 자식 프로세스가 Create Process 함수 호출을 통해 자식 프로세스를 생성하면, 그 사이에서도 부모 자식 관계는 형성된다.

 

Create Process 선언

BOOL CreateProcess(

   LPSTR lpApplicationName,

   LPSTR lpCommandLine,

   LPSECURITY_ATTRIBUTES lpProcessAttributes,

   LPSECURITY_ATTRIBUTES lpThreadAttributes,

   BOOL bInheritHandles,

   DWORD dwCreationFlags,

   LPVOID lpEnvironment,

   LPCTSTR lpCurrentDirectory,

   LPSTARTUPINFO lpStartupInfo,

   LPPROCESS_INFORMATION lpProcess_Information

);

 

lpApplicationName : 생성할 프로세스의 실행 파일 이름을 인자로 전달한다. 경로명을 지정할 수 있고, 경로명을 지정하지 않을 경우 프로그램의 현재 디렉터리(Current Directory)에서 실행파일을 찾게된다.

 

lpCommandLine : 생성하는 프로세스에 인자를 전달할 때 이 두번째 매개변수를 사용한다. 첫번째 전달인자에 NULL을 전달하고, 두번째 전달인자에 실행파일의 이름을 전달할 수 있다. 이 경우 표준검색 경로를 기준으로 실행파일을 찾게된다.

 

lpProcessAttributes : 프로세스의 보안 속성을 지정할 때 사용하는 인자이다. 보통 NULL을 전달하고 디폴트 보안속성이 지정된다.

 

lpThreadAttributes : 쓰레드의 보안속성을 지정할 때 쓴다. NULL을 전달할 경우 디폴트 보안 속성이 지정된다.

 

hInheritHandle : 전달인자가 TRUE인 경우, 자식 프로세스는 부모 프로세스가 소유하는 핸들 중 일부(상속 가능한 핸들)를 상속한다.

 

dwCreationFlag : 생성하는 프로세스의 특성( ex)우선순위) 을 결정 지을때 사용되는 옵션이다.

 

lpEnvironment : 프로세스마다 EnvironmentBlock(환경 블록)이라는 메모리 블록을 관리한다. 이 블록을 통해서 프로세스가 실행에 필요로 하는 문자열을 지정할 수 있다.

 

lpCurrentDirectory : 생성하는 프로세스의 현재 디렉토리를 설정하는 인자이다. 전달인자는 디렉터리 정보를 포함하는 완전 경로 형태로 구성되어야 한다. NULL이 전달될 경우 부모 프로세스의 현재 디렉터리가 새로 생성하는 자식 프로세스의 현재 디렉터리가 된다.

 

lpStartupInfo : STARTUPINFO 구조체 변수를 초기화한 다음에 이 변수의 포인터를 전달한다. STARTUPINFO 구조체 변수는 생성하는 프로세스의 속성을 지정할 때 사용된다.

 

lpProcessInformation : 생성하는 프로세스 정보를 얻기위해 사용된다. PROCESS_INFORMATION 구조체 변수의 주소값을 인자로 전달한다. 전달된 주소값이 가리키는 변수에 프로세스 정보가 채워진다.

 

 

STARTUPINFO 구조체 선언

typedef struct __STARTUPINFO{

     DWORD cb;

     LPTSTR lpReserved;

     LPTSTR lpDesktop;

     LPTSTR lpTitle;

     DWORD dwX;

     DWORD dwY;

     DWORD dwXSize;

     DWORD dwYSize;

     DWORD dwxCountChars;

     DWORD dwYCountChars;

     DWORD dwFillAttribute;

     DWORD dwFlags;

     WORD wShowWindow;

     WORD cbReserved2;

     LPBYTE lpReserved2;

     HANDLE hStdInput;

     HANDLE hStdOutput;

     HANDLE hStdError;

}STARTUPINFO, *LPSTARTUPINFO;

 

현재 디렉터리의 설정

일반적으로 프로세스가 생성되면 프로세스의 현재 디렉터리는 프로세스의 실행파일이 존재하는 디렉터리로 생성된다.

 

프로세스의 현재 디렉터리의 위치확인

DWORD GetCurrentDirectory(

     DWORD nBufferLength;

     LPTSTR lpBuffer

);

 

두번째 전달인자 lpBuffer는 현재 디렉터리 정보가 저장된 메모리 버퍼의 포인터이고, 첫번째 전달인자 nBufferLength는 현재 디렉터리 정보가 저장될 메모리 버퍼의 크기로서, 저장 가능한 문자열 길이정보가 전달되어야 한다.

 

BOOL SetCurrentDirectory(

     LPCTSTR lpPathName

);

위 함수 호출 시 lpPathName 전달인자를 통해서 현재 디렉터리를 변경할 수 있다.

 

CreateProcess 함수 호출시 아래와 같은 형태는 불가능하다.

CreateProcess(

     NULL,

     _T("ABC.exe 10 20"),

   NULL,

}

CreateProcess 함수는 내부적으로 두번째 인자로 전달된 문자열에 변경을 가한다. 그렇기 때문에 두번째 전달인자는 문자열의 변수 형태여야 한다.

 

CreateProcess 함수의 첫번째 전달인자를 통해서 실행파일의 이름을 전달할 경우, 현재 디렉터리 기준으로 실행파일을 찾게 되지만, 두번째 전달인자를 통해서 실행파일이름을 전달할 경우 아래의 표준 검색경로 순서대로 실행파일을 찾는다.

1.실행중인 프로세스의 실행파일이 존재하는 디렉터리

2.실행중인 프로세스의 현재 디렉터리

3.Windows의 시스템 디렉터리

4.Windows 디렉터리

5.환경변수 PATH에 의해 지정되어 있는 디렉터리

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90

+ Recent posts