2010년 7월 8일 목요일

시스템프로그램 #2

잡 (job)
: 여러 개의 프로세스를 하나의 그룹으로 묶어서 관리하거나 (*)sandbox를 만들어서 프로세스들이 수행하는 작업에 제한을 둘 때 사용하는 커널 오브젝트.
*sandbox : 제한 사항의 집합
IsProcessInJob() : 프로세스가 잡에 포함되어 있는지 확인
CreateJobObject() : 잡 커널 오브젝트 생성
AssignProcessToJobObject() : 프로세스를 잡 내에 배치



- 잡에 포함된 프로세스가 child process 생성시 동일한 잡에 포함된다.
- 프로세스가 잡의 제한 사항을 준수하기 위해 생성시 CREATE_SUSPENDED 옵션을 이용하여 생성하고 프로세스를 잡 내에 배치시킨 후 실행을 재개한다.
- 프로세스가 이미 잡에 포함되어 있다면 다른 잡에 할당될 수 없으며 해제될 수도 없다.


OpenJobObject() : 생성되어 있는 잡 커널 오브젝트 핸들을 얻을 때 사용
SetInformationJobObject() : 잡에 제한 사항 설정
QueryInformationJobObject() : 현재 제한 사항 확인 및 잡의 통계 정보 조회
TerminateJobObject() : 잡 내의 모든 프로세스 종료
GetProcessIoCounters() : 잡에 포함되지 않은 프로세스의 읽기, 쓰기, 기타 동작의 수행 횟수 및 바이트 수를 얻을 때 사용.


잡 통지(job notification)
- 잡에 할당된 CPU 시간이 소진될 경우 잡 내의 모든 프로세스는 강제 종료되고 잡 커널 오브젝트는 signal 상태가 된다.
- SetInformationJobObject()를 이용하여 CPU 시간을 할당하면 Nonsignal 상태가 될 수 있다.
- IOCP (I/O Completion Port)를 연결하여 잡 통지를 받을 수 있다.


GetQueuedCompletionStatus() : IOCP로부터 정보를 가져온다.


PID를 이용하여 전체 경로명 얻기
GetModuleFileNameEx() : 함수 호출 실패 위험 있음
GetProcessImageFileName() : 커널 모드 형태 반환
QueryFullProcessImageName() : 추천


Chapter 6 스레드 기본


CreateThread() : API
_beginthreadex : C/C++ RTL (추천)


스레드 종료
1) 스레드 함수 반환(추천)
2) ExitThread() 호출 (_endthreadex()가 낫다.)
ExitThread()
- 스레드에서 사용한 운영체제 리소스를 정리
- C/C++ RTL의 리소스 정리 안함. (메모리 누수)
_endthreadex()
- C/C++ RTL의 리소스 정리 후 ExitThread() 호출
3) TerminateThread() 호출
TerminateThread()
- 비동기 함수
- 소유했던 프로세스가 종료되지 않는다면 스레드 스택이 정리되지 않으므로 다른 스레드에서 접근 가능하다.
- DLL이 스레드 종료 통지를 받지 못 한다. GetExitCodeThread()
4) 프로세스 종료

C/C++ Run-Time Library에 대한 고찰



표1 런타임 라이브러리


멀티스레드 어플리케이션과 표준 C RTL
: 표준 C RTL의 전역 변수는 멀티스레드 환경을 고려하지 않고 작성되었기 때문에 멀티스레드 환경에서 문제가 될 수 있다. 따라서 스레드 별로 적절한 데이터 블록의 생성이 필요하다.


_beginthreadex()
: 생성할 스레드를 위한 적절한 데이터 블록(_tiddata 구조체)을 할당한 후 CreateThread()를 호출하여 스레드를 생성한다.


ExitThread()
: 스레드 내에서 우리가 생성한 C++ 오브젝트의 소멸자를 호출하지 못한다는 단점과 _tiddata가 할당된 메모리 블럭을 해제하지 않기 때문에 메모리 누수가 발생한다. 따라서 ExitThread() 대신 _endthreadex()를 사용하여 스레드를 제거하는 것이 낫다.

CreateThread()
: C RTL 함수 호출시 _tiddata 블록의 존재여부를 판단하여 _tiddata 블록을 새로 할당하고 초기화한다.
=> 그렇다면 굳이 _beginthreadex()를 사용해야만 하는가?
YES
이유 1. CreateThread() 사용시 C/C++ 런타임 라이브러리가 제공하는 signal 함수 사용시 구조적 예외 처리 프레임이 준비되지 않기 때문에 signal 관련 함수 호출시 프로세스가 종료된다
이유 2. 스레드 종료 함수는 _tiddata 메모리 블럭을 해제하기 위해 _endthreadex()를 호출해야만 하는데 CreateThread()를 호출하면 EndThread()를 호출하고 싶지 않은가! _endthreadex()를 이용해야 한다.
_beginthread()와 _endthread()는 사용을 삼가한다.

속해 있는 스레드나 프로세스 핸들 얻기
GetCurrentProcess()
GetCurrentThread()
둘 다 pseudohandle을 반환
DuplicateHandle()을 이용하여 (*)pseudohandle을 복사하여 실제 핸들로 변환 가능하다.
(*)pseudohandle : 항상 자신이 속해 있는 스레드 또는 프로세스의 핸들을 가리킨다.


시간 사용 정보를 얻는 함수
GetProcessTimes()
GetThreadTimes()


