'Category'에 해당되는 글 88건

  1. 2010.04.03 CMainFrame과 CView의 이벤트 드리븐 차이점 1
  2. 2010.04.03 포인터 문법에 관한 고찰
  3. 2010.04.03 다이얼로그 타이틀바의 클릭 & 드래그하여 이동하는 기능을 막는 방법
  4. 2010.04.03 개발자의 使命
  5. 2010.04.03 동적 배열을 다른 클래스에서 사용하려면..?
  6. 2010.04.03 INSTANCE와 HANDLE의 차이점
  7. 2010.04.02 기본적인 쓰레드의 생성
  8. 2010.04.02 메모리 레이아웃과 릭(Leak)
  9. 2010.04.02 쓰레드의 임계 영역(Critical Section)에서 발생할 수 있는 이슈
  10. 2010.04.02 NULL 포인터에 대한 접근..
2010. 4. 3. 12:06

CMainFrame과 CView의 이벤트 드리븐 차이점

다음의 예제를 살펴 봅시다.

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

LRESULT CMainFrame::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{

    switch( message )
    {

    case WM_LBUTTONDOWN:

    // 실행창 아무 곳에서 마우스 왼쪽 버튼을 눌러도 아무 반응이 없다.
    // 메시지 박스가 뜨지 않는다. 에러도 없다.
    MessageBox(_T("LRESULT CMainFrame::WindowProc(msg, wParam, lParam)"),

                   _T("SUCCEEDED"), MB_OK);        
 
         break;


    }
}

 

LRESULT CmyView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)

{

    // TODO: Add your specialized code here and/or call the base class
    switch( message )
    {

    case WM_LBUTTONDOWN:
 

        // 이것은 에러없이 메시지 박스가 잘 뜬다.
        MessageBox(_T("void CmyView::OnLButtonDown(UINT nFlags, CPoint point)"),

               _T("SUCCEEDED"), MB_OK);


        break;
    }
 

    return CView::WindowProc(message, wParam, lParam);

}


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

똑같은 함수의 재정의인데 CMainFrame::WindowProc( ... )에서는 안되고

CmyView::WindowProc( ... )에서는 가능한 이유는 무엇일까요? 이것은 당연합니다.

CMainFrame은 오직 Non-Client 영역에 대한 메시지만 받을 수 있습니다.

Non-Client 영역이라고 함은 캡션바(타이틀)과 메뉴바에 대한 영역의 메시지만 받을 수 있습니다. 

그러니까 CMainFrame에서는 WM_LBUTTONDOWN이 아니라, WM_NCLBUTTONDOWN이라고 해야 캡

션바나 메뉴바를
클릭했을 때 이벤트를 받을 수 있습니다. 클라이언트 영역을 아무리 클릭해봐야 실제로

CMainFrame에서
클라이언트 영역(작업 영역 : 흰색 윈도우 부분)을 포함하지만, 자신의 영역이 CMyView
 
클래스 윈도우 영역이
가려져 있기 때문에 이벤트가 발생하지 않는 겁니다. Z-ORDER가 CMyView 클래스

윈도우 영역이 더 높기 때문
입니다. 그러니까 쉽게 말해서 CMainFrame의 클라이언트 영역이 존재하지만,

그 위에 CMyView 윈도우 영역으로
덮혀져 있기 때문에 메시지를 받을 수 없는 겁니다. 반면에 CMyView

클래스 윈도우 영역에서는 Non-Client 영역
에 대한 메시지를 받을 수 없지요. 왜냐하면 그 크기가 흰색 영

역만을 포함하기 때문입니다.
 
 
Written By Sim-Hyeon, Choe



 

2010. 4. 3. 11:55

포인터 문법에 관한 고찰

아래의 문법은 컴파일하여도 전혀 문제가 되지 않습니다.

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

int array[10];

int *ptr = array;

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

2번째 라인을 int *ptr = &array;로 바꾸어 컴파일하면 에러가 발생합니다.

array는 포인터 상수로, &array와 같은 주소값을 가짐에도 불구하고 말입니다.


이유가 무엇일까요?

아주 예전에 제가 위의 문제로 고민을 한적이 있었습니다. 하하.

