본문 바로가기

study Computer Engineering/컴구학개론

[컴구학개론] 시즌1 1화 : [2-8] 함수 호출과 메모리 관리 Supporting Procedures in Computer Hardware

DRAM - 이 것과 레지스터를 적절히 사용하여, 우리가 더블 클릭한 프로그램을 원할하게 수행합니다. 샘숭과 하이닉스가 먹고 있는 디램시장. 프로그램 수행의 중추를 담당합니다만.

 

1. 레지스터 사용 관례

그림 1-1 : ISA 아키텍쳐의 종류에 따라 레지스터를 어떻게 쓸지는 다릅니다. 위의 경우는 RISC-V ISA의 경우입니다.

참고 : [내 공부용] 글로벌 포인터의 경우 DRAM에서 static data의 주소를 가리킵니다. 언제나 접근 가능한 변수들의 위치를 포인터가 잘 가리키고 있어야합니다. 글러벌 변수들이 저장되는 곳이죠. 8,9번과 18-27번에 해당하는 saved register의 경우에는, 다른 프로시져 호출이 끝난 후에도 그대로 이어서 사용해야 되기 때문에, 반드시 "복원해 주어야 할" 로컬 변수들이 저장됩니다. 이것의 경우 호출한 프로시져에서 리턴 시에 해당 레지스터로 램으로 스필한 데이터를 복원해줍니다. 그렇지 않은 변수들은 temporaries에 저장됩니다. 

 

2.  중첩 함수 호출

 

 

프로그램을 실행할 때, 램에 올라간 한 프로그램의 메모리 할당

 

  스택의 경우에는 높은 메모리 주소에서 낮으 메모리 주소로, 즉, 하방으로 자라납니다. (Dynamic data segment: heap힙과는 반대입니다. 그래서 두 영역이 오버랩되면, memory leak이 발생하죠.) 프로시져를 호출할 경우에, 기존의 레이어의 호출된 프로시져가 호출자에게 반드시 :

1). 복원시켜주어야할 것들은 saved registers, return address, arguments등이 램의 스택에 할당되고,

2). 또한 그 호출받은 프로시져 자체의 레지스터에 저장될 수 없는 로컬 array와 structs등이 저장됩니다.

+) 호출된 프로시져 자체의 로컬 변수들은, 스택으로 이동하여 자리를 비워놨으니, 당연히 레지스터 중 saved(12), temps(7)을 19개나 되는 자원들을 이용하면 되겠죠.

 

  만약 프로시져가 끝날 경우, 1). saved registers, return address, arguments들을 다시 호출자 프로시져가 사용할 수 있게 레지스터에 복원시키면서 동시에, 2). 유용하게 스택에서 사용한 로컬 어레이, 스트럭쳐 등은 pop되어 없어져 버리게됩니다. 그 데이터를 쓸 것이라면, 리턴을 꼭 해주어야하죠. 아니면, 저 스택과 함께 사라집니다.

 

 

 

3. 힙( Dynamic Data Segment )

: 살아있는 동안, 크기가 동적으로 자라고, 줄어드는 데이터 구조에 적합하다. 자바에서 new ClassName()으로 만든 객체도 이 다이너믹 데이터 시그먼트에 저장됩니다. 

C의 경우, 힙에 "자료구조"를 생성하고, 해당 자료구조를 소멸시켜, 메모리를 프리하게 하는 과정을 "명시적 함수"로 관리해야합니다.  malloc, free등이 그러한 작업을 하는 함수입니다. 그러한 작업을 염두해두어야 하기 때문에, 버그가 많이 발생할 수 있는데, 대표적으로 :

 

1. memory leak : 위의 힙이 위로 성장하는 데, 지나치게 많은 객체나 동적 데이터 구조를 힙에 쌓아두면 위에서 밑으로 성장하는 스택과 충돌하여, 버그를 일으킬 수 있습니다. 

2. dangling pointers(덩그러니 매달려 있는 포인터) : 만약 free를, 동적 자료구조를 유지해야하는 상황에서도 오사용할 경우, 포인터만 있고, 막상 가리키는 데이터가 없는 경우가 있을 수 있습니다. 이런 경우, 런타임 에러가 일어나면, 또 앱이 깨질 것입니다.

 

등이 있습니다. 그래서 자바에서는, 바보가 되기 쉬운 닝겐에게 이러한 것들의 통제권을 뺏어 왔습니다. automatic memory allocation, garbage collection은 해당 위의 두가지 런타임 에러가 안나게 해줍니다. 즉, 빠르게 필요없게 된 객체를 청소해서 메모리를 비워두고, 포인터가 비지 않게 컴파일 에러를 내도록 노력합니다. 

 

 

4. 글로벌 포인터 : 다른 언어와 방식이 다를 수 있음에 유념 [Swift 선호 주의!!]

C언어의 경우, 자료가 automatic / static으로 저장됩니다. 프로시져 바깥에서 정의된 변수들은 static으로 간주되며, static이라는 키워드로 생성된 것도 static data로 간주됩니다. 이러한 static으로 간주되는 것들에 접근하기 위해 x3을 global pointer로 비워둡니다. 파이썬에서는 global variable의 개념과 유사. 스위프트에서는 글로벌 변수를 다음과 같이 생성한다고 하니, 참고하시면 좋을 것 같습니다. 링크 :  https://www.it-swarm.dev/ko/swift/%EC%A0%84%EC%97%AD-%EB%B3%80%EC%88%98%EB%A5%BC-%EB%A7%8C%EB%93%9C%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%80/1049326894/

 

5. 스위프트에서의 메모리 관리 [Swift 선호 주의!!]

 위의 배경지식을 가지고 스위프트 메모리 매니지먼트를 읽어보면 매우 이해가 쉬우실 겁니다. : 

https://medium.com/elements/memory-management-in-swift-31e20f942bbc