ID값을 얻는 함수
GetCurrentProcessId()
GetCurrentThreadId()

시스템 프로그램 #1

프로세스 : 수행 중인 프로그램의 인스턴스(instance)로서 커널오브젝트와 주소 공간으로 구성
 프로세스의 상위 2GB는 커널 영역, 하위 2GB는 유저 영역

HMODULE과 HINSTANCE는 혼용 가능
GetModuleFileName()
GetModuleHandle()

GetCommandLine()
CommandLineToArgvW()

프로세스의 환경 변수
GetEnvironmentStrings()
FreeEnvironmentStrings()

GetEnvironmentVariable() : 환경 변수의 존재 여부와 값 확인
ExpandEnvironmentStrings()
SetEnvironmentVariable()

프로세스의 선호도
 : 프로세스의 스레드가 가용 CPU들중 일부 CPU에서만 수행되도록 할 수 있다.

프로세스의 에러 모드
 : 프로세스들은 심각한 에러를 어떻게 처리할지를 시스템에게 알려주기 위한 일련의 플래그 값을 가지고 있다
SetErrorMode()

프로세스의 현재 디렉토리와 드라이브
GetCurrentDirectory() : API
SetCurrentDirectory() : API
_chdir() : C RTL
GetFullPathName()

시스템 버전
GetVersion()
GetVersionEx()

그림1, 버전 자료형의 구조


프로세스의 생성
CreateProcess()
새로 생성된 프로세스가 완전히 초기화되기 전에 반환

그림2 프로세스의 생성

그림2는 CreateProcess()의 호출에서 반환까지의 과정이다. CreateProcess()는 새로 생성된 프로세스가 완전히 초기화되기 전에 반환한다.

ProcessID, ThreadID
 : 시스템 전체에 걸쳐 고유한 ID. 단순히 시스템 내에서 프로세스들과 스레드들을 쉽게 구분하기 위해 사용.
GetCurrentProcessId()
GetCurrentThreadId()
GetProcessId()
GetThreadId()

프로세스의 종료
방법.1 주 스레드의 진입점 함수 반환(추천)

그림3 진입점 함수 반환에 의한 프로세스 종료

방법.2 프로세스 내의 어떤 스레드가 ExitProcess() 함수를 호출
 - 사용자가 ExitProcess() 호출시 C 런타임 리소스 해제가 적절하게 이루어지지 않는다. 따라서 메모리나 다른 리소스의 누수가 발생할 수 있다.
 - ExitProcess()의 매개변수로 종료 코드를 설정한다.
방법.3 다른 프로세스의 스레드가 TerminateProcess() 함수를 호출
 - 자신의 프로세스 뿐만 아니라 다른 프로세스까지도 종료시킬 수 있다.
 - 비동기 함수이므로 프로세스의 정확한 종료 시점을 알기 위해서는 추가적인 작업이 필요하다.
 - 프로세스가 사용하던 리소스는 완벽하게 정리된다.
방법.4 프로세스 내의 모든 스레드가 각각 종료

프로세스의 종료시
1) 프로세스 내에 남아 있는 스레드 종료
2) 프로세스에 할당되었던 모든 사용자 오브젝트와 GDI 오브젝트 삭제 및 모든 커널 오브젝트 파괴 (커널 오브젝트의 usage count가 0이 되는 경우에만)
3) 프로세스의 종료 코드는 STILL_ACTIVE에서 ExitProcess나 TerminateProcess 호출 시 설정한 종료 코드로 변경
4) 프로세스 커널 오브젝트의 상태가 시그널 상태로 변경
5) 프로세스 커널 오브젝트의 usage count 1 감소

프로세스 간의 자료 전송 기법
 : DDE(Dynamic Data Exchange), OLE, 파이프(PIPE), 메일슬롯(mailslot), 메모리 맵 파일(MMF) 등

Child process의 독립적인 수행
Child process 생성 후 CloseHandle()을 이용하여 child process의 핸들 및 child process의 주 스레드 핸들을 해제한다.
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);

상승된 권한으로 프로세스를 수행시
CreateProcess() 대신 ShellExecuteEx를 사용

프로세스를 나열할 수 있는 프로세스 상태 관련 함수
EnumProcess()

동기화 기법

쓰레드 동기화 기법

 

 같은시간, 오직 하나의 쓰레드 또는 제한된 쓰레드만이 공유영역(Data 영역, 힙 영역) 에 접근하기 위한 방법

 

Critical Section 는 단일 프로세스의 스레드에 대해서만 동작합니다.반면에, 뮤텍스는 여러 프로세스의 스레드에 대해서도 동작합니다.세마포어는 CS, 뮤텍스가 가지는 특징에 하나를 더 가집니다. 세마포어는 특정 영역의 코드를 실행하는 스레드의 최대 개수를 설정할 수 있습니다.

 

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

프로세스 생성시 : Non-Signaled 상태로 생성

프로세스 종료시 : Signaled 상태로 변경됨

 

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

DWORD  WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds)

hHandle : 상태 확인을 원하는 커널 오브젝트의 핸들

dwMilliseconds: 커널 오브젝트가 Signaled 상태가 될때까지 기다릴 수 있는 최대 시간, INFINITE 로 설정시 프로세스 종료시까지 무한정 기다림

 

 

메모리 접근 동기화#

 

유저모드 동기화#

 

