블로그 이미지
대갈장군

calendar

1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Notice

'목록들'에 해당되는 글 157

  1. 2012.12.14 Dependency Walker의 검색 가능한 의존성의 타입들
  2. 2012.12.14 Visual C++ 2010에서 작성한 프로그램의 배포 방법 세 가지
  3. 2012.12.13 커널 오브젝트를 이용한 스레드 동기화 - 이벤트 오브젝트
  4. 2012.12.13 커널 오브젝트 (Kernel Objects)
  5. 2012.12.13 Understanding InvokeRequired() 함수
  6. 2012.02.03 무도빠
  7. 2012.01.26 Projective Texture Mapping with OpenGL GLSL9
  8. 2011.05.24 감수왕 임재범
  9. 2011.05.04 위대한 탄생의 이은미씨 이야기를 듣고난 후8
  10. 2011.03.22 나는 가수다! 논란에 대해2
  11. 2011.03.12 윈도우 메모리 파이널!
  12. 2011.03.12 윈도우 메모리 - 4 (힙과 메모리 맵)
  13. 2011.03.11 윈도우 메모리 - 3 (VirtualAlloc)
  14. 2011.03.11 윈도우 메모리 - 2 (malloc 함수)2
  15. 2011.03.10 윈도우 메모리 - 1
  16. 2011.03.09 영화 인셉션에 나오는 명대사2
  17. 2010.09.25 Visual C++ 컴파일시 링크 되는 정적 혹은 동적 라이브러리들
  18. 2010.09.25 C Standard Library
  19. 2010.09.11 영웅이 없는 나라
  20. 2010.09.04 윈도우 7 및 윈도우 비스타 USB로 설치하기
  21. 2010.09.01 Application Domain
  22. 2010.07.09 행복하게 사는 법2
  23. 2010.07.02 Starcraft II 도배한 대한항공 747 비행기!
  24. 2010.07.02 인테넷 무료 저장 공간 Dropbox!4
  25. 2010.07.01 USB로 윈도우 XP 설치하기4
  26. 2010.06.25 C# 이벤트 - 객체 지향적 메시지
  27. 2010.06.18 C# - 제너릭 (Generic) 에 대한 질문
  28. 2010.06.18 Covariance and Contravariance
  29. 2010.06.17 델리게이트 - Delegate
  30. 2010.05.15 OpenGL 인접한 점들을 그릴때 발생하는 문제점
2012. 12. 14. 00:47 프로그래밍/Dependency Walker

의존성 검사 프로그램인 Dependency Walker는 다음과 같은 타입의 의존성을 검사할 수 있다.


1. Implicit Dependency (다른 말로는 load-time dependency): 굳이 직역하자면 '암묵적 의존'이라고 할 수 있겠다. 모듈 A가 컴파일 혹은 링크시에 모듈 B를 사용하기 위해 하나의 라이브러리 파일 (LIB)을 암묵적으로 내포했다면 모듈 A의 소스코드는 실질적으로 모듈 B의 함수들을 불러다 사용하는 것이다. 고로 모듈 B는 모듈 A의 '로드 타임 디펜던시'이다. 한국어로 설명하자면 모듈 B는 모듈 A를 실행하는 시점에 반드시 로딩 되어야 하는 필수적인 놈이다라는 말... 고로 모듈 B에 있는 함수를 실행중 (Run-time)에 사용하고 말건 간에 모듈 A는 반드시 모듈 B를 로딩해야 한다. 모듈 B는 모듈 A의 import table에 포함된다. 


2. Delay-load Dependency: '지연 로딩 의존'. 이 녀석은 1번과 거의 동일한데 한 가지 중요한 차이점이 있다면 모듈 A가 진짜로 모듈 B의 함수를 호출하면 그제서야 모듈 B를 로딩한다는 점! 고로 이름이 'Delay-load' (늦게 로딩)이다. 이 경우에는 모듈 A의 'Delay-load import table'에 모듈 B가 포함된다. 


3. Forward Dependency: '전진형 의존' (가수 전진 아님) ㅋㅋ 한국어로 바꾸자니 참 어이없네. 이 녀석의 경우 모듈 A가 모듈 B를 컴파일 혹은 링크시에 LIB 파일로 연결하고 모듈 A의 소스코드가 모듈 B의 함수를 호출하는 경우를 말하는데, (여기까지는 1과 2와 같다) 모듈 B의 함수가 실질적으로는 또 다른 모듈인 모듈 C의 함수를 연결해서 호출하는 경우를 말한다. 모듈 B와 모듈 C 둘 다 실질적으로 모듈 A의 필요한 라이브러리들이지만 모듈 B만 모듈 A의 import table에 이름을 올린다.


4. Explicit Dependency (혹은 다른 말로 다이나믹 의존 or 런타임 의존): '명시적 의존' 모듈 A가 모듈 B와 컴파일 혹은 링크 타임에 연결되지 않았으나 런타임에 모듈 A가 동적으로 모듈 B를 LoadLibrary() 같은 함수를 통해 소스코드 내부에서 임의로 호출하여 연결하는 경우를 말한다. 자, 이런 경우에는 모듈 B는 모듈 A의 'Run time dependency'이다. 즉, 모듈 B는 모듈 A를 실질적으로 실행하는 시점에 요구되는 것이라는 말. 하지만 모듈 A의 import table에는 모듈 B가 당연히 없다. 왜냐면 소스 코드 내부에서 임의로 사용자 맘대로 호출하니까. 이런 타입의 대표적인 것들이 OCXs, COM object 그리고 Visual Basic 어플리케이션들이다. 


5. System Hook Dependency (다른 말로 '주입된 의존'): 이 타입의 의존성은 다른 어플리케이션이 특정 이벤트를 프로세스 내에서 '후킹'할때 발생한다. 그 프로세스가 해당 이벤트를 발생시킬 때, 운영체제는 해당 이벤트를 처리하기 위해 해당 프로세스로 모듈을 'Inject' 즉, '주입'하게 된다. 이렇게 주입된 모듈을 다른 어떤 모듈에 의존적이진 않지만 주입된 프로세스의 주소 공간에 머무른다. 


1,2 그리고 3번의 경우는 쉽게 찾아낼 수 있다. 왜냐면 import table에 자신들의 이름을 당당히 밝히기 때문이다. 4번과 5번은 찾아내기 쉽지 않은데 Dependency Walker 2.0이후 버전의 Runtime Profiling을 이용하면 검색 가능하단다. 


http://www.dependencywalker.com/help/html/dependency_types.htm

posted by 대갈장군
2012. 12. 14. 00:09 프로그래밍/MSDN

http://msdn.microsoft.com/en-us/library/dd293574(v=vs.100).aspx


Visual C++ 2010에서 작성한 C++ 프로그램을 다른 컴퓨터에서 실행하기 위해서는 작성한 어플리케이션, 그리고 어플리케이션을 돌리기 위해 필요한 라이브러리 파일을 같이 제공해야만 한다. 요렇게 하기 위한 방법이 세가지 있다.


1. Central Deployment 

중앙 배포 방식이다. 컴퓨터의 운영체제가 설치된 하드 디스크의 Windows 폴더 안에 보면 System32 폴더가 있다. 요 폴더에 들어가면 각종 DLL들이 무수히 많은데 바로 여기가 윈도우즈 운영체제가 사용하는 '중앙 통제 센터'이다. 여기다가 어플리케이션을 돌리는데 필요한 DLL들을 복사해 넣으면 내가 만든 어플리케이션이 문제 없이 작동한다. 바로 이때 사용되는 것이 VCRedist_archtecture.exe 파일인데 인터넷에서 Visual C++ 2010 Redistribution Package 라고 치면 좌롸롸롹 뜨는 것들이다. 단순히 exe 파일이므로 그냥 다운 받아서 더블 클릭하면 알아서 설치한다. 


이 방식의 장점은 중앙의 통제된 한 곳에 모든 것을 다 집어 넣기 때문에 업데이트나 갱신이 용이하다는 점이다. 보안 취약점이 발견되었을때 단순히 DLL 파일 하나를 업데이트 함으로써 그 DLL을 사용하는 모든 프로그램의 보안 취약점을 한 방에 해결할 수 있다.


2. Local Deployment

지역 배포 방식이다. 어플리케이션이 존재하는 폴더 내부에 필요한 파일들을 복사해서 넣는 방법이다. 


이 방식은 쉽고 간단하지만 문제점으로는 업데이트가 자동으로 안되므로 유지 보수를 위해서는 DLL을 업데이트 하는 방법을 제공해야 한다는 점...


3. Static Linking

이건 정적 링크 방식이다. 어플리케이션 자체에 라이브러리를 심어버리는 방식이다. 장점은 당연히 DLL 문제에서 완전 해방 된다는 점이다. 다만 문제점은 마이크로소프트 업데이트가 만약 내가 링크한 라이브러리를 업데이트 해버리면 어플리케이션이 업데이트된 라이브러리를 사용할 수 없다는 점이다. 결론적으로 보안 취약성이 문제이군.


참, 그리고 재미있는 부분이 있는데, Visual C++ 2008과 2010의 가장 큰 차이점을 설명하고 있는데, 재미있는군.


우선, Visual C++ 라이브러리는 더이상 manifest에 의존하지 않는다. 그리고 더이상 WinSxS 폴더에 설치되지 않는다. 헐, 그래? 그러고 보니 2010으로 컴파일한 실행 파일 폴더에 manifest 가 없네? 


또한 작성된 응용 프로그램이 더이상 manifest 정보를 요구하지 않는단다. 흠, 그렇다면 필요한 라이브러리의 정보가 어디에 포함되는 걸까? 명세서 (manifest) 파일이 존재 하지 않는다면 아마도 실행파일 내부에 어딘가 설명되는가 본데?

posted by 대갈장군
2012. 12. 13. 05:40 프로그래밍

앞서 커널 오브젝트에 대해서 알아보았는데 이중에 가장 사용하기 쉬운 녀석이 이벤트 오브젝트이다. 


우선 이벤트 오브젝트를 설명하기 앞서 대기 함수에 대해서 설명하자. 대기 함수라 함은 인자로 전달한 커널 오브젝트가 시그널 상태 (파란불)가 될 때까지 (혹은 타임 아웃 제한 시간동안) 이 함수를 호출한 스레드를 대기상태로 빠뜨린다. 


바로 WaitForSingleObject() 함수이다. 원형은 다음과 같다.

DWORD WINAPI WaitForSingleObject(
  _In_  HANDLE hHandle,
  _In_  DWORD dwMilliseconds
);

첫번째 인자로 전달된 hHandle (커널 오브젝트의 핸들)이 시그널 상태가 될때까지 기다리되 두번째 인자로 전달된 시간만큼 최대한 기다렸다가 만약 제한 시간안에 소식이 없으면 그냥 포기하고 대기 상태를 푼다.


커널 오브젝트가 정상적으로 시그널 상태가 되어 대기 함수를 깨우는 경우에는 WaitForSingleObject() 함수의 리턴 값은 WAIT_OBJECT_0가 된다. 만약 기다리다 지쳐 포기하고 리턴하는 경우네는 WAIT_TIMEOUT이 리턴된다. 


고로 다음과 같은 형식의 코드로 작성이 가능하다.


DWORD dwWaitResult;

dwWaitResult = WaitForSingleObject(ghReadyEvent, 5000);

 

if (dwWaitResult == WAIT_OBJECT_0) 

{


}


자 그러면 이제 대표적인 커널 오브젝트인 이벤트 오브젝트를 이용하는 방법을 보자. 우선 이벤트 오브젝트를 생성하는 방법은 다음과 같다.

HANDLE WINAPI CreateEvent(
  _In_opt_  LPSECURITY_ATTRIBUTES lpEventAttributes,
  _In_      BOOL bManualReset,
  _In_      BOOL bInitialState,
  _In_opt_  LPCTSTR lpName
);


여기서 주의해야 할 부분은 두 번째 인자와 세 번째인데 이것은 '수동 리셋 이벤트 (TRUE)' 로 이벤트 오브젝트를 만들지 아니면 '자동 리셋 이벤트 (FALSE)'로 만들지 설정하는 부분이다. 세 번째 인자는 초기 상태를 시그널 상태 (TRUE)로 만들지 아니면 논시그널 상태 (FALSE)로 만들지 설정하는 부분이다.


수동 리셋 이벤트는 시그널 상태가 되면 대기중인 모든 스레드가 시그널 상태가 되는 반면 자동 리셋 이벤트를 대기중인 스레드 중 한개만 신호상태가 되어 CPU를 점유하고 나머지는 계속 대기한다.


일반적으로 시작하면서 바로 파란불로 만드는 경우는 드물기 때문에 세 번째 인자는 FALSE로 간다고 보고 두 번째 인자는 사용자의 의도에 따라 TRUE 혹은 FALSE로 셋팅해야 한다.


그리고 이제 중요한 함수인 시그널 상태 혹은 논시그널 상태로 이벤트 오브젝트를 만들어주는 함수인데 바로 SetEvent() 와 ResetEvent() 함수이다.


SetEvent()와 ResetEvent() 함수 둘 다 커널 오브젝트인 이벤트 오브젝트의 핸들을 인자로 받아 상태를 변경한다. 


이름이 쪼금 헷갈리게 되어 있다. 왜 SetEvent 대신 GoEvent() 라던가 ReadyEvent() 라든지 좀 더 직관적으로 이해 되기 쉬운 함수 이름을 사용 안했는지 모르겠다.


암튼, SetEvent() 함수를 호출하게 되면 전달된 이벤트 오브젝트를 시그널 상태 (즉, 파란불: 준비되었다)로 변경한다. 반면 ResetEvent()는 이벤트 오브젝트를 논시그널 상태 (즉, 빨간불: 아직 멀었어!)로 변경한다.


이제 실제 코드 예제를 보자.


1. 이벤트 오브젝트 핸들 선언

HANDLE ghHDFaceReadyEvent;       


2. 이벤트 오브젝트의 생성 (자동 리셋, 초기 논시그널 상태)

ghHDFaceReadyEvent = CreateEvent(NULL, false, false, TEXT("FRE")); 


3. 작업 스레드에서 해당 이벤트 오브젝트의 시그널 상태를 기다림 (무제한 시간동안)

dwWaitResult = WaitForSingleObject(ghHDFaceReadyEvent, INFINITE); 


4. 필요한 시점에서 해당 이벤트 상태를 시그널 상태로 변경

SetEvent(ghHDFaceReadyEvent);


5. 작업 스레드가 시그널 상태가 된 것을 확인하고 바로 작업에 착수한다. 그리고 모든 일이 끝나면 이벤트를 논시그널 상태로 바꾼다. 물론 자동 리셋 이벤트의 경우 리셋 굳이 안해도 된다. 수동인 경우는 해줘야 다음 루프에서 대기 없이 바로 작업에 착수하는 것을 막을 수 있다.

if (dwWaitResult == WAIT_OBJECT_0) 

{

    ResetEvent(ghHDFaceReadyEvent);

}