우리가 생각한대로 int *ptr = &array가 되어야 합리적으로 보여질 수 있습니다.

하지만 다음과 같은 에러를 출력하는 것을 보실 수 있습니다.

'initializing' : cannot convert from 'int (*)[10]' to 'int *'

참 이상하죠? 배열명 array 자체는 주소값을 의미하고 거기에 단지 &연산을 했을 뿐인데 말입니다.

독자는 분명히 array == &array 라는 사실을 의심치 않을 겁니다.

왜냐하면, printf( "%x", array  ); 와

            printf( "%x", &array ); 이 같고,

            printf( "%d", sizeof(array)  ); 와

            printf( "%d", sizeof(&array) ); 가 같은데 말이죠.

 즉, 대상체가 의미하는 주소값과 대상체의 크기는 모두 동일하다는 것입니다.

그럼 다시 위의 에러를 살펴 봅시다.

[배열 포인터]형을 [포인터]형으로 변환이 불가능하다는 의미가 될 겁니다.

그래서 다시, int (*ptr)[10] = &array; 해서 컴파일 해 보았습니다.

에러없이 컴파일이 잘 되는군요. 하지만 상식적으로 저 역시 이해가 되질 않습니다.

여전히 의문을 가질 수 밖에 없었습니다. 그렇다면 좀 더 시각을 달리해보는건 어떨까

합니다. 배열명 array는 우리가 알고 있는대로 [포인터 상수]입니다.

배열명 자체가 주소값을 의미합니다. 그렇다면 상수에 &연산이 가능하다는 것조차도

논리적으로 좀 이상하지  않겠습니까?

&연산은 변수의 선두 번지를 구하는 연산이라는 것을 독자들도 잘 아실 겁니다.

그렇지만 &연산은 가능합니다. 왜냐하면, printf( "%d", &array );가 무리없이 컴파일되니까

말입니다. 저는 사실 이것도 뭔가 찝찝하다고 생각하였는데 독자가 동의하실지 모르겠습니다.

상수에 &연산을 허용한다는 것은 결국 &(&array)도 허용해야 하지 않겠습니까?

하지만 이것은 컴파일 에러를 출력합니다. '&' requires l-value로 말이죠.

(VC++로 컴파일하였을 때 기준)

하지만 다른 여러 컴파일러에서는 int *ptr = &array;가 허용되는 것으로 알고 있습니다.
(리눅스의 gcc가 허용하는 것으로 알고 있습니다)

우리들이 의도하신대로 말이죠. 제가 말하고 싶은 결론은 컴파일러의 Dependent한 문제라고

볼 수 밖에 없습니다. 항상 우리가 생각하는 논리와 의도대로 모든 문법이 구성이 되어있지 않

습니다. 저 역시 이 부분은 개인적으로 굉장히 아쉬운 부분으로 생각하고 있습니다.

결국은 컴파일러 문제죠. 여기까지는 컴파일러가 정한 문법 규칙이라고 밖에 볼 수 없습니다.

제가 그렇게 확신하는 이유가 다음의 Disassembling 코드때문입니다.

 

9:        printf( "%d\n", array  );

0040D76B   lea           eax,[ebp-28h]
0040D76E   push        eax
0040D76F   push        offset string "%d" (0042201c)
0040D774   call          printf (0040d6c0)
0040D779   add          esp,8 

10:       printf( "%d\n", &array );

0040D77C   lea          ecx,[ebp-28h]
0040D77F   push        ecx
0040D780   push        offset string "%d" (0042201c)
0040D785   call          printf (0040d6c0)
0040D78A   add         esp,8

 
같은 Addressing Mode로 연산을 한다는 사실을 위의 코드로 증명하였습니다.

( [확장자 .c]로 하여 int *ptr = &array; 컴파일하면 에러는 나지 않고 경고만 출력하더군요. )

컴파일러에 의해서 어쩔수 없는 부분은 그려려니 인정하고 이해보다는 암기하여

사용할 수 밖에 없다는 것이라고 생각합니다.

Written By Sim-Hyeon, Choe

2010. 4. 3. 11:08

다이얼로그 타이틀바의 클릭 & 드래그하여 이동하는 기능을 막는 방법