크리티컬 섹션(Critical Section) 기반의 동기화#

 

  1. // 크리티컬 섹션 오브젝트 선언
  2. CRITICAL_SECTION   hCriticalSection;
  3. // 초기화
  4. InitializeCriticalSection(&hCriticalSection);
  5. EnterCriticalSection (&hCriticalSection); // 임계영역 진입을 위해 크리티컬 섹션 오브젝트 획득
  6. // Code..
    LeaveCriticalSection (&hCriticalSection); // 크리티컬 섹션 오브젝트 반환
  7. // 리소스 반환
  8. DeleteCriticalSection(&hCriticalSection);

 

예제)

  1. #include <stdio.h>
    #include <windows.h>
    #include <process.h>
    #include <tchar.h>
  2. #define NUM_OF_GATE  6
  3. LONG gTotalCount = 0;
  4. CRITICAL_SECTION   hCriticalSection;

  5. void IncreaseCount()
    {
     EnterCriticalSection (&hCriticalSection);
     gTotalCount++;
     LeaveCriticalSection (&hCriticalSection);
    }

  6. unsigned int WINAPI ThreadProc( LPVOID lpParam )
    {
     for(DWORD i=0; i<1000; i++)
     {
      IncreaseCount();
     }
  7.  return 0;
    }

  8. int _tmain(int argc, TCHAR* argv[])
    {
     DWORD dwThreadId[NUM_OF_GATE];
     HANDLE hThread[NUM_OF_GATE];
  9.  InitializeCriticalSection(&hCriticalSection);
  10.  for(DWORD i=0; i<NUM_OF_GATE; i++)
     {
      hThread[i] = (HANDLE)
       _beginthreadex (
       NULL,
       0,          
       ThreadProc,    
       NULL,                  
       CREATE_SUSPENDED,    
       (unsigned *)&dwThreadId[i]  
       );
  11.    if(hThread[i] == NULL)
       {
        _tprintf(_T("Thread creation fault! \n"));
        return -1;
       }
     }
  12.  for(DWORD i=0; i<NUM_OF_GATE; i++)
     {
      ResumeThread(hThread[i]);
     }

  13.  WaitForMultipleObjects(NUM_OF_GATE, hThread, TRUE, INFINITE);
  14.  _tprintf(_T("total count: %d \n"), gTotalCount);
  15.  for(DWORD i=0; i<NUM_OF_GATE; i++)
     {
      CloseHandle(hThread[i]);
     }
  16.  DeleteCriticalSection(&hCriticalSection);
  17.  return 0;
    }

 

인터락 함수(Interlocked Family Of Function) 기반의 동기화#
  1. // 전역으로 선언된 하나의 변수를 증가 또는 감소 시키기위해 사용되는 특화된 함수
  2. LONG  InterlockedIncrement(LONG volatile* Addend);
  3. LONG  InterlockedDecrement(LONG volatile* Addend);

 

 

 

커널모드 동기화#

 

뮤텍스(Mutex) 기반의 동기화#

 

  1. HANDLE hMutex;
  2. // Mutex 생성
  3.  hMutex = CreateMutex( NULL, FALSE, _T("NamedMutex"));
  4. // Mutex 획득
  5. WaitForSingleObject(hMutex, INFINITE);
  6. //  Mutex 반환
  7. ReleaseMutex(hMutex);

 

 

  1. #include <stdio.h>
    #include <windows.h>
    #include <process.h>
    #include <tchar.h>
  2. #define NUM_OF_GATE  6
  3. LONG gTotalCount = 0;
    HANDLE hMutex;
  4. void IncreaseCount()
    {
     AcquireMutex(hMutex);
  5.  gTotalCount++;
  6.  ReleaseMutex(hMutex);
    }

  7. unsigned int WINAPI ThreadProc( LPVOID lpParam )
    {
     for(DWORD i=0; i<1000; i++)
     {
      IncreaseCount();
     }
  8.  return 0;
    }
  9. DWORD AcquireMutex(HANDLE mutex)
  10. {
  11. return WaitForSingleObject(mutex, INFINITE);
  12. }
    int _tmain(int argc, TCHAR* argv[])
    {
        DWORD dwThreadIDs[NUM_OF_GATE];
        HANDLE hThreads[NUM_OF_GATE];

  13.     hMutex = CreateMutex (
                 NULL,     // 디폴트 보안관리자.
                       FALSE,    // 누구나 소유 할 수 있는 상태로 생성.
                       NULL      // numaned mutex
           );
  14.     if (hMutex == NULL)
        {
            _tprintf(_T("CreateMutex error: %d\n"), GetLastError());
        }
  15.  for(DWORD i=0; i<NUM_OF_GATE; i++)
     {
           hThreads[i] = (HANDLE)
          _beginthreadex (
           NULL,
           0,          
           ThreadProc,    
           NULL,                  
           CREATE_SUSPENDED,    
           (unsigned *)&dwThreadIDs[i]  
             );
  16.      if(hThreads[i] == NULL)
         {
             _tprintf(_T("Thread creation fault! \n"));
             return -1;
         }
     }
  17.  for(DWORD i=0; i<NUM_OF_GATE; i++)
     {
      ResumeThread(hThreads[i]);
     }
     WaitForMultipleObjects(NUM_OF_GATE, hThreads, TRUE, INFINITE);
  18.  _tprintf(_T("total count: %d \n"), gTotalCount);
  19.  for(DWORD i=0; i<NUM_OF_GATE; i++)
     {
      CloseHandle(hThreads[i]);
     }

     CloseHandle(hMutex);
  20.  return 0;
    }

 

