C 라이브러리는 단일 스레드 프로세스를 염두에 두고 작성되었으며 일부 함수들이 중간 결과를 저장하기 위해 전역 저장소를 사용한다. 이런 라이브러리는 스레드에 안전하지 않다. 예를 들어 서로 다른 두 스레드가 전역 저장소를 각자 수정하게 되면 이상한 결과가 나올수 있기 때문이다.
 C 라이브러리 함수들이 스레드에 안전하지 않은 이유를 잘 보여주는 함수로 strtok 가 있다. strtok는 문자열을 토큰단위로 분리하는 함수로 한번 호출 할 때마다 다음 토큰의 위치를 돌려준다. 이를 위해 함수는 일련의 함수 호출들 사이에서 영속적 상태를 유지하는데 그 상태를 함수를 호출하는 스레드가 공유하는 정적 저장소에 저장한다는 것이 문제이다. 
 Microsoft C는 LIBCMT.LIB 라는 스레드에 안전한 C 라이브러리 구현을 통해 이러한 문제를 해결한다. 스레드를 생성하고자 하는 경우 CreateThread 함수 대신 _beginthreadex 함수를 사용할 것을 권장한다. _beginthreadex 함수는 스레드 고유 작업 장소를 생성한다. 스레드 종료 시에는 ExitThread 함수 대신 _endthreadex 함수를 사용한다. 이 함수를 사용하려면 헤더 파일들을 포함하기 전에 반드시 전처리 변수 _MT를 정의해야 한다.
 Visual Studio 경우 다중 스레드에서 C 라이브러리를 제대로 사용하려면 다음과 같은 사항을 지켜야한다.
  1) 프로젝트 속성 -> 구성 속성 -> C/C++ -> 코드 생성 -> 런타임 라이브러리에서 다중 스레드 DLL(/MD) 를 선택한다.
  2) 스레드 생성시 _beginthreadex 함수를 종료시에는 _endthreadex 함수 또는 스레드 루틴 끝에서 return 문으로 실행을 끝나게 한다.

출처 : Windows 시스템 프로그래밍 윈도우즈 API 핵심 바이블

'0x001 Programming > 01. C | C++' 카테고리의 다른 글

[Example] 생산자 | 소비자  (0) 2017.12.29
[Tip] 동기화  (0) 2017.12.29
[Tip] 프로세스 관리  (0) 2017.12.29
[Example] 이진 검색 트리를 이용한 파일 정렬  (0) 2017.12.29
[Tip] Heap Memory  (0) 2017.12.29

모든 프로세스는 하나나 그 이상의 스레드들을 가진다. 스레드는 실행의 기본 단위이다. 


Windows 프로세스는 다음과 같은 구성 요소를 가진다.
 1) 하나 이상의 스레드들
 2) 다른 프로세스의 주소 공간과 구별되는 가상 주소 공간
 3) 하나나 그 이상의 코드 구역( Code Segment )들 ( DLL 코드 포함 )
 4) 전역 변수들을 담은 하나나 그 이상의 자료 구역들 ( Data Segment )
 5) 환경 변수 정보 / 프로세스 힙 / 핸들 및 자원 정보
한 프로세스 안의 각 스레드는 코드, 전역변수, 환경 문자열, 자원들을 공유한다.

스레드는 다음과 같은 구성 요소를 가진다.
 1) 프로시저 호출, 인터럽트, 예외 처리부 등을 위한 스택
 2) TLS(Thread Local Storage) 각 스레드 만의 고유한 저장소
 3) 스레드 생성시 지정된 스택에 대한 인수 등
 4) 커널이 관리하는 Context 구조체

 1. 프로세스 생성


  가장 기본적인 Windows 프로세스 관리 함수는 스레드 하나를 가진 프로세스를 생성하는 CreateProcess 함수이다. 함수의 원형은 다음과 같다. 
 

 

 CreateProcess 함수는 HANDLE 값을 반환하지 않고 성공 실패 여부만 반환한다. 생성된 프로세스 관련 정보는 PROCESS_INFORMATION 구조체를 통해 전달 받는다.  

 

 

 

 

 파일 핸들과 마찬가지로 프로세스 핸들이나 스래드 핸들도 더 이상 필요하지 않게 되면 닫아주어야 한다.

2. 상속 가능 핸들
  프로세스를 생성 할때 부모 프로세스의 객체들에 대한 핸들을 통해 자식 프로세스에서도 객체들에 접근하고 싶은 상황이 발생한다. 이런 경우 부모프로세스가 가지고 있는 핸들 정보를 자식 프로세스에게 상속 할수 있다. 핸들 값을 상속 하기 위해서는 SECURITY_ATTRIBUTES 구조체의 보안 서술자 필드를 설정해주어야 한다. 상속된 핸들은 원래의 핸들과 개별적인 복사본이다. 부모와 자식이 같은 파일에 서로 다른 파일 포인터를 이용하여 접근한다. 또한 두 프로세스는 자신의 핸들을 각자 닫을 수 있으며 실제로 각자 닫아야 한다.



 

 

 

 

