티스토리 툴바

블로그 이미지
대갈장군

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

2012/01/25 13: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를 손쉽게 만들수 있겠다. 아직 코드가 정리가 안되었는데 조만간 정리 좀 해서 올려야 겠다. 간단하게 바꿔서... 
저작자 표시 비영리
크리에이티브 커먼즈 라이선스
Creative Commons License
posted by 대갈장군
2011/05/23 13:49 분류없음
그는 분명 다른 가수들처럼 입으로 소리를 내고 노래를 하지만 이상하게도 그의 노래는 가슴이 듣는다.

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

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

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

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

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

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

쾌유하시길 기원합니다.








저작자 표시 비영리
크리에이티브 커먼즈 라이선스
Creative Commons License
posted by 대갈장군
2011/05/03 11:13 분류없음
일주일에 보는 프로그램이 몇 개 안되는데 그중 하나가 위대한 탄생이다.

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

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


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

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


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

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

기분 나빴다.


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

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

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

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

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


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

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

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















 
저작자 표시 비영리
크리에이티브 커먼즈 라이선스
Creative Commons License
posted by 대갈장군
2011/03/21 13:10 분류없음
청중 500명이 뽑은 7등이 재도전의 기회를 받는다...

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

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

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

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

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

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

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

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

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

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

 
저작자 표시 비영리
크리에이티브 커먼즈 라이선스
Creative Commons License
posted by 대갈장군
2011/03/11 14: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로 분류되고 있다.  

이렇게 펼쳐놓고 보니 메모리 별거 아니다... ㅡ.ㅡ 라고 믿고 싶다. 일단 메모리의 구조에 대한 이해가 끝났다면 다음으로 고고고~
저작자 표시 비영리
크리에이티브 커먼즈 라이선스
Creative Commons License
posted by 대갈장군
2011/03/11 14: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++ 책에서는 제시하고 있다. 작은 데이터 구조를 여러개 관리하는 프로그램에서는 매우 효과적인 클래스 관리법이 될 수 있을 것 같다.

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


저작자 표시 비영리
크리에이티브 커먼즈 라이선스
Creative Commons License
posted by 대갈장군
2011/03/11 09: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은 메모리 낭비가 너무 심히다.



저작자 표시 비영리
크리에이티브 커먼즈 라이선스
Creative Commons License
posted by 대갈장군
2011/03/10 13: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 처럼 바이트 단위로 할당을 하지 않는다. 경제적인 것은 그만큼 손이 많이 간다. 고로 해제하는 과정을 사용자가 반드시 꼼꼼히 해야 한다는 불편함이 있다. 


저작자 표시 비영리
크리에이티브 커먼즈 라이선스
Creative Commons License
posted by 대갈장군
2011/03/09 15:56 프로그래밍/Windows API
보다 나은 개발을 위해서는 윈도우 메모리에 대해서 전반적인 이해가 필요로 된다. 내가 김상형님의 책을 좋아하는 이유는 김상형님은 책을 쓰실때 항상 '왜' 라는 질문에 대답을 해주시기 때문이다.

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

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

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

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

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

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

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

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

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

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


저작자 표시 비영리
크리에이티브 커먼즈 라이선스
Creative Commons License
posted by 대갈장군
2011/03/08 11: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.

- 바로 거기 어딘가.

 

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

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

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

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

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

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




저작자 표시 비영리
크리에이티브 커먼즈 라이선스
Creative Commons License

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

영화 인셉션에 나오는 명대사  (2) 2011/03/08
진실 = 변하지 않는 마음  (0) 2010/02/22
Alaska  (0) 2010/02/17
기도  (2) 2010/02/02
'무한도전' 논란에 대한 내 생각  (1) 2009/11/23
노무현 대통령 타살 의혹  (0) 2009/05/26
posted by 대갈장군
prev 1 2 3 4 5 ... 13 next