세마포어(Semaphore) 기반의 동기화#

뮤텍스와 비슷하나 세미포어는 카운트 기능을 가지고 있음

임계영역에 접근 가능한 쓰레드의 개수를 조절하는 기능 내포

- 임계 영역의 접근 허용 쓰레드 개수를 하나로 제한하기 위해 사용되는 세마포어를 바이너리 세마포어라 하고, 이는 즉 뮤텍스와 동일한 기능

 

  1. // 세마포어 변수 설정
  2. HANDLE hSemaphore;
  3. // 세마포어 생성
  4.     hSemaphore = CreateSemaphore (
                       NULL,    // 디폴트 보안관리자.
                       TABLE_CNT,      // 세마포어 초기 값.
                       TABLE_CNT,      // 세마포어 최대 값.
                       NULL     // unnamed 세마포어 구성.
                        );
  5. // WaitForSingleObject(hSemaphore, INFINITE); // 세마포어 획득
  6. // code...
  7. // ReleaseSemaphore(hSemaphore,1,NULL); // 세마포어 반환

 

  1.  /*
     MyongDongKyoJa.cpp
     프로그램 설명: 카운트 세마포어에 대한 이해
     시뮬레이션 제한 요소:
      1. 테이블이 총 10개이고, 동시에 총 10분의 손님만 받을 수 있다고 가정한다.
      2. 오늘 점심시간에 식사하러 오실 예상되는 손님의 수는 총 50분이다.
      3. 각 손님들께서 식사 하시는 시간은 대략 10분에서 30분 사이이다.
    */

  2. #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <windows.h>
    #include <process.h>
    #include <tchar.h>
  3. #define NUM_OF_CUSTOMER 50
    #define RANGE_MIN 10
    #define RANGE_MAX (30 - RANGE_MIN)
    #define TABLE_CNT 10

  4. HANDLE hSemaphore;
    DWORD randTimeArr[50];
  5. void TakeMeal(DWORD time)
    {
     WaitForSingleObject(hSemaphore, INFINITE);
     _tprintf( _T("Enter Customer %d~ \n"), GetCurrentThreadId());
  6.  _tprintf(_T("Customer %d having launch~ \n"), GetCurrentThreadId());
     Sleep(1000 * time); // 식사중인 상태를 시뮬레이션 하는 함수.
  7.  ReleaseSemaphore(hSemaphore, 1, NULL);
     _tprintf( _T("Out Customer %d~ \n\n"), GetCurrentThreadId());
    }

  8. unsigned int WINAPI ThreadProc( LPVOID lpParam )
    {
     TakeMeal((DWORD)lpParam);
     return 0;
    }

  9. int _tmain(int argc, TCHAR* argv[])
    {
        DWORD dwThreadIDs[NUM_OF_CUSTOMER];
        HANDLE hThreads[NUM_OF_CUSTOMER];
     
     srand( (unsigned)time( NULL ) );   // random function seed 설정

  10.  // 쓰레드에게 전달할 random 값 총 50개 생성.
     for(int i=0; i<NUM_OF_CUSTOMER ;i++)
     {
            randTimeArr[i] = (DWORD) (
        ((double)rand() / (double)RAND_MAX) * RANGE_MAX + RANGE_MIN
       );
       }
  11.  // 세마포어 생성.
        hSemaphore = CreateSemaphore (
                 NULL,    // 디폴트 보안관리자.
                       TABLE_CNT,      // 세마포어 초기 값.
                       TABLE_CNT,      // 세마포어 최대 값.
           NULL     // unnamed 세마포어 구성.
               );
        if (hSemaphore == NULL)
        {
            _tprintf(_T("CreateSemaphore error: %d\n"), GetLastError());
        }

  12.  // Customer를 의미하는 쓰레드 생성.
     for(int i=0; i<NUM_OF_CUSTOMER; i++)
     {
            hThreads[i] = (HANDLE)
          _beginthreadex (
           NULL,
           0,          
           ThreadProc,    
           (void*)randTimeArr[i],                  
           CREATE_SUSPENDED,    
           (unsigned *)&dwThreadIDs[i]  
             );
  13.      if(hThreads[i] == NULL)
         {
             _tprintf(_T("Thread creation fault! \n"));
             return -1;
         }
     }
  14.  for(int i=0; i<NUM_OF_CUSTOMER; i++)
     {
      ResumeThread(hThreads[i]);
     }
  15.  WaitForMultipleObjects(NUM_OF_CUSTOMER, hThreads, TRUE, INFINITE);
  16.     _tprintf(_T("----END-----------\n"));
  17.  for(int i=0; i<NUM_OF_CUSTOMER; i++)
     {
      CloseHandle(hThreads[i]);
     }
     
     CloseHandle(hSemaphore);
  18.  return 0;
    }

 

 

이름있는 뮤텍스(Named Mutex) 기반의 프로세스 동기화#