스레드의 동기화가 필요한 프로그래밍에서는 이벤트를 이용한 동기화가 제법 효과적이다. 테스트를 해보진 않았지만 프로세스간에 커널 오브젝트가 공유 되므로 서로 다른 프로그램끼리 같은 이벤트 핸들을 이용해서 서로 작업이 완료되었거나 준비되었음을 알려주면 아주 멎진 멀티-프로세스 프로그램을 만들수 있을 것 같다. 하지만 굳이 그렇게 분리된 형태의 프로그램을 작성해야 할 필요가 있나 하는 의문이 들긴 하지만... 뭐 어쨌든 된다고 하니까 어딘가 필요하지 싶다. 


다만 주의해야 할 것은 대기함수를 호출할때 대기 시간을 무제한으로 하게 되면 코딩의 오류나 어떤 문제로 인해 이벤트 오브젝트가 시그널 상태가 영원히 되지 않으면 대기함수를 호출한 스레드는 영원히 잠자게 된다. 데드락은 아니지만 대략 좋지 않은 상태다... 이걸 주의 해야 한다.


posted by 대갈장군
2012. 12. 13. 04:43 프로그래밍

http://msdn.microsoft.com/en-us/library/windows/desktop/ms724485(v=vs.85).aspx


Kernel Object (줄여서 KO) 핸들은 프로세스 한정적이다. 즉, 각각의 프로세스마다 고유하다는 말인데, 그말인 즉슨, 임의의 프로세스는 하나의 커널 오브젝트 핸들을 얻기 위해서는 반드시 오브젝트를 생성하거나 혹은 이미 존재하고 있는 오브젝트를 열어야 한다는 말이다. 하나의 프로세스 당 만들수 있는 최대의 커널 핸들의 갯수는 2의 24승이다. 뭐, 무제한이라고 보면 되겠네. 그러나, 핸들들은 Paged Pool에 저장되므로 결론적으로 사용가능한 메모리에 따라 생성할 수 있는 핸들의 갯수는 제한된다. 32 비트 윈도우즈 시스템의 경우 생성할 수 있는 핸들의 갯수는 2의 24승 보다 훠어얼씬 적다. 


프로세스는 오브젝트의 이름과 접근 권한을 가지고 있다면 이미 존재하고 있는 커널 오브젝트에 새로운 핸들을 할당하는 것이 가능하다. 심지어 그 커널 오브젝트가 다른 프로세스에 의해 만들어 졌다고 해도 가능하다. 커널 오브젝트 핸들은 그 자체에 접근 권한을 설정하는 속성을 가지고 있어서 접근을 허용 또는 차단을 할 수 있다. 보안 속성이라고도 한다. 각각의 커널 오브젝트는 고유의 접근 권한을 가지는데 예를 들자면 이벤트 핸들 같은 경우에는 'Set' 혹은 'Wait'를 가질 수 있고 파일 핸들 같은 경우에는 'Read' 혹은 'Write' 같은 접근 권한을 가질 수 있다.


아래의 그림을 보면 어플리케이션이 하나의 이벤트 오브젝트를 생성하는데 CreateEvent 함수가 이벤트 오브젝트를 생성하고 오브젝트 핸들을 리턴하고 있다.



Application creating an event object


이벤트 오브젝트가 생성된 후, 어플리케이션은 이벤트 핸들을 이용해 이벤트를 Set 혹은 Wait 시킬 수 있다. 이 핸들은 어플리케이션이 핸들을 닫거나 어플리케이션 자체가 종료될 때 까지 유효하다. 


대부분의 커널 오브젝트들은 하나의 오브젝트를 위해 다수의 핸들을 제공할 수 있다. 예를 들자면 아래 그림처럼 CreateEvent로 핸들을 하나 생성한 후 다시 OpenEvent로 같은 이벤트 오브젝트를 위한 핸들을 생성할 수 있다는 말이다. 


Application creating an event object with multiple handles


이런 방법으로 어플리케이션은 여러개의 핸들에게 다른 속성의 접근 권한을 부여 할 수 있다. 예를 들자면 Handle 1은 Set과 Wait 모두를 할 수 있게 접근 권한을 주고 Handle 2는 오로지 Wait만 가능하도록 설정 가능 하다는 말이다.


만약 다른 프로세스가 어떤 이벤트의 이름과 그 이벤트로의 접근 권한을 가지고 있다면 그 프로세스는 자신만의 이벤트 오브젝트 핸들을 OpenEvent 함수를 이용해서 생성 할 수 있다. DuplicateHandle을 이용하면 같은 프로세스 내에서 핸들을 복사 하여 생성할 수도 있고 다른 프로세스로 복사해서 전달 할 수도 있다.


하나의 오브젝트는 최소한 한개의 오브젝트 핸들이 존재하는 한 메모리에 계속 상주한다. 아래 그림처럼 어플리케이션이 CloseHandle함수를 사용해서 모든 오브젝트 핸들을 닫게 되면 비로서 오브젝트는 메모리상에서 사라지게 된다. 

Application closing event object handles to remove object from memory


흠, 이것은 마치 COM 오브젝트가 스스로를 관리하는 카운터를 가지고 있다가 0이되면 자폭하는 것과 같은 원리다. 제법 능동화된 메모리 관리 기법이지만 제대로 해제하지 않으면 어플리케이션이 종료되기 전까지는 절대 사라지지 않는 메모리 누수를 가지는 위험성이 존재한다.


윈도우 시스템은 다른 커널 오브젝트와는 조금 다른 방식으로 파일 오브젝트를 관리한다. 파일 오브젝트는 파일 포인터를 가지고 있다. 이 포인터는 파일내부의 읽혀질 혹은 쓰여질 다음 바이트의 위치를 가리키는 포인터이다.  어플리케이션이 새로운 파일 핸들을 생성할 때 시스템은 새로운 파일 오브젝트를 생성한다. 그러므로 하나 이상의 파일 오브젝트는 디스크 상의 한개의 파일을 가리킬 수 있는데 다음 그림과 같다. 


Multiple file objects referring to a file on disk


Duplication 이나 Inheritance (상속)을 통하면 한개 이상의 파일핸들이 같은 파일 오브젝트를 가리킬 수 있다. 다음 그림처럼.



Two file handles refer to same file object



사실 위 두개의 그림의 차이가 뭔지 잘 느낌이 안온다. 아마도 같은 파일 오브젝트를 공유하는 경우에 좀 더 자원경쟁에 안정적인 구현이 가능하다는 점이 다를까? 여러개의 다른 파일 오브젝트가 동시 다발적으로 한개의 파일에 접근하는 경우를 예방할 수 있다는 것이 장점일까? 


다음 도표를 보면 다양한 커널 오브젝트들이 나열되어 있다. 보면 생성 함수와 파괴 함수가 나열되어 있다. 시스템은 커널 오브젝트를 가리키는 최후의 핸들이 닫히면 자동으로 메모리에서 커널 오브젝트를 날려 버린다.


내가 내 생각대로 정리하자면, 커널 오브젝트는 보안 속성 (접근 제한)과 상태를 가지고 시스템에 의해 관리를 받는 일종의 구조체이다. 이런 커널 오브젝트가 중요한 이유는 시스템의 모든 중요 업무는 이런 커널 오브젝트를 통해서 이루어지고 있기 때문에다. 왜냐면 시스템이 이런 커널 오브젝트를 체계적으로 관리하여 사용자의 삽질을 막고 동시에 안전성을 높인다. 하지만 나에게 지금 커널 오브젝트가 중요한 이유는 바로 '상태' 때문이다. 커널 오브젝트가 상태를 가지기 때문에 스레드 간의 동기화에 요긴하게 쓰인다. 다름 글에서 어떻게 사용하는지 써보겠다.


Kernel objectCreator functionDestroyer function
Access tokenCreateRestrictedToken,DuplicateTokenDuplicateTokenEx,OpenProcessToken,OpenThreadTokenCloseHandle
Change notificationFindFirstChangeNotificationFindCloseChangeNotification
Communications deviceCreateFileCloseHandle
Console inputCreateFile, with CONIN$CloseHandle
Console screen bufferCreateFile, with CONOUT$CloseHandle
DesktopGetThreadDesktopApplications cannot delete this object.
EventCreateEventCreateEventEx,OpenEventCloseHandle
Event logOpenEventLog,RegisterEventSource,OpenBackupEventLogCloseEventLog
FileCreateFileCloseHandleDeleteFile
File mappingCreateFileMapping,OpenFileMappingCloseHandle
Find fileFindFirstFileFindClose
HeapHeapCreateHeapDestroy
I/O completion portCreateIoCompletionPortCloseHandle
JobCreateJobObjectCloseHandle
MailslotCreateMailslotCloseHandle
Memory resource notificationCreateMemoryResourceNotificationCloseHandle
ModuleLoadLibraryGetModuleHandleFreeLibrary
MutexCreateMutexCreateMutexEx,OpenMutexCloseHandle
PipeCreateNamedPipeCreatePipeCloseHandle,DisconnectNamedPipe
ProcessCreateProcessOpenProcess,GetCurrentProcessCloseHandle,TerminateProcess
SemaphoreCreateSemaphore,CreateSemaphoreEx,OpenSemaphoreCloseHandle
Socketsocketacceptclosesocket
ThreadCreateThread,CreateRemoteThread,GetCurrentThreadCloseHandle,TerminateThread
TimerCreateWaitableTimer,CreateWaitableTimerEx,OpenWaitableTimerCloseHandle
Update resourceBeginUpdateResourceEndUpdateResource
Window stationGetProcessWindowStationApplications cannot delete this object.

 







posted by 대갈장군
2012. 12. 13. 03:44 프로그래밍

WinForms는 작업 (예를 들자면 윈도우 디스플레이, 이벤트 처리 등등)을 하기 위해서 Native Windows System Assembly들을 사용한다. 


Thread Safe 하다는 것은 Multiple Threads 들이 하나의 오브젝트의 상태를 동시에 접근하여 변경하려고 하더라도 그 오브젝트의 '상태 (State)' '일관성있게' (Consistent) 유지한다는 것을 의미한다. 


Windows 폼 컨트롤의 "State" (상태)라고 하는 것은 텍스트 박스 안의 텍스트 같은 것을 말한다. 체크 박스를 예로 들자면 체크 상태를 말하는 것이다.


Multithreaded 환경에서 스레드에 안전하지 않은 (Non-Thread-Safe) 오브젝트를 사용하는 것은 상태의 오염 (State Corruption)을 가져 올 수 있다. 


이런 점을 피해가기 위해서 등장한 것이 바로 "Thread Apartment"라고 불리는 컨셉이다. 이것은 두가지 종류가 있는데 하나는 Single Threaded (STA)이고 다른 하나는 Multithreaded (MTA) 이다. A는 Apartment의 약자이다. .NET의 기본적인 디폴트 값은 MTA이다.


프로세스가 STA (Single Threaded Apartment) 모드로 작동한다고 가정하면 그 코드는 오직 하나의 스레드에서만 돌아간다는 말이다. 이런 STA 모드는 UI Application의 경우 아무런 효용 가지가 없다. 왜냐면 백그라운드 스레드로 작업을 할 수가 없기 때문이다. 


그래서 .NET은 WinForms Application을 Multithreaded로 생성하는 대신 폼의 내부에 속한 각각의 컴포넌트(예를 들자면 텍스트 박스)에 대해 그 컴포넌트가 생성되었던 스레드를 기억해 두었다가 사용자가 생성되었을 당시 사용되었던 스레드가 아닌 다른 스레드를 이용해서 해당 컴포넌트를 호출하거나 사용하려 하면 Exception이 발생하게 된다. 


InvokeRequired() 함수는 기본적으로 컴포넌트가 생성될 당시의 스레드 ID와 현재의 스레드 ID를 비교하는 함수이다.



posted by 대갈장군
2012. 2. 3. 00:10 카테고리 없음
나는 아무래도 무한도전 '빠' 인가 보다.

무한도전이 얼마전 나가수를 패러디한 나름가수다 편을 보았는데 문득 그런 생각이 들었다.

마치 연말 연예 대상을 받았던 나가수에게 이렇게 말하는 것 같았다.

'보라구, 난 어떤 프로그램도 무한도전이라는 그릇에 담을 수 있다구...'

나름가수다 편은 노래 자체는 수준이 좀 떨어지지만 긴장감, 감동 그리고 구성은 나가수보다 오히려 더 팽팽했다.

무한도전 멤버들이 완성해 놓은 캐릭터를 바탕으로 꾸며지는 작고 소소한 이야기와 그 위로 나열되는 긴장과 기대감 그리고 반전은 오히려 나가수가 무한도전을 패러디 해야 하지 않겠나 하는 생각이 들 정도 였다.

무한도전처럼 큰 그릇이 있을까? 모든 아이디어를 담을 수 있고 생각하면 도전할 수 있고 실패를 두려워 하지 않는 그런 예능 프로가 또 있을까? 아니, 두번 다시 나올 수 있을까? 아마 없지 않을까 싶다...

그런 면에서 유재석은 정말 대단한 사람인 것 같다. 왜냐면 무한도전의 그런 도전이 가능한 이유가 그에게 있기 때문이다. 과거 MC  대격돌이나 동거동락 같은 프로그램을 본 사람은 알겠지만 그의 개그 능력 (깐죽 거리는 개그)은 정말 상상을 초월한다.

강호동과 콤비로 유강라인으로 쿵쿵따에서 활약했던 걸 생각하면 정말 그의 개그 능력은 무궁무진 하다고 본다. 하지만 유재석은 철저하게 그의 개그 능력을 통제하면서 다른 멤버들을 다독이며 무한도전의 중심을 잡고 있다. 

큰 배를 이끄는 선장 처럼 휘몰아치는 파도에 견딜 수 있게 단단하게 배의 방향을 잡고 있는 선장과 같이 무게 중심을 꽉 잡고 있는 유재석.

그를 중심으로 똘똘 뭉친 무도 멤버들 그리고 김태호 피디. 겉으로 보기에는 형식이 없고 질서가 없는 무한도전 같지만 그들이 아무렇게나 보여질 수 있는 근본적인 힘은 모든 멤버와 스텦들이 단단하고 튼튼하게 하나의 흔들리지 않는 가치관으로 뭉쳐 있기 때문인 것 같다. 

그들은 마치 단단한 쇠공같아서 어디 던져놔도 잘 굴러간다. 설령 그게 불구덩이라도 말이다. 

나의 유일한 토요일 예능 프로 무한도전이 영원했으면 좋겠다. 변하지 않고 계속 되면 그것이 곧 진리이고 진실이다. 무한도전 화이팅!


 
posted by 대갈장군
2012. 1. 26. 03:32 OpenGL
Projetive Texture Mapping 은 3차원 텍스쳐 기법 중의 하나로 3차원 물체위에 텍스쳐를 투영시키는 기법이다. 

