블로그 이미지
대갈장군

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

Notice

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 대갈장군
prev 1 2 3 4 5 6 7 ··· 16 next