뮤텍스는 커널 오브젝트이므로 프로세스에 소속되는것이 아니라 운영체제가 관리한다. 따라서 뮤텍스에 유일한 이름을 지어주고 이것을 다른 프로세스에서 획득하여 사용하는것이 가능하다. 아래는 서로다른 프로세스에 속해있는 두개의 쓰레드를 동기화 하는 예제 코드이다.

 

  1. HANDLE OopenMutex(
  2. DWORD dwDesiredAccess, // 접근권한
  3. BOOL bInheritHandle, // 핸들 상속여부
  4. LPCSTR lpName // 뮤텍스 오브젝트 이름
  5. );

 

 

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

  2. HANDLE hMutex;
    DWORD dwWaitResult;
  3. void ProcessBaseCriticalSection()
    {
     dwWaitResult = WaitForSingleObject(hMutex, INFINITE);
  4.  switch (dwWaitResult)
     {
      // 쓰레드가 뮤텍스를 소유하였다.
     case WAIT_OBJECT_0:
      _tprintf(_T ("thread got mutex ! \n") );
      break;
  5.   // time-out 발생하였다.
     case WAIT_TIMEOUT:
      _tprintf(_T ("timer expired ! \n") );
      return;
  6.   // 뮤텍스 반환이 적절이 이뤄지지 않았다.
     case WAIT_ABANDONED:
      return;
     }
  7.  for(DWORD i=0; i<5; i++)
     {
      _tprintf( _T("Thread Running ! \n") );
      Sleep(2000);
     }
  8.  ReleaseMutex(hMutex);
    }

  9. int _tmain(int argc, TCHAR* argv[])
    {
  10. #if 0
     hMutex = CreateMutex(
      NULL,                      
      FALSE,                    
      _T("NamedMutex")  
      );        
    #else
  11.  hMutex = OpenMutex(
      MUTEX_ALL_ACCESS,    
      FALSE,                
      _T("NamedMutex")  
      );
  12. #endif
  13.  if (hMutex == NULL)
     {
      _tprintf(_T("CreateMutex error: %d\n"), GetLastError());
      return -1;
     }
  14.  ProcessBaseCriticalSection();
  15.  CloseHandle(hMutex);
  16.  return 0;
    }

 

 

 

실행순서 동기화#

 

이벤트(Event) 기반의 동기화#

쓰레드나 커널 오브젝트의 경우 초기에는 Non-Signaled 상태로 생성되고, 종료시 Signaled 상태로 자동변경 된다. 그러나 이벤트 오브젝트는 자동으로 Signaled 상태로 변경되는것이 아니라 함수호출을 통해서 Signaled 상태로 변경을 해야한다. 따라서 프로그래머는 이벤트 커널 오브젝트의 상태를 직접 조작함으로써 실행 순서를 동기화 할 수 있다. 실행순서 동기화를 위한 내용을 정리하자면 아래와 같다.

 

  • A 쓰레드의 특정 시점까지 완료후 B 쓰레드가 시작되도록 하는 프로그램을 작성 하고자 한다.
  • A, B 쓰레드를 차례대로 생성하고, A 쓰레드는 Non-Signaled 상태로 이벤트 커널 오브젝트를 생성한다.
  • B 쓰레드 쓰레드 시작 부분에 WaitForSingleObject(hEvent,INFINITE) 함수를 호출하여 event 가 Signaled 상태가 되기를 기다린다.
  • 이벤트 오브젝트는 A 쓰레드에의해 Non-Signaled 상태이므로 B 쓰레드가 블로킹된다.
  • A 쓰레드는 SetEvent() 함수를 통해 이벤트 오브젝트의 상태를 Signaled 상태로 바꾸면 이때 블로킹되어있던 B 쓰레드가 블로킹 상태를 빠져나와 쓰레드가 실행된다.

 

  1.  // 이벤트 오브젝트 생성
  2. HANDLE CreateEvent(
  3. LPSECURITY_ATTRIBUTES lpEnventAttrivutes, // 보안속성 지정

  4. BOOL bManualReset, // 리셋모드 결정

    BOOL bInitialState, // 이벤트 오브젝트의 초기 상태결정 TRUE:Signaled, FALSE:Non-Signaled)

  5. LPCTSTR lpName // 오브젝트 이름

  6. );

  7. // Non-Signaled 상태로 변경
  8. BOOL ResetEvent(HANDLE hEvent);
  9. // Signaled 상태로 변경
  10. BOOL SetEvent(HANDLE hEvent);

 

  • 참고 : BOOL bManualReset

TRUE:수동리셋모드 - 블로킹된 쓰레드가 WiatForSingleObject 함수를 빠져나온후에도 이벤트의 상태가 변하지 않고 Signaled 상태로 남음

FALSE:자동리셋모드 - 블로킹된 쓰레드가 WiatForSingleObject 함수를 빠져나옴과 동시에 이벤트의 상태가 Non-Signaled 상태로 변함

 

 

예제)

  1. /*
    StringEvent.cpp
    프로그램 설명: 1. 생산자/소비자 모델의 이해
    2. 동기화 event에 대한 이해.
    */
  2. #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <process.h>    /* _beginthreadex, _endthreadex */

  3. unsigned int WINAPI OutputThreadFunction(LPVOID lpParam);
  4. TCHAR string[100];
    HANDLE hEvent;
  5. int _tmain(int argc, TCHAR* argv[])
    {
     HANDLE  hThread;
     DWORD dwThreadID;
  6.  hEvent = CreateEvent( // event object 생성.
      NULL,  // 상속 불가.
      TRUE,  // manual-reset mode로 생성.
      FALSE,  // non-signaled 상태로 생성.
      NULL  // 이름 없는 event.
      );
     if(hEvent==NULL){
      _fputts(_T("Event object creation error \n"), stdout);
      return -1;
     }
  7.  hThread = (HANDLE)_beginthreadex (
      NULL, 0,
      OutputThreadFunction,
      NULL, 0,
      (unsigned *)&dwThreadID
      );
  8.  if(hThread==0) {
      _fputts(_T("Thread creation error \n"), stdout);
      return -1;
     }
  9.  _fputts(_T("Insert string: "), stdout);
     _fgetts(string, 30, stdin);
  10.  SetEvent(hEvent); // event의 state를 signaled 상태로 변경.
  11.  WaitForSingleObject(hThread, INFINITE);
  12.  CloseHandle(hEvent); // event 오브젝트 소멸
     CloseHandle(hThread);
  13.  return 0;
    }
  14. unsigned int WINAPI OutputThreadFunction(LPVOID lpParam)
    {
  15.  WaitForSingleObject(hEvent, INFINITE); // event가 signaled 상태가 되기를 기다린다.
  16.  _fputts(_T("output string: "), stdout);
     _fputts(string, stdout);
  17.  return 0;
  18. }

 