OpenGL은 잘 알고 있다시피 화면을 그리기 위해 Fixed pipeline를 이용한 방법과 GLSL 과 같은 쉐이더를 이용한 방법이 있다. Fixed pipeline을 이용해 Projective Texture Mapping을 구현한 예는 이미 널리 퍼져 있다. (http://www.opengl.org/resources/code/samples/mjktips/projtex/index.html)

헌데 GLSL을 이용한 방법은 안타깝게도 완벽한 예제가 없어 몇일을 구글링 했던 기억이 난다. 가장 완벽에 가까운 Tutorial은 http://www.ozone3d.net/tutorials/glsl_texturing_p08.php인데 이것도 쉐이더의 코드만 제공하고 있을뿐 중요한 부분에 대한 구현 방법은 생략했다. 

우선 Projective Texture Mapping을 위해 이해해야 할 것이 있다. 바로 텍스쳐의 좌표 생성에 관한 것인데, 일반적으로 2D 텍스쳐의 경우 0에서 1의 범위를 가지는 텍스쳐 좌표 값을 텍스쳐가 입혀질 물체의 버텍스에 설정함으로써 텍스쳐가 입혀지는데 Projective Texture Mapping은 이렇게 텍스쳐 좌표값을 생성하는 것이 아니라 마치 공간상에 프로젝터를 한대 놓고 그 프로젝터에서 발사되는 색상이 물체 위로 영사되게 되는 방식이다. (아래 그림처럼)



그렇다면 어떻게 저게 가능할까? 방법은 의외로 간단하다. (이론적으로는..) 프로젝터를 원하는 위치에 놓고 원하는 방향을 바라보게 한 다음 텍스쳐를 쏘면 된다... 말은 참 쉽다...

이런 원리에 대한 대답은 첨부된 10.1.1.104.6914 (5).pdf 파일에 있다... Projective Texture Mapping의 원리에 대한 아주 잘 설명하고 있다. 여기서 중간쯤 보면 Eye Linear Texgen 이라는 부분이 중요한데, Eye space에 근거하여 텍스쳐 좌표를 생성해내는 방법을 설명하고 있다. 바로 거기 행렬 곱들이 보이는데 저놈이 우리가 구현해야 할 부분이다.

 



제일 첫번째에 들어가있는 행렬은 Bias를 위한 행렬로써 Projective Texture Mapping 을 위해 [-1, 1]로 노말라이즈 되어 있는 월드 좌표계를 변환하여 텍스쳐가 사용하는 [0, 1]의 좌표 공간으로 바꿔주는 역활을 한다. 알고보면 각 축의 양의 방향으로 1만큼 이동시킨후 크기를 반으로 축소시키는 Translate + Scale 행렬 이다.

그리고 뒤에 좌라락 붙어 있는 놈들이 Projector's Projection Matrix, Projector's View Matrix, Eye's View Matrix 이다. 이 각 부분을 실제적으로 OpenGL에서 어떻게 가져 오는가가 바로 내가 그토록 찾던 부분이었다. 

이제 본격적으로 GLSL 코드를 살펴보자. 우선 버텍스 쉐이더는 다음과 같다.
uniform mat4 TexGenMatCam0;
uniform mat4 ViewMat;

void main()
{
	mat4 InvViewMat = inverse(ViewMat);	
	
	vec4 posEye =  gl_ModelViewMatrix * gl_Vertex;
	vec4 posWorld = InvViewMat * posEye;
		
	gl_TexCoord[0] = TexGenMatCam0 * posWorld;

	gl_Position = ftransform();		
} 
다음은 프래그먼트 쉐이더...
uniform sampler2D projMap_forCam1;

void main (void)
{
    vec4 final_color = vec4(0.0, 0.0, 0.0, 1.0);
    if( gl_TexCoord[0].q > 0.0 )
    {
		vec4 ProjMapColor_forCam1 = texture2DProj(projMap_forCam1, gl_TexCoord[0]);
		final_color = ProjMapColor_forCam1;			
    }

		
    gl_FragColor = final_color;			
}
 
일단 버텍스 쉐이더만 이해가 되면 프래그먼트는 자연스럽게 흘러간다. 알다시피 텍스쳐 좌표는 버텍스 쉐이더에서 계산이 되어야 한다. 하지만 어플리케이션에서 공급해줘야 할 것이 있으니 바로 TexGenMatCam0와 ViewMat이다. TexGenMatCam0가 바로 앞서 본 텍스쳐 생성 매트릭스이고 ViewMat은 Eye Space의 view matrix다. 자 이것들을 어떻게 얻어 오느냐... 우선 TexGenMatCam0를 얻어오는 방법은,

	glMatrixMode(GL_MODELVIEW_MATRIX);
	glPushMatrix();
	glLoadIdentity();
			
	gluLookAt(0.0f, 0.5f, 0.0f, 0, 0, 1.0f, 0, 1.0f, 0.0f);
	glGetFloatv(GL_MODELVIEW_MATRIX, ProjViewMatCam0);

	glLoadIdentity();
	gluPerspective( 90.0, 1.0, 0.0, 5.0);
	glGetFloatv(GL_MODELVIEW_MATRIX, ProjProjectionMatCam0);

	glLoadIdentity();
	glLoadMatrixd(bias);

	glMultMatrixf(ProjProjectionMatCam0);
	glMultMatrixf(ProjViewMatCam0);

	glGetFloatv(GL_MODELVIEW_MATRIX, TenLinearGexMatCam0);
 

요렇게 얻어오면 된다. gluLookAt을 이용해 View 행렬을 셋팅하고 gluPerspective를 이용해서 Projection 매트릭스 셋팅, 그리고 얻어진 요 녀석과 bias 행렬을 곱해서 TexLinearGexMatCam0를 얻어낸다. 이렇게 얻어낸 녀석을 바로 쉐이더로 전다... 참고로 bias 행렬은 다음과 같다.

const GLdouble bias[16] = { 0.5, 0.0, 0.0, 0.0,
                            0.0, 0.5, 0.0, 0.0,
							0.0, 0.0, 0.5, 0.0,
							0.5, 0.5, 0.5, 1.0 };
자 이제 버텍스 쉐이더에 대해 설명하자면 이 녀석은 각각의 버텍스가 들어오면 어플리케이션으로부터 받은 ViewMat의 역행렬을 일단 구해 놓고 Pre-defined 된 ModelView 매트릭스에 각각의 버텍스를 곱하여 Eye space의 좌표를 계산한다. 이것이 바로 posEye이다.

그리고 이제 계산된 Eye space 좌표계에 ViewMat의 Inverse matrix인 InvViewMat을 곱해주면 Eye space에서 World space로 좌표계를 변환시켜 준다. 그리고 얻어지는 녀석이 posWorld이다.

이제 남은 일은 이 월드 좌표계의 점을 우리가 셋팅 해놓은 프로젝터의 View matrix와 Projection matrix로 곱해주고 bias 행렬을 통해 위치와 크기를 변환시켜 주면 되는데 그 세개의 매트릭스를 미리 계산해서 구해 놓은 값이 바로 TexGenMatCam0이다. 이것은 위에 나온 코드로 어플리케이션에서 가져오면 된다.

그리하여 만들어지는 것이 바로 텍스쳐 좌표이고 이 값들을 Pre-define 된 텍스쳐 좌표계 0번에 집어 넣는다.

그러고 나서 이제 프로그먼트 쉐이더로 넘어가면 일단 입력으로 텍스쳐 하나가 들어오는데 바로 우리가 영사할 텍스쳐다.  
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, TexID_Cam1);
		glEnable(GL_TEXTURE_2D);

어플리케이션에서는 위와 같이 텍스쳐를 활성화 시키면 된다. 그리고 재미있는 부분이 있는데 gl_TexCoord[0].q 가 0보다 큰가를 체크하는데 바로 이부분 때문에 내가 Fixed 파이프라인을 이용하는 방법에서 GLSL로 바꾸었다.

Fixed 파이프 라인을 이용하면 바로 이 부분을 콘트롤 할 수가 없다. Projective Texture Mapping의 문제점이 바로 Reverse Projection인데 이것은 Fixed 파이프 라인에서는 Reverse projection을 근본적으로 제거하는 것이 힘들다. 고로 결국은 쉐이더를 이용해야 편한다. 바로 q 값이 그 것을 하게 해주는 인자 값이다. 만약 이 q 값이 0보다 적다면 이것은 reverse projection을 의미하는 것으로써 이 경우에는 아무것도 그리지 않으면 물체에 reverse projection이 일어나지 않는다.

그렇게 해서 만들어지는 결과는 다음과 같다.



 위 그림은 4개의 Projective Texture Mapping이 3차원 그릇의 각 면에 영사되고 있는 형태다. 파노라마 이미지가 있다면 위와 같은 방식으로 360도 Virtual Space를 손쉽게 만들수 있겠다. 아직 코드가 정리가 안되었는데 조만간 정리 좀 해서 올려야 겠다. 간단하게 바꿔서... 
posted by 대갈장군
2011. 5. 24. 02:49 카테고리 없음
그는 분명 다른 가수들처럼 입으로 소리를 내고 노래를 하지만 이상하게도 그의 노래는 가슴이 듣는다.

진실은 가슴으로 전달된다는 그 말이 사실인가 보다...

오랜 시간 쌓아둔 수많은 감정과 진심을 노래를 통해 한번에 쏟아내는 임재범. 진정한 감수왕이다.

진짜 친구가 없어 털어놓고 이야기 한번 못해봤다는 그의 말에 오히려 다행이다 라고 생각했다.

왜냐면, 그런 친구가 없었기에 지금 그가 우리에게 자신의 이야기를 털어놓고 노래를 통해 할 수 있기 때문에.

몇 시간에 걸쳐 이야기 해도 다 못할 이야기를 그는 3분 짜리 노래 한 곡으로 우리에게 털어놓고 있지 않은가?

계속해서 우리에게 인생이야기를 들려주기를 기대해 본다.

쾌유하시길 기원합니다.








posted by 대갈장군
2011. 5. 4. 00:13 카테고리 없음
일주일에 보는 프로그램이 몇 개 안되는데 그중 하나가 위대한 탄생이다.

헌데 지난주 방송을 보니 이은미씨가 재미있는 이야기를 하더군...

시청자는 드라마를 좋아한다는 것을 알고 있지만 오디션 프로그램인 만큼 노래로 평가해야 한다는 말.


예전에 슈퍼스타 K에서 윤종신이 했던 말이 떠올랐다. 노래에 점수를 매긴다는 것은 사실 의미 없는 것이라고.

노래는 전적으로 듣는 사람의 주관적이고 개인적인 판단이라는 것이었다.


이은미씨는 주관적으로 판단하는 모든 시청자의 의견을 '드라마'를 위해서 라는 식으로 매도 했다.

난 아닌데? 난 쟤가 노래 잘해서 투표했는데? 왜 나를 드라마 좋아하는 사람으로 매도하지?

기분 나빴다.


이은미씨 처럼 객관적으로 노래 실력만 평가할 수준이 안되는 일반 사람들은 아예 평가하지도 말아야 겠네?

가수와 배우는 무대위에 서서 관객을 위해 노래와 연기를 통해 감정을 전달하는 직업아닌가?

원했든 원하지 않았든 그들의 인생 스토리가 그들의 연기와 노래에 뭍혀 있는 것은 어쩔수 없는 것 아닌가?

그것마저 배제하려고 했다면 애당초 인생 스토리, 백그라운드에 대한 방송은 아예 하지를 말던가.

'위대한 탄생'이라는 제목은 마치 드라마 제목 처럼 붙여놓고 왜 이제와서 '엄격한 시험'같은 느낌을 풍기는가?


그렇게 객관적인 노래 실력 평가가 중요하다면 단지 '노력'하는 모습이 아름다워서 뽑았다는 권리세는 도데체 뭔가?

그렇게 객관적인 노래 실력 평가가 중요하다면 단지 '변화'하는 모습이 아름다워서 점수를 잘준 데이비드 오는 도데체 뭔가?

이런 자기 모순이 어디 있는가? 스스로를 부정하는 모습이 상당히 우습다.















 
posted by 대갈장군
2011. 3. 22. 02:10 카테고리 없음
청중 500명이 뽑은 7등이 재도전의 기회를 받는다...

어이가 없었다. 가장 기분 나쁜 점은 지금까지 타이틀로 내새워 오던 원칙을 30초 만에 자기들끼리 뒤엎었다는 점이다. 관객의 귀에 맞기겠다면서 500명의 청중 평가단을 모셔 왔으면서 결정은 자기내들끼리 꿍짝꿍짝.

김건모에 비하면 새까만 후배 가수 6명에게 면전에 대놓고 괜찮겠냐고 물어보는데 옆에 사람이 없을때 물어보던가 이건 뭐 공산주의 투표도 아니고, 최소한 무기명 투표를 하던가...

아니면 청중 평가단 제도를 좀 더 보완해서 탈락자들을 모아 패자 부활전을 만들어 보겠다던가... 상식이 좀 통하는 방식으로 해야지.

그리고 편집도 문제. 이소라씨가 삐쳐서 방송 못해먹겠다고 나가서 그 충격과 여파로 재도전이라는 카드를 내민것 처럼 편집했던데, 그건 모든 잘못을 이소라씨에게 돌리려는 수법에 불과하다고 생각함.

감정이 격해서져 그렇게 행동했든, 아니면 연기로 그렇게 했든, 뭐든간에 기본적으로 원칙은 고수될 것이라는 마인드를 모두 가지고 있었을꺼야. 왜냐면~ 나는 가수다 광고 할 때부터 주구장창 서바이벌 서바이벌 탈락! 탈락!을 외쳐왔잖아.

이소리씨 속마음은 김건모씨가 떨어지던 말건 상관은 없는데 일단 선배니까 최대한 예의는 갖추어 보자라는 생각이었을 뿐인지도 몰라.

궁극적인 실수는 김영희 PD라고 봐야지... 의도적으로 논란을 일으키기 위해 그랬든, 아니면 정말 충격이라서 그랬든, 둘 다 삽질임에는 변함이 없지.

그럴거면 왜 바쁜 사람 500명 모아서 평가하는지... 차라리 노래방 기계 500대 가져다 놓고 점수 평균내던가? 그게 훨씬 더 공정하겠네?

참, 그리고 결과는 각자 대기실에서 발표하던가... 왜 뻔히 눈치 보게끔 서로 다 보이는데서 발표하고 난리인지 모르겠네.

처음이라 실수다라고 말한다면 그래도 대범하게 받아들이겠는데, 핑계만 줄줄 대는 모습이 참 안스럽다. 일밤이 살아나길 바라는 사람중 하나로써 참으로 안타깝다. 프로그램이 정말 산으로 가고 있으니... 에휴

 
posted by 대갈장군
2011. 3. 12. 04:33 프로그래밍/Windows API
드디어 실제로 메모리 주소 공간을 들여다 볼 차례...

http://technet.microsoft.com/en-us/sysinternals/dd535533 에서 VMMap 프로그램을 다운 받아서 실행시키면 현재 내 컴퓨터에서 돌아가고 있는 프로세스의 주소 공간을 들여다 볼 수가 있다.


