Operating System

[System Software] 프로그램의 메모리 주소 공간 구조

Sara.H 2021. 2. 8. 21:10

8강) 

좋은건 크게 봐야해!

- 어떤 프로그래밍 언어이든 실행할 때 보면 프로시저 구조로 되어있다. 

즉 함수 구조로 되어있음.

가령 C언어의 경우 main() 함수에서 A() => B() 와 같이 함수 호출이 이어져 나간다. 

 

- 컴파일 후 실행 파일이 만들어지고, 이 프로그램을 메모리에 올리면 프로세스가 된다. 

메모리 상에 주소가 매겨지게 되는데, 각 프로세스마다 (가상의) 0번지부터 시작하게 됨. 

물리 주소는 ISA에 따라서 달라진다. (MIPS 의 경우 4기가 상의 어딘가에 있을 것) 

 

메모리 레이아웃 설명 

- Code : Read only 영역이다. 

- 전역변수와 Static 변수들은 데이터 영역에 올라간다. 

*주의) 함수 안에서 선언된 static 변수들 또한 데이터 영역에 올라가서 프로그램의 라이프사이클과 동일한 라이프사이클을 갖는다. 

- 로컬 변수들은 stack 에서 유지한다. 

- main() 함수가 실행되면 이 함수 안에서 유지하던 변수, 값들 (즉, context 들) 은 A() 함수가 호출되는 순간 이후로는 건드리지 않게 된다. (주소값을 복사해서 넘겨주거나 하는 경우는 다르겠지만)

- 어떠한 함수를 호출하면 그 함수를 호출하기 이전의 함수들의 정보를 stack 에 넣어둔다. return 하면 stack 에서 정보들을 꺼내 다시 수행을 시작한다. 

 

모든 계산들이 메모리에서만 이루어지는게 아니고, 스택에 레지스터 관련 정보들이 들어가야 할 때 도 있다. 


9강) 메모리 구조 Cont'd

 

- 함수의 매개변수들도 지역변수의 공간처럼 스택에 잡힘. 함수가 리턴되면 변수 공간이 사라짐. 함수와 라이프사이클을 같이함. 

 

- 프로그램이 시작되면 프로그램 카운터가 main(), 즉 진입점을 가리키게 되고 순차적으로 실행을 이어나간다. 

- 함수가 호출되면 해당 함수 (프로시저)로의 위치로  PC가 점프해서 함수를 실행시킨다. 

- 해당 함수의 실행이 끝나면 함수가 호출된 위치로 돌아와야 한다. 

 

함수 호출이라는 것은 단순 리드온리 코드만으로 되는 것이 아니다. 

호출될 때 매개변수도 전달해야 하고, 지역변수 공간도 스택에 잡아야 한다. 

데이터 영역이라는 것은 전역변수 (프로그램 시작되면서 잡히는 공간들) 담고 있고, 스택에는 프로그램 실행 도중에 필요한 값들이 담긴다고 보면 됨. 

 

- malloc() 

동적으로 공간을 잡기 위해서 포인터를 지원하는 것이다. 

malloc() 은 포인터가 어떠한 주소 공간을 가리키도록 지시하는 함수 !

지역 변수는 함수가 리턴될 때 사라지는 반면, malloc 과 같이 동적으로 잡는 공간은 (main 함수는 예외) 사라지지 않는다. 

포인터가 지역변수라서 생성된 공간만 남고 포인터만 사라질수도 있음. 

개발자가 직접  free 를 호출해야 공간을 해제할 수 있음. => 함수와 운명을 갖이 하지 않고, 스택의 거꾸로에서부터 동적으로 영역을 잡아나가는 Heap 에 존재한다. 

 

레지스터 

- 글로벌 포인터 : 데이터 영역을 가리킨다. 

- 스택 포인터 : 스택 영역을 가리킨다. 

- 프로그램 카운터 : 다음 실행할 주소 위치 

 

0번 레지스터는 항상 숫자 0을 담고 있다. 

1번 레지스터 : 어셈블러가 사용 

26-7번은 운영체제가 사용 (시스템 소프트웨어 전용 레지스터가 있다 ... 정도)

 

함수가 되돌아갈 주소를 레지스터에 담는다. 

레지스터가 허용하는 범위에 있다면 레지스터에 넣고, 빨리 처리하도록 함. 31번, 리턴 어드레스 레지스터에 호출된 지점을 저장함. 

함수 실행이 다 끝나면 ra레지스터에 적혀있는 주소로 되돌아 가면 됨. (=   PC 값을 ra register 값으로 바꿔주면 된다는 것)

 

하지만 함수가 여러 번 호출되면 값을 덮어쓰게 되어서 이전것이 지워지게 됨. 

이런 경우를 방지하기 위해 호출이 많아지면 stack 을 이용해서 ra 를 저장한다. 

 

매개변수를 전달할때도 마찬가지로, 개수가 4개보다 적으면 레지스터를 활용할 수 있다. 

함수에 매개변수가 4개를 넘어가는 경우 stack 을 이용해서 아규먼트를 전달할 것. 

 

함수 결과값을 저장하는 레지스터도 두 개 있다. 

리턴 밸류를 전달하는 것 도 기본적으로 2,3번 레지스터를 써서 전달하게 됨. 

 

함수가 호출되기 이전에 사용하던 레지스터 값은 저장해두고, 새로 호출된 함수가 자유롭게 쓰다가, 

원래 함수가 쓰던 값으로 레지스터 값을 복원해준 후에 리턴을 해야 함. 

새로운 함수가 호출될 때 레지스터 값들은 stack 에 저장해 둠. 

==> 아무렇게나 하는 것이 아니라, 파란색으로 칠해진 temporary 레지스터들에는 무조건 저장을 해둬야 한다. 

만약 다른 함수 호출로 인해서 덮어 써야 하는 상황이라면 나 자신의 스택에 레지스터 값들을 저장해 둔 후 리턴을 하기 전에 레지스터에 값들을 복원시켜 놓아야 할 의무가 있다.

 

함수를 호출 하는 쪽이 save 를 해야 하는가?
호출되는 쪽이 save 한 다음에 사용하고 나중에 복원해야 하는가? 

=> 중요한 질문 !

* t 레지스터는 임시로 저장하는 공간. 덮어쓰워질 수 있다. 

* s 레지스터는 함수 호출을 하더라도 값이 남아 있어야 하는 공간.

 

함수가 호출되면 이전 함수가 쓰던 레지스터 값들을 스택에 저장해두어야 한다. 

함수 호출이 끝나고 리턴할 때에도 스택에 주소를 저장해야 하고 

매개변수의 전달 또한 스택을 사용한다. 

지역변수의 공간 할당 또한 스택을 ... 

 

가장 위에 있는 값이 가장 먼저 빠져나오게 된다.  스택 포인터는 스택의  Top 을 갖고 있고, 스택에서 정보를 꺼낼 때는 스택 포인터가 가리키는 위치에서 Pop 하면 됨. 

 

 t0 라는 레지스터의 값을 스택에 넣는 것 (SW : Store Word)

스택에서 정보를 꺼낼 때 는 sp + 4 바이트를 (4바이트 저장한 경우라면) 다시 새로운 스택 포인터의 주소로 지정해준다. (뽑아내면 주소가 낮은 곳에서 높은 곳으로 올라가므로)

 

참고 : www.kocw.net/home/search/kemView.do?kemId=1223247