두개의 NON CLIENT MESSAGE를 함께 처리해야 하기 때문에 WindowProc 오버라이딩합니다.

[CLASS WIZARD] -> [Class Info] TAB -> 메시지 필터 (Chlid Window)로 선택하면

WindowProc 프로시저를 오버라이딩이 가능합니다.

 

LRESULT CCantMoveDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{

    static bFlag = FALSE; 

    switch( message )
 
  {

    case WM_NCLBUTTONDOWN:    // 넌 클라이언트 영역(캡션바)을 클릭했다면

        bFlag = TRUE;

 

    case WM_NCMOUSEMOVE:       // 넌 클라이언트 영역을 클릭하고 있는 상태에서 이동한다면

        if( bFlag )
       
{

            bFlag = FALSE;

 

            return TRUE;                  // 디폴트 윈도우 프로시저로 넘기지 않고 정상적인 처리로 리턴
       
}

        return 0;

    } 

    return CDialog::WindowProc(message, wParam, lParam);

}


Written By Sim-Hyeon, Choe

2010. 4. 3. 06:35

개발자의 使命


  어제 회사 선배들과 함께 술자리를 가졌다. 제법 취한지라 집에 도착하자마자 잠이 들었고 어쩌다가 새벽

에 일어났다. 잠을 다시 청하자니 이미 각성한 상태인지라 글이나 쓰고자 블로그에 접속하였다. 방금전까지

데브피아에서 이런 저런 글들을 읽어 보았는데, 문득 개발자라면 어떠한 사명을 가지고 있어야 하는가에 대

한 고민을 하고 있었다. 두말할 나위도 없이 자신에게 주어진 개발 업무, 프로젝트에 집중하는 것은 당연하

겠지만, 보다 개발자로서 자긍심과 가치를 느낄 수 있는 또 다른 노력이 무엇이 있을까하고 말이다. 나는 여

지껏 프로그래밍 공부를 하면서 혼자 깨닫고 터득하는 데에 깊은 몰입을 해왔던 것 같다. 혼자서 여러 서적

을 읽어가며 탐구하고 개발 관련 정보나 글을 읽는 것에 심취해 있다보면 나에겐 그 무엇보다 즐거운 일이었

으니 말이다. 하지만 그것이 오직 나만 즐거운 것이었고 또 나만을 위한 것임을 깨달았다. 지나온 발자취를

다시 되돌아보면, 그 무엇도 남아 있었던 게 없었다. 깨달음과 터득은 매 그 순간 뿐이고 그것도 나 혼자에만
 
국한된, 철저히 폐쇄적인 행위일 뿐이었다. 이젠 마인드를 달리하여 나뿐만 아니라, 나와 같은 개발자 혹은

그것이 꿈인 사람들과 함께 공유하길 원한다. 개발 관련 지식이든 정보든 희노애락 모든 것들을 공유하면서

말이다. 개발자로서 함께 공유하는 마인드를 가지고 그것에 충실히 한다면, 보다 자긍심과 가치를 느끼고

진정 필요한 사명이라고 생각한다.

Written By Sim-Hyeon, Choe
2010. 4. 3. 04:46

동적 배열을 다른 클래스에서 사용하려면..?


다음 예제를 살펴 봅시다.
-------------------------------------------------------------

class AAA
 

AAA::TestA()
{
    // 문자열 2차원 배열 선언
    char(* strResult)[15] = new char[500][15];
}

 

class BBB


#include "AAA.h"

AAA testa;  -> testa class 변수 선언

BBB::TestB()

{

    while( ... )
    {
        printf( "%s", testa.strResult[i] );  // 에러
    }

}

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

위의 예제에서 처럼 클래스 AAA에 선언된 배열 strResult를 어떻게 가져올 수 있을까요?

class AAA에서 다음과 같은 접근 제어 함수를 만들 수 있습니다.
 

"AAA.h" 

class AAA
{

public:

    char (*GetStrResult())[15];    // 2차원 배열 포인터를 리턴

}

"AAA.cpp"

// 클래스 AAA의 멤버인 2차원 배열 포인터를 아래와 같이 리턴할 수 있습니다.
char (*AAA::GetStrResult())[15]
{

    char (*strResult)[15] = new char[500][15]; 

    return strResult;

}

 

