이번 챕터는 기본적으로 더듬더듬 예전의 개념을 찾아 블럭으로 차곡차곡 쌓은 다음, 아하! 하고 봐야 한다.
그럼, 쌓아 보자!
자아, CPU의 구조는 대략 이렇다.
하드디스크에 있던 '프로그램'이 메모리에 적재되면, '프로세스'가 되고, 실행된다!
뭐 이런 과정을 거친다.
자세한 설명은 아마 이 블로그를 참고하도록 하자.
main memory와 register는 CPU가 직접 접근할 수 있는 유일한 저장소이다. register access는 1 이하의 cycle을 필요로 하는데 반해, main memory는 아주 많은 cycle을 필요로 한다.
따라서 processor는 memory에 접근해야할 때 stall(대기시간)이 필요하다. 째깍째깍..
이를 줄이기 위해 위의 그림과 같이 cache라는 임시 저장소가 있다! CPU register와 main memory 사이의 임시 저장소!
메모리를 보호하기 위한 HW에는 base, limit register라는 게 있다. 레지스터도 임시 저장소인데, cache보다도 후다닥 엄청 빠르다!
어쨌든, 이렇게, 시작 주소와 길이를 가리키는 register가 존재한다.
어떤 data(symbol)를 어떤 실제 주소(physical address)로 할지 정하는 작업은 address binding이라고 한다. 이를 언제할지는 다음과 같은 선택권이 있다.
가상의 주소다. CPU에 의해 만들어진다. 우리가 보는 아쥬 연속적이고 직관적인 주소다.
실제 메모리 주소다. 즉 실제로는 우리가 생각하는 것처럼 저장되어 있지 않다는 거다.
가상의 logical address는 어떤 '과정'을 거쳐 physical address로 mapping된다. 이 어떤 '과정'은 MMU (memory management unit)이라는 HW가 수행한다. 아주 간단한 MMU는
이런 식으로 덧셈을 해주는 식으로 실제 메모리 주소를 찾아준다.
library! 코딩할 때 #include
로 불러왔던 그런 library!는 다음과 같이 불러와진다.
linux에서는 .a 파일, window에서는 .lib파일인, 이 library는 complie time에 함께 불러져 복사되어 메모리에 저장된다.
즉 프로그램에서 printf
를 불렀다면, compile 할 때 함께 link되어 exe file이 만들어진다.
자알 생각해보면 exe 파일 만들 때 library가 복사되어 저장된다는 거니까 library를 많이 부를수록 점-점- 커진다! 악! 메모리 비좁아!
이런 방법도 있다. run time에 나랑 같은 library 쓰는 다른 애가 있다면 걔랑 연결해서, 공유해서 쓰는 거다. 오오 메모리에는 오로지 하나의 library만 load되니까 좀 여유 공간이 생겼다!
linux에서는 .so 파일, window에서는 .dll파일이 해당된다.
이러한 library는 두 가지 방법으로 실행될 수 있다.
excution time까지 loading과 linking(binding)이 미뤄진다.
프로그램을 수행하면서 그때그때 library를 불렀을 때마다 loading과 linking이 이루어진다는 거다.
loading과 linking이 프로그램이 시작될 때 된다.
printf를 부르는 a.out 프로그램이 두 개 있다고 하자.
진짜 prinf library를 부르는 게 아니라, 메모리에 올려져 있는 printf에 대한 library를 실제로! 공유하면서 사용하는 것이기 때문에 이를 호출하도록 가짜의 code가 필요하다. 이것을 stub이라고 한다.
process는 main memory에서 HDD와 같은 backing store로 옮겨지거나(swap out), backing store에서 main memory로 옮겨진다.(swap in)
이는 transfer time에서 큰 overhead를 발생시킨다.
자, 이제 기본적인 작업이 끝났다. 그럼 memory allocation을 어떻게 할 수 있는지, 알아보자!