겸손한 개발을 위한 자양분


namespace 에 대해 오해하고 있는 소수의 분들을 위해...

Static Library 를 제작하고, 또 사용하다보면 Name Conflict 에러가 발생하는 경우가 있습니다.
이 에러는 프로젝트에 포함된 이종 라이브러리가 같은 이름의 함수명/변수명을 갖게 될 경우 나타납니다.

가령, 프로젝트에서 a.lib , b.lib 두개의 라이브러리를 link 할때에
a.lib 에서 fnA() 라는 함수가 선언되어있고,
b.lib 에서 fnA() 라는 함수가 선언되어있는 경우
두 함수의 이름이 같기 때문에 발생하는 에러입니다.

이 때 conflict 를 피하기 위하여 생긴것이 namespace 입니다.
namespace 는 내부에 정의된 이름들에 대해 마치 class의 캡슐화와 같은 효과를 줍니다.

.net 프로그래밍을 하셨다면,
using namespace std;
라는 구문을 보셨을텐데요,

이 명령은
소스코드 내에서 cout 등의 함수들을 호출할때
std::cout 을 호출하는것으로 인식하게 해 줍니다.

즉, 표준 함수의 namespace 가 std 인 셈입니다.

개발을 하다보면,
동일 소스를 사용해서 이종 Static Library 를 생산해야할 때가 있습니다.
이럴때에는 소스가 동일하기 때문에 똑같은 이름의 함수/변수를 갖는 Static Library 가 생기게 됩니다.
( 물론 소스를 복사해서 쓸수도 있지만, 동일 소스가 여러개로 보관되면 관리상의 치명적인 문제점이 생기게 되겠죠 )

이럴때, 프로젝트를 기준으로 하여
소스가 동일하더라도, 프로젝트별 namespace 를 달리하게 되면
하나의 코드로 여러개의 library 를 만들더라도 conflict 문제 없이 빌드할 수 있습니다.

일반적으로 namespace 를 사용하는 소스의 예는 다음과 같습니다.

using namespace myprojectns;
 
namespace myprojectns{

int g_nCount;

void fnA(){
Sleep(0);
}

}


마치 Class 와 비슷한 모양을 갖게 되는데요
위의 예에서는 myprojectns 로 설정된 네임스페이스 내에
직접 코딩을 하게 되면 외부의 소스에서는 접근할때에 ( extern 되어있다고 가정 )
myprojectns::g_nCount
myprojectns::fnA()
로 접근 할 수 있습니다.

다음은 이를 간단히 적용하기 위한 매크로 입니다.

// MyNamespace.h

#pragma once
#if defined(_LIB_SDK_PROJECT_Y)
#define MYNAMESPACE MYNAMESPACE_LIB_PROJECT_Y
#elif defined(_LIB_SDK_PROJECT_Z)
#define MYNAMESPACE MYNAMESPACE_LIB_PROJECT_Z
#else
#error MYNAMESPACE Required. Check MyNamespace.h
#endif
#define MN MYNAMESPACE 

#define _START_OF_INTERNAL_SRC  namespace MYNAMESPACE {using namespace MYNAMESPACE ;
#define _END_OF_INTERNAL_SRC  }
#define _SO_INTERNAL_SRC   _START_OF_INTERNAL_SRC  
#define _EO_INTERNAL_SRC   _END_OF_INTERNAL_SRC 

_LIB_SDK_PROJECT_Y 는 Project의 Properties 에서 설정한 Preprocessor definitions 입니다.
Project별로 다른 정의를 주어야하는 부분입니다.

위의 매크로는 Preprocessor definitions 에서 프로젝트 특성을 읽어
서로 다른 namespace 를 적용할 수 있도록 해줍니다.

예에서는 Preprocessor definitions 가
_LIB_SDK_PROJECT_Y 인경우 MYNAMESPACE_LIB_PROJECT_Y
_LIB_SDK_PROJECT_Z 인경우 MYNAMESPACE_LIB_PROJECT_Z
의 namespace 가 정의됩니다.