"BBB.cpp"

BBB::TestB()
{

    char (*ptr)[15] = NULL;

    while( ... )
    {

        ptr = testa.GetStrResult();


        printf( "%s", ptr + x );    // x는 출력할 문자열의 인덱스

    }

}

Written By Sim-Hyeon, Choe

2010. 4. 3. 04:23

INSTANCE와 HANDLE의 차이점


인스턴스(INSTANCE)라는 개념은 어떤 대상이나 단위의 구체적인 그 실체를 의미합니다. 특정한

어떤 대상에만 국한되는 개념이 아니라, 범용적인
개념으로 통용됩니다. 간단하게 예를 들어.. int a;와
 
같은 선언이  있다면, a는 int형의 인스턴스라고 할 수 있습니다.
int는 단순히 그 자료형의 길이를 표현

할 뿐, 그 자체로는 아무것도 나타낼
수 없죠. int a;라고해야 비로소 그 int형의 실체인 a가 생성되는 것

처럼 말입니다.
쉽게 이야기하면 int 자체는 하나의 틀입니다. 붕어빵 틀을 생각해 봅시다. 그 틀로 여러

개의 붕어빵을 만들 수가 있지요?
int a, b, c;처럼 말입니다. 틀은 하나뿐이지만, 그 틀로 만들 수 있는

실체는 복수입니다. a나 b, c 모두 int의 인스턴스지만 서로 구분되지 않습니까.
같은 틀을 사용했지만

그 실체는 구분하기 위한 개념이라고 할 수 있습니다.
Win32 API에서 통상적으로 불리는 인스턴스는 프

로세스(실행중인 프로그램)을
구분하기 위한 개념이 되지요. 하드디스크에 깔려있는 프로그램은 하나뿐

이지만,
그 프로그램의 실체 프로세스는 여러개가 될 수 있지요. 위에서 말한 것처럼 그 프로그램의 실체

를 구분해주기 위한 단위입니다.

핸들(HANDLE)운영체제에 의해 생성된 리소스나 오브젝트를 제어하기 위한 32bit의 정수값입니다.

자동차의 핸들을 생각해 봅시다. 우리는 자동차의 방향을 움직이기 위해서 복잡한 구조를 알아야 하거나 다

룰 필요가 없죠. 단지 핸들만 있으면 방향을 제어할 수 있는 것처럼 말입니다.
HINSTANCE hInstance는

그 프로세스(실행중인 프로그램)의 핸들값입니다.
프로세스 하나는 여러개의 윈도우를 가질 수 있죠.

그 윈도우들을 구분하고 제어하기 위한 값이 핸들(HWND hWnd)입니다.

Written By Sim-Hyeon, Choe

2010. 4. 2. 17:04

기본적인 쓰레드의 생성

아주 간단합니다. CreateThread 함수의 파라미터는 MSDN을 참고하시면 되겠죠.

// 쓰레드의 엔트리가 될 함수의 선언
DWORD WINAPI ThreadProc( void *argument ); 

HANDLE hThread = NULL;

// 메인에서
hThread = CreateThread( NULL, 0, ThreadProc, NULL(or needs Argument), 0, NULL );

if( hThread == NULL )
{
    printf( "CreateThread function failed!" );
}

CloseHandle( hThread );

 
DWORD WINAPI ThreadProc( void *argument )
{

    // argument를 캐스팅하여 사용. 

    while( TRUE )
    {
        // 처리해야 할 루틴 삽입
    }

    return 0;
}

Written By Sim-Hyeon, Choe
2010. 4. 2. 16:39

