일반적으로 컴퓨터는 Stack영역, Data영역과 Code영역의 메모리를 가진다. 
이중 Stack영역의 구조에 대해서 한번 살펴보자.
위 소스의 실행 결과 사진이다.

위 소스의 실행 결과는 다음의 사진과 같다. 
[주의 점 : stack 메모리의 구조가 gcc에서는 보안상의 이유로 랜덤하게 나오므로
              가능하면 Visual Studio6.0으로 테스트 해 보기 바란다.]


 스택이라는 공간은 push를 통해서 값을 집어넣고 pop을 통해서 값을 빼 내는 구조이다(FILO,First In, Last Out). 
이러한 구조적 특성을 이용하여 다양하게 사용을 하는데

우선 이 표를 보게 되면
1) 각 변수가 스택의 높은 주소값으로 부터 낮은 주소값으로 값이 들어감 (스택 공간의 주소 할당 방식이므로 태클을 걸 수가 없다.)
2) 각 변수들의 저장 공간의 크기는 int 형이므로  4byte의 크기를 가짐
3) 각 함수들 사이에 8바이트의 빈 공간이 생김
정도를 찾아 볼 수 있다.

이중에 8byte의 빈 공간이 있는 걸 볼 수 있는데 이 공간은 컨텍스트 스위칭(Context Switching)을 위한 공간으로 실행되던 이전 함수의 베이스 포인터(Base Pointer;BP)와 함수의 주소(Instruction Pointer;IP)를 저장하는 공간이다.

그럼 이제 저 빈칸들 채워 보자.


음, 여기서 Base Pointer와 Instruction Pointer에 대해서 집고 넘어가야하겠다.
BP와 IP는 운영체제와 관계가 깊고 또한 어셈블리언어를 다루어 보셨다면 알고 계실 것이다. 
잠깐 여기서 어셈블리 레벨에서의 동작을 설명해 보자.
C같은 경우에는 무조건 main이 먼저 시작된다. 그러므로 메인의 
현재 IP는 call명령어의 처리과정에서 자동으로 채워주는 부분이고, 어셈블리 수준에서 BP는 직접 입력을 하게 되어있다. 

여기서 위의 동작을 잠깐 설명을 해보자.
스택은 시작시에 stack pointer, base pointer, instruction pointer라는 포인터를 관리하게 된다. 

* base pointer는 실행되고 있는 함수. 즉 procedure의 시작 위치를 가리키는 포인터이고, 
  함수가 실행 도중에 다른 함수가 호출된다면 base pointer에 들어가는 값은 이전 함수의 현재 base pointer의 주소를 저장하게 된다.
* stack pointer는 현재 스택의 주소를 가리키고 있는 포인터이다. 
* instruction pointer는 실행되던 함수의 주소를  저장하는 pointer이다. 

1) EHA함수 호출시 어셈블리 Level에서 일어나는 동작이다. 
2) EHA함수 리턴시 어셈블리 Level에서 일어나는 동작이다. 

위의 설명을 본 프로그램의 소스 실행 동작과 연결 시켜보자.
우선 메인함수가 실행되다가 EHA라는 함수를 만나게 된다. 
그럼 스택 메모리에 기존 메인 메모리에 할당된 4개의 변수 선언부 다음위치인 12ff6c로 stack pointer가 이동을 하게 되고 
그 위치에 메인함수의 위치를 instruction pointer에 저장한다. 그다음 다시 stack pointer를 1 증가시키고 
현 위치인 12ff68을 base pointer가 가리키게 되고 기존의 base pointer의 위치인 12ff80을 저장하게 된다.
그리고 그 다음 위치부터 EHA에서 선언한 변수를 저장할 공간을 계산한뒤 stack pointer의 위치를 계산한 위치로 옮긴다. 
그리고 base pointer의 위치에서 계산을해서 각 변수에 값을 할당하는 방식으로 스택을 꾸미게 된다.

위 작업은 os에 따라 저장공간의 위치는 변할 수 있겠지만 동작순서는 별 차이가 없을 것이다.

좀 설명이 장황하게 된것 같다.

지금 내용은 운영체제(os)와 밀접한 관계를 가지고 있고 
또한 cpu가 명령어를 어떻게 처리하는가에 대한 지식도 있어야 함은 물론이요.
어셈블리의 대한 지식도 필요로 하게 된다. 

그래서 전체적인 모습을 머리속으로 그리기가 힘든 내용임에는 틀림에 없다. 
계속 공부를 하다보면 차차 이해를 할 수 있을 것라고 믿는다. 


ps) context switching ; 작업간 전환

작업간 전환이란 운영체계 또는 운영 환경과 관련된 용어로서, 자신의 현 상태를 잃지 않은 채 한 프로그램에서 다른 프로그램으로 전환할 수 있게 해주는 것을 말한다. 작업간 전환은 멀티태스킹과 같은 것이 아니라는 점에 유의할 필요가 있다. 멀티태스킹에서는 CPU가 여러 프로그램들 사이를 빠르게 왔다 갔다 하며 처리함으로써, 모든 프로그램들이 마치 동시에 실행되는 것처럼 보이게 하지만, 작업간 전환에서는 CPU가 여러 프로그램 사이를 왔다 갔다 하는 것 대신에, 한번에 오직 하나의 프로그램만을 실행시킨다.

DOS 환경에서 작업간 전환 능력을 부여할 수 있는 유틸리티 프로그램들도 많이 존재한다.

  1. 우하앤랴ㅣ다 2009.05.06 09:39

    1등

  2. 붕어3세 2016.12.10 04:57 신고

    오래된 글이지만 소스코드가 보이지않아 보시는 분들 도움되시라고 댓글 남깁니다.
    #include <stdio.h>
    void EHA(void);
    void test(void);
    int main(void)
    {
    int a = 0;
    int b = 0;
    int c = 0;
    int d = 0;

    printf("main의 각 값들의 주소 : %x, %x, %x, %x\n", &a, &b, &c, &d);
    printf("main의 주소 : %x\n", main);
    printf("EHA의 주소 : %x\n", EHA);
    printf("test의 주소 :%x\n", test);
    EHA(); return 0;
    }
    // 실험용 함수 1
    void EHA(void)
    {
    int a = 0;
    int b = 0;
    int c = 0;
    printf("///////////////////////////\n");
    printf("EHA함수\n");
    printf("EHA의 각 값들의 주소: %x, %x, %x\n",&a,&b,&c);
    printf("변수 a의 1칸 앞에 들어있는 값 : %x\n",*(&a+1));
    // 메인함수 시작 지점
    printf("변수 a의 2칸 앞에 들어있는 값 : %x\n",*(&a+2));
    printf("변수 a의 3칸 앞에 들어있는 값 : %x\n",*(&a+3));
    printf("///////////////////////////\n");
    test();
    }
    //실험용 함수 2
    void test(void)
    {
    int a = 0;
    int b = 0;
    int c = 0;
    printf("///////////////////////////\n");
    printf("테스트 함수\n");
    printf("TEST의 각 값들의 주소: %x, %x, %x\n",&a,&b,&c);
    printf("a + 1 의 값%x\n",*(&a+1));
    // 메인함수 시작 지점
    printf("a + 2 의 값%x\n",*(&a+2));
    printf("a + 3 의 값%x\n",*(&a+3));
    printf("///////////////////////////\n");
    }

+ Recent posts