cpp 소스에서의 사용은 다음과 같습니다.

// _LIB_SDK_PROJECT_Y 의 소스일 때

#include <stdio.h>
#include "MyNamespace.h"

_SO_INTERNAL_SRC


VOID fnA()
{
...
}

BOOL fnB()
{
...
}

_EO_INTERNAL_SRC 

헤더파일을 포함하고, 소스의 위 아래를 _SO_ , _EO_  매크로로 감싸주면
헤더에 정의된 namespace 가 project 별로 적용됩니다.

외부에서 위 소스의 함수를 호출하고자 할때에는 다음과 같이 호출할 수 있습니다.
//_LIB_SDK_PROJECT_Y 라이브러리를 링크했을 때

#include <stdio.h>

void initialEntry()
{

MYNAMESPACE_LIB_PROJECT_Y::fnA();
MYNAMESPACE_LIB_PROJECT_Y::fnB();

}

혹은,
//_LIB_SDK_PROJECT_Y 라이브러리를 링크했을 때

#include <stdio.h>

using namespace MYNAMESPACE_LIB_PROJECT_Y;

void initialEntry()
{

fnA();
fnB();

}


외부 library 제작이 필요없는 실행파일 기반 프로젝트이거나
소형 프로젝트일 경우에는 namespace를 사용하지 않아도 될것 같습니다.

하지만, 여러명이 작업하는 프로젝트이거나 ( 소스별 extern function name conflict 문제 )
static library 를 만드는 프로젝트일 경우에는
사용할 필요성이 높아 보이는 부분입니다.

-------------------------------------------------------------------------------------------

덧붙혀서,
매크로를 자세히 보신 분은 MYNAMESPACE 가 MN 으로 정의된걸 보셨을텐데요
이는 내부에서의 사용편의성을 위한 정의입니다.

가령, 배포용 라이브러리를 제작할때에
내부소스에서 사용되는 함수/변수들은 namespace를 주고,
extern 되는 함수에 대해서는
(사용편의성을 위해) namespace없이 unique한 이름으로 정의하는 경우
내부에서 사용되는 소스의 경우 _SO_, _EO_ 매크로를 사용하고,
외부에 extern 되는 소스는 _SO_, _EO_ 매크로의 사용없이
MN::fnA() 등과 같이 바로 접근할 수 있도록 정의한 부분입니다.

가령,
// extern source

#include <stdio.h>
#include "MyNamespace.h"

extern "C" void fnProjectInit()
{
MN::fnA();
}

( extern의 위치는 이해의 편의를 위해... )

lib 제작시 외부에 제공되는 함수는 위의 모습처럼 _SO_, _EO_ 없이 사용하고,
내부에서 다른소스의 함수를 가져다 쓰는 형태를 만들어주면,
외부에서는 namespace 없이 fnProjectInit()을 바로 호출할 수 있고,
소스의 내부에 정의된 함수나 변수이름등은 중복확률이 낮아지게 되는 효과를 갖게 됩니다.


원문 : http://www.techmixer.com/install-windows-vista-from-bootable-usb-flash-memory-drive/

Create a bootable Windows Vista on Flash Memory

1. Flash memory drive 를 FAT32 file system 으로 포멧

Windows Vista 가 설치된 PC에서 CMD.EXE 를 실행하여 다음의 명령어를 수행합니다.
( 아래는 USB 드라이브가 "disk 1" 인 경우입니다. 사전에 "list disk" 명령어를 사용하여, USB 의 장치 번호를 반드시 확인하세요 )

  1. diskpart
  2. select disk 1
  3. clean
  4. create partition primary
  5. select partition 1
  6. active
  7. format fs=fat32
  8. assign
  9. exit

2. Vista DVD 를 Flash Drive 로 복사

아래의 명령어를 사용하여, 비스타 DVD를 플래시 드라이브로 복사합니다. ( 비스타를 d: , USB가 e: 드라이브인 경우의 예 )

  • xcopy d:\*.* /s/e/f e:\