메모리 레이아웃과 릭(Leak)


  프로그램이 실행되면 로더에 의해 메모리에 적재되는데, 이때 프로그램이 적재되는 공간이 바로 프로세스 영역(VMM 관리자에 의해서 물리 메모리의 일부와 하드 디스크의 메모리 맵 파일 혹은 페이지 파일을 포함하는 개념의 가상 메모리로 맵핑)입니다. 프로세스 영역에 적재된 어플리케이션을 관리하기 위해서 커널은 해당 어플리케이션의 프로세스를 생성시켜 주게되고 프로세스의 커널 오브젝트에 의해 해당 프로세스는 관리되게 됩니다.

  프로세스 영역의 크기는 1.99GB(2000을 기준함. 운영체제마다 LIMIT는 다름)로 스택, 코드, 힙 영역 등이 프로세스 영역내에서 할당됩니다. 그리고 가상 메모리 공간이나 힙 메모리 할당도 모두 프로세스 영역에서 이루어집니다. 프로세스가 종료되면 커널은 프로세스 영역을 파괴하고 반환(프로세스 오브젝트 파괴)하죠. 그렇다면 당연히 프로세스 영역내에 존재하는 스택, 코드, 힙 영역 등은 모두 마찬가지로 파괴됩니다. 이것은 유저 프로그램에서 커널이 관리하고 있는 커널 레벨의 메모리 영역에 대한 누수는 있을 수가 없음을 의미합니다. 물론, 커널 레벨에서 작동하는 디바이스 드라이버라면 얘기가 달라집니다. 메모리 레이아웃을 제대로 이해하기 위해서는 x86의 어드레싱, 가상메모리, 페이징 메커니즘을 이해하고 있어야 합니다. DRAM CHIP 자체는 정적인 한계를 가집니다. 메모리 레이아웃은 스택, 힙, 데이터 영역뿐만 아니라, 이외에도 NULL값 할당 영역, Off Limit 영역, 커널 영역과 같은 메모리 영역들이 존재(메모리 레이아웃은 벤더에 의존적인 부분이기 때문에 운영체제마다 다름)하고 있으며 일반적으로 프로그래머가 알고 있는 스택과 힙, 데이터 영역은 프로세스 영역의 아주 작은 공간으로 사용되고 있겠지요. 하하. 그리고 제가 위에서 언급하였던 모든 메모리 영역은 "4GB의 가상 메모리 영역"에 맵핑되어 있습니다. 우리 사용자가 말하는 기본적인 동적 메모리 할당의 위치는 "프로세스 영역내의 힙 영역에 대한 메모리 할당"입니다. 힙 영역에 할당한다는 의미는 프로세스 영역내에 존재하는 임의의 가상 메모리 일부분을 커널은 관리할 수 있도록 힙 오브젝트를 생성시켜 주게 되는 것을 의미하 여기서 힙 오브젝트는 할당된 메모리 영역에 대한 부가적인 정보를 가지게 됩니다. 예를 들어, 할당된 영역의 크기와 주소 위치 또 할당된 영역이 가지고 있는 데이터를 의미하는데 중요한 것은 할당된 메모리에 대한 안전성의 확보라는 것입니다. 그러니까 설사 또다른 메모리 할당 명령이 내려지더라도 이미 힙 오브젝트에 의해서 관리하고 있는 메모리 영역은 침범할 수가 없다는 의미입니다. (결국엔 "할당되지 않은 영역"도 사용은 가능하겠죠. 하지만 커널이 생성한 힙 오브젝트에 의해 관리되어지는 대상이 아니기 때문에 쓰기(write)할 때에는 이미 할당된 다른 메모리에 접근하여 Access Violation을 일으킬 수 있는 가능성을 가지고 있으며, 또한 다른 메모리 사용으로 그 영역이 침범당할 우려가 있습니다.)

  다시 한번 정리해서 유저 프로그램단에서 설명을 드리자면, new와 같은 키워드로 할당한 메모리 공간은 실행된 프로세스 영역 내의 힙 영역입니다. 우리가 사용하는 물리적인 메모리의 커널 영역을 의미하지는 않습니다. 힙 영역에 해제를 해주지 않고 계속해서 할당하게 되면 Heap Overflow가 발생하게 되는데 즉, 이것이 메모리의 릭(Leak)이죠. 하지만 어디까지나 그것은 프로세스가 실행중에 해당 로세스 영역을 계속 사용할 때를 의미합니다. 유저 프로그램이 종료될 때는 운영체제의 메모리 관리자에 의해서 모두 해제가 됩니다. 실시간 실행되어야하는 감시 어플리케이션이라든지 서버와 같은 프로그램을 개발할 때, 특히 메모리 릭을 주의해야겠죠.