출처 : Windows 시스템 프로그래밍 윈도우즈 API 핵심 바이블

'0x001 Programming > 01. C | C++' 카테고리의 다른 글

[Tip] 동기화  (0) 2017.12.29
[Tip] Thread 사용시 주의 사항  (0) 2017.12.29
[Example] 이진 검색 트리를 이용한 파일 정렬  (0) 2017.12.29
[Tip] Heap Memory  (0) 2017.12.29
[Example] 예외 처리  (0) 2017.12.29
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <malloc.h>
#include <io.h>
#include <process.h>

#define KEY_SIZE 8

#define EMPTY _T ("")
#define YES _T ("y")
#define NO _T("n")
#define CR 0x0D
#define LF 0x0A
#define TSIZE sizeof (TCHAR)

#ifdef _UNICODE 
#define _tstrrchr wcsrchr
#else
#define _tstrrchr strrchr
#endif

#ifdef _UNICODE 
#define _tstrstr wcsstr
#else
#define _tstrstr strstr
#endif

#ifdef _UNICODE 
#define _memtchr wmemchr
#else
#define _memtchr memchr
#endif

#define GetExceptionCode            _exception_code
#define exception_code              _exception_code
#define GetExceptionInformation     (struct _EXCEPTION_POINTERS *)_exception_info
#define exception_info              (struct _EXCEPTION_POINTERS *)_exception_info
#define AbnormalTermination         _abnormal_termination
#define abnormal_termination        _abnormal_termination

unsigned long __cdecl _exception_code(void);
void *        __cdecl _exception_info(void);
int           __cdecl _abnormal_termination(void);

typedef struct _TREENODE
{
 struct _TREENODE *Left, *Right;
 TCHAR key[KEY_SIZE];
 LPTSTR pData;

} TREENODE, *LPTNODE, **LPPTNODE;

#define NODE_SIZE sizeof( TREENODE )
#define NODE_HEAP_ISIZE 0x8000
#define DATA_HEAP_ISIZE 0x8000
#define MAX_DATA_LEN 0x1000
#define TKEY_SIZE KEY_SIZE * sizeof( TCHAR )
#define STATUS_FILE_ERROR 0xE0000001

VOID ReportError (LPCTSTR userMessage, DWORD exitCode, BOOL printErrorMessage);
VOID ReportException (LPCTSTR userMessage, DWORD exceptionCode);
DWORD Options (int argc, LPCTSTR argv [], LPCTSTR OptStr, ...);

LPTNODE FillTree( HANDLE, HANDLE, HANDLE);
BOOL Scan( LPTNODE );
int KeyCompare(LPCTSTR, LPCTSTR);
BOOL InsertTree( LPPTNODE, LPTNODE );

int _tmain(int argc, LPTSTR argv[])
{
 HANDLE hIn = INVALID_HANDLE_VALUE, hNode = NULL, hData = NULL;
 LPTNODE pRoot;
 BOOL noPrint;
 CHAR errorMessage[0x100] = {0x0, };
 
 int iFirstFile = Options( argc, argv, _T("n"), &noPrint, NULL);
 int iFile = 0;

 for( iFile = iFirstFile; iFile < argc; iFile ++ )
 {
  __try
  {
   hIn = CreateFile( argv[iFile], GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);

   if( hIn == INVALID_HANDLE_VALUE )
   {
    RaiseException( STATUS_FILE_ERROR, 0, 0, NULL );
   }

   __try
   {
    hNode = HeapCreate( HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE, NODE_HEAP_ISIZE, 0);
    hData = HeapCreate( HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE, DATA_HEAP_ISIZE, 0);

    pRoot = FillTree(hIn, hNode, hData);

    _tprintf(_T("Sorted file : %s \n"), argv[iFile]);

    Scan( pRoot );

   }
   __finally
   {
    if( hNode != NULL )
    {
     HeapDestroy( hNode );
    }

    if( hNode != NULL )
    {
     HeapDestroy( hData );
    }

    hNode = NULL;
    hData = NULL;

    if( hIn != INVALID_HANDLE_VALUE )
     CloseHandle(hIn);

    hIn = INVALID_HANDLE_VALUE;
    
   }

  }
  __except( (GetExceptionCode() == STATUS_FILE_ERROR || GetExceptionCode() == STATUS_NO_MEMORY) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH )
  {
   _stprintf( errorMessage, _T("\n %s %s "), _T("sortBT error onfile : "), argv[iFile]);
   ReportError( errorMessage, 0, TRUE );
  }
 }


 return 0;
}