3. BIOS 설정에서 USB Drive 로 부팅을 설정

4. USB flash memory drive 로 부팅하여, Vista 를 설치

일단, 생각할 수 있는 가장 간단한 코드로 구해본 CPU 점유율

 CRITICAL_SECTION cs;
 InitializeCriticalSection(&cs);

 DWORD prvTick=GetTickCount();
 for(int i = 0; i<1000000; i++)
 {
  EnterCriticalSection(&cs);
  Sleep(0);
  LeaveCriticalSection(&cs);
 }
 DWORD curTick = GetTickCount() - prvTick;

위의 코드에서
CS를 썼을 때에는 0.78초
CS를 안썼을 때에는 0.81초의 TickCount가 반환

즉, CS의 점유율이 100만번 호출에 0.03 초라는 계산.

위의 코드에서도,
사실 정확히 하려면 쓰레드의 KernelTime과 UserTime 을 구해 더해야하겠지만,
ns로 반환되는 KernelTime/UserTime 의 오차율이 ms를 상회하는 약 0.06초 이므로 의미가 없다.

멀티 쓰레드 환경에서의 점유율도 구해야하겠지만 귀찮으므로 pass.

우선, 커널 변수를 이용하여, 테이블 위치 확인

0: kd> dd KeServiceDescriptorTable
8055c700 80504450 00000000 0000011c 805048c4
8055c710  00000000 00000000 00000000 00000000
8055c720  00000000 00000000 00000000 00000000
8055c730  00000000 00000000 00000000 00000000
8055c740  00000002 00002710 bf80c0b6 00000000
8055c750  f719ba80 f6b89b60 86d74950 806f60c0
8055c760  00000000 00000000 ffea8ad6 ffffffff
8055c770  ee4ae396 01c90284 00000000 00000000

0: kd> dd KeServiceDescriptorTableShadow
8055c6c0  80504450 00000000 0000011c 805048c4
8055c6d0  bf999b80 00000000 0000029b bf99a890
8055c6e0  00000000 00000000 00000000 00000000
8055c6f0  00000000 00000000 00000000 00000000
8055c700  80504450 00000000 0000011c 805048c4
8055c710  00000000 00000000 00000000 00000000
8055c720  00000000 00000000 00000000 00000000
8055c730  00000000 00000000 00000000 00000000

KeServiceDescriptorTable 에서, NtOsKrnl 에 연결된 서비스.
KeServiceDescriptorTableShadow 에서, Win32K 에 연결된 서비스 를 확인 할 수 있다.

typedef struct _SERVICE_DESCRIPTOR_TABLE
{
PULONG  ServiceTable;  // array of entry-points
PULONG  puCounterTable;  // array of counters
ULONG  uTableSize;   // number of table entries
PUCHAR  pbArgumentTable; // array of byte counts
} SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE;

ServiceDescriptorTable의 구조가 위와 같으므로,
앞의 SDT에서는
ServiceTable Array of Entry 가 80504450
Entry의 개수는 0000011c 개 임을 알 수 있다.

출력하여 보면
0: kd> dds 0x80504450 L11c
80504450  805a4614 nt!NtAcceptConnectPort
80504454  805f0adc nt!NtAccessCheck
80504458  805f4312 nt!NtAccessCheckAndAuditAlarm
8050445c  805f0b0e nt!NtAccessCheckByType
80504460  805f434c nt!NtAccessCheckByTypeAndAuditAlarm
80504464  805f0b44 nt!NtAccessCheckByTypeResultList
80504468  805f4390 nt!NtAccessCheckByTypeResultListAndAuditAlarm
8050446c  805f43d4 nt!NtAccessCheckByTypeResultListAndAuditAlarmByHandle
80504470  806153a2 nt!NtAddAtom
...

위와 같이 List를 확인할 수 있다.
덧붙혀, 위의 순서가 바로 서비스 Index가 된다.