Windows_/Windows32_API

Windows :: 커널모드 스레드 동기화

sosal 2014. 7. 25. 12:14
반응형

/*

 * http://sosal.kr/

 * made by so_Sal

 */

 


커널모드 동기화

  ::  뮤택스 동기화 기법
          :: CreateMutex()
          :: WaitForSingleObject()
          :: ReleaseMutex()
  ::  세마포어 동기화 기법
          :: CreateSemaphore()
       
:: WaitForSingleObject()
          :: ReleaseSemaphore()

 
:: 다중프로세스 동기화 // 이름있는 뮤텍스, 세마포어
          :: Named Mutex, OpenMutex()
        
:: Named Mutex, OpenSemaphore()
  :: 예제들
 

뮤텍스는 세마포어의 일부에 포함되는 기능중 하나입니다.
유저모드의 CriticalSection 관련 동기화 함수와 마찬가지로,
단 하나의 스레드만 임계영역에 접근 가능하게 하는 커널 동기화 기법입니다.
세마포어는 스레드가 임계영역에 들어올 수 있는 최대 스레드 수를 지정할 수 있습니다.

세마포어 : 임계영역에 접근하기 위한 다수의 키를 두어,
               키 수만큼 스레드가 임계영역에 동시 접근하도록 한다.
뮤텍스   :  오로지 하나의 스레드만 임계영역에 접근토록 한다.


================================ 뮤텍스 관련 함수들 ================================

HANDLE CreateMutex(
1.      LPSECURITY_ATTRIBUTES lpMutexAttributes,
2.      BOOL bInitialOwner,
3.      LPCTSTR lpName
);

 1. LPSECURITY_ATTRIBUTES lpMutexAttributes, //
    커널모드 동기화 기법이기 때문에 커널오브젝트의 생성을 동반합니다.
    보안 구조체는 커널 오브젝트 생성시 자주 볼 수 있는 구조체입니다.
    이 변수만으로 커널에서 처리되는 동기화 기법임을 짐작할 수 있습니다.

2. BOOL bInitialOwner :: 소유자 지정 (TRUE,FALSE)
   열쇠를 만든 스레드 (흔히 보통 메인스레드)가 초기 열쇠 소유자임을 설정하는 변수입니다.
   TRUE :: CreateMutex()를 호출한 스레드가 열쇠를 가짐
   FALSE :: 함수가 호출된 즉시 열쇠의 소유자가 없도록 설정.

3. LPCTSTR lpName :: 뮤텍스 이름 지정
   뮤텍스의 이름을 나타내는 매개변수입니다.
   A라는 프로세스에 있는 스레드와 뮤텍스는 B라는 프로세스는 알지 못합니다.
   B 프로세스는 A 프로세스에 대한 핸들 테이블 정보가 없기 때문입니다.
   이를 해결하기 위해 이름있는 뮤택스, 세마포어가 존재합니다.

뮤택스 생성함수의 리턴값이 HANDLE입니다.
HANDLE은 커널 오브젝트를 가리키는 간접 지명자로,
커널이 스레드의 동기화를 직접 관리한다는 사실을 간접적으로 알 수 있습니다.

커널 오브젝트는 OS가 직접 관리해주기 때문에,
유저모드 동기화에서 볼 수 있는 크리티컬 섹션 오브젝트의 초기화 함수가 필요없습니다.
유저모드에는OS에게 동기화 작업에 필요한 기본작업을 OS에게 요청하는 초기화 함수가 필요하지만,
커널 모드 동기화는 이미 OS가 알고 있기 때문입니다.


뮤텍스 :: 커널 오브젝트 :: Signaled - non-Signaled

스레드가 뮤택스 열쇠를 가져갈 수 있는 상태 :: Signal
이미 다른 스레드가 쓰고 있어서 가져올 수 없는 상태 :: Non-Signal
로 나뉘어 집니다.


아래는 뮤텍스 커널 오브젝트의 상태를 Signaled, Non-Signaled 상태를 바꾸는 함수들입니다.

DWORD WaitForSingleObject(HANDLE hHandle,
                                              DWORD  dwMilliseconds);
뮤텍스 커널 오브젝트의 상태가 Signaled가 되길 기다리며,
Signaled상태가 되면 키를 획득하는 함수입니다.
임계영역의 접근 허락권한을 기다립니다.

hHandle :: 임계영역으로의 접근 허락을 기다리는 스레드의 핸들
dwMilliseconds :: Milli 단위로 기다리는 최대 시각을 입력할 수 있습니다.
                          이 시간내에 스레드가 접근하지 못한다면 WAIT_TIMEOUT이 리턴됩니다.
                          INFINITE 값을 인자로 전달시, 접근 가능할 때 까지 무한히 기다립니다.

함수 성공시 뮤택스 커널 오브젝트는 Non-Signaled 상태가 되며
스레드는 임계영역으로 진입합니다.