김상형님의 책에 있는 샘플 프로그램을 돌리고 있는 샘플 프로세스의 메모리 주소 공간을 보여준다. 알다시피 프로세스는 총 4GB의 주소 공간을 가지지만 실질적으로 사용자가 사용하는 주소 공간은 0x00010000 부터 0x7FFEFFFF 이다. 대략 2GB 이다. 0x00000000 부터 0x0000FFFF 까지는 준비된 영역으로 접근시 Access Violation이 일어난다. 그리고 2GB 이후의 공간은 운영체제가 필요로 하는 주소 공간이다. 뭔 2GB 씩이나 쓰냐고 하겠지만 2GB도 작아서 3GB로 확장 하게 만들어 놨을 정도다.

아무튼, 위 그림에서 보면 아래쪽에 좌악 주소 공간의 크기 순으로 정렬되어 있는데 타입이 여러가지가 있다. 우선 Free는 할당 되지 않은 공간을 말하고 Private Data의 경우는 시스템의 페이징 파일에 맵핑 되어 있는 것을 말한다. 그리고 Image의 경우는 exe나 DLL 같은 이미지 파일을 맵핑 하고 있는 것을 말하고 Mapped File은 당연히 메모리 맵에 맵핑된 데이터를 말한다. 그리고 Heap도 보이는데 이것도 사실은 Private Data로 분류되고 있다.  

이렇게 펼쳐놓고 보니 메모리 별거 아니다... ㅡ.ㅡ 라고 믿고 싶다. 일단 메모리의 구조에 대한 이해가 끝났다면 다음으로 고고고~
posted by 대갈장군
2011. 3. 12. 04:15 프로그래밍/Windows API
마지막으로 힙과 메모리 맵이 남았다. 힙은 프로세스의 주소 공간 상의 예약 영역으로 가상 메모리 (VirtualAlloc 함수) 방식에 비해서 훠어얼씨인 더 효율적이다. 다만 '예약' 상태가 없기 때문에 편리성은 쬐에끔 떨어진다.

간단하게 결론만 말해서 작은 데이터의 연속적인 생성과 삭제 그리고 접근이 필요하다면 당연히 힙을 이용해야 한다. 만약 거대 메모리를 할당해야 한다면 VirtualAlloc 함수를 이용하여 적극적인 '예약'을 통해 보다 효율적으로 주어진 주소 공간을 사용하면 된다. 그리고 마지막으로 무지막지하게 큰 데이터를 불러와야 하거나 프로세스 간에 서로 메모리를 공유해야 한다면 최선의 방식은 메모리 맵이다.

힙의 할당과 해제는 다음과 같은 명령으로 수행 가능하다.
ptrHeap = HeapAlloc(GetProcessHeap(), 0, sizeof(int));
HeapFree(GetProcessHealp, 0, ptrHeap);

사실 힙은 사용하는 방법은 간단한 편인데 왜 힙이 좋은 녀석인가에 대한 논의가 좀 필요하다. 우선 힙이 어떤 원리를 가지는가 알아보면, 힙은 프로세스 실행시 기본으로 1MB의 영역을 예약하게 된다. 중요한 것은 이 주소 공간은 프로세스가 가지는 다수의 스레드에서 접근 가능하므로 운영체제는 힙에 대해 순차적 접근 및 처리를 기본적으로 하게 된다. 다수의 스레드에 의한 경쟁 상태를 방지 하기 위해서 이다.

고로 바로 이 순차적 처리 부분에서 성능의 저하가 약간 있을 수 있다. 하지만 힙은 여러개 생성할 수 있으며 생성시에 HEAP_NO_SERIALIZE 플래그를 주면 동기화 문제를 고려하지 않고 즉각적인 접근과 변경을 허용하게 된다.

만일 다수의 스레드가 접근해서 읽기와 쓰기를 하는 경우라면 반드시 순차적 접근을 하도록 내버려 두어야 하지만 만약 사용자가 단일 스레드를 위한 하나의 힙을 생성하고 그 스레드만 접근한다는 보장이 된다면 순차적 접근을 무시하도록 하여 보다 빠른 성능을 내게 할 수 있다.

또한 하나의 통일된 힙에 모든 자료와 데이터가 섞이게 되는 경우 자료 A의 오류로 인한 자료 B로의 침범이 일어났을때 에러가 발생하면 운영체제는 자료 B에 의한 에러로 오판 할 수 있다. 

적절한 새로운 힙의 생성은 메모리 공간을 보다 효율적으로 사용하게 해주는 방법이 되기도 한다. 다양한 크기의 자료가 뒤섞여있는 힙에서는 조각화 현상이 나타나게 되므로 실제로 비어있는 여유 공간이 추가적인 데이터를 삽입하기에 충분하더라고 데이터의 배열 문제로 공간이 없다고 생각하게 된다. 이런 문제는 같은 사이즈를 가지는 데이터 별로 힙을 생성해 관리하면 쉽게 해결된다. 

그런 식으로 함으로써 추가적으로 유사한 데이터들이 한 주소 영역에 뭉치는 결과를 가져오며 이것은 시스템 램과 페이징 파일 사이의 스와핑을 줄여준다. 사실 이 스와핑이야 말로 성능 저하의 가장 큰 주범인데 이것을 예방 할 수 있다면 아주 효과적이라고 할 수 있겠다.

그리고 앞서 말했듯이 단일 스레드가 단일 힙을 엑세스 한다는 조건만 만족시켜주면 순차적 접근을 할 필요가 없으므로 동기화 비용을 줄일 수 있다. 

마지막으로 빠른 해제도 가능하다. 해제해야 할 데이터가 한군데 모여 있기 때문에 해제가 매우 빠르다. 마구 마구 섞여 있는 경우에는 해제도 번거롭다.

이런 여러가지 이유 때문에 힙은 아주 유용하게 쓰일 수 있는 놈이다. 

이런 장점을 이용해 클래스의 오브젝트 생성시 힙을 따로 할당하는 방법을 제프리 리처의 WIndows via C/C++ 책에서는 제시하고 있다. 작은 데이터 구조를 여러개 관리하는 프로그램에서는 매우 효과적인 클래스 관리법이 될 수 있을 것 같다.

메모리 맵은 하드디스크 공간에 존재하는 파일을 메모리로 맵핑 시키는 기법이다. 이로써 서로 다른 프로세스가 하나의 맵 파일을 공유할 수 있고 프로세스 간에 자료 교환이 가능하다. 


posted by 대갈장군
2011. 3. 11. 23:54 프로그래밍/Windows API
윈도우에서 메모리를 할당하는 다음 방법은 VirtualAlloc() 함수다. 이 함수는 malloc 함수에서 보다 발전된 형태로 사용자에게 여러 가지 추가 기능을 제공한다.

가장 큰 차이점은 '예약'과 '확정' 상태가 존재한다는 것인데 malloc의 경우 '확정' 상태만 있었다. 여기에 예약이 추가 되었는데 이로써 보다 효율적인 메모리 사용이 가능해 졌다. 예약만 한 경우 물리적 메모리는 전혀 소모되지 않는다. 오직 확정에만 가상 메모리가 실제로 할당된다.

함수의 원형은 다음과 같다.
LPVOID WINAPI VirtualAlloc( __in_opt  LPVOID lpAddress, __in      SIZE_T dwSize, __in      DWORD flAllocationType, __in      DWORD flProtect );

첫번째 인자가 할당하고자 하는 주소의 위치 (4GB의 프로세스 주소 공간에서), 두번째 인자는 할당할 크기, 세번째 인자는 할당 타입인데 바로 여기서 확정, 예약, 높은 번지 할당등을 설정할 수 있다. 자세한 내용은 다음 표 참조. 마지막 인자는 할당할 페이지의 엑세스 타입 설정을 하는데 이것도 malloc과의 다른점 중에 하나다. 읽기 쓰기 접근 제한 설정이 가능하다.

ValueMeaning
MEM_COMMIT
0x1000

Allocates physical storage in memory or in the paging file on disk for the specified reserved memory pages. The function initializes the memory to zero.

To reserve and commit pages in one step, call VirtualAlloc with MEM_COMMIT | MEM_RESERVE.

The function fails if you attempt to commit a page that has not been reserved. The resulting error code is ERROR_INVALID_ADDRESS.

An attempt to commit a page that is already committed does not cause the function to fail. This means that you can commit pages without first determining the current commitment state of each page.

MEM_RESERVE
0x2000

Reserves a range of the process's virtual address space without allocating any actual physical storage in memory or in the paging file on disk.

You can commit reserved pages in subsequent calls to the VirtualAlloc function. To reserve and commit pages in one step, call VirtualAlloc with MEM_COMMIT | MEM_RESERVE.

Other memory allocation functions, such as malloc and LocalAlloc, cannot use a reserved range of memory until it is released.

MEM_RESET
0x80000

Indicates that data in the memory range specified by lpAddress and dwSize is no longer of interest. The pages should not be read from or written to the paging file. However, the memory block will be used again later, so it should not be decommitted. This value cannot be used with any other value.

Using this value does not guarantee that the range operated on with MEM_RESET will contain zeroes. If you want the range to contain zeroes, decommit the memory and then recommit it.

When you specify MEM_RESET, the VirtualAlloc function ignores the value offlProtect. However, you must still set flProtect to a valid protection value, such as PAGE_NOACCESS.

VirtualAlloc returns an error if you use MEM_RESET and the range of memory is mapped to a file. A shared view is only acceptable if it is mapped to a paging file.

간단한 예를 보면, 