Wrttien By Sim-Hyeon, Choe
2010. 4. 2. 16:16

쓰레드의 임계 영역(Critical Section)에서 발생할 수 있는 이슈

Windows 운영체제에서 일반적으로 쓰레드가 프로그램의 기본 실행 단위입니다.

쓰레드가 프로그램의 기본 실행 단위라는 말은 모든 프로세스는 쓰레드를 가지고 있음을 의미합니다. 

프로그램의 실행 과정을 간략히 살펴보면 실행된 프로그램은 로더의 의해서 메모리에 적재되고, 커널이

실행된 프로그램을 상주시키기 위한 하나의 프로세스를 생성시켜 주게 됩니다. 프로세스가 생성후, 메인
 
쓰레드를 하나 생성하고 그 메인 쓰레드가 main() 함수(Entry Point)를 실행하게 되는 것입니다.

(여기서 프로세스와 쓰레드는 커널에 의해 생성되고 커널 오브젝트에 의해 관리되어 집니다)

다시 한번 더 정리하자면,

기본적으로 모든 프로세스는 최소한의 메인 쓰레드를 가지고 있다는 뜻입니다. 

쓰레드는 생성시, 독립적인 메모리 공간(Stack 영역)을 가지며 프로세스 내의 나머지 외부 영역

(Global)은 (다른 쓰레드와) 공유하게 됩니다. 외부 영역을 공유하게 됨으로써, 쓰레드간의 편리하게

통신할 수 있다는 점이 장점이 될 수도 있습니다. 하지만, 공유된 메모리 영역을 두 개 이상의 쓰레드가

접근할 경우에 문제가 될 수 있다는 것이 바로 문제의 핵심입니다.

이렇게 두 개 이상의 쓰레드에 의해서 같은 공유 메모리 영역에 접근하면서 생길 수 있는 문제 구역 즉,

다시 한번더 정리하자면 두 개 이상의 쓰레드에 의해서 접근되면 안되는 메모리 영역을 두고 임계 영역

(Critical Section)이라고 합니다. 프로세스 내의 쓰레드들은 동시에 실행되는 것이 아니라 일정한

CPU Time을 조금씩 조금씩 나눠서 할당받기 때문에 실제 활성화(Activation)되는 쓰레드는 하나뿐입

니다. 하지만, 해당 영역이 문제가 되는 것은 확실합니다.

문제가 되는 이유를 아주 간단한 예를 들어, 설명해 드리겠습니다.

우선 A, B 두 개의 쓰레드가 있으며 두 개의 쓰레드가 전역으로 선언된 변수를

같이 사용하고 있다고 가정하겠습니다.

 

int shared_var = 0;     // UnInitialized Data Segment - BSS 영역