수동리셋(Muanual-Reset) 모드 이벤트 활용 예#

문자열입력, 문자열 출력, 문자열 길이를 출력하는 3개의 쓰레드가 있다.

  문자열 출력과 문자열 길이를 출력하는 쓰레드는 문자열이 입력되기를 기다리고 있다가 문자열의 입력이 완료되면  동시에 블로킹을 풀고 쓰레드가 실행되도록한다.

블로킹된 두개의 쓰레드를 동시에 풀기위해 Event 가 사용되고, 쓰레드가 동시에 실행되더라도 문자열 출력이 섞이지 않도록 하기위해 Mutex 가 사용된다.

 

  1.  /*
     StringEvent2.cpp
     프로그램 설명: manual-reset mode 동기화 적용 사례.
    */
  2. #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <process.h>    /* _beginthreadex, _endthreadex */

  3. unsigned int WINAPI OutputThreadFunction(LPVOID lpParam);
    unsigned int WINAPI CountThreadFunction(LPVOID lpParam);
  4. TCHAR string[100];
    HANDLE hEvent;
  5. int _tmain(int argc, TCHAR* argv[])
    {
     HANDLE  hThread[2];
     DWORD dwThreadID[2];
  6.  hEvent = CreateEvent( // event object 생성.
        NULL,  // 상속 불가.
        TRUE,  // manual-reset mode로 생성.
        FALSE,  // non-signaled 상태로 생성.
        NULL  // 이름 없는 event.
        );
     if(hEvent==NULL){
      _fputts(_T("Event object creation error \n"), stdout);
      return -1;
     }
     
     hThread[0] = (HANDLE)_beginthreadex (
          NULL, 0,
          OutputThreadFunction,
          NULL, 0,
          (unsigned *)&dwThreadID[0]
         );
  7.  hThread[1] = (HANDLE)_beginthreadex (
          NULL, 0,
          CountThreadFunction,
          NULL, 0,
          (unsigned *)&dwThreadID[1]
         );

  8.  if(hThread[0]==0 ||hThread[1]==0)
     {
      _fputts(_T("Thread creation error \n"), stdout);
      return -1;
     }
  9.  _fputts(_T("Insert string: "), stdout);
     _fgetts(string, 30, stdin);
  10.  SetEvent(hEvent); // event의 state를 signaled 상태로 변경.
  11.  WaitForMultipleObjects (
        2,           // 배열의 길이.
        hThread,     // 핸들의 배열.
        TRUE,        // 모든 핸들이 신호받은 상태로 될 때 리턴.
        INFINITE  // 무한 대기.
     );
     
     CloseHandle(hEvent); // event 오브젝트 소멸
     CloseHandle(hThread[0]);
     CloseHandle(hThread[1]);
  12.     return 0;
    }
  13. unsigned int WINAPI OutputThreadFunction(LPVOID lpParam)
    {
  14.   WaitForSingleObject(hEvent, INFINITE); // event가 signaled 상태가 되기를 기다린다.
  15.   _fputts(_T("Output string: "), stdout);
      _fputts(string, stdout);
  16.   return 0;
    }
  17. unsigned int WINAPI CountThreadFunction(LPVOID lpParam)
    {
  18.   WaitForSingleObject(hEvent, INFINITE); // event가 signaled 상태가 되기를 기다린다.
  19.   _tprintf(_T("Output string length: %d \n"), _tcslen(string)-1);
  20.   return 0;
    }

 

위 코드의 문제점 : WaitForMultipleObjects() 함수를 통해 블로킹된 두개의 쓰레드가 동시에 풀리면서 출력순서를 보장하지 못한다.

