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영역이 손상되었을때 복구하는 도구로 사용된다.

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

 

 

2010년 5월 13일 목요일

해킨토시 설치하기

The first time I installed OSX on my P1610 I used Kalyway 10.5.2, it was pretty successful,
but there were a few remaining issues and it was a lot of hard work.
Quite a few months down the line and much has improved in the world of hackintosh,
My latest install is with iAtkos v7, which can be found at any good torrent search.

I do suggest burning the disc at the slowest speed possible to minimise errors on the DVDR.
Here is how I installed iAtkos v7 OS X 10.5.7 on my Fujitsu P1610.
I'm not guaranteeing it will work for you, it's just how I did it.

MANY MANY THANKS to all that put the work in to make this possible.


<Preparation>
-------------
The very first thing I did was replace the standard Atheros WiFi card in my P1610,
with a Airport compatible 802.11N Broadcom based WiFi card, that I picked up off eBay.
No idea if iAtkos v7 supports the original Atheros card as I got rid of ages ago.

Downloaded and burned the iAtkos v7 disc image at x2 speed.

Plug in my external USB DVD rom and put the iAtkos v7 disc in.
Turn on your Fujitsu Lifebook P1610 and at the POST screen hit F2 to enter the BIOS.
In the BIOS go over to Boot and down to Boot Device Priority.
in the list of Boot priority order, I removed everything except CD/DVD drive,
hit x to remove or add a device to the list.
Hit F10 to save and exit, then enter to confirm.


<Installation>
--------------
Boot from CD/DVD drive and at the Darwin loader
press F8 for options and type -v to boot in verbose mode.
Once the installer loads up
click the button to continue

under utilities menu
click on Disk Utility

Select the drive in your P1610, then choose the Partition tab.
You could create just one partition if you want just Mac OS X, format it in Mac OS Extended (Journaled) format.

----
I have mine setup as a dualboot, Mac OS X and Windows XP Tablet.
So created two equal sized paritions the first being a Mac OS Extended (Journaled)
the second being (MS-DOS) FAT so I can install Windows XP Tablet to it.
Once the partitions are created,
I then install Windows XP Tablet as I normally would, during the XP installation I reformat the FAT partition to NTFS.
Since it doesn't see the Mac OS Extended (Journaled) partition there is no issues here.
After installing Windows XP, boot from the iAtkos disc again with F8 and typing -v.
----

Agree to the edited iAtkos license agreement,
Select a Destination - click on the Mac OS partiton, then continue
Install Summary - Customize
(the following are the options I used, they may not be the best, but they work for me.)

-iATKOS v7 Main System

-Bootloader
--Chameleon v2

-X86 Patches
--/Extra directory
--DSDT
--Decrypters
---AppleDecrypt
--SMBIOS drivers
---SMBIOS Enablers
----SMBIOS Resolver
---SMBIOS' for X86
----AppleSMBIOS-28
--Kernel
---9.7.0 Kernel voodoo
--Disabler

-Drivers
--VGA
---Intel
----GMA 950
-----GMA 950 Laptop
--System
---SATA/IDE
----Generic IDE
---CardBus Driver
---USB
---Sound
----Voodoo HDA driver
---PS/2 mouse/keyboard
----Apple PS/2 driver
---Laptop Battery
---NTFS-3G
---Network
----Wireless
-----Broadcom BCM43xx

-Post-Install Actions


Click Done, then Install


<Setup>
-------
Once the Install has succeeded, click restart,
and when you get the Fujitsu POST screen hit F2 to go into the BIOS
Unplug the USB DVD drive
Remove the CD/DVD drive from the Boot priority order,
and re-add the hard drive Drive0:
Press F10 to save and exit, then enter to confirm.
When you get the Chameleon boot screen, push down three times to select Boot Verbose and hit enter.

OSX will start up and you should hear some funky music playing out the speakers,

Select Country
Keyboard
Dou you already own a mac? - Continue
Select a wireless Service
Enter your Apple ID - you can leave it blank and hit Continue
Registration Information - Alt+Q to skip
Create Your Account Name and a password
Select Time Zone
Set the Date and Time
Don't Forget to Register - Done


<Display> 반드시 설정해야한다 .
---------
First things first, set the correct screen resolution.
Click on System preferences, the metallic looking Cogs icon at the bottom
then under the Hardware heading, Displays.
select 1280x768 to get the correct resolution.

/Library/Preferences/SystemConfiguration/com.apple.Boot.plist 에서

<key>Graphic Mode</key>

<string>1280*768*32</string>

를 입력후 저장 한다 맨 아래쪽에

만약 하지 않게 되면 재부팅 했을경우 gui를 볼수 없게 된다.



<Ethernet Networking>
---------------------
1. 터미널 실행
2. sudo -s 실행(관리자 권한 획득)
3. 관리자 암호 입력
4. vi /System/Library/Extensions/IONetworkingFamily.kext/Contents/Plugins/AppleYukon2.kext/Contents

/Info.plist

아래 색칠된 부분 수정

<key>Yukon-88E8055</key>
        <dict>
            <key>CFBundleIdentifier</key>
            <string>com.apple.iokit.AppleYukon2</string>
            <key>EnableLowPwr</key>
            <integer>1</integer>
            <key>IOClass</key>
            <string>yukon2osx</string>
            <key>IOPCIPrimaryMatch</key>
            <string>0x436311ab</string>
            <key>IOPCISecondaryMatch</key>
            <string>0x139a10cf</string>
            <key>IOProviderClass</key>
            <string>IOPCIDevice</string>
            <key>InitialWaitForLinkUp</key>
            <string></string>
            <key>MACNumber</key>
            <integer>1</integer>
            <key>Model</key>
            <string>Yukon Gigabit Adapter 88E8055 Singleport Copper SA</string>
            <key>NetworkNumber</key>
            <integer>6000</integer>
            <key>Vendor</key>
            <string>marvell</string>
        </dict>


5. rm /System/Library/Extensions.mkext
6. diskutil repairPermissions /
7. reboot


<Keyboard layout>
-----------------
http://www.insanelymac.com/forum/index.php...st&p=767201
download and install the Microsoft Keyboard layouts,
once that's done, restart the computer and go into System Preferences and select International.
Select Input Menu.
scroll through and check the box for the keyboard you have.
mine is British - Microsoft.
Then up in the top right you'll see a little Flag where you can click on to change the keyboard layout.


<Upgrade to 10.5.8>
-------------------
Click on the Apple icon, top right, and select Software update,
uncheck the 10.5.8 update for now, and update everything else.
once it's all updated and restarted, check for updates again,
hopefully there is now only the 10.5.8 update left.
Check that and apply that update, it takes a while,
but it should restart a couple of times and you'll now have 10.5.8 running on your P1610.


<Things that are not working>
-----------------------------
Touchscreen, Buttons on the screen bezel, SD card reader.
I don't think I'm going to be able to get the Touchscreen to work,
as the P1610 uses a serial touchscreen controller, which there doesn't seem to be any support for.
Again the same for the buttons on the screen bezel, no support.
But I reckon with a bit more hunting around I should be able to get the SD card reader working. Hopefully.

2010년 5월 7일 금요일

파일을 DB처럼 사용하는법