ptr = (int *)VirtualAlloc(NULL, sizeof(int)*10, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
VirtualFree(ptr, sizeof(int)*10,MEM_DECOMMIT);
VirtualFree(ptr, 0, MEM_RELEASE);
VirtualFree에 대한 설명이 빠졌는데 메모리 해제 함수다. MEM_DECOMMIT은 확정 해제, MEM_RELEASE는 예약 해제다. 메모리 할당도 MEM_RESERVE와 MEM_COMMIT를 같이 불러 호출하거나 따로 각각 두번 호출해야 한다. malloc보다 좀 번거롭긴 하다.

예약을 할 수 있다는 차이점 이외에도 할당 단위의 차이가 있다. 일단 일반적인 컴퓨터를 기준으로 봤을때 4K 사이즈로 할당이 이루어 진다. 즉, 내가 10K 할당을 요구하면 4의 배수인 12K로 할당한다는 말. 그리고 할당을 시작하는 할당 단위가 있다. 보통의 경우 64K 단위로써 매우 큰 편이다. 즉, 작은 사이즈의 메모리를 여러번 반복적으로 할당하게 되면 할당을 시작하는 단위를 64K로 끊기 때문에 메모리의 조각화가 심해진다. 

그래서 작은 메모리의 할당에는 malloc이 훨씬 더 경제적이다. 그런 경우에는 다음에 알아볼 힙을 사용하는 것도 좋은 방법이다. 

VirtualAlloc의 다른 재미있는 점은 접근 권한 설정이 가능하다는 점인데 너무 보안에 신경을 쓴게 아닌가 싶다.. ㅡ.ㅡ 또 다른 한가지 흥미로운 점은 메모리 잠금 기능인데 할당된 메모리를 물리적 RAM에 상주 하도록 잠그는 함수다. 이로써 보다 빠른 접근 속도를 제공하는데 잘못 사용하면 사용가능한 물리적 RAM을 제한함으로써 운영체제 전체에 속도저하를 유발할 수 있다. 

내가 봤을때 마이크로 소프트에서 야심차게 내놓은 메모리 할당 / 해제 함수인 것 같은데 소규모 프로그램 개발자에게는 그닥 매력적이지 않다. 물론 가능한 기능들은 malloc에 비해서 많고 예약과 확정이라는 효율적인 메커니즘을 제공하기는 하지만 작은 용량을 가지는 다수의 메모리를 반복적으로 할당해야 하는 경우 VirtualAlloc은 메모리 낭비가 너무 심히다.



'프로그래밍 > Windows API' 카테고리의 다른 글

윈도우 메모리 파이널!  (0) 2011.03.12
윈도우 메모리 - 4 (힙과 메모리 맵)  (0) 2011.03.12
윈도우 메모리 - 2 (malloc 함수)  (2) 2011.03.11
윈도우 메모리 - 1  (0) 2011.03.10
윈도우 - 차일드, 팝업  (0) 2009.11.17
posted by 대갈장군
2011. 3. 11. 03:20 프로그래밍/Windows API
앞서 언급했던 윈도우 메모리에 관한 이야기 2탄으로 바로 malloc() 함수에 대한 이야기다.

malloc은 뭐 워낙 유명한 함수라서 굳이 이야기 안해도 사용하는 방법쯤은 다 알거라 생각한다. 바로 코드로 들어가 보자.

   
   1:  #include <stdio.h>
   2:  #include <stdlib.h>
   3:  #include <string.h>
   4:   
   5:  #ifdef _DEBUG
   6:  #include <vld.h>
   7:  #include <vldapi.h>
   8:  #endif
   9:   
  10:  void main()
  11:  {
  12:      // Test int
  13:      int *ptr;
  14:      ptr = (int*)malloc(sizeof(int)*10);
  15:   
  16:      for(int i = 0; i < 10; i++)
  17:      {
  18:          ptr[i] = i * 10;
  19:          printf("ptr[%d] = %d\n", i, ptr[i]);
  20:      }
  21:   
  22:      free(ptr);
  23:   
  24:      // Test char
  25:      char *ptr_char;
  26:      ptr_char = (char *)malloc(sizeof(char)*20);
  27:   
  28:      char *str1 = "MEMORY LEAK";
  29:      strcpy(ptr_char, str1);
  30:   
  31:      printf("ptr_char = %s\n", ptr_char);
  32:   
  33:      free(ptr_char);
  34:   
  35:      // Test array of pointers
  36:      int **double_ptr;
  37:   
  38:      double_ptr = (int **)malloc(sizeof(int *)*10);
  39:   
  40:      for(int i = 0; i < 10; i++)
  41:      {
  42:          double_ptr[i] = (int *)malloc(sizeof(int)*10);
  43:          for(int j = 0; j < 10; j++)
  44:          {
  45:              double_ptr[i][j] = (i * 10) + j;
  46:              printf("double_ptr[%d][%d] = %d\n", i, j, double_ptr[i][j]);
  47:          }
  48:      }
  49:   
  50:      for(int i = 0; i < 10; i++)
  51:      {
  52:          free(double_ptr[i]);
  53:      }
  54:   
  55:      free(double_ptr);
  56:   
  57:  }

위의 코드의 제일 윗부분에 라인 6번과 7번을 보면 vld.h 라는 녀석이 있는데 이 녀석은 이른바 visual leak detector라고 불리는 오픈 소스로서 메모리 누수가 발생하면 콘솔 출력창에 어디서 뭐가 왜 어떻게 유출되었는지 상세 정보를 보여주는 아주 착한 놈이다.

이 오픈 소스는 http://www.codeproject.com/KB/applications/visualleakdetector.aspx 에 가면 다운 받을 수 있다.

코드는 세 파트로 나뉘어져 있는데 첫번째 파트에서는 간단하게 10개의 int 메모리를 할당하고 0 에서 9까지 넣어 출력했고 두번째 파트는 char 메모리 공간을 생성하여 MEMORY LEAK라는 섬뜩한 문구를 출력해 봤고 마지막은 이중 포인터를 이용해서 2차원 int 배열을 생성하여 0부터 99까지 각 배열에 대입 후 출력해 봤다.

암튼, 22번 라인, 33번 라인 그리고 50번에서 55번까지 free()로 할당된 메모리를 해제하는 명령들이 나와있는데 메모리는 할당보다 해제가 더 중요하다. 화장은 하는 것보다 지우는 것이 더 중요한 것 처럼... 

예를 들어 두번째 테스트는 Test char 파트에서 free()를 호출 안하면 (라인 33번 지우면), 다음과 같은 에러가 프로그램 종료 후 출력창에 뜬다.


너무나도 친절하게 얼마만큼의 메모리가 누수되었는지 그리고 들어있던 내용은 무엇인지 그리고 게다가 코드의 어느 부분에서 누수가 있었는지 알려준다... 너무 친절해서 눈물이 날 지경이다..

라인 33번에서 55번까지는 이중 포인터의 개념인데 integer 값을 10개 저장하는 배열을 10개 만들었다. 즉, 10 x 10 = 100 개의 int 배열을 만든 셈이다. 중요한 것은 해제인데 해제 할때는 10개의 개별 배열을 모두 하나씩 해제한 후 마지막에 이중 포인터를 해제 한다. 만약 개별 배열들을 해제하지 않고 이중 포인터만 해제해 버리면 10개의 개별 배열은 부모 잃어버린 자식들이 되므로 메모리 누수가 일어난다. 

개별 배열(자식)을 해제 하기 전에 이중 포인터 (부모)를 날려 버리면 자식들을 찾을 수가 없다. 왜냐면 부모 연락처가 없기 때문에... 

malloc을 이용한 할당은 가장 경제적인 메모리 할당이다. 왜냐면 바이트 단위로 사용자가 원하는 만큼 할당하기 때문이다. 좀 있다 살펴볼 다른 메모리 할당 함수는 malloc 처럼 바이트 단위로 할당을 하지 않는다. 경제적인 것은 그만큼 손이 많이 간다. 고로 해제하는 과정을 사용자가 반드시 꼼꼼히 해야 한다는 불편함이 있다. 


posted by 대갈장군
2011. 3. 10. 05:56 프로그래밍/Windows API
보다 나은 개발을 위해서는 윈도우 메모리에 대해서 전반적인 이해가 필요로 된다. 내가 김상형님의 책을 좋아하는 이유는 김상형님은 책을 쓰실때 항상 '왜' 라는 질문에 대답을 해주시기 때문이다.

과거 16비트 시절의 메모리 구조는 한 마디로 요약하자면 '위험천만'이었다. 가장 큰 문제는 사용자가 잘못 작성한 프로그램이 건드려서는 안되는 중요한 메모리 영역을 건드릴 수 있었다는 점이다. 메모리가 '전역 변수' 처럼 모든 프로그램 및 운영체제에게 통일된 표기 방식으로 공개 되어 있었기 때문이다.

그래서 실수로 운영체제의 영역을 지우거나 바꿔버리면 심지어 윈도우를 다시 깔아야 하는 말도 안되는 불상사가 종종 있었다고 한다. 안타깝게도 나는 그런 일을 겪어볼 행운(?)이 없었다. 

그러면서 32비트 운영체제가 도입되고 윈도우 95/98로 넘어오면서 드디어 새로운 메모리 체계가 완성된다. 바로 '가상 메모리' 시스템이다. 

가상 메모리는 간단히 말해서 물리적 메모리 + 페이징 파일이다. 여기서 물리적 메모리는 우리가 너무나 너무나 잘 알고 있는 바로 RAM이고 페이징 파일은 RAM을 흉내내고 있는 하드 디스크의 일부를 말한다.

RAM은 속도가 빠르지만 용량이 충분하지 않고 페이징 파일은 하드디스크를 이용하므로 용량은 방대하나 RAM에 비해 속도가 떨어진다.

하지만 이것이 전부가 아니다. 가상 메모리를 이용하는 운영체제는 각 프로그램이 운영체제가 제공하는 최대 주소 공간을 각 프로그램에게 할당해 준다. 즉, 32비트 운영체제라면 4GB의 주소 공간을 각 프로그램 다시 말하자면 하나의 프로세스에 할당해 준다.

이 4GB의 주소 공간은 사실 껍데기에 불과하며 물리적 메모리가 아니다. 예를 들어 malloc 함수를 통해 메모리 할당을 요청하면 프로그램이 가지고 있는 4GB 주소 공간의 어딘가에 요청한 메모리가 할당되었다고 알려주지만 실제로는 가상 메모리에 실질적인 메모리가 할당된 후 할당된 주소가 '페이지 테이블'이라고 불리는 맵에 저장되고 4GB에 할당 된 것 처럼 보이는 메모리는 실제로는 가상 메모리로 자동 연결되게 되어 있다.

이런 식의 이중 연결을 구현하는 이유는 이렇게 함으로써 이 프로그램이 다른 프로그램의 주소 영역, 혹은 운영체제의 고유 메모리 영역을 아예 터치 할 수 없도록 차단해 주기 때문이다. 고로 프로세스 A가 망해도 A만 망하지 B,C,D 기타 운영 체제에게는 눈꼽만큼의 영향도 안준다.

이는 16비트 운영체제의 단점을 한방에 커버 시켜주는 그야 말로 최대 강점이다. 최대 강점이면서 동시에 최대 단점이다. 왜냐면 프로세스간에 통신이 겁나 복잡해 졌기 때문이다. 보안성의 강화는 접근성을 약화 시킬수 밖에 없다.

다음 글타래에서는 가상 메모리 시스템에서 메모리를 할당하는 다양한 방법에 대해서 알아보자. 


posted by 대갈장군
2011. 3. 9. 01:36 나의 이야기

 

What is the most resilient parasite?

- 가장 생명력이 강한 기생충이 무엇인가?

 

A bacteria? A virus?

- 박테리아? 바이러스?

 

An intestinal worm?

- 장에 기생하는 벌레?

 

Uh...

- 음...

 

What Mr. Cobb is trying to say-

- Mr. Cobb 씨가 하려는 말은,

 

An idea.

- '생각' 입니다.

 

Resilient. Highly contagious.

- 질기고 매우 전염성이 강하죠.

 

Once an idea has taken hold of the brain, it's almost impossible to eradicate.

- 한번 머리속에 자리를 잡은 하나의 생각은 제거하는 것이 거의 불가능에 가깝습니다.

 

An idea that is fully formed, fully understood, that sticks.

- 완전히 형성되고 완전히 이해되버린 달라 붙은 생각. 

 

Right in there somewhere.

- 바로 거기 어딘가.

 

인셉션은 개인적으로 매우 뛰어난 영화라고 생각한다. 영화 제목부터 시작해서 배우의 연기, 내용, 흐름, 결말까지 그 모든 것이 통일된 하나의 의미를 가리키고 있다. 

그중에서도 가장 의미심장한 대사가 바로 위에 번역한 대사가 아닐까 싶다. 위 대사를 듣는 순간 망치로 머리를 한대 맞은 것 같았다.

가장 질기고 생명력이 강한 기생충은 바로 '생각'이라는 것. 많은 것을 암시하는 한 문장이다.

한번 사람의 머리 속에 어떠한 것에 대한 생각이 자리잡히게 되면 그 생각을 깨기 위해서는 그 이상의 것이 필요하다. 내가 현재 진실이라고 믿고 있는 것이 거짓이 아니길 바라는 사람의 감정적인 심리 요소도 그런 틀을 깨기 어렵게 만드는 이유중에 하나라고 생각한다.

내가 지금 믿고 진실들이 과연 진실일까? 라는 생각을 되뇌이게 만든 이 영화, 무서운 영화다. 내가 믿고 보고 말하는 진실들이 결국은 내 머리 속에서 굳어진 생각에서부터 나온 일부라는 것.

소름끼치도록 무서운 진실을 이야기 하고 있는 영화. 그래서 이 영화가 더 마음에 든다...




'나의 이야기' 카테고리의 다른 글

조용필 - 꿈  (0) 2013.05.20
범죄와의 전쟁  (0) 2013.01.24
진실 = 변하지 않는 마음  (0) 2010.02.23
Alaska  (0) 2010.02.18
기도  (2) 2010.02.03
posted by 대갈장군
2010. 9. 25. 01:58 프로그래밍/Visual C++ 2008
/MT, /MD, /MTd, /MDd 와 같은 컴파일 옵션에 따라 링크 되는 라이브러리가 다르다.
일단 정적으로 링크하게 되면 libcmt.lib 또는 libcmtd.lib가 선택이 되며 동적으로 링크할 경우 msvcrt.lib와 msvcrtd.lib가 선택된다. 이들의 차이는 정적 / 동적 링크이며 d가 붙으면 디버그 모드, 없으면 릴리즈 모드다.

C run-time library

Associated DLL

Characteristics

Option

Preprocessor directives

libcmt.lib

None, static link.

Multithreaded, static link

/MT

_MT

msvcrt.lib

msvcr80.dll

Multithreaded, dynamic link (import library for MSVCR80.DLL). Be aware that if you use the Standard C++ Library, your program will need MSVCP80.DLL to run.

/MD

_MT, _DLL

libcmtd.lib

None, static link

Multithreaded, static link (debug)

/MTd

_DEBUG, _MT

msvcrtd.lib

msvcr80d.dll

Multithreaded, dynamic link (import library for MSVCR80D.DLL) (debug).

/MDd

_DEBUG, _MT, _DLL

msvcmrt.lib

msvcm80.dll

C Runtime import library. Used for mixed managed/native code.

/clr

 

msvcurt.lib

msvcm80.dll

C Runtime import library compiled as 100% pure MSIL code. All code complies with the ECMA URT spec for MSIL.

/clr:pure

 

헌데 재미 있는 것은 Visual C++는 추가적으로 헤더의 선언 유무에 따라 Standard C++ library도 링크한다는 것이다. 자, 여기서 조금 헷갈리는데 위의 테이블은 분명 Visual C++의 프로젝트 옵션의 컴파일 설정에 따라 링크하는 라이브러리가 결정되었다. 헌데 아래 테이블에 나오는 라이브러리는 단순히 프로젝트 옵션의 컴파일 설정에 의해 결정되는 것이 아니라 Standard C++ library header가 프로그램에 포함되었는지를 보고 링크가 이루어 진다.  

Standard C++ Library

Characteristics

Option

Preprocessor directives

LIBCPMT.LIB

Multithreaded, static link

/MT

_MT

MSVCPRT.LIB

Multithreaded, dynamic link (import library for MSVCP80.dll)

/MD

_MT, _DLL

LIBCPMTD.LIB

Multithreaded, static link

/MTd

_DEBUG, _MT

MSVCPRTD.LIB

Multithreaded, dynamic link (import library for MSVCP80D.DLL)

/MDd

_DEBUG, _MT, _DLL

바로 요 아래에 있는 것이 Standard C++ library header 파일들의 목록이다. 바로 요놈들중에 한놈이라도 프로그램에 선언되어 사용되는 놈이 있다면 위의 테이블에 나온놈들중에서 현재 프로젝트의 컴파일 옵션에 따라 적당한 라이브러리가 추가로 링크가된다. 

algorithm>

<bitset>

<complex>

<deque>

<exception>

<fstream>

<functional>

<hash_map>

<hash_set>

<iomanip>

<ios>

<iosfwd>

<iostream>

<iso646.h>

<istream>

<iterator>

<limits>

<list>

<locale>

<map>

<memory>

<new>

<numeric>

<ostream>

<queue>

<set>

<sstream>

<stack>

<stdexcept>

<streambuf>

<string>

<strstream>

<utility>

<valarray>

<vector>

 

In addition, the following C++ wrappers are documented:

<cassert>

<cctype>

<cerrno>

<cfloat>

<ciso646>

<climits>

<clocale>

<cmath>

<csetjmp>

<csignal>

<cstdarg>

<cstddef>

<cstdio>

<cstdlib>

<cstring>

<ctime>

<cwchar>

<cwctype>

 

이렇게 표만 잔뜩 늘어 놓으면 재미가 없으니, 예를 보자.

우선 아래 그림을 보면 빈 코드가 컴파일 되었는데 포함된 헤더파일은 오직 stdio.h 밖에 없다. 그리고 아래 Output을 살펴보면 MSVCRTD.lib를 볼수 있는데 이것은 현재 이 프로그램을 /MDd 옵션으로 컴파일 되었기 때문이다.


그렇다면 만약 ctime 부분을 포함시키면 어떻게 될까? ctime은 Standard C++ library header 중의 하나이기 때문에 현재 컴파일 설정인 /MDd에 해당하는 라이브러리인 MSVCPRTD.lib가 포함될까? 정답은 아래 그림속에...


앞서본 그림과 달리 아래 부분에 msvcprtd.lib가 포함되어 있다! 호~이!

posted by 대갈장군
2010. 9. 25. 01:40 프로그래밍/C
우선 역사를 보자. C 언어는 기준이 완성되기 전까지는 Input/Output 기능과 같은 기본 함수를 언어자체적으로 지원하지 않았다. 그것에 불편을 느낀 다수의 사용자들이 모여 지금의 C Standard Library를 작성하게 되었다. 

Unix와 C는 둘 다 AT&T's Bell Laboratories에서 1960년대 그리고 1970년대에 개발되었는데 C 언어가 인기가 높아지고 많은 사람들이 사용하기 시작하면서 호환성 문제가 발생한다. 고로 American National Standards Institute(ANSI)에서 표준을 세우고 규격화하여 내놓은 것이 바로 ANSI C이다. 그리고 이것은 1989년에 완성된어 C89라고 명명되어 졌다. 

그렇다면, C Standard Library는 뭐냐 하면, C Programming 언어에서 사용되는 기본 명령들, 예를 들자면 input/output 및 string 핸들링 함수와 같은 것들을 구현하기 위한 headers와 library routines를 모아놓은 ISO C 기준의 일부분이다. 

즉, 다시 말해서 C Standard Library는 문서에 의해 규정된 '인터페이스 기준'이다. 고로 걍 설명서일 뿐이라는 것. 사실 C Standard Library는 24개의 헤더 파일인데 요 헤더 파일들에는 함수들과 데이터 타입 그리고 매크로등이 들어가 있다. 

이것은 다른 언어 (예를 들자면 자바)에 비교해서 아주 간단한 함수들만 선언된 헤더파일들이다. C Standard Library는 기껏해야 수학 함수, 문자열 함수, 타입 변환 함수 및 파일 입출력, 콘솔 화면 입출력 함수 정도만 명시되어 있다. 고로 C Standard Library는 C++ Standard Template Library와 같은 고급 컨테이너 타입이나 GUI, 네트워킹 툴등의 고급 기능은 전혀 없다.

아래의 목록이 바로 ISO가 명시하는 C Standard Library의 헤더 파일들이다.

ISO C library headers

NameFromDescription
<assert.h> Contains the assert macro, used to assist with detecting logical errors and other types of bug in debugging versions of a program.
<complex.h> C99 A set of functions for manipulating complex numbers.
<ctype.h> Contains functions used to classify characters by their types or to convert between upper and lower case in a way that is independent of the used character set (typically ASCII or one of its extensions, although implementations utilizing EBCDIC are also known).
<errno.h> For testing error codes reported by library functions.
<fenv.h> C99 For controlling floating-point environment.
<float.h> Contains defined constants specifying the implementation-specific properties of the floating-point library, such as the minimum difference between two different floating-point numbers (_EPSILON), the maximum number of digits of accuracy (_DIG) and the range of numbers which can be represented (_MIN_MAX).
<inttypes.h> C99 For precise conversion between integer types.
<iso646.h> NA1 For programming in ISO 646 variant character sets.
<limits.h> Contains defined constants specifying the implementation-specific properties of the integer types, such as the range of numbers which can be represented (_MIN_MAX).
<locale.h> For setlocale and related constants. This is used to choose an appropriate locale.
<math.h> For computing common mathematical functions.
<setjmp.h> Declares the macros setjmp and longjmp, which are used for non-local exits.
<signal.h> For controlling various exceptional conditions.
<stdarg.h> For accessing a varying number of arguments passed to functions.
<stdbool.h> C99 For a boolean data type.
<stdint.h> C99 For defining various integer types.
<stddef.h> For defining several useful types and macros.
<stdio.h> Provides the core input and output capabilities of the C language. This file includes the venerable printffunction.
<stdlib.h> For performing a variety of operations, including conversion, pseudo-random numbers, memory allocation, process control, environment, signalling, searching, and sorting.
<string.h> For manipulating several kinds of strings.
<tgmath.h> C99 For type-generic mathematical functions.
<time.h> For converting between various time and date formats.
<wchar.h> NA1 For manipulating wide streams and several kinds of strings using wide characters - key to supporting a range of languages.
<wctype.h> NA1 For classifying wide characters.



posted by 대갈장군
2010. 9. 11. 00:20 카테고리 없음
얼마전 미국의 마약 단속반 직원 몇명이 멕시코에서 마약 갱들에 의해 보복살해 당한 일이 있었다. 기억은 잘 안나지만 세네명이 죽은 것으로 알고 있다.

미국은 사체를 수습해 신속하게 미국 본토로 데려왔고 오바마 대통령은 사체가 도착할 때 미리 공항에 나가서 거수 경례를 하며 죽은 '영웅'들을 맞이했다. 

그 사진을 보는 순간 여러가지 생각이 교차했다. 마치 오바마 대통령은 그들에게 당신들의 죽음을 헛되지 않게 하겠다고 그리고 또 기억하겠다고 말하는 것 같았다. 

우리나라는 '영웅'이 되기 힘들다. 왜일까?

우선 사람들의 기준이 높다. 1등만 기억하는 더러운 세상이라는 유행어가 괜히 생긴게 아니다. 고로, 우리는 칭찬에 인색하다.

반면 손가락질에는 능숙하다. 연예인, 국회의원등 공인들에게 적용되는 잣대는 매우 엄격하며 작은 헛점도 큰 뉴스 거리가 된다. 사람들은 '쯧쯧, 저럴 줄 알았다.. 깨끗한 놈은 하나도 없구만...' 하게 된다.

결국 그런 과정의 반복은 우리 사회 전반에 걸쳐 '불신'으로 나타난다. 누군가가 무엇인가를 잘했다고 해도 일단 의심부터 하게 되는 것이 사실아닌가?

우리는 '선택적 진실'을 즐긴다. 내가 자주 보는 뉴스 사이트는 정해져 있다. 모든 뉴스는 그 사이트를 통해 읽는다. 이미 나는 선택을 한것이다. 그리고 우리는 '튀는 것'을 싫어한다. 관심있는 뉴스를 본 후 추천순으로 정렬하여 가장 추천을 많이 받은 글을 '진실에 가까운 것'으로 받아들인다. 사실 이 과정의 시작부터 우리는 진실을 선택한 것이다. 

우리 스스로에 물어보자. 나는 나에게 얼마나 엄격한 기준을 적용하는가? 그와 같은 기준을 남들에게도 적용하는가? 나는 누군가가 어떤일을 했다면 의심부터 하는가 아니면 칭찬부터 하는가?

부풀려진 '영웅'으로 인해 역효과도 발생하지만 무조건적인 '의심'으로 인해 사회에 걸쳐 나타나는 '불신'보다는 낫다고 생각한다.
최소한 영웅을 좋아하는 사회는 '불신'보다는 '칭찬'을 먼저 하기 때문이다.



posted by 대갈장군
2010. 9. 4. 00:00 프로그래밍
이 내용은 http://www.intowindows.com/how-to-install-windows-7vista-from-usb-drive-detailed-100-working-guide/ 를 참조해서 작성했습니다.

XP에서 XP설치 USB를 만드는 방법은 이미 전에 소개한 바 있으나 Windows 7과 비스타를 설치하는 방법은 조금 다르다. 

우선 작업을 시작하기 전에 필요한 것들을 살펴보면,
  • 최소 4GB 용량을 가지는 USB 
  • 윈도우 7 또는 윈도우 비스타 설치 파일 (DVD를 가지고 있다면 OK)
  • 메인보드가 USB 부팅을 지원해야 함
위 조건을 충족한다면 다음 순서대로 명령수행 해 봅시다~

1. USB 키를 멀쩡한 컴퓨터에 꼽고 안에 들어있는 내용을 비운다.

2. Command Prompt를 열기 (단, 열때 Administrator 권한을 열어야 함)
    Start menu -> All Programs -> Accessories 에 보면 Command Prompt가 있고 우클릭하면 Run as administrator 옵션 있음.

3. 열린 Command Prompt에 DISKPART 라고 치고 엔터 날리기


   그러면 위 그림 아래 부분처럼 DISKPART> 프롬프트가 뜰것임. 그런 다음 LIST DISK 라고 치고 엔터 날리면 현재 이 컴퓨터에 
   연결되어 있는 모든 드라이브 정보가 나오는데 그것들 중에 아까 꼳은 USB 드라이브 정보도 있다.
   그 녀석의 DISK 번호가 무엇인지 잘 기억해 두자.

4. 만약 꼳은 USB의 번호가 Disk 1 이라고 가정하고 아래 명령을 차례대로 수행하자
   SELECT DISK 1
   CLEAN
   CREATE PARTITION PRIMARY
   SELECT PARTITION 1
   ACTIVE
   FORMAT FS=NTFS
   ASSIGN
   EXIT

   위의 명령중 FORMAT FS=NTFS를 실행하면 포멧한다고 시간이 좀 걸린다. 


   위 그림을 보면서 따라하면 더 쉽겠죵

5. 준비해둔 윈도우 7 혹은 비스타 DVD를 CD 드라이브에 넣고 CD 드라이브의 이름을 확인한다. 일반적으로 CD 드라이브는 D 드라이브죠.
    그리고 USB의 드라이브 이름도 확인한다. 내 컴퓨터를 열면 USB의 드라이브 이름을 확인할 수 있다. 여기서는 H라고 가정하자.

6. 아까 열었던 Command Prompt에서 다음 명령을 넣자

    D: CD BOOT
   CD BOOT
    여기서 D:가 바로 윈도우 설치 시디가 들어가 있는 CD 드라이브를 의미한다. 만약 자신의 컴퓨터의 CD 드라이브가 D가 아니라면 바꿔주세용.

7. 마지막으로 다음 명령을 날려준다. 다만 마지막에 들어간 H: 는 현재 자신의 USB 드라이브 이름이므로 자신에게 맞게 바꿔주시길.

    BOOTSECT.EXE /NT60 H:
   

8. 이제 CD 드라이브에 있는 윈도우 7이나 비스타를 모조리 USB로 복사하자. Ctrl+A, Ctrl+C, Ctrl+V! 붙여넣기 콤보 발사

9. 마지막으로 윈도우를 설치할 컴퓨터의 BIOS 셋업에 들어가서 (컴퓨터 시작시 Del 키나 F2 또는 F10, F11 키를 눌러서 들어감) 부팅 순서에서 USB를 최상위로 맞춰놓고 컴퓨터에 USB를 꼽아준 다음 재부팅 시키면 알아서 설치를 시작할 것이다.

종종 일부 노트북은 UBS가 컴퓨터에 연결되어 있지 않은 경우 BIOS의 부팅 순서 설정에 UBS를 보여주지 않는 경우가 있다. 그런 경우를 방지하려면 USB를 꼳아 놓은 상태에서 BIOS를 들어가면 될것이다. Good Luck!
  
posted by 대갈장군
2010. 9. 1. 00:10 프로그래밍/70-536
Application Domain, 줄여서 AD는 뭣하는 녀석인가? 가장 빠르고 쉽게 답하자면 '프로세스'와 비슷한 놈이다. 프로세스는 운영체제가 제공하는 일종의 '독립된 프로그램 운영 공간'인데 바로 이 AD는 .NET Framework에 의해 제공되며 운영체제가 제공하는 프로세스보다 성능 효율 그리고 보안적인 면에서 우위를 점하고 있는 놈이다.

그렇다면 이 AD가 가지는 장점을 나열해 보자.
  • 다른 어셈블리로의 직접적인 엑세스 방지 (보안성 증가)
  • 보안설정이 각각의 AD에 적용 가능 (보안성 증가)
  • 하나의 AD에서 발생한 unhandled exception은 다른 AD에 영향을 미치지 않음 (안정성 증가)
  • 프로세스 생성에 비해 오버헤드가 적다 (효율성 증가)
위에서 언급된 장점들을 이용하면 아주 효율적인 프로그램 작성이 가능한데, 두 가지의 예를 들자면, 우선 하나의 AD가 오류로 인해 정지하더라도 다른 AD 영역은 안정적으로 돌아가므로 프로그램 전체의 안정성이 증가한다. 이것을 우습게 보면 안되는 것이 인터넷 환경에서 예측할 수 없는 오류로 에러가 발생했을 경우 이 오류로 인해 저장되지 않은 모든 작업이 한번에 날아가 버린다면 얼마나 허무하겠는가? 고로 이런면에서 AD는 매우 중요한 '프로그램 안정성'을 제공할 수 있다는 것이다.

다른 예로는 효율성인데, 일반적으로 하나의 프로세스에서 돌아가는 프로그램은 모든 라이브러리 (어셈블리)가 실행시에 정적 혹은 동적으로 연결된다. 대표적인 동적 라이브러리가 DLL인데 일반적으로 이 DLL들은 사이즈가 무지막지하게 큰 경우가 있다. AD를 사용하면 이런 무거운 DLL들을 실행시에 별도의 AD 영역으로 불러들인 다음 사용하고 다시 unload 해 주면 효율성이 증대 된다. 물론 현재의 컴퓨터 환경에서 남아도는 메모리가 많은데 이렇게 까지 할 필요가 있나라는 생각이 들기도 하지만, 어쨌든, 그렇게 할 수 있다는 것은 프로세스에 비해 효율적일수 있다는 것.

Application Domain의 생성 및 어셈블리 로드, 그리고 해제에 대한 코드는 아래와 같다.

AppDomain AD = AppDomain.CreateDomain("MyDomain"); // AD 생성

AD.ExecuteAssembly("Assembly.exe"); // AD에 어셈블리 로드

AppDomain.Unload(AD); // AD 해제

위에서 말한 각각의 AD에 대한 보안 설정에 대해 자세히 알아보자. 일단 왜 개별 보안 설정이 위력적인지 알아야 한다. 예를 들어 인터넷에서 Third-party(대형 기업이 만든것이 아닌 제 삼자)가 만든 어셈블리를 사용한다고 했을때, 권한 설정이 되어 있지 않다면 이 어셈블리는 하고싶은 것을 다 할수 있게된다. 만약 이 어셈블리에 보안 취약점이 있고 해커가 그것을 이용해 사용자의 컴퓨터를 악용하려 한다고 해도 막을 방법이 없다. 사용자는 자신의 코드는 안전하다고 믿었으나 아무것도 모른체 눈뜨고 당할 수 밖에 없다. 

만약 이런 시나리오에서 '권한 설정'을 추가하게 되면 이야기가 달라진다. 새로운 AD를 설정하여 보안 취약성을 가지고 있는 어셈블리를 불러온 다음 권한 설정을 통해 하드디스크 읽고 쓰기를 금지시켜 버리면 비록 그 어셈블리가 취약성을 가지고는 있지만 악용할 수는 없게 된다.

바로 이것이 권한제한 -> 보안 취약성 제거 -> 피해 최소화 로 이어지는 방어법이다. 이것을 영어로 표현하면 Defense-in-depth 라고 하는데 위키로 찾아보니 군대 용어라고 하는데 겹겹이 방어하여 피해를 최소화하는 방어 전술이래라 뭐래라... -_- 틀린말은 아니네..

아무튼, 이런 권한 설정을 위한 방법이 바로 Evidence 인데... 책에 보니까 이 Evidence 한국말로 '증거' 에 대해서 쏼라쏼라 뭐라뭐라 많이 써놨는데 주욱 읽어보니 다 잡소리고 걍 줄여서 한마디로 '보안 등급' 정도로 생각하면 되겠다. 단계별로 생성과정을 설명하자면 우선, object[] 타입으로 생성되는 변수가 하나 있어야 하는데 이 변수는 System.Security.SecurityZone 열거자와 System.Security.Policy.Zone 객체를 사용해서 사용자가 원하는 '보안 등급 영역'을 설정할 수 있다. 대표적인 예가 SecurityZone.Internet인데 인터넷의 보안 등급 영역을 사용하라는 말이다. 인터넷 영역의 보안 등급은 당연히 매우 높음이다. 왜냐면 보안 취약성이 높기 때문이지. 아무튼, 이렇게 생성한 object[] 변수를 Evidence 객체 생성자에 때려 넣으면 비로서 Evidence 객체가 생성된다.

이제 이 생성된 Evidence 객체를 AD를 만들때 붙여 넣으면 Application Domain 전체에 그 보안 등급을 적용하는게 되고 만약 특정 어셈블리를 로드할 때만 붙여 넣으면 해당 어셈블리에 대해서만 보안 등급을 적용하는 것이다.

뭐 구질 구질하게 설명하는 것보다 간단하게 코드로 살펴보자.

우선 특정 어셈블리에만 보안 등급을 설정하려면, 
object[] MyZone = { new Zone(SecurityZone.Internet)};
Evidence MyEvidence = new Evidence(MyZone, null);
AppDomain MyDomain = AppDomain.CreateDomain("MyDomain");
MyDomain.ExcuteAssembly("AnotherAssembly.exe", MyEvidence);

그리고 전체 Application Domain에 적용하고 싶다면,
object[] MyZone = { new Zone(SecurityZone.Internet)};
Evidence MyEvidence = new Evidence(MyZone, null);
AppDomain MyDomain = AppDomain.CreateDomain("MyDomain", MyEvidence);
MyDomain.ExcuteAssembly("AnotherAssembly.exe");

여기서 잘 보면 Evidence 객체를 생성할때 생성자에 두 개의 변수를 입력 받도록 되어 있고 두번째 녀석은 null로 되어 있는데 첫번째 인자는 host evidence이고 두 번째 인자는 assembly evidence이다. 첫번째 인자를 설정하면 두번째 녀석은 설정안해도 자동으로 첫번째 인자를 이용한다. 다만 프로그램에서 어셈블리 생성시 적용되는 보안 등급을 다르게 해야 한다면 두번째 인자도 설정해주면 된다. 뭐, 별로 그럴일은 없어 보이지만..



posted by 대갈장군
2010. 7. 9. 05:04 카테고리 없음
행복, 사랑, 진실. 이런 단어들은 원래부터 정의를 내리기 힘든 것들이다. 사람마다 기준이 다르기 때문이다.

돈이 아주 많고 누릴것 다 누리고 사는 사람이 스스로를 불행하다고 하며 무일푼의 거지가 행복하다는 것은 행복이라는 것이 얼마나 상대적인가를 보여주는 극단적인 예이다.

사람이 행복하다고 느끼는 순간은 일반적으로 자신이 원하는 것을 모두다 가졌거나 간절히 바라던 것을 이루었을 때이다. 하지만, 세상에는 가지려고 아무리 용을 써도 가지지 못하는 것이 존재하며 사람이 아무리 많이 가진다고 한들 세상의 모든 것을 가질 수는 없다. 게다가 죽으면 살아생전 가진 것들은 말짱 꽝이다.

행복해지기 위한 가장 빠르고 쉬운 방법은 '가지려는 마음'을 버리는 것이 아닐까 싶다. 동시에 '가지고 있는 것'에 대한 감사의 마음을 가지는 것. 이 두 가지가 동시에 작용하면 나는 이세상 누구도 부럽지 않은 행복한 사람이 된다.

사람이 행복할 수 없는 가장 큰 이유는 '비교' 때문이다. 나보다 더 가진 사람을 부러워하고 나보다 못 가진 사람을 깔보는 바로 그것이 사람을 불행하게 만들며 순간의 현실에 집착하게 만든다. 하지만 현실은 시간이 지나면 변하게 마련이다. 현실에 집착하게 되면 앞으로 걸어갈 수조차 없다.

사람이 행복할 수 없는 또 다른 이유는 '두려움' 때문이다. 원래 태어나면서부터 언젠가는 죽게되어 있는 사람은 미래에 대한 두려움은 떨쳐버릴수 없는 그림자처럼 늘 따라다니게 마련이다. 결국 벋어날 수 없는 것이라면 당연하게 받아들여야 한다.

나 역시도 죽음에 대한 두려움은 크다. 하지만 지금까지 살아온 날들에 더 감사하는 마음을 가지기 위해 노력한다. 인생이라는 것 자체를 즐겨보지 못하고 일찍 죽는 사람도 얼마나 많은가?

결론적으로 행복해 지기 위해서는, 가지려는 욕심을 버리고 가진것에 감사하며 자신만의 기준을 가지고 인생을 즐기면 된다.

죽음의 문턱에서 스스로의 인생에 대해 후회없이 살았다라고 말할 수 있다면 그 사람은 행복하게 살아온 것이다.
posted by 대갈장군
2010. 7. 2. 03:32 카테고리 없음

역시 한국은 스타크래프트의 나라인가 보다... 대한 항공이 스타 2로 도배한 747 기종을 도입할 예정이란다.


더 재미 있는 것은 기내식 메뉴인데... 


ㅎㅎㅎ 마지막에 gg를 부르는 눈보라 빙수! 메뉴 이름들이 센스 짱이다... 


posted by 대갈장군
2010. 7. 2. 03:25 카테고리 없음

인터넷 무료 저장 공간을 제공하는 웹사이트는 많지만 내가 요즘 들어 사용하게 된 Dropbox 라는 놈은 더더욱 편리한 것 같아서 좋다.

이 녀석을 설치하게 되면 2.5 GB의 저장 공간을 주는데 재미 있는 것은 단순한 저장 공간이 아니라 '동기화'를 알아서 해주는 저장 공간이다.

즉, 내가 여러대의 컴퓨터를 사용하더라도 지정된 폴더에 자료를 집어 넣기만 하면 설치된 Dropbox 프로그램이 알아서 들어온 자료를 인터넷 저장 공간에 업로드 하게 되고 다른 컴퓨터에서 Dropbox 프로그램을 설치만 하면 똑같은 폴더가 고대로 다른 컴퓨터에 나타나게 된다..

물론 FTP 같은 사이트는 어떤 면에서 더 간단하기는 하지만 Dropbox의 장점은 설치만 하면 바로 동기화가 자동으로 되므로 별도의 접속과정 없이 알아서 변경된 파일을 업데이트 하며 일반 폴더와 똑같은 형태와 모양으로 동작하기 때문에 사용자에게 매우 편리한 인터페이스를 제공한다.

실험삼아 몇장의 사진을 학교에서 올려놓고 집에서 접속해 봤더니 깔끔하게 동작한다. 집과 학교를 오가면서 조금 큰 파일을 복사해야 할 때에는 USB나 CD를 이용하곤 했는데 이 Dropbox를 이용하면 훨씬 더 쉽고 간편하게 자료를 사용할 수 있겠다....

초대장이 필요하신 분은 댓글 남기시면 초대장 쏘겠습니다. 물론 무료구~요~~
posted by 대갈장군
2010. 7. 1. 00:29 프로그래밍
아는 형한테서 받은 노트북이 너무 오래 되서 그런지 자꾸 CD를 못읽는 것이 문제의 시작이었다. XP 설치 시디를 어쩌다 인식하면 설치도중에 CD 내놓으라고 하면서 멈춰 버리기를 무한 반복하던중 구세주를 만났으니... 바로 다음 웹사이트 였다.http://www.boot-land.net/forums/index.php?showtopic=4900

여기 들어가면 USB_MultiBoot_10.zip 파일을 다운로드 받을 수 있다. 이 프로그램은 USB를 포멧하고 Windows XP나 기타 운영체제를 복사하도록 해주고 거기다 추가로 컴퓨터가 이 USB를 부팅시 제대로 인식할 수 있도록 각종 설정 파일을 설치하도록 도와 준다.

간단하게 말해서 윈도우 XP USB를 만들어준다. 다만 이것을 사용하기 위해서는 컴퓨터가 USB 부팅을 지원해야 한다. 다행히도 내가 받은 노트북은 BIOS 셋업에 보니 Harddisk 우선 순위에서 USB를 상위에 설정할 수 있게 되어 있었따.

우선 XP를 C 드라이브에 전체 복사 해 놓고 USB_MultiBoot_10.cmd 프로그램을 돌리면 cmd 창이 뜨면서 수동으로 각종 셋팅을 지정할 수 있는 창이 뜬다.


음, 마치 내가 해커가 된 기분... 캬캬캬

우선 0을 선택하고 엔터 때리면 USB-stick 옵션이 USB-Harddisk 옵션으로 바뀐다. 그리고 1번을 누르고 엔터 날리면 XP 복사본이 어디 있냐고 친절하게 물어온다. 간단하게 복사했던 XP 설치 시디 위치를 찾아주면 OK. 그리고 2번을 누르고 엔터 날려서 USB가 어느 드라이브에 꼳혀 있는지 알려준다.

그 외에 건드려야 할 것은 없다. 아, 그리고 위 과정에서 한번은 윈도우 설치에 대해 각종 정보를 입력하고 작은 윈도우가 뜨는데 걍 Unattended로 선택하고 필요한 정보, 뭐 사용자 이름, 시리얼 넘버 등등 같은거 설정해주면 된다. 걍 빈칸으로 넘어가도 상관없다.

다만 시리얼 넘버를 넣어주면 설치 중에 그것을 물어오지 않아서 편리하다.

그리고 나서 3번 누르고 엔터~ 그러면 마지막에 15분 걸리는데 XP 소스 파일 복사할래 말래 물어보는데 그냥 yes 날리면 뭐 잔뜩 복사한다. 끄읏! 그리고 USB 넣고 컴터 켜고 부팅한 다음 1번 선택하고 엔터.. 그러면 끄웃!
posted by 대갈장군
2010. 6. 25. 02:37 프로그래밍/C#
이벤트, 이벤트 핸들러. 이 두 단어는 API와 C++을 공부한 나에게는 아주 익숙하게 들린다. C#이 아닌 다른 언어에서는 이벤트란 API에서 비동기적으로 특정한 호출을 일으키는 명령을 의미하고 이벤트 헨들러는 그것을 받는 핸들러를 의미한다라고 아주 자연스럽게 이해가 된다.

하지만 C#에서 이벤트와 이벤트 핸들러는 좀 다른 의미다. 겹쳐지는 의미가 있기는 한데 바로 '비동기적 호출'이라는 점이다. C#에서 이벤트는 호출되어야 하는 함수의 목록을 의미한다. 중요한 것은 '목록'이라는 건데 이건 다수가 될 수 있다는 점이다. 즉, 멀티캐스트라는 것...

즉, 이벤트는 멀티캐스트 델리게이트로 구현되며 호출되어야 하는 함수 목록을 순차적으로 가진다... 그리고 이벤트에 의해 호출되는 함수를 '이벤트 핸들러'라고 한다. 이벤트를 핸들 하는 놈이라는 의미에서 이벤트 핸들러라 하는 갑다.

사실 C에 보면 메시지를 통해 이런 것을 구현했었다. 메시지를 특정 윈도우에 보내면 해당 윈도우의 메시지 처리 함수에서 메시지에 해당하는 함수를 구현하는 그런 방식이었다. 하지만 이것은 일단 복잡하고 비-객체 지향적이란다. 뭐, 객체 지향이네 아니네는 코딩을 어떻게 하느냐의 차이지만 C에서는 아무래도 언어 자체적인 객체 지향성이 C#에 비해 떨어진다는 의미다.

아무튼 이런 C의 메시지를 객체 지향적으로 포장한 것이 C#의 이벤트라는 것이다. 이벤트는 다음과 같이 선언한다.

엑세스지정자 event 델리게이트타입 이름;

이 위에 선언식을 보아하니 델리게이트와 좀 헷갈리지 않는가? 델리게이트의 선언은 다음과 같다.

엑세스지정자 delegate 리턴타입 이름(인수목록);

event를 선언하기 위해 사용된 델리게이트타입이라는 놈은 바로 델리게이트 타입 선언에 의해 선언된 놈이다. 즉, 예를 들면,

public delegate void MyDelegate(int a, int b);
public event MyDelegate
PrintOutEverything
MyEvent;

위와 같이 사용된다는 것... 델리게이트와 델리게이트 타입. 이 두 놈이 조금 복잡한듯 하나 위에 놈은 델리게이트의 타입 선언문이고 아래는 선언된 델리게이트 타입을 사용한다는 차이만 알면 될듯.

또 한가지 중요한 것은 이벤트 핸들러 즉, 호출 되는 함수는 리턴값을 가지지 않는다는 것. 원래 이벤트라는 것이 알려주기 위함이지 얻어오기 위함이 아니기 때문이다. 더군다나 이벤트 핸들러는 비동기적으로 호출되므로 리턴한다고 해도 받아 줄 놈이 없어졌을 수도 있다. 

그런데 자세히 보면 이벤트라는 놈은 사실 델리게이트타입을 재정의 한 것일뿐 특별한 차이점이 델리게이트와 비교해서 없다. 즉, 이벤트라는 지시자가 없더라도 델리게이트만으로도 모든 기능을 구현할 수 있다는 말이다. 사실 나도 이 부분을 MSDN 책을 보면서 '왜 두번 거쳐서 복잡하게 처리할까?' 라고 생각했던 부분인데 김상형님의 책에서 시원하게 설명해 주고 있다. 

이벤트는 델리게이트와 다르게 인수 정보를 사용자에게 보여주지 않고 감추며 멀티스레드에 안전하다는 장점이 있다. 즉, 사용자에게 보다 쉽고 안전한 방식으로 델리게이트를 사용하도록 했다는 것. 지금 보기에는 이것이 더 복잡해 보일지 모르나 비쥬얼 스튜디오의 마법사를 이용하면 이 과정이 사용자에게는 모두 숨겨지고 단 한번의 더블클릭으로 이루어 진다. 

그렇다면 실제로 이벤트를 폼에 적용하는 예를 살펴보자.

C#의 프로젝트를 폼 형태로 생성하면 메인 클래스는 반드시 Form을 상속받게 되어 있다. 이 Form 클래스에는 Paint라는 이벤트가 멤버로 들어가 있는데 바로 다음과 같이 선언되어 있다.

public event PaintEventHandler Paint;

여기서 PaintEventHandler가 바로 델리게이트 타입이다. 이 델리게이트 타입의 선언을 따라가보면 다음과 같이 선언 되어 있다.

public delegate void PaintEventHandler(Object sender, PaintEventArgs e);

보시다시피 리턴값이 없는 델리게이트 선언문이다. 고로 사용자는 위의 인수를 가지는 함수를 생성하여 Paint 이벤트에 대입만 해주면 화면 그리기 명령을 손쉽게 수행할 수 있는 것이다. 

이 방법 외에 윈도우에 그림을 그리는 방법이 한가지 더 있는데 바로 OnPaint 가상 함수를 재정의 하는 것이다. 이 경우에는 Control (부모)에 정의 되어 있는 OnPaint() 함수를 오버라이드 하는 것이다. 이렇게 하면 이벤트 핸들러를 사용할 필요가 없지. 

그렇다면 이 둘의 차이는? 만약 이 둘다 정의해 놓고 호출해 보면 오버라이딩 된 함수가 먼저 호출된다. 즉, OnPaint() 가상함수가 우선순위가 높다는 것이다.

헌데 막상 비주얼 스튜디오에서 더블클릭해서 이벤트 추가를 해보면 오버라이딩을 하지 않고 이벤트 핸들러를 사용하는 것을 알수 있다. 그 이유는 상속을 안받아도 되기 때문. 가상 함수를 오버라이딩 시킬려면 해당 함수를 가진 부모를 상속받아야 하는 불편함이 있다. 그에 비해 이벤트 핸들러는 델리게이트를 이용하므로 타입과 인수목록만 맞으면 자식 부모 관계 없이 호출 가능하다는 장점이 있다. 고로 이벤트 핸들러의 승리!

1. 이벤트의 정의는?
2. 이벤트 핸들러는 리턴값을 가지지 않는데 왜 그럴까?
3. 델리게이트와 이벤트는 어떻게 다른가?
4. 왜 Visual Studio에서 마법사로 함수를 추가하면 가상함수 오버라이딩 대신 이벤트 핸들러를 사용하는가?


'프로그래밍 > C#' 카테고리의 다른 글

Asynchronous programming  (0) 2017.05.31
C# - 제너릭 (Generic) 에 대한 질문  (0) 2010.06.18
Covariance and Contravariance  (0) 2010.06.18
델리게이트 - Delegate  (0) 2010.06.17
posted by 대갈장군
2010. 6. 18. 10:04 프로그래밍/C#
1. 제네릭이나 템플릿을 사용하는 이유? 역효과는?


2. 제네릭 타입 구체화 (Generic Type Instantiation) 이란?


3. 제네릭의 타입을 호출시 정확히 알려줘야 할 경우는 어떤 경우인가?


4. 제약 조건 (where)에서 가장 유용하다고 생각되는 것은 base, 즉, base를 상속받은 클래스이어야 한다는 조건인데 왜 그럴까?


5. 제네릭은 C++의 템플릿과 유사한데 C++과 무엇이 다른가?


6. 일반 컬렉션 (ArrayList)를 사용하게되면 예상되는 문제점은 없는가?


7. 일반 컬렉션 대신 제네릭 컬렉션 (List<T>)를 사용하게되면 왜 더 안전한 코딩인가?


'프로그래밍 > C#' 카테고리의 다른 글

Asynchronous programming  (0) 2017.05.31
C# 이벤트 - 객체 지향적 메시지  (0) 2010.06.25
Covariance and Contravariance  (0) 2010.06.18
델리게이트 - Delegate  (0) 2010.06.17
posted by 대갈장군
2010. 6. 18. 00:43 프로그래밍/C#
델리게이트에 대해서 공부하다 보니 공변성 (Covariance)반공변성 (Contravariance) 에 대한 이야기가 나왔다.

공변성과 반공변성... 뭔 소리인가... 라고 생각한다면 당신은 정상인...

수학 쫌 했다는 분들은 알아들을 수도 있겠다만 나는 처음 이 두 단어를 접하고 언어의 한계성을 느꼈다...

아무튼, 공변성과 반공변성은 사실 같은 원리에 근거한 같은 법칙을 말한다. 여기서 같은 원리란, 부모는 자식을 받아들일수 있으나 자식은 부모를 받아들일수 없다는 원칙이다. 쓰다보니 이런 패륜적인 원리일 줄이야... 헐.

일반적으로 자식 클래스는 부모 클래스로 부터 상속을 받아 여러가지 함수 및 멤버 변수를 추가하게 된다. 자, 그렇다면 자식은 양적으로 부모 보다 더 '많이' 가지고 있다. 반면 부모 클래스는 여러 자식들에게 공통으로 나누어 주어야 하므로 '적게' 가지고 있다.

부모/자식 클래스라는 헷갈리는 말 대신, 많다/적다로 표현하면 훨씬 더 이해가 빠르게 될 것이다. 그렇다면 부모 클래스에 자식 클래스를 대입하면 어떻게 되나? 다른 말로 적은 것 (부모)에 많은 것 (자식)을 넣게되면 자식이 가진 몇몇 함수 및 변수는 잘려 나가겠지만 그래도 잘라내면 그만이므로 문제가 없다.

헌데 반대로 자식에 부모를 대입한다면, 즉, 많은 것 (자식)에게 적은 것 (부모)를 대입한다면 문제가 생기는데 왜냐면 부모는 자식이 가지고 있는 것들을 가지고 있지 않기 때문에 '비어있는 공간'이 생기게 되고 이는 호출 오류를 일으킨다. 왜? 없는 것을 호출하니까...

그래서 부모(적은 것)는 자식(많은 것)을 받아들이지만 그 반대는 성립하지 않는다는 것이다.

함수를 살펴보면 "리턴타입 함수이름(인수)" 이런 형태다. 여기서 리턴 타입과 인수가 바로 '대입'이 일어나는 두 곳이다. 자 여기서 바로 공변성과 반공변성이 등장한다... 

일단 리턴타입의 경우 공변성을 가지는데 다른말로 풀어 쓰자면 위에서 설명한 기본 원리인 '부모는 자식을 받지만 그 반대는 성립하지 않는다'는 원칙을 눈으로 볼때 그대로 따른다는 의미다. 

즉, 선언한 델리게이트의 리턴 타입이 부모 클래스라면 자식 클래스 및 부모 클래스 타입을 리턴 타입으로 가지는 모든 함수를 받아준다는 이야기다. 당연히 그 반대는 안되죠오..

이제 남은 것은 '인수'인데, 여기서 이른바 '
반공변성
반공변성'
이 나온다. 말은 참 어려운것 같지만 한번만 풀어서 생각하면 같은 원리다. 이 반공변성은 위에서 말한 리턴 타입의 경우와 정 반대로 동작한다는 의미에서 붙여진 이름이다.

이것은 언제 '인수'가 대입 되는가를 생각해 보면 이해가 된다. 리턴타입의 경우 델리게이트 객체에 함수를 대입할 때 바로 '대입'이 일어나지만 '인수'는 델리게이트 객체에 함수를 대입할 때가 아니라 그 후에 대입된 함수를 호출 할 때 '대입'이 일어난다. 

고로 리턴 타입과는 달리 인수는 비교해야 하는 시점이 호출할 때이므로 델리게이트 객체에 함수를 대입할 때의 인수가 호출 시점의 인수보다 '부모'이어야 한다. 이것이 코드에서 보았을 때는 공변성과 정반대로 보이게 되므로 이름을 반공변성이라고 붙였다만 사실 알고보면 똑같은 원리에 근거한 것이다.

정리하자면 다음과 같이 말할 수 있다.

리턴타입 델리게이트객체(인수) = 리턴타입 함수객체(
);

위의 공식을 보면 좌측 리턴 타입이 우측 리턴 타입보다 큰데 이것은 좌측 리턴 타입이 우측 리턴 타입을 받아준다는 의미고 마찬가지로 우측 인수가 좌측 인수보다 크므로 받아준다는 의미다. 

이런 공변성과 반공변성을 잘 알고 있으면 델리게이트의 활용에 큰 도움이 될듯..







'프로그래밍 > C#' 카테고리의 다른 글

Asynchronous programming  (0) 2017.05.31
C# 이벤트 - 객체 지향적 메시지  (0) 2010.06.25
C# - 제너릭 (Generic) 에 대한 질문  (0) 2010.06.18
델리게이트 - Delegate  (0) 2010.06.17
posted by 대갈장군
2010. 6. 17. 23:31 프로그래밍/C#
델리게이트는 C#에 등장하는 새로운 함수 포인터다. 델리게이트의 사전적 의미는 '대표자'라는 의미로 명사일때와 동사일때 발음이 차이가 있으며... 아차차, 주제가 이게 아니지. 이놈의 토플 공부 습관은 사라지지가 않는다. -_-

그렇다면 우선 델리게이트 선언 형식을 살펴보자. 간단하다.

액세스지정자 delegate 리턴타입 이름(인수목록);

이것은 C++에서 사용하는 함수 포인터의 선언 형식보다 훨씬 더 가독성이 좋은 편이다. 

일단, C++과 비교해서 C#의 함수 포인터인 델리게이트의 장점 및 차이점을 나열해 보자.

  • C++의 함수 포인터에 비해 타입 체크가 훨씬 더 엄격하므로 잘못된 대입을 미리 막는다.
  • Delegate는 함수 포인터와 다르게 '클래스 타입'이다. 
  • 인수 목록에 반드시 이름이 필요하다.

  • 사실 위 3가지 차이점 중에 2번이 가장 크고 중요한 차이라고 개인적으로 생각한다. delegate가 클래스 타입이라고 했는데 이는 즉, delegate가 클래스를 선언하는데 사용되는 일종의 명시자라는 말이다. 자, 클래스라면 생성자를 사용해서 객체화를 하여 사용해야 한다. 고로 delegate를 사용하기 위해서는 delegate 클래스 타입으로 임의의 객체의 선언 및 생성자를 이용한 객체화가 요구된다.

    추가로 delegate는 엄연히 독립 타입이므로 어디에나 선언 가능하며 클래스 내부에 선언되면 액세스 지정자 (public, private) 를 줄수가 있다. 만약 외부에 전역으로 선언되면 엑세스 지정자는 굳이 필요하지 않다.

    다음과 두 함수를 보면 인수목록이 같고 리턴 타입이 같으므로 delegate를 쓸수 있다.

    public static void MyFunction1(int input) { Console.WriteLine("MyFunction1" + input); }
    public static void MyFunction2(int input) { Console.WriteLine("MyFunction2" + input); }

    이 두 함수를 가리키기 위해 델리게이트 타입의 클래스를 전역으로 선언한다면 다음과 같이 선언 할 수 있다.

    delegate void MyDele(int a);

    그리고 이제 이 선언된 MyDele 클래스의 객체를 객체화 (생성자를 이용한 구현) 를 하기만 하면 포인터 처럼 바로 사용가능하다.

    MyDele mDele;
    mDele = new MyDele(MyFunction1); //또는 mDele = MyFunction1;
    mDele(50);
    mDele = new MyDele(MyFunction2); //또는 mDele = MyFunction2;
    mDele(100);

    다시 한번 강조하지만 delegate는 클래스 타입이므로 delegate를 사용하기 위해서는 우선 delegate 클래스 타입으로 특정한 리턴 타입과 인수 목록을 가지는 클래스(MyDele)를 생성하고 이 클래스의 객체(mDele)를 생성하여 생성자로 가리킬 함수(MyFunction1 또는 MyFunction2)를 대입하면 만들어진 객체가 바로 그 함수가 된다.

    일단 C++ 방법에 비해 복잡해 보이지만 훨씬 더 '
    세련된
    세련된' 방식이다. 참고로 new로 선언한 후 해제하지 않았는데 우리 뒤에는 항상 Garbage Collector 가 알아서 쓰레기를 수거해 가는 것을 잊지 말자. +.+

    함수 포인터나 델리게이트의 장점은 바로 같은 인수로 리턴 타입을 가지는 여러개의 함수를 돌아가면서 가리킬 수 있다는 것인데 이런 장점은 사실 확 와닿지가 않는다. 이것의 장점을 몸으로 느끼기 위해서는 프로그램이 조금 커야 하며 설계 부터 상속을 염두해두고 만들어야 하며 가상함수들이 즐비해야 비로서 '아~ 이래서 델리게이트가 무려 챕터 하나를 차지하는구나...' 하고 느낄것이다.

    마지막으로 C++의 함수 포인터와 다른 큰 차이점으로 델리게이트는 다른 클래스에 속한 메서드를 가리킬 수 있다는 점이다. C++의 함수 포인터의 경우 임의의 클래스에 선언되어 있을 경우 그 해당 클래스 내부에 있는 메서드 만을 가릴킬 수 있었는데 델리게이트는 그것을 극복했다. 

    이것도 사실 '화악~' 마음에 와 닿지는 않지만 C++의 함수 포인터보다는 훠~얼씬 더 좋다는 것 정도는 알겠다. 

    정리하자면 C++의 함수 포인터는 단순한 변수였다. 말그대로 주소를 저장하는 포인터 였으나 C#에서 등장한 델리게이트라는 놈은 함수 포인터보다 훨씬더 가독성이 좋으며 독립적인 클래스 타입으로써 객체를 생성하여 사용한다는 점이 큰 차이다... 암튼, 당연히 델리게이트가 함수 포인터 보다는 좋다는 이야기지...

    더 많은 이야기를 하고 싶지만 코드가 들어가면 이야기가 너무 길어질 것 같아서 오늘은 여기까지만... 다음엔 공변성과 반공변성에 대해서 토크를 해야 할 듯...



    '프로그래밍 > C#' 카테고리의 다른 글

    Asynchronous programming  (0) 2017.05.31
    C# 이벤트 - 객체 지향적 메시지  (0) 2010.06.25
    C# - 제너릭 (Generic) 에 대한 질문  (0) 2010.06.18
    Covariance and Contravariance  (0) 2010.06.18
    posted by 대갈장군
    2010. 5. 15. 04:20 OpenGL
    OpenGL에서 작게는 10만개에서 많게는 100만개 정도 까지의 점을 그려야 하는 일이 있었다. 각각의 점들은 UTM 좌표들인데 이 UTM은 타원인 지구를 2차원 평면을 평평하게 편 다음 각 구역을 나누어 기준점으로부터의 거리를 미터로 나타낸다.

    그래서 UTM은 좌표의 구성이 Easting, Northing, Altitude 요렇게 세가지 축으로 구성된다. 뭐, 걍 X,Y,Z라고 보면 된다. 

    이 UTM 좌표 값은 미터로 표시되기 때문에 그 값이 무지 크다. 내가 스캔한 영역의 점들은 기본적으로 Easting 값이 710,000 이상이었으니까 무지하게 큰 값이다.

    이론에 따라 이 UTM 좌표를 XYZ로 생각하고 각 점들을 그려내 보았는데 이상하게도 몇개의 점들이 서로 서로 모여서 Z 방향으로 라인을 만드는 것이었다.

    다른 형태로 출력한 같은 데이터를 QT modeler라는 프로그램을 이용해서 보면 훨씬 조밀조밀하게 나오는데 이상하게 OpenGL로 렌더링 한 점들은 모두가 띄엄띄엄 한 것이었다.

    아무리 옵션을 바꿔봐도 문제는 고쳐지지 않았고 데이터 스트럭쳐 확인 및 브레이크 포인트를 이용해 각각의 요소를 확인해 보았지만 어디에도 타입 변경에 따른 문제는 없었다.

    고민고민하다가 생각한 것이 너무 큰 정수부분을 없애자는 것이었다. 임의의 인접한 두점은 대략 0.001 정도의 간격을 가지는데 정수부분의 70만 인것을 감안하면 소수 부분은 턱없이 적은 차이라는 점이 맘에 걸렸었다.

    그래서 모든 점들을 다 읽어들인후 중간값을 찾아내 그 정수값을 모두 빼내었다. 그리고 나서 다시 똑같은 방법으로 렌더링을 했더니 이제서야 모든 점들이 제대로 분포하는 것이었다.

    그렇게 고치고 나서 생각해보니 한 3년전에 들었던 수치 분석 수업에서 제한된 표현크기를 가지는 컴퓨터의 타입들(예를 들자면 integer, double 같은 것)을 사용할때는 아주가까운 두값이 연산에서 수많은 에러가 발생가능하다는 교수님의 말이 스쳐 지나갔다.

    아직 왜 인지는 정확히 모르겠으나 분명히 큰 정수부분의 표현을 위해 제한된 비트를 소모해 버리느라 소수점 이하 영역의 차이를 제대로 계산해 내지 못한 것은 분명하다.

    아무튼 이번 경험은 소중한 헤딩이었다. ㅋㅋㅋ
    posted by 대갈장군
    prev 1 2 3 4 5 6 next