따라서 위 코드를  Mutex 를 사용한 코드로 수정해야 한다.

 


  1. #include "stdafx.h"
  2. #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <process.h>    /* _beginthreadex, _endthreadex */

  3. unsigned int WINAPI OutputThreadFunction(LPVOID lpParam);
    unsigned int WINAPI CountThreadFunction(LPVOID lpParam);
    DWORD AcquireMutex(HANDLE mutex);

  4. TCHAR string[100];
    HANDLE hEvent;
    HANDLE hMutex;

  5. int _tmain(int argc, TCHAR* argv[])
    {
     HANDLE  hThread[2];
     DWORD dwThreadID[2];
  6.  hMutex = CreateMutex( NULL, FALSE, _T("NamedMutex"));
     if(hMutex==NULL)
     {
      _fputts(_T("Mutex object creation error \n"), stdout);
      return -1;
     }
  7.  hEvent = CreateEvent( // event object 생성.
      NULL,  // 상속 불가.
      TRUE,  // manual-reset mode로 생성.
      FALSE,  // non-signaled 상태로 생성.
      NULL  // 이름 없는 event.
      );
  8.  if(hEvent==NULL)
     {
      _fputts(_T("Event object creation error \n"), stdout);
      return -1;
     }
  9.  hThread[0] = (HANDLE)_beginthreadex (
      NULL, 0,
      OutputThreadFunction,
      NULL, 0,
      (unsigned *)&dwThreadID[0]
      );
  10.  hThread[1] = (HANDLE)_beginthreadex (
      NULL, 0,
      CountThreadFunction,
      NULL, 0,
      (unsigned *)&dwThreadID[1]
      );
  11.  if(hThread[0]==0 ||hThread[1]==0)
     {
      _fputts(_T("Thread creation error \n"), stdout);
      return -1;
     }
  12.  _fputts(_T("Insert string: "), stdout);
     _fgetts(string, 30, stdin);
  13.  SetEvent(hEvent); // event의 state를 signaled 상태로 변경.
  14.  WaitForMultipleObjects (
      2,           // 배열의 길이.
      hThread,     // 핸들의 배열.
      TRUE,        // 모든 핸들이 신호받은 상태로 될 때 리턴.
      INFINITE  // 무한 대기.
      );
  15.  CloseHandle(hEvent); // event 오브젝트 소멸
     CloseHandle(hThread[0]);
     CloseHandle(hThread[1]);
  16.  return 0;
    }
  17. unsigned int WINAPI OutputThreadFunction(LPVOID lpParam)
    {
  18.  WaitForSingleObject(hEvent, INFINITE); // event가 signaled 상태가 되기를 기다린다.
  19.  AcquireMutex(hMutex);
  20.  _fputts(_T("Output string: "), stdout);
     _fputts(string, stdout);
  21.  ReleaseMutex(hMutex);
  22.  return 0;
    }
  23. unsigned int WINAPI CountThreadFunction(LPVOID lpParam)
    {
  24.  WaitForSingleObject(hEvent, INFINITE); // event가 signaled 상태가 되기를 기다린다.
  25.  AcquireMutex(hMutex);
  26.  _tprintf(_T("Output string length: %d \n"), _tcslen(string)-1);
  27.  ReleaseMutex(hMutex);
  28.  return 0;
    }
  29. DWORD AcquireMutex(HANDLE mutex)
    {
     return WaitForSingleObject(mutex, INFINITE);
    }

 

 

타이머 기반 동기화#

Windows 에서는 Signaled/Non-Signaled 상태 개념이 중요하다. 어떤 커널 오브젝트는 특정 상황이 도래하면 Signaled 상태가 되고, 또 어떤 커널 오브젝트는 명시적인 함수 호출을 통해서 Signaled 상태가 되기도 한다.

타이머는 생성시 Non-Signaled 상태로 생성되고 정해진 시간이 지나면 자동으로 Signaled 상태가되는 특성이 있다.

 

  • 타이머 오브젝트 생성

HANDLE CreateWaitableTimer(

LPSECURITY_ATTRIBUTES lpTimerAttributes, //  보안속성 지정

BOOL bManualReset, // 수동리셋/자동리셋 모드 설정

LPCTSTR lpTimerName // 타이머 오브텍트 이름 설정

);

 

  •  알람 시간 설정

BOOL SetWaitableTimer(

HANDLE hTimer, // 알람을 설정할 타이머 오브젝트 핸들

const LARGE_INTEGER* pDueTime, // 알람이 울리는 시간 지정, 단위는 NanoSeconds (+값일경우 절대시간, -값일경우 상대시간을 의미)

LONG lPeriod, // 타이머의 주기, 단위는 Milliseconds

PTIMERAPCROUTINE pfnCompletetionRoutine, // 완료루틴 타이머 생성 용도로 사용

LPVOID lpArgToCompletionRoutine, // 완료루틴 타이머 생성 용도로 사용

BOOL fResume // 전원관리와 관련된 매개변수, 기본적으로 FALSE 로 세팅함

);

 

  •  타이머 해제

BOOL CancelWaitableTimer(

HANDLE hTimer // 알람을 해제할 타이머 오브젝트의 핸들

);

 

수동리셋 타이머(Manual-Reset Timer)#

 

예제)

  1.  /*
     ManualResetTimer.cpp
     프로그램 설명: 수동 리셋 타이머 오브젝트에 대한 이해.
    */
  2. #define _WIN32_WINNT 0x0400
  3. #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>

  4. int _tmain(int argc, TCHAR* argv[])
    {
        HANDLE hTimer = NULL;
        LARGE_INTEGER liDueTime;
  5.     liDueTime.QuadPart=-100000000;
  6.     hTimer = CreateWaitableTimer(NULL, TRUE, _T("WaitableTimer")); // 수동리셋 타이머 생성
        if (!hTimer)
        {
            _tprintf( _T("CreateWaitableTimer failed (%d)\n"), GetLastError());
            return 1;
        }
  7.     _tprintf( _T("Waiting for 10 seconds...\n"));
  8.     SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, FALSE); // 타이머 시간 설정
  9.     WaitForSingleObject(hTimer, INFINITE); // 수동리셋 타이머 이므로 타이머가 특정시간이 지난후 Signaled 상태로 변경되면 쓰레드가 블로킹에서 풀려나지만 타이머의 상태는 계속 Signaled 상태로 유지된다.

  10.     _tprintf( _T("Timer was signaled.\n") );
        MessageBeep(MB_ICONEXCLAMATION);
  11.     return 0;
    }

 