class CInfo
{
public:
char szName[40];
char szPhone[40];
char szMobile[40];
char szAddress[40];
char szEmail[40];
char szEtc[1024-40*5-7];
char szn[3];


CInfo(){
  memset(szName, 0 ,sizeof(char)*40);
  memset(szPhone, 0 ,sizeof(char)*40);
  memset(szMobile, 0 ,sizeof(char)*40);
  memset(szAddress, 0 ,sizeof(char)*40);
  memset(szEmail, 0 ,sizeof(char)*40);
  memset(szEtc, 0 ,sizeof(char)*1024-40*5-7);
  memset(szn,0,sizeof(szn));
  strncpy(szn,_T("\r\n"),sizeof("\r\n"));
}
};

1.데이터 삽입(Insert)



void CFileDB::InsertInFile(CInfo &Info)
{


m_hFile = CreateFile(_T("c:\\test001.dat"), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

DWORD nWrite = 0;

SetFilePointer(m_hFile,0,NULL,FILE_END);

//WriteFile(m_hFile,_T("\n"),sizeof("\n"),&nWrite,NULL);




if(m_hFile != INVALID_HANDLE_VALUE)
{
  WriteFile(m_hFile,&Info,sizeof(Info),&nWrite,NULL);
}

CloseHandle(m_hFile);


}



2.데이터 조회(Select)



void CFileDB::SelectInFile(void)
{

UpdateData(TRUE);


DWORD dwStartTime, dwFinishTime;
CInfo Info;
memset(&Info,0,sizeof(Info));



m_hFile = CreateFile(_T("c:\\test001.dat"), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

SetFilePointer(m_hFile,0,NULL,FILE_BEGIN);

int nNumOfData = GetFileSize(m_hFile,NULL)/sizeof(CInfo);

DWORD nRead = 0;

if(m_hFile != INVALID_HANDLE_VALUE)
{
  for(int i = 0 ; i< nNumOfData ; ++i)
  {
   SetFilePointer(m_hFile,sizeof(Info)*i,NULL,FILE_BEGIN);
   ReadFile(m_hFile,&Info,sizeof(Info),&nRead,NULL);

   if(m_strNameSearch.Compare(Info.szName) == 0 )
   {
    // 데이타를 표시한다.
    m_uIndex = i;
    m_strName = Info.szName;
    m_strPhone = Info.szPhone;
    m_strMobile = Info.szMobile;
    m_strAddress = Info.szAddress;
    m_strEmail = Info.szEmail;
    dwFinishTime = GetTickCount();
    m_uTime = 0;

   }
  }
 
}

CloseHandle(m_hFile);

UpdateData(FALSE);

}



3.데이터 수정(Update)



void CFileDB::UpdateInFile(void)
{

UpdateData(TRUE);


DWORD dwStartTime, dwFinishTime;
CInfo Info;
memset(&Info,0,sizeof(Info));

m_hFile = CreateFile(_T("c:\\test001.dat"), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

SetFilePointer(m_hFile,0,NULL,FILE_BEGIN);

int nNumOfData = GetFileSize(m_hFile,NULL)/sizeof(CInfo);

DWORD nRead = 0;
DWORD nWrite = 0;

if(m_hFile != INVALID_HANDLE_VALUE)
{
  for(int i = 0 ; i< nNumOfData ; ++i)
  {
   SetFilePointer(m_hFile,sizeof(Info)*i,NULL,FILE_BEGIN);
   ReadFile(m_hFile,&Info,sizeof(Info),&nRead,NULL);

   if(m_strNameSearch.Compare(Info.szName) == 0 )
   {
    // 데이타를 표시한다.
    m_uIndex = i;
    strncpy(Info.szName,m_strName,strlen(m_strName));
    strncpy(Info.szPhone, m_strPhone,strlen(m_strPhone));
    strncpy(Info.szMobile, m_strMobile,strlen(m_strMobile));
    strncpy(Info.szAddress, m_strAddress,strlen(m_strAddress));
    strncpy(Info.szEmail, m_strEmail,strlen(m_strEmail));
    dwFinishTime = GetTickCount();
    m_uTime = 0;

    SetFilePointer(m_hFile,sizeof(Info)*i,NULL,FILE_BEGIN);
    WriteFile(m_hFile,&Info,sizeof(Info),&nWrite,NULL);

    MessageBox(_T("수정이 완료되었습니다."));

   }
  }

}

CloseHandle(m_hFile);

UpdateData(FALSE);

}



4.데이터 삭제(Delete)

void CFileDB::DeleteInFile(void)
{
CInfo Info;
DWORD nRead = 0;
DWORD nWrite = 0;

BOOL bCompare = FALSE;


m_hFile = CreateFile(_T("c:\\test001.dat"), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
SetFilePointer(m_hFile,0,NULL,FILE_BEGIN);

int nNumOfData = GetFileSize(m_hFile,NULL)/sizeof(CInfo);

if(m_hFile != INVALID_HANDLE_VALUE)
{
  for(int i = 0 ; i< nNumOfData ; ++i)
  {
   SetFilePointer(m_hFile,sizeof(Info)*i,NULL,FILE_BEGIN);
   ReadFile(m_hFile,&Info,sizeof(Info),&nRead,NULL);

   if(m_strName.Compare(Info.szName) == 0 )
   {
    bCompare = TRUE;
   
   }

   if(bCompare)
   {
    //바로 뒤에 있는 데이터를 읽어서
    SetFilePointer(m_hFile,sizeof(Info)*(i+1),NULL,FILE_BEGIN);
    ReadFile(m_hFile,&Info,sizeof(Info),&nRead,NULL);
    //데이터를 삭제한 곳에 덮어쓴다.
    SetFilePointer(m_hFile,sizeof(Info)*i,NULL,FILE_BEGIN);
    WriteFile(m_hFile,&Info,sizeof(Info),&nWrite,NULL);  

   }


  }
}

if(bCompare)
{
  CInfo blank;
  memset(&blank,0,sizeof(CInfo));
  SetFilePointer(m_hFile,sizeof(CInfo)*(nNumOfData -1),NULL,FILE_BEGIN);
  WriteFile(m_hFile,&blank,sizeof(blank),&nWrite,NULL);

  MessageBox(_T("삭제가 완료되었습니다."));

}

CloseHandle(m_hFile);

}

2010년 4월 27일 화요일

프로세스감추기 2

이번엔 프로세스를 숨기는 방법에 대해 말씀드리려고 합니다.

윈도우에는 유저영역과 커널영역이 있습니다.

SDK를 이용해서 만들어지는 모든 응용 프로그램은 유저 영역에서만

다루어집니다.

아니, 윈도우에서는 응용 프로그램이 커널 영역을 건드리는 것을 막아 놓았습니다.

사실, 핸들이라는 것을 통해서 응용 프로그램은 간접적으로 커널 영역을 건드리는데요,

커널을 만지지 않고서는 아무 작업도 할 수 없기 때문에

윈도우가 고안한 방법일 겁니다.

커널을 직접 건드리기 위해선 DDK라는 것을 통해 시스템 프로그램 내지는

드라이버(확장자가 .sys입니다)를 만드는게 보통인데요,

제가 설명하고자 하는 방법은 SDK만을 이용한 방법입니다.

프로세스를 숨기는 기본적인 원리는 이렇습니다.

윈도우에는 커널영역이 있다고 했죠?

그리고 그 영역에 접근하기 위한 핸들이 있다고 했습니다.

각각의 핸들에는 대응되는 오브젝트가 있습니다.

예를 들어 윈도우 핸들에는 윈도우 정보를 담고 있는 구조체가,

프로세스 핸들에는 프로세스 정보를 담고 있는 구조체가 있습니다.

이 프로세스의 정보를 담은 구조체의 이름은 EPROCESS입니다.

EPROCESS에는 엄청나게 많은 멤버가 있는데요, 그 중 이 테마에 중요한 것은

ActiveProcessLinks라는 멤버 하나 뿐입니다.

이름에서 대략 눈치채셨겠지만, 프로세스들은 연결리스트 구조로 연결되어있습니다.

때문에 목표하는 프로세스를 연결리스트에서 끊기만 하면 감쪽같이

목록에서 사라지게 되지요.

혹시 CPU 사용 권한을 잃게 될 까 걱정하지 않아도 됩니다. 왜냐면,

작업은 쓰레드를 기반으로 이루어지기 때문에, 프로세스는 이름일 뿐입니다.

윈도우즈의 버전에 따라 ActiveProcessLinks의 오프셋값이 다른데요,

XP의 경우 구조체의 시작 번지로 부터 0x088만큼 떨어진(오프셋된) 위치에

ActiveProcessLinks가 있습니다. 이 멤버의 자료형은 LIST_ENTRY인데, 이는

SDK플랫폼에도 정의가 되어있는 구조체로, 다음 두 멤버를 가집니다.

PLIST_ENTRY Flink;
PLIST_ENTRY Blink;

그러므로,
ActiveProcessLinks.Filnk->Blink = ActiveProcessLinks.Blink;
ActiveProcessLinks.Bilnk->Flink = ActiveProcessLinks.Flink;
의 작업을 거쳐주면 되는 겁니다.

그럼 문제는 EPROCESS의 주소를 어떻게 알아내는가 인데요..

바로 여기에 여러가지 테크닉이 존재합니다.

어떤 방법을 사용하든지 Native API를 사용하게 될 텐데요,

이는 SDK에 정의되어있지 않으므로, GetProcAddress를 통해 직접 주소를 얻어내야 합니다.

제가 설명할 방법에 쓰이는 API함수는 다음 두 개입니다.

NTSTATUS __stdcall ZwQuerySystemInformation(IN SYSTEM_INFORMATION_CLASS SystemInformationClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL );

NTSTATUS __stdcall ZwSystemDebugControl(IN SYSDBG_COMMAND SysDbgChunks, IN OUT PVOID pQueryBuff, DWORD dwSize, DWORD, DWORD, NTSTATUS *pResult);

NTSTATUS는 DDK에서 쓰이는 자료형으로, SDK에서 사용하려면 LONG형을 typedef해야 합니다.

(아시는 분은 알겠지만, 참고로 예기해 드립니다. IN, OUT, OPTIONAL은 아무 의미없는 #define으로, 인자가 입력인지, 출력인지, 그리고 사용하지 않으므로 NULL을 넣어도 되는가 등을 예기해주는 겁니다)

SYSTEM_INFORMATION_CLASS 는 enum으로 정의된 자료형으로, 여러가지 값이

정의되어 있습니다. 어떤 분이 찾아내셨는지는 모르겠지만;;

그 목록에 나와있지 않은 값들 중에서 16을 넣게 되면, 재미있는 일이 벌어지는데요,

이걸 이용할 것입니다. 사용할 값은 16뿐이므로 SYSTEM_INFORMATION_CLASS를

다음과 같이 정의해서 사용하면 됩니다.

typedef enum _SYSTEM_INFORMATION_CLASS
{
SystemHandleInformation = 16
} SYSTEM_INFORMATION_CLASS;

너무 길어지네요;; 2부에서 계속합니다.

 

 

SystemHandleInformation이란 이름에서 알 수 있듯,

16은 시스템 상에 로드되어있는 모든 핸들에 대한 정보를 얻어오게 합니다.

그 정보는 다음과 같은 구조체의 배열에 저장됩니다.

typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG ProcessId;
UCHAR ObjectTypeNumber;
UCHAR Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

눈여겨 보아야 할 것은 첫번째, 두번째, 다섯번째 인자인데요,

첫번째 인자는 이 핸들을 소유한 프로세스의 아이디입니다. 두번째 인자는

이 핸들의 타입인데요, 핸들에는 여러 종류가 있으니 이를 구분해 주는 겁니다.

윈도우 핸들, 쓰레드 핸들 등 많은 종류가 있지만, 프로세스는 5번입니다.

즉, 이 핸들이 우리가 찾고자 하는 프로세스인지 아닌지 조사하려면,

우선 첫번째 인자와 찾고자 하는 프로세스의 아이디를 조사하고,

두번째 인자가 5인지를 조사하면 되는 겁니다.

그리고 대망의 다섯번째 인자는 바로 EPROCESS구조체의 주소입니다.

이제 ZwQuerySystemInformation의 각 인자에 대해 설명드리겠습니다.

첫번째 인자는 어떤 종류의 정보를 얻어올 것인가로, 16을 입력할 경우 시스템 핸들 정보를

얻어온다고 했습니다.

두번째 인자는 정보를 입력받을 버퍼의 포인터입니다.

세번째 인자는 버퍼의 크기입니다.

네번째 인자는 정보의 크기입니다.

시스템 상에 핸들이 얼마나 많은지 모르기 때문에, 적당한 버퍼의 크기를 알 수가 없습니다.

그래서 1바이트 부터 시작해서 계속 2씩 곱해가면서 적당한 사이즈를 찾는데요,

버퍼의 크기가 부적절한 경우 ZwQuerySystemInformation은

STATUS_INFO_LENGTH_MISMATCH라는 값을 반환합니다.

이 값 역시 DDK에서 사용하는 매크로로, ((NTSTATUS)0xC0000004L)와 같이 정의하면 됩니다.

사이즈가 적당치 않은 경우는 버퍼를 free하고 크기를 두배로 늘린 뒤 다시 시도하는

반복문을 돌려서, 적당한 크기를 찾습니다.

크기가 충분해서 Query Information이 성공하면,

ZwQuerySystemInformation은 0이상의 값을 반환합니다.

네번째 인자는 OPTIONAL이므로 그냥 0을 주면 됩니다.

아무튼 이렇게 해서 얻어낸 정보의 앞 4바이트는 배열의 인자 개수를 나타냅니다.

다시말하면 시스템상에 존재하는 핸들의 숫자입니다.

이제 이 숫자만큼 루프를 돌면서 원하는 핸들을 찾아내면 됩니다.

헥헥.; 여기까지가 EPROCESS의 주소를 알아내는 방법입니다.

이제 다 끝난게 아닌가 하시겠지만, 사실 더 있습니다.

이렇게 얻어낸 EPROCESS의 주소는 선형 주소라는 것으로, 직접 접근할 수가 없습니다.

주소에는 물리 주소, 선형 주소, 논리 주소 이렇게 3개가 있는데요,

보통 그냥 사용해 왔던 포인터 변수는 논리 주소입니다.

선형 주소는 가상 주소라고도 하는데요, ZwSystemDebugControl은 여기서 쓰입니다.

이녀석도 첫번째 인자로 enum자료형을 받는데요, 여기서 사용할 값은

8, 9 두 개이므로, 다음과같이 정의해서 쓰면 됩니다.

typedef enum _SYSDBG_COMMAND
{
SysDbgCopyMemoryChunks_0 = 0x08, SysDbgCopyMemoryChunks_1 = 0x09
} SYSDBG_COMMAND;

두번째 인자는 데이터를 받을 구조체의 포인터입니다.

이 구조체는 MEMORY_CHUNKS라는 구조체로, 다음과 같이 정의됩니다.

typedef struct _MEMORY_CHUNKS
{
PVOID pVirtualAddress;
PVOID pBuffer;
DWORD dwBufferSize;
} MEMORY_CHUNKS, *PMEMORY_CHUNKS;

첫번째 멤버로 접근하고자 하는 메모리의 주소를 입력하고,

두번째 멤버와 세번째 멤버에 데이터를 저장할 버퍼의 주소와 그 크기를 입력합니다.

가상메모리를 읽는 경우(SysDbgCopyMemoryChunks_0),

이 버퍼로 가상 메모리의 데이터가 쓰여집니다.

가상메모리를 쓰는 경우(SysDbgCopyMemoryChunks_1),

이 버퍼에 쓰인 내용이 가상 메모리에 쓰여집니다.

한편 ZwSystemDebugControl의 세번째 인자는 읽고자 하는 데이터의 크기입니다.

포인터를 읽을 것이기 때문에 여기에는 4를 지정하면 됩니다.

네번째와 다섯번째 인자는 여기서 중요하지 않기 때문에 그냥 비워두었습니다.

그리고 여섯번째 인자로 성공의 여부가 들어오게 됩니다.

이렇게 해서 가상메모리를 읽거나 쓸 수가 있습니다.

그럼 3부에서 계속하겠습니다.

 

 

이제 진짜 끝인가 하시겠지만 사실 조금 더 있습니다.

가상메모리는 그냥 읽고 쓸 수 없습니다. 이걸 읽고 쓰려면 그에 맞는 권한(Privilege)

를 획득해야 하는데, 이 권한을 조절할 줄 알면 많은 것을 할 수 있습니다(^^훗)

이 권한을 통해 할 수 있는 일에 비해 이를 얻는 방법은 매우 쉽습니다.

Native API따위를 이용하지 않아도 됩니다.

이부분은 인터넷에 자료가 많이 있으므로 소스만 올리겠습니다.

TOKEN_PRIVILEGES priv = { 1, {0, 0, SE_PRIVILEGE_ENABLED} };
LookupPrivilegeValue(0, lpName, &priv.Privileges[0].Luid);
HANDLE hToken;
OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
AdjustTokenPrivileges(hToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), 0, 0);
CloseHandle(hToken);

권한에는 여러 종류가 있는데, 그 중에는 셧다운 권한, 디버그 권한 등이 있습니다.

이름에서 예측할 수 있지만, 셧다운 권한은 컴퓨터를 시스템 종료할 수 있는 권한입니다.

이를 획득한 후 ExitWindows와 같은 함수를 이용하면 간단히 시스템 종료를 할 수 있습니다.

중요한 것은 디버그 권한입니다.

이 권한은 매우 강력한 권한으로, 이 권한을 획득해야만 가상메모리를 읽거나 쓸 수 있습니다.

(여담이지만)추가로, 이 권한을 얻으면 작업관리자도 종료하지 못하는 중대한

프로세스들을 죽일 수 있습니다.

이러한 프로세스들이 그냥은 Terminate되지 않는 이유는 OpenProcess함수가 NULL을

리턴하기 때문인데요, 그 이유는 PROCESS_ALL_ACCESS권한으로 프로세스를 열려면

디버그 권한이 필요하기 때문입니다.

따라서 디버그 권한을 얻은 뒤 OpenProcess를 하면 정상적으로 핸들을 얻게 되고,

TerminateProcess도 정상적으로 동작합니다.

이 방법을 이용하면 smss.exe, winlogon.exe등을 죽일 수가 있는데,

실험해 본 결과 이럴 경우 블루스크린이 뜨면서 바로 재부팅됩니다;;



어떠셨는지요;;;; 아마 프로세스 숨기는 법에 대해 궁금해 하셨던 분이 많을 겁니다.

저도 그렇습니다.

드라이버 개발자도 아니면서 이걸 궁금해 하는 이유는 하나 뿐이죠..

악성프로그램 제작.

그 증거로 이건 제가 중국 해커싸이트 여기저기 돌아다니면서 종합한 겁니다.

저라고 남말할 처지는 아니지만 .. 악용하지 말아주셨으면 합니다;;

그런 의미에서 전체 소스는 제시하지 않습니다.

(아니..사실 권한을 얻는 소스만 해도 충분히 악용될 수 있겠군요..;;)

죄송합니다; 앞으로는 소스가 제공될 수 없게 &#46124;습니다;; 사정이 생겨서;;

#pragma 의 쓰임새

아직 #pragma까진 써본적 없지만, 앞으로 필요할것 같아서 퍼왔음

#pragma는 define 이나 include와 같이 #으로 시작하는 전처리구문(precompiler)의 하나이다.
컴파일러에 종속적인 구문이라 컴파일러가 변경되었을 경우 제대로된 동작을 보장하지 못하므로 프로젝트 진행중에 서로 다른 컴파일러를 사용한다면 사용하지 않음이 바람직 하겠다.
- 대신 대체하는 문법을 사용해야 되겠다.

#pragma once
이것은 "컴파일러에게 한번만 컴파일해!" 라고 명령한다.
헤더의 중복을 막아준다.
무슨말인가 하면

a.h를 구현한 a.cpp, a.h는 독립적이다.(include가 없다.)
b.h를 구현한 b.cpp, c.h, a.h순서로 include
c.h를 구현한 c.cpp, a.h를 include

컴파일하면 b.h에서 c.h를 포함시키라고 되어있네? 하고 c.h에 들어가고 어? a.h를 포함하라고 그러네? 이러고 a.h를 포함한 c.h가 b.h로 돌아온다 그리고 a.h를 포함하라는 명령을 받고 a.h를 추가하다보면 같은 변수와 함수선언이 되어있다. 에러에러~
같은 선언이 두 번 반복되니 당연히 충돌이 난다. 컴파일러가 똑똑하여 단순히 경고 처리만 해주고 알아서 하나로 종합해줄 수도 있지만 대부분의 기본적인 컴파일러는 이건 아니잖아~ 한다.

이럴 때 써주는 것이다. pragma once
이는 c기본문법을 사용하여 구현할 수 있다.

#ifdef _MYCOMPILECK
#define _MYCOMPILECK
// 헤더 파일의 내용 선언
#endif



#pragma comment()
기본적인 pragma comment()의 형식은 다음과 같다.

#pragma comment( comment-type, ["comment string"] )

[] 안의 구문은 comment-type에 따라 필요할 경우 사용하는 것이다.
comment type에는 compiler, exestr, lib, linker, user 등이 올 수 있다.

#pragma comment( linker, "/subsystem:windows" )
#pragma comment( linker, "/subsystem:console" )

linker 를 사용하면 프로젝트를 console application인지 win32 application인지 명시해줄 수 있다.

또한 섹션의 설정을 할 수 있다.

#pragme comment( linker, "SECTION:.SHAREDATA,RWS" )

#pragma data_seg("SHAREDATA") 와 함께 사용하여 공유 메모리를 생성한다.
위의 명령어 대신 def 파일 안에 아래와 같이 해주어도 된다.

SECTIONS
SHAREDATA READ WRITE SHARED


이 중 가장 대표적인 사용법은 명시적인 라이브러리의 링크이다.

#pragma comment(lib, "xxxx.lib")

와 같이 사용하여 해당 라이브러리를 링크시켜 준다.
여러사람이 같이 수행하는 프로젝트의 경우 이와 같은 방법을 사용하여 lib를 링크하는 것이 라이브러리가 링크되어있다는 사실을 알기에도 좋고 굳이 주석다라 설명할 필요도 없어 좋지 않나 싶다. (있다는 사실은 알지만 아직 프로젝트 수행중 실제로 사용해 본적은 없음)



#pragma data_seg()
pragma data_seg()의 형식은 다음과 같다.

#pragma data_seg( ["section-name"[, "section-class"] ] )

[]는 사용하지 않아도 된다는 의미이다.

#pragma data_seg( "SHAREDATA" )
int x;
char y;
#pragma data_seg()

DLL 파일을 만들어보면서 제일 많이 사용해 보았고 가장 헷갈려 했던 부분이기도 하다.
DLL의 데이터 공유를 하기 위해 사용한다.
공유할 섹션을 만드는 것이다. 위의 명령어는 필수적으로 위에서 사용된 두 가지중 한가지 방법과 함께 사용 되어야 한다.

#pragme comment( linker, "SECTION:.SHAREDATA,RWS" )
SECTIONS
SHAREDATA READ WRITE SHARED

둘 다 해당 SECTION(SHAREDATA)의 허용 범위(?속성?)를 설정하는 것이다. READ, WRITE, SHARED 세 가지를 쓴다는 의미~
해당 사항에 대해 msdn에서 자세한 정보를 발견하지 못해 적지 못하였다(검색능력의 부족!!)
이제 변수 x와 y는 해당 dll을 사용하는 외부 파일과 같이 공유할 수 있는 변수가 되었다.(외부에서 접근 가능하게 되었다.)

이렇게 공유하는 변수는 물론 new로 메모리를 할당한 변수도 공유 가능하다.
특히 new 나 memalloc(이건 아직 미확인이지만 같은 메모리 할당이므로 가능할 것으로 본다)으로 메모리할당한 변수들은 dll외부에서도 해제(delete) 가능하다.



#pragma warning
특정 경고를 끄고 싶을 때 사용한다.
비 쥬얼 스튜디오의 버전이 다르기 때문에 뜨는 경고는 더더욱이 귀찮은 존재이다.(하지만 수정해서 손해볼 것은 없다. 그것이 곧 버그로 이어질 수 있기 때문이다. 특히 형변환의 경우 강제 캐스팅하여 확실히 명시해주는 것이 좋다. 일부러 그 값을 떼어낸다는 프로그래머의 의지를 컴파일러에게 보여주자. 부지런할수록 후에 손이 가는 일이 적어진다. 노력하자~)

형식은 이와 같다.

#pragma warning( warning-specifier : warning-number-list [; warning-specifier : warning-number-list...] )
#pragma warning( push[ ,n ] )
#pragma warning( pop )

실제 사용은 아래와 같이 한다.

#pragma warning( disable:4996 )




#pragma message()
컴파일 중에 메세지를 뿌려준다.
말이 필요없다-.-/

#pragma message("merong")

AfxGetApp() 와 AfxGetMainWnd()의 차이점

exe 파일을 클릭하는 순간 하나의 프로세스가 동작하게 됩니다.

 프로그램당 하나의 프로세스가 시작이 되는 것이며..
 
 최초로 하는 짓은 프로세스의 진입점을 찾는 것입니다.

 즉 main 함수를 찾으며.. mfc에서는 winmain을 참조하게 되겠지요..

 그렇게 되면서 최초에 프로세스가 하나의 쓰레드를 참조하게 되는데..
 
 님이 생성하신 CXXXApp 입니다.

 이녀석의 상속 구조를 보면 CWinApp로 부터 상속했으며..
 
 또 CWinApp는 다음과 같이 선언된것을 볼수 있습니다.

 class CWinApp : public CWinThread

 즉 쓰레드에서 상속받은 것입니다.

 하나의 프로세스에서는 여러개의 쓰레드를 생성 시킬수 있는데..
 
 바로 app라는 의미는 이렇게 생성시킨 쓰레드를 의미합니다.

 그렇다면.. AfxGetApp()의 의미는 가장 먼저 생성시킨 app를 반환한다라는 의미이죠..

 즉 님이 최초 생성시킨.. CXXXApp의 포인터가 반환되는 것입니다.

 따라서 CXXXApp * pApp = (CXXXApp*)AfxGetApp(); // 형변환을 해야 하는 이유는 아시겠죠??

 

 이렇게 생성시킨 쓰레드에 가장 중심이 되는 윈도우를 생성 시키고 등록 시킵니다.

 app에 있는

 BOOL CXXXApp::InitInstance()
 {
  ...
  CMainFrame* pMainFrame = new CMainFrame;
  if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
   return FALSE;

     m_pMainWnd = pMainFrame; // 이렇게 설정하는 과정이 현 쓰레드에 MainWnd를 설정하는 과정입니다.
  ....
 }
 만일 다이얼로그 베이스면
 m_pMainWnd = dlg;
 이렇게 다이얼로그를 메인 윈도우로 등록시키겠지요..

 afx라는 접두어를 주목하세요..
 mfc에서는 모든 전역 자원에는 afx라는 접두어를 붙였습니다.
 이는 모든 위치에서 이 전역자원을 쉽게 콜 할수 있으며..
 님이 가장 중심이 되는 쓰레드나 메인 윈도우를 부를때.. 쉽게 사용 가능합니다.


 따라서 님이 어느 장소에서나

 CMainFrame * pMain = (CMainFrame *)AfxGetMainWnd();
 이렇게 사용함으로서 쉽게 메인 윈도우를 부를수 있습니다.

 

 

 결국.. 모든 프로그램은 하나의 프로세스로 부터 기본이 되는 쓰레드가 존재하며..
 mfc에서 그 중심 쓰레드는 CWinApp에서 관리하게 되고..
 그 쓰레드에서 창을 생성하여...
 가장 기본이 되는 창을 그 쓰레드에 메인 윈도우로 설정하는 과정에 대해서
 이해를 하신다면 쉽게 해결되는 문제입니다.


 메인윈도우가 생성된뒤.. SDI 구조의 경우 또다시
 여러개의 차일드 프레임과 여러개으 뷰, 그리고 도큐먼트등을 둘수 있습니다.

 이들은 한개가 아니기 때문에 Active된 녀석을 얻는 함수를 디폴트로 제공하죠

AfxGetApp()

MFC Programming을 하다 보면 Dialog나 임의의 클래스 에서 Main Frame 이나 View Class의 Method를 사용하여야 할 경우가 있다. 이러한 경우 AfxGetApp() 라는 API가 유용하게 쓰인다.

 

즉, (CMainFrame *)AfxGetApp()->m_pMainWnd) 를 하게 되면 임의의 클래스 에서 Main Frame의 Member 및 Method의 접근을 할 수 있다.

 

또한 Main Frame에서 View나 Document를 접근하는 경우에는 아래와 같이 쉽게 할 수 있다.

 

CMainFrame *pFrame;

pFrame = AfxGetApp()->m_pMainWnd;

CSDIView *pView;

pView = pFrame->GetActiveView;

 

CSDIDoc *pDoc;

pDoc = pView->GetDocument();

 

이처럼 MFC를 사용하여 동등 Level의 클래스에 접근하는 방법은 우선 Application의 Class에 접근한 후 Application의 Main Frame, View로 접근 하면 된다.

CreateProcess 와 핸들

CreateProcess를 호출하고 나면, LPPROCESS_INFORMATION lpProcessInformation  에 있는 Handle 들을 닫아줘야 합니다.

이거 안 해주면... Task Manager에서 확인하면 Handle 값이 계속해서 2개씩 증가하는 것을 확인할 수 있습니다.

MSDN에 보면... 이런 글이 있습니다.
Handles in PROCESS_INFORMATION must be closed with CloseHandle when they are no longer needed.

물론, 이 CreateProcess를 호출하는 Process가 한 번 실행 되었다가 종료되면 모르지만, 계속 실행하는 Service나 기타 다른 프로그램이라면, 주의할 대목입니다.

그리고 보면, 참 생각없이 코딩했다는 생각이 드네요...

MSDN의 Sample을 볼까요?

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

void _tmain( int argc, TCHAR *argv[] )
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    if( argc != 2 )
    {
        printf("Usage: %s [cmdline]\n", argv[0]);
        return;
    }

    // Start the child process. 
    if( !CreateProcess( NULL,   // No module name (use command line)
        argv[1],        // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        0,              // No creation flags
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
    ) 
    {
        printf( "CreateProcess failed (%d)\n", GetLastError() );
        return;
    }

    // Wait until child process exits.
    WaitForSingleObject( pi.hProcess, INFINITE );

    // Close process and thread handles. 
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );
}


afx_msg 설명

메시지 맵( Message Map )

  SDK 프로그램에서는 윈도우 시스템에서 들어온 메시지를 switch문을 사용하여 처리하였다. 그러나 MFC에서는 메시지 처리를 위해 message map이라는 mechanism을 사용하고 있다. Message map은 메시지 번호와 메시지가 발생하였을 때 호출되는 함수의 포인터 등의 정보를 갖고 있는 테이블로 프로그램에 전달된 메시지와 메시지 핸들러 함수를 연결하는데 사용한다.



MFC에서 message를 처리하기 위해서는 다음의 세 가지 단계가 필요하다.

1.        윈도우 클래스의 멤버 함수로 메시지 핸들러 함수를 선언한다.



2.        message map에 message와 message handler 함수를 묶는 message의 매크로를 추가한다. (.cpp file에 존재한다. )



3.        메시지 핸들러 함수의 기능을 구현한다.



위의 세 단계중에서 1, 2 단계는 Class Wizard로 구현이 되고, 마지막 함수의 기능 구현만 구현하면 된다.



● 메시지 핸들러( Message Handler ) 함수

  윈도우로부터 애플리케이션에 메시지가 전달될 때 해당 메시지를 처리하는 멤버 함수이다.함수이름은 WM_를 떼고, 대신 On을 붙여서 시작하고 함수 선언 시에 afx_msg를 붙여 메시지 핸들러 함수라는 것을 나타낸다.



MFC를 처음에 시작하면 많이 만나게 되는 afx_msg에 관한 설명이다.
물론 이해는 잘 안되지만......
대충 보게 되면 어떤 메세지를 위한 구조체가 결정되어있고 그것을 불러다 쓰면 된다는 말이다.
위저드를 통해서 모든것은 가능하기 때문에 어렵지는 않지만 나중에 디버그나 코딩을 수월하게 하려면 이것에 대한 개념을 잡는것이 중요하다 하겠다. 

리스트컨트롤 체크박스 통지받기

void DemoDlg::OnItemchangedLinksList(NMHDR* pNMHDR, LRESULT* pResult)
{
         NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
         *pResult = 0;
 
         if (pNMListView->uOldState == 0 && pNMListView->uNewState == 0)
                 return;  // No change
 
         BOOL bPrevState = (BOOL)(((pNMListView->uOldState &
                                   LVIS_STATEIMAGEMASK)>>12)-1);   // Old check box state
         if (bPrevState < 0)       // On startup there's no previous state 
                 bPrevState = 0; // so assign as false (unchecked)
 
         // New check box state
         BOOL bChecked=(BOOL)(((pNMListView->uNewState & LVIS_STATEIMAGEMASK)>>12)-1);
         if (bChecked < 0) // On non-checkbox notifications assume false
                 bChecked = 0;
 
         if (bPrevState == bChecked) // No change in check box
                 return;
 
         // Now bChecked holds the new check box state
 
         // ....
}

 

Setting the check box state of an item

void SetLVCheck (WPARAM ItemIndex, BOOL bCheck)
{
         ListView_SetItemState (m_lvTestList.m_hWnd, ItemIndex,
                 UINT((int(bCheck) + 1) << 12), LVIS_STATEIMAGEMASK);
}

삼바서버 설정(smb.conf)

출처: http://cafe.naver.com/devctrl/4267

삼버 서버를 구동하기 위해서는 먼저 네트워크 환경에 맞게 설정을 해야 한다.

삼바의 설정 파일은 /etc/samba/ 아래에 있으며 smb.conf가 기본 설정 파일이다.

삼바의 설정은 매우 복잡하며 환경에 따라서 다양한 설정 방법 등이 존재하므로 처음부터 모든 것을 설정하기 보다는 가장 기본적인 것부터 설정해 나가는 것이 효율적이다.

 

# testparm

smb.conf 파일의 구문이 정확한가를 검증한다. 삼바 데몬을 실행하기에 앞서 이 유틸리티로 문법 적합성 검사를 해야 한다.

 

# service smb restart (/etc/init.d/smb restart)

smb.conf 파일을 변경한 후에는 반드시 데몬을 재시작 해야 한다.

 

[global]

삼바 서버가 공유하는 자원들에 공통적으로 적용할 기본 값을 설정하는 곳이다.

 

[homes]

리눅스 계정 사용자들의 홈 디렉토리를 로그인 홈으로 사용하기 위한 설정이다.

 

[printers]

네트워크 공유 프린트에 대한 설정 부분이다.

 

 

=== Global Settings  ===========================================================

[global]

# 한국어 지원을 위한 설정

dos charset = cp949

unix charset = cp949 (utf8, euc-kr)

 

# 윈도우즈의 작업 그룹과 같다. 공유하고자 하는 작업 그룹 이름을 작성한다.

# 삼바 서버와 클라이언트의 작업 그룹 명은 반드시 일치해야 한다.

workgroup = RND

 

# 윈도우 네트워크 환경에서 보여줄 삼바 서버에 대한 설명이다.

server string = Samba Server

 

# 삼바 서버에 접속을 허용할 IP 지정.

# IP 주소 또는 네트워크 / 넷마스크 형태로도 지정할 수 있다.

hosts allow = 192.168.41. 127.

--> 192.168.41.0 네트워크에 속한 모든 호스트들과 로컬 시스템(127.0.0.1)만 허용

hosts allow = 192.168.41. except 192.168.41.100  192.168.41.101

--> 192.168.41.100번과 101번을 제외한 192.168.124의 모든 호스트들은 접속 허용

hosts allow = 192.168.41.0/255.255.255.0

--> 192.168.41.0 네트워크에 속한 모든 사용자들을 허용하고 넷마스크를 병용하여 사용

hosts allow = wow19, wow20

--> wow19와 wow20라는 호스트들만 서버 접속 허용

 

# 삼버 서버의 printcap에 정의된 모든 프린터 목록을 자동적으로 로딩되게 할 것인가를 지정

# 네트워크 프린터를 삼바 서버에 연결하여 사용하고자 한다면 선택해야 한다.

load printers = yes

 

# 서버에 의해 사용되는 printcap name을 겹쳐쓰기 하고자 할 때 그 위치를 지정

printcap name = /etc/printcap

 

# 현재 사용하는 프린터 시스템 종류 지정

# 비표준 프린터 시스템이 아니면 지정할 필요가 없다.

printing = lprng

 

# 삼바 서버의 guest 사용자를 허용하고자 할 때에는 이 주석을 제거한다.

# 즉, 이 옵션은 삼바 서버에 손님 권한으로 접속하였을 때에 어떤 권한을 부여할 것인가를 설정하는 것이다.

# 가능한 시스템에 특별한 권한이 없는 nobody와 같은 권한으로 설정하기 바란다.

#guest account = nobody

 

# 삼바 서버로 접속하는 개별 사용자드의 호스트 정보를 %m으로 받아서 개별 로그 파일을 생성하도록 한다.

log file = /var/log/samba/%m.log

 

# 접속한 호스트별로 로그 파일을 사용하지 않는다면 하나의 로그 파일을 사용할 수도 있다.

log file = /var/log/samba/smbd.log

 

# 로그 파일 최대 크기 지정

# 제한을 두었을 경우에 이를 초과하게 되면 .old 확장자를 가진 파일로 저장되고 새로운 파일이 생성된다.

# 0으로 설정하면 파일의 크기에 제한을 두지 않는다.

max log size = 0

 

# 클라인언트가 삼바 서버에  접속할 때 인증 레벨을 부여하는 옵션

# user, share, server, domain 4가지 보안 모드가 있다.

# user:

#    삼바 서버에 접속하는 사용자는 반드시 윈도우에서 사용하는 로그인 ID와 동일해야 한다.

# share:

#    공유 디렉토리 등에 설정하는 것으로써 ID와 패스워드의 인증없이 접속하는 것을 허용한다.

# server:

#    별도의 인증 서버에 ID와 패스워드 인증을 받도록 한다.

# domain

#    윈도우 NT 서버가 있어야 가능하며, 삼바 서버가 사용자 명과 패스워드를 윈도우 NT의 도메인 컨트롤러(Domain Controller)에

#    전달하여 유효한지 확인 하는 방법이다. 아예 Windows NT 도메인으로 삼바 서버를 참가시키는 방법을 사용한다.

security = user

 

# security 값이 server로 설정되었을 때에만 설정할 수 있는 옵션으로 인증 서버로 사용할 서버를 지정한다.

password server =

 

# 패스워드 문자로 대소문자를 조합하여 사용할 문자의 개수를 지정한다.

password level = 8

 

# 삼바 사용자 명을 대소문자 조합하여 사용할 문자의 개수를 지정한다.

username level = 8

 

# 삼바 서버에 클라이언트의 접속이 이뤄지는 과정에서 인증을 위하여 암호화된 패스워드 옵션을 사용할 수 있다.

# 기본 패스워드 모드는 encrypted 모드이다.

encrypt passwords = yes

 

# encrypt passwords 항목과 함께 사용되는 것으로 암호화된 삼바 사용자의 아이디와 패스워드가 기록되는 파일이다.
# 삼바의 암호 모드가 윈도우와 호환되도록 설정한다.
# 이 파일에 패스워드를 추가하는 명령은 smbpasswd이다.
smb passwd file = /etc/samba/smbpasswd

 

# 클라이언트 호스트에서 사용자의 패스워드를 변경할 수 있도록 해주는 옵션이다.
# 단, 이 경우에는 encrypt password, smb passwd file 두 옵션을 반드시 사용해야 한다.

unix password sync = Yes

passwd program = /usr/bin/passwd %u

passwd chat = *New*UNIX*password* %n\\n *ReType*new*UNIX*password* %n\\n

*passwd:*all*authentiction*tokens*updated*successfully*

 

# 대부분 삼바에서 사용하는 ID와 리눅스 계정 ID는 동일하게 사용한다. 만약 삼바 사용자 명과 리눅스 계정 사용자 명을 다르게 사용할 경우

# 이를 매칭할 수 있도록 하기 위한 옵션으로써 매칭 테이블 파일을 설정한다.

username map = /etc/samba/smbusers

 

# 접속하는 각 클라이언트마다 서로 다른 설정을 사용할 수 있게 해주는 것으로 %m은 접속하는 NetBIOS 이름으로 대치된다.

include = /etc/samba/smb.conf.%m

 

# 사용자의 로컬 네트워크 상에서 삼바 서버가 최적의 성능을 발휘할 수 있도록 튜닝할 때 사용한다.

# IPTOS_LOWDELAY, IPTOS_THROUGHPUT 등의 옵션이 있다.

socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192

 

# 삼바 서버에서 두 개 이상의 네트워크 인터페이스(NIC)를 사용하도록 지원하기 위한 옵션이다.

# 삼바 서버가 사용하는 모든 인터페이스의 네트워크 대역을 설정해 두어야만 그 네트워크 인터페이스를 사용할 수 있다.

# 삼바에 연결된 네트워크 인터페이스를 설정하는 것으로 인터페이스는 IP / netmask 조합으로 지정할 수 있다.

interfaces = 192.168.41.3/24 192.168.124.33/24

 

# 지역 서브넷에서 삼바 서버를 잘 인식하도록 하기 위하여 자기 자신을 알리도록 한다.

remote browse sync =

remote announce =

 

# 삼바 서버가 nmbd에 의해 서브넷 상에서 로컬 마스터 브라우저가 될 수 있도록 허용하는 것으로

# no라고 설정하면 nmbd 데몬은 서브넷 상에서 로컬 마스터 브라우저가 되지 않는다.

local master = no

 

# 삼바 서버가 브라우저 선거에 있어서 자신을 알릴 수 있는 레벨을 설정하는 것으로
# 이 값에 의해서 nmbd 데몬이 로컬 브로드캐스트 지역에서 WORKGROUP에 대해 로컬마스터 브라우저가 될 수 있는지 결정된다.
# 이 값을 0으로 설정하면, nmbd 데몬은 윈도우 머신에 대해서 선거권을 상실하므로 로컬마스터 브라우저가 되지 못한다.

os level = 33

 

# 삼바가 도메인 마스터 브라우저가 되도록 해준다. 이것은 삼바와 서브넷 간의 브라우저 리스트를 모집할 수 있게 해준다.

# 만일 NT 도메인 컨트롤러를 가지고 있다면 이 기능을 사용해서는 안된다.

domain master = yes

 

# 윈도우 계열 사용자들도 도메인 로그인 가능하게 할 것인가 지정

domain logons = yes

 

# 삼바 구동시 로컬 마스터 선거를 강요하여 선거에서 이길 수 있게 보다 많은 가능성을 부여해 주는 옵션이다.

# 이 옵션은 domain master = yes 옵션과 같이 사용하여 nmbd 데몬에 의해 도메인 마스터가 될 수 있도록 해준다.

preferred master = yes

 

# 사용자가 성공적으로 로그인을 하였을 때 다운로드하여 작동할 수 있도록 배치파일(*.bat) 또는 NT 명령 파일(.cmd)을 지시해 주는 옵션이다.

# 배치 파일은 마지막 줄에 cr/lf가 들어있어야 하므로 도스편집기에서 만드는 것을 권장한다.

# 내용은 사용자가 임의대로 지정할 수 있다.

# 예) 보통 모든 클라이언트 머신들이 서버와 똑같은 시간에 시간을 맞추도록 할 수 있다.

# NET TIME \\\\SERVER /SET /YES

logon script = %m.bat

logon script = %U.bat

 

# 윈도우98 및 NT에서 user.dat과 같은 로우밍 프로파일(roaming profile)을 어디에 지정할 것인가를 지정해 주는 옵션이다.

# %L은 서버의 NetBIOS이름으로 대치되고, %U는 사용자 이름으로 대치된다.

# 이 옵션을 사용할 때 [Profile]공유 항목에서 주석을 풀어 주어야 한다.

logon path = \\\\%L\\Profiles\\%U

 

# 삼바 서버에서 nmbd 데몬이 wins 서버의 역할을 할 수 있는지 여부를 지정한다.

# 만일 이 옵션을 선택하기 위해서는 반드시 다중 서브넷 네트워크를 가지고 있어야 하며 wins서버로 될 특정 nmbd 데몬이 있어야 한다.

wins support = yes

 

(참고) WINS(Windows Internet Naming Service)
마이크로소프트 윈도우NT 서버의 일부인 WINS는 각 구성 변경에 수반되는 사용자 또는 관리자가 없는 IP 주소들과 컴퓨터 이름 및 위치들과의 결합을 관리한다. WINS는 컴퓨터 이름과 IP 주소를 서로 매칭시켜 데이터를 테이블 내에 자동으로 만드는데, 이 이름들은 다른 사람의 컴퓨터 이름과 중복되지 않도록 고유한 이름으로 견지한다. 컴퓨터가 다른 장소로 옮겨지면, IP 주소의 서브넷 부분이 변경될 수 있다. WINS를 사용하면 새로운 서브넷 정보가 WINS 테이블 내에서 자동으로 갱신된다. WINS는 어떤 컴퓨터가 네트워크에 처음 정의될 때 IP 주소를 협상하는 NT서버의 DHCP를 보충하여 완전하게 한다. 예를 들면 같은 네트워크에서 사람이 많아지면 네트워크에 부하도 많이 발생한다. 이 경우 WINS를 사용하면 컴퓨터 이름과 IP 목록을 관리해주기 때문에 동보 통신에 의한 부담을 줄일 수 있다.

 

# WINS 서버 사용할시 IP 지정

wins server = host

 

# nmbd에 의해서 wins기능을 갖추지 못한 호스트들을 대신하여 브로드캐스트 이름 질의를 대신 응답해 줄 수 있도록 지정해 주는 옵션이다.

# 이것을 사용하려면 네트워크 상에 최소 하나 이상의 WINS 서버가 있어야 한다.

wins proxy = yes

 

# nmbd 데몬이 wins server 역할을 하고 등록되지 않는 NetBIOS 이름을 찾아줄 때
# DNS server를 사용하여 NetBIOS 이름을 찾아줄 것인지의 여부를 지정하는 옵션이다.
dns proxy = no

 

# 대소문자를 유지 보존할 것인가를 지정하는 옵션이다. 시스템의 기본 값은 no이다.

preserve case = no

short preserve case = lower

 

# DOS 파일들의 기본 문자는 대문자로 인식한다. 만약 lower로 설정한다면 소문자로 인식한다.

default case = lower

 

# 대소문자 구분을 할 것인가 말 것인가를 지정하는 옵션이다.

case sensitive = no

 

 

=== Share Definitions ==========================================================

# 사용자들의 홈 디렉토리 서비스에서 사용하는 기본적인 설정 값이다.
[homes]
# 공유 자원에 대한 설명 필드와 같다.
comment = Home Directories
# 윈도우 네트워크 브라우저에서 디렉토리를 보일 것인지 결정한다.
browseable = no
# 사용자에게 쓰기 권한을 준다.
writable = yes

# 도메인 로그온을 사용하고자 할 때 사용한다. 일반적으로 사용하지 않는다.
[netlogon]
comment = Network Logon Service
path = /home/netlogon
guest ok = yes
writable = no
share modes = no

# 특정한 프로파일을 지정할 때 사용한다. 일반적으로 사용하지 않는다.
[Profiles]
path = /home/profiles
browseable = no
guest ok = yes

# 삼바 프린터를 네트워크 공유 프린터로 사용할 때
[printers]
comment = All Printers
# 프린터 위치
path = /var/spool/samba
browseable = no
# Set public = yes to allow user 'guest account' to print
# guest 계정으로 지정한 사용자, 즉 nobody가 printing을 할 수 있다
guest ok = yes
# 쓰기 설정
writable = no
# 프린트 사용 여부
printable = yes

# 여러 사람들이 파일을 공유할 목적으로 유효하게 사용할 수 있다.
# 현재 기본 값인 /tmp는 임시 작업 공간 디렉토리이므로 /var/tmp처럼 다른 디렉토리를 만들어서 사용하도록 한다.
[tmp]
comment = Temporary file space
path = /tmp
read only = no
public = yes

# 윈도우에서 보이는 공유 자원(폴더) 이름이지만 staff 그룹에 있는 사용자들을 제외한 사용자들은 오직 읽기만 사용 가능하다.
[public]
# 공유 자원에 대한 설명 필드와 같다.
comment = Public Stuff
# 삼바 서버로 공유할 실제 공유 자원(폴더)이다.
path = /home/samba/public
# 손님 사용자에게 접근을 허용한다.
public = yes
read only = no

printable = no
write list = @staff

# 사용자 정의 섹션
[posein]
comment = shared-files in posein directory
# 공유 디렉토리의 경로를 지정
path = /home/posein/pds
# 공유 디렉토리를 읽기만 가능하게 할지를 지정
read only = no
writable = yes
# 서비스 디렉토리에 사용 가능한 사용자를 말하며, 만약 이 옵션이 생략되면 모든 사용자가 접근할 수 있다.
valid user = posein xitem prehee
# guest ok와 같은 옵션으로 no로 설정하면 다른 사용자들은 이용할 수 없고 개인 사용자만 사용할 수 있게 된다.
public = no
# 이용 가능한 공유 리스트를 보여줄 것인가를 지정하는 것으로 no로 지정하면 리스트를 보여주지 않는다.
browseable = no
# 서비스로 지정된 디렉토리에 스풀 파일을 지정할 것인가를 지정하는 것으로, 프린터 공유 디렉토리가 아니므로 대부분 no로 설정한다.
printable = no
# create mode와 같은 옵션으로 파일을 생성할 때 사용되는 모드를 나타낸다.
create mask = 0644 
# 'no'로 하면 심볼릭 링크를 따르는 것이 방지된다. follow symlinks를 끄는 것은 잠재적인 보안 구멍을 제거한다.
follow symlinks = no

* 옵션
- read only : 공유 디렉토리를 읽기만 가능하다.
- writeble users : 공유 디렉토리를 쓰기 가능하다.
- valid users : 공유 디렉토리에 로그인 할 수 있도록 한다.
- public, guest ok : 다른 사용자들이 이용할 수 있도록 한다.
- browseable : 공유 디렉토리의 리스트를 보여준다.
- printable : 공유 디렉토리에 스풀 파일을 저장할 것인지를 지정한다.
- path : 공유할 디렉토리의 절대 경로이다.
- comment : 코멘트를 뜻한다.
- create mask, creat mode : 파일을 생성할 때 사용되는 모드를 의미한다.
- write list : 쓰기가 가능한 특정 사용자를 지정한다.