LPTNODE FillTree (HANDLE hIn, HANDLE hNode, HANDLE hData)
{
 LPTNODE pRoot = NULL, pNode;
 DWORD nRead, i;
 BOOL atCR;
 TCHAR dataHold[MAX_DATA_LEN];
 LPTSTR pString;
     
 while (TRUE) {
  pNode = HeapAlloc (hNode, HEAP_ZERO_MEMORY, NODE_SIZE);
  pNode->pData = NULL;
  (pNode->Left) = pNode->Right = NULL;

  if (!ReadFile (hIn, pNode->key, TKEY_SIZE,
    &nRead, NULL) || nRead != TKEY_SIZE)
  
   return pRoot; 
  atCR = FALSE;   

  for (i = 0; i < MAX_DATA_LEN; i++) {
   ReadFile (hIn, &dataHold[i], TSIZE, &nRead, NULL);
   if (atCR && dataHold[i] == LF) break;
   atCR = (dataHold[i] == CR);
  }
  dataHold[i - 1] = _T('\0');


  pString = HeapAlloc (hData, HEAP_ZERO_MEMORY,
    (SIZE_T)(KEY_SIZE + _tcslen (dataHold) + 1) * TSIZE);
  memcpy (pString, pNode->key, TKEY_SIZE);
  pString[KEY_SIZE] = _T('\0');
  _tcscat (pString, dataHold);
  pNode->pData = pString;

  InsertTree (&pRoot, pNode);

 } 
 return NULL;
}

BOOL InsertTree (LPPTNODE ppRoot, LPTNODE pNode)
{
 if (*ppRoot == NULL) {
  *ppRoot = pNode;
  return TRUE;
 }
 if (KeyCompare (pNode->key, (*ppRoot)->key) < 0)
  InsertTree (&((*ppRoot)->Left), pNode);
 else
  InsertTree (&((*ppRoot)->Right), pNode);
 return TRUE;
}

int KeyCompare (LPCTSTR pKey1, LPCTSTR pKey2)
{
 return _tcsncmp (pKey1, pKey2, KEY_SIZE);
}

static BOOL Scan (LPTNODE pNode)
{
 if (pNode == NULL)
  return TRUE;
 Scan (pNode->Left);
 _tprintf (_T ("%s\n"), pNode->pData);
 Scan (pNode->Right);
 return TRUE;
}


DWORD Options (int argc, LPCTSTR argv [], LPCTSTR OptStr, ...)
{
 va_list pFlagList;
 LPBOOL pFlag;
 int iFlag = 0, iArg;

 va_start (pFlagList, OptStr);

 while ((pFlag = va_arg (pFlagList, LPBOOL)) != NULL
    && iFlag < (int)_tcslen (OptStr)) {
  *pFlag = FALSE;
  for (iArg = 1; !(*pFlag) && iArg < argc && argv [iArg] [0] == _T('-'); iArg++)
   *pFlag = _memtchr (argv [iArg], OptStr [iFlag],
     _tcslen (argv [iArg])) != NULL;
  iFlag++;
 }

 va_end (pFlagList);

 for (iArg = 1; iArg < argc && argv [iArg] [0] == _T('-'); iArg++);

 return iArg;
}

VOID ReportException (LPCTSTR userMessage, DWORD exceptionCode)
{ 
 if (lstrlen (userMessage) > 0)
  ReportError (userMessage, 0, TRUE);

 if (exceptionCode != 0) 
  RaiseException (
   (0x0FFFFFFF & exceptionCode) | 0xE0000000, 0, 0, NULL);
 
 return;
}

VOID ReportError (LPCTSTR userMessage, DWORD exitCode, BOOL printErrorMessage)
{
 DWORD eMsgLen, errNum = GetLastError ();
 LPTSTR lpvSysMsg;
 _ftprintf (stderr, _T("%s\n"), userMessage);
 if (printErrorMessage) {
  eMsgLen = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
    NULL, errNum, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
    (LPTSTR) &lpvSysMsg, 0, NULL);
  if (eMsgLen > 0)
  {
   _ftprintf (stderr, _T("%s\n"), lpvSysMsg);
  }
  else
  {
   _ftprintf (stderr, _T("Last Error Number; %d.\n"), errNum);
  }

  if (lpvSysMsg != NULL) LocalFree (lpvSysMsg); 
 }

 if (exitCode > 0)
  ExitProcess (exitCode);

 return;
}
출처 : Windows 시스템 프로그래밍 윈도우즈 API 핵심 바이블​

'0x001 Programming > 01. C | C++' 카테고리의 다른 글

[Tip] Thread 사용시 주의 사항  (0) 2017.12.29
[Tip] 프로세스 관리  (0) 2017.12.29
[Tip] Heap Memory  (0) 2017.12.29
[Example] 예외 처리  (0) 2017.12.29
[Tip] 예외 처리  (0) 2017.12.29

+ Recent posts