DWORD WINAPI Thread_A(void *arg)
{
    shared_val = shared_val + 5;

DWORD WINAPI Thread_B(void *arg)
{
    printf( "%d \n", shared_val );
}

 

위의 예제를 보시면 아시겠지만, A 쓰레드는 전역 변수 shared_val을 5씩 증가시켜 주는 역할을 하며,

B 쓰레드는 전역 변수 shared_val을 연속적으로 출력해주는 역할을 하는 쓰레드입니다.

(이 프로그램의 의도는 연속적으로 계속 5씩 증가하는 결과를 보여주는 프로그램입니다)

여기서 반드시 정확하게 이해하고 있어야 하는 것은 연산되어지는 과정입니다.

shared_val = shared_val + 5;를 연산하기 위해서 우선 r-value의 첫번째 shared_val 을 임시 메모리
 
공간에 할당하게 됩니다. 그리고 5를 다른 임시 메모리 공간에 할당하게 됩니다. 할당된 두 임시 메모리

공간에서 각각의 값을 읽어와 연산을 하고 연산된 결과 값을 리턴하여 l-value의 shared_val에 저장하

게 됩니다.

제가 위에서도 말씀드렸지만, 쓰레드는 분명 Cpu Time을 할당받는다고 하였습니다. A 쓰레드가 어떤

작업을 수행하고 있는데 CPU Time을 초과하게 되면 B 쓰레드에게 CPU Time이 할당되게 됩니다.

그러니까 A 쓰레드가 수행중인 작업을 끝내든 못 끝내든 상관없이 말입니다.

문제는 이겁니다. 만약에 아까 코드에서 A 쓰레드가 shared + 5를 연산하기 위해 임시 메모리에 할당한

후, 각각 값을 메모리에 저장한 상태에서 cpu time이 끝난다면 어떻게 될까요? 연산까지는 하지 못한

상태에서 말이죠. (※ cpu time은 각 쓰레드에게 주어지는 시간은 분명 일정하지만 프로세스도 마찬가

지로 cpu time을 할당받는데 프로 세스가 받는 cpu time은 사용자나 외부 환경에 의해서 일정치가 않

습니다)

일단, 임시 메모리에 저장한 값은 다른 임시 메모리 영역으로 옮겨 놓고, B 쓰레드를 호출하게 되는데,

B 쓰레드가 하는 역할은 전역 변수 shared_val의 값을 출력하는 것입니다. 바로 이곳이 문제가 되는 부

분입니다. 전역 변수 shared_val의 값은 A 쓰레드가 연산하기 직전에 cpu time을 B 쓰레드에게 넘겨

주었는데, B 쓰레드는 A 쓰레드가 한 작업에 상관없이 무조건 출력을 하게 되는데, 결과값은 증가되지

않는 값을 출력하는 것이 문제점입니다.

사실, 지금과 같은 예제로 인한 Critical Section 접근 문제는 거의 희박합니다. 하지만, B 쓰레드가 단

순한 출력이 아니라, A 쓰레드보다 더욱 복잡한 연산을 수행하며, 두 개의 쓰레드가 아닌 여러개의 쓰레

드가 접근할 때는 문제가 심각해 질 수 있다는 것을 말씀드리고 싶었습니다. 실제 임계 영역을 해결해야

하는 문제는 프로그래밍 중에 꾀나 빈번히 발생합니다.

어디까지나 제가 보여드린 예제는 모의적인 코드에 불과하며, 충분히 문제가 불거질 수가 있다는 것을

이해하시면 됩니다. Critical Section Problem을 해결하기 위해서는 Critical Section을 공유하고 있는
 
쓰레드에 대해서 동기화(Synchronization)를 해야 합니다. 동기화 기법은 크게 User Mode

(CRITICAL_SECTION OBJECT)와 Kernel Mode(Mutex, Semaphore, Event 등)에서의 동기화

기법이 있는데, 운영체제 관련 자료를 참고하시기 바랍니다.

Written By Sim-Hyeon, Choe

2010. 4. 2. 15:18

NULL 포인터에 대한 접근..


*((char *)NULL) = 'A';

보통 초급자분들이 코딩을 하다보면 NULL 포인터를 참조하여 접근하는 경우를 흔히 볼 수 있습니다.

실수도 실수지만 호기심으로 위와 같은 문법을 작성하고 실행시킨 후에 참조 에러가 나는 원인을 궁금해

하는 분들이 많았습니다.


char *temp = NULL;

NULL 포인터는 메모리의 어느곳도 가리키지 않는 포인터를 의미합니다.

NULL 포인터를 Debugging해보면 0x00000000를 가리킨다는 사실을 확인하실 수 있는데,

그렇다고 하여도 널 포인터는 어느곳도 가리키지 않는 포인터를 의미합니다.

어느곳도 가리키지 않는곳에 문자 상수 'A'를 대입하려고 했으니 문제가 발생하겠지요.

윈도우즈 운영체제에서 유저가 NULL 포인터 영역의 접근을 제한하고 있는 것이 보다 정확한

의미가 되겠습니다.

보통 NULL 포인터의 용도는 리소스 할당 실패를 검출하거나, 일종의 포인터 초기화, 

메모리 할당 실패 정도로 말씀드릴 수 있겠습니다.

ex1) if( NULL == fopen(..., ...) )

ex2) struct DATA *link;

        link->next = NULL;

ex3) char *string = (char *)malloc( sizeof(20) * char );

       if( string == NULL )
       {
            printf( "Memory allocation error" );
       }


Written By Sim-Hyeon, Choe