#define _WIN32_WINNT   0x0400

CreateWaitableTimer, SetWaitableTimer 은 Windows NT 4.0 이후부터 제공되기 시작한 함수이기때문에 매크로 선언을 미리 해 줘야 사용 가능함.

만약 Widows XP 이후 버전부터 제공하기 시작한 함수가 있다면 _WINNT_WIN32 를 0x0501 로 정의해야한다.

 

 

 

주기적 타이머(Periodic-Timer)#

일정 시간 간격으로 알람이 울리는 타이머.

 

  1.  /*
     PeriodicTimer.cpp
     프로그램 설명: 주기적 타이머에 대한 이해.
    */
  2. #define _WIN32_WINNT 0x0400
  3. #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>

  4. int _tmain(int argc, TCHAR* argv[])
    {
        HANDLE hTimer = NULL;
        LARGE_INTEGER liDueTime;
  5.     liDueTime.QuadPart=-100000000;
  6.     hTimer = CreateWaitableTimer(NULL, FALSE, _T("WaitableTimer")); // 자동리셋 타이머 생성
        if (!hTimer)
        {
            _tprintf( _T("CreateWaitableTimer failed (%d)\n"), GetLastError());
            return 1;
        }
  7.     _tprintf( _T("Waiting for 10 seconds...\n"));
  8.     SetWaitableTimer(hTimer, &liDueTime, 5000, NULL, NULL, FALSE); // 타이머 주기 설정
  9.     while(1)
       {
          WaitForSingleObject(hTimer, INFINITE); // 자동리셋 타이머 이므로 타이머가 Signaled 상태로 변경됨과 동시에 쓰레드가 블로킹 상태에서 빠져나오고 타이머의 상태는 Non-Signaled 상태로 바뀐다.
          _tprintf( _T("Timer was signaled.\n") );
          MessageBeep(MB_ICONEXCLAMATION);
       }
        return 0;
    }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2010년 6월 9일 수요일

apache 설치하기 (CentOS)

운영체제를 깔때 서버 버전으로 깔아서 기본적으로 Httpd 가 설치되어 있었다.

하지만 내가 복사해야될 폴더며 내가 필요한 것들은 당췌 어디 있는지... 알 수가 없었다 ;;;

그래서 과감히 소프트웨어 추가 삭제에서 삭제를 하고, 다운 받아서 다시 깔았다.

http://www.apache.org  에서 Linux 용 최신 버전 다운


1. tar 파일의 압축을 푼다.

# tar xvf httpd-2.2.13.tar


2. 컴파일 스크립트 수행

# ./configure --with-layout=apache --prefix-/usr/local/web/apache --enable-module=so --enable-mods-shared=all --enable-so -enable-rewrite

prefix = 설치 경로
--enable-module=so 는 tomcat 연동이나 다른 연동시 필요하므로 꼭 추가.
여기서
configure: error: no acceptable C compiler found in $PATH
이런 에러가 뜬다면 gcc 가 깔려 있는지 확인하자.
# rpm -qa | grep gcc

없다면.
# yum install gcc

mod_deflate has been requested but can not be built due to prerequisite failures
에러가 난다면.

# yum -y install zlib-devel

뭐 대략적으로 설치가 안되서 나는 에러다.

3. 컴파일

# make


4. install

# make install


에러 없이 설치가 되었다면 성공.


부팅시 자동으로 띄우고 싶다면.


#cp /usr/local/web/apachectl /etc/init.d/httpd

#vi /etc/init.d/httpd


#!bin/sh 밑에 아래 내용을 넣는다.
#chkconfig: 2345 90 90
#processname: /usr/local/web/apache/bin/apachectl
#config: /usr/local/web/apache/conf/httpd.conf
#pidfile: /usr/local/web/apache/logs/httpd.conf


저장.

#chkconfig --add httpd
#chkconfig --list httpd


하면 httpd 가 있는지 확인.

# ntsysv

하면 파란 화면이 뜬다.
여기서 httpd 에 * 가 쳐져 있다면 자동 실행된다.

#service httpd start


하고

http://localhost

들어가면

It works!

라고 뜬다.

2010년 5월 15일 토요일

2010년 5월 14일 금요일

디스크 초기화

clearhdd 1 타이핑후 y를 선택한다음 재부팅 하면 된다.

 

mbr(master boot record) 을삭제해야한다.

MBR은 파티션 위치정보를 가지고 있다.그리고 운영체제가 저장되어있는 파티션의 부트섹터를

읽어올수있는 프로그램을 포함하고 있다.

부트섹터에는 OS를 메모리에 올릴수있는 프로그램이 내장되어 있다

 

FDISK 는 파티션을 나누는 즉 디스크영역을 만드는 것이고

FDISK /mbr 은 mbr영역까지 삭제하는 명령이다.

 

실제로 Fdisk/mbr은 mbr영역중 파티션테이블 64byte를 제외한 나머지 mbr영역을 fdisk가 다시

기록해주는 것이다.

파티션테이블을 제외한 mbr영역이 손상되었을때 복구하는 도구로 사용된다.

파티션테이블 손상시에는 다른 복구유틸이나 백업본을 이용해야한다.