BOOL ReleaseMutex(HANDLE hMutex)

임계영역으로 진입한 스레드가, 키를 반환할 때 쓰이는 함수입니다.
함수 상공시 뮤택스 커널 오브젝트의 상태를 Signaled 상태로 돌아갑니다.
    


BOOL CloseHandle(HANDLE hOBject)
뮤택스 커널오브젝트의 소멸은 CloseHandle() 호출로 해결합니다.

유저모드와 달리 운영체제가 커널오브젝트의 존재를 알고 관리해주기 때문에
전처리과정인 동기화 구조체의 초기화나 리소스 해제 함수를 따로 호출해줄 필요가 없습니다.

 

================================ 세마포어 관련 함수들 ================================
:: WaitForSingleObject()
:: ReleaseSemaphore()
위 2 함수에 대해서는 뮤텍스 설명부분에 나와 있으므로 생략합니다.
뮤택스와 세마포어 모두 동일하게 쓰입니다.


HANDLE CreateSemaphore(
1.   LPSECURITY_ATTRIBUTES lpSemaphoreAttriutes,
2.   LONG lInitialCount,
3.   LONG lMaximumCount,
4.   LPCTSTR lpName
);

 1. LPSECURITY_ATTRIBUTES lpSemaphoreAttriutes
    커널모드 동기화 기법이기 때문에 커널오브젝트의 생성을 동반합니다.
    보안 구조체는 커널 오브젝트 생성시 자주 볼 수 있는 구조체입니다.
    이 변수만으로 커널에서 처리되는 동기화 기법임을 짐작할 수 있습니다.

2.   LONG lInitialCount

세마포어의 갯수는 열쇠의 갯수를 뜻합니다.
열쇠가 3개면 3개의 스레드가 임계영역에 동시 접근 가능함을 뜻합니다.
세마포어의 가장 큰 특징을 보여주는 인자입니다.

3.   LONG lMaximumCount
세마포어가 가질 수 있는 최대 크기의 값을 뜻합니다.
이 값이 1이 될 경우 뮤텍스와 동일한 기능을 하겠습니다.
기본적으로 lInitialCount의 값보다 커야합니다.

4.   LPCTSTR lpName
세마포어의 이름을 나타내는 매개변수입니다.
A라는 프로세스에 있는 스레드와 뮤텍스는 B라는 프로세스는 알지 못합니다.
B 프로세스는 A 프로세스에 대한 핸들 테이블 정보가 없기 때문입니다.
이를 해결하기 위해 이름있는 뮤택스, 세마포어가 존재합니다.


================================ 뮤텍스 예제 ================================  


#include<stdio.h>
#include<tchar.h>
#include<windows.h>
#include<process.h>

#define MAX_THREADS 10

DWORD MT_TotalCount = 0; // 스레드가 접근할 전역변수
HANDLE hMutex; //뮤텍스는 커널오브젝트이므로, 핸들로써 관리된다.

void IncreaseCount(){   
    WaitForSingleObject(hMutex, INFINITE); //임계영역 진입
    MT_TotalCount++;                                //전역변수
    ReleaseMutex(hMutex);                       //탈출
}

unsigned int WINAPI ThreadProc(LPVOID lpParam){
   for(DWORD i=0; i<1000; i++){    // 스레드 스택
       IncreaseCount();
   }
   _endthreadex(0);
   return 0;
}

int _tmain(int argc,TCHAR* argv[]){
     DWORD dwThreadIDs[MAX_THREADS];
     HANDLE hThreads[MAX_THREADS];

     hMutex = CreateMutex(
           NULL,      //보안속성 구조체 없음
           FALSE,    // Signaled 상태로 뮤택스 생성
           NULL       //unnamed pipe
           );

     if(hMutex == NULL)
        _tprintf(_T("CreateMutex() error :: %d\n"), GetLastError());
 
 for(DWORD i=0; i<MAX_THREADS; i++){
           hThreads[i] = (HANDLE)_beginthreadex(
           NULL,0,ThreadProc,NULL,
           CREATE_SUSPENDED,
           (unsigned *)&dwThreadIDs[i]
      );    //스레드 생성

     if(hThreads[i] == NULL){
         _tprintf( _T("Thread creation fault! \n") );
         return -1;
      }

 }
 
 for(DWORD i=0;i<MAX_THREADS;i++){
     ResumeThread(hThreads[i]);
 } //스레드 시작 (SUSPEND_COUNT 를 0으로)

 
 WaitForMultipleObjects(
    MAX_THREADS,hThreads,TRUE,INFINITE);

 _tprintf( _T("Mutex total count : %d \n"), MT_TotalCount);
// 10000 수가 나온다면 동기화 성공


 for(DWORD i=0; i<MAX_THREADS; i++)
      CloseHandle(hThreads[i]);

 CloseHandle(hMutex);

 return 0;
}

================================ 뮤텍스 예제 ================================