● Logical Address vs Physical Address (논리적 주소 vs 물리적 주소)
프로그램이 시작되면 독자적인 주소 공간이 형성된다.
물리적인 메모리 내에서 3GB~4GB 주소 공간에는 운영체제 커널이 올라가 있고, 1GB~3GB 주소 공간에는 여러 사용자 프로그램들이 섞여서 올라가게 된다.
프로그램이 실행되려면 물리적인 메모리 어딘가로 올라가야 하고 그렇게 되면 주소가 바뀌게 된다.
프로그램이 물리적인 메모리에 어디로 올라갈지 주소를 결정하는 것을 주소 바인딩이라고 한다.
- Symbolic Address
프로그래머가 프로그램을 만들 때, 특정 변수에 값을 저장하고 그 변수를 메모리 몇 번지에 저장하라고 하지 않는다.
또한, 변수에 데이터를 저장한 후 함수를 호출할 때, 함수 이름을 호출하지 메모리 몇 번지로 점프해!라고 하지 않는다.
이러한 방식으로 프로그래머 입장에서는 숫자로 된 주소를 사용하지 않고 Symbol로 된 주소를 사용한다.
- Logical Address (Virtual Address)
각 프로세스마다 독립적으로 가지는 주소 공간이다. 각 프로세스마다 0번지부터 시작하며 CPU가 보는 주소이다.
- Physical Address
메모리에 실제 올라가는 위치이다.
주소 바인딩 : 주소를 결정하는 것. symbolic Address → Logical Address → Physical address.
● Address Binding (주소 바인딩)
컴파일이 되면 독자적인 숫자 주소가 만들어지고, 실행이 되려면 물리적 메모리에 올라가야 하기 때문에 주소 변환이 필요하다.
주소는 Symbolic Address → Logical Address → Physical Address로 변환이 된다. 그렇다면 Logical Address → Physical Address 하는 시점은 언제인가?
논리적 주소에서 물리적 주소로 변환되는, Binding이 되는 시점은 크게 세 가지로 나눌 수 있다.
주소 변환이 컴파일 시에 이루어지면 Compile Time Binding, 프로그램이 시작될 때 이루어지면 Load Time Binding, 프로그램이 시작되고 바뀔 수 있으면 Run Time Binding 또는 Excution Time Binding이다.
왼쪽의 소스코드가 사용자가 쓴 코드이다.
거기에는 Symbolic Address로 주소가 표현이 되어있고 A위치에 있는 값과 B 위치에 있는 값을 더하고 C로 점프해!라는 의미이다.
프로그래머는 Symbolic Address로 메모리 주소를 사용하며, 컴파일 후 실행 파일이 만들어지게 되면 Symbolic Address에서 Logical Address(숫자 주소)로 바뀐다.
각각의 문장들이 메모리의 0번지, 10번지, 20번지 등등으로 바뀌게 된다.
이 파일이 실행이 되려면 Physical Memory에 올라가야 하고 Physical Memory의 주소가 결정되는 것을 주소 Binding이라고 한다.
- Compile Time Binding
Compile 할 때 이미 물리적 주소가 결정이 된다. 컴파일 시 이미 물리적 주소가 결정이 되어야 하기 때문에 Logical Memory 그대로 물리적 메모리 주소로 올려야 한다.
물리적 메모리에 주소가 많이 비었는데도 불구하고 이 프로그램은 항상 0번지부터 Logical Address에 무조건 올려야 하기 때문에 굉장히 비효율적이다.
컴파일 시 물리적 메모리 주소가 Fix 되기 때문에 절대 코드라고 부르며 메모리의 위치를 바꾸고 싶으면 컴파일을 다시 해야 한다.
컴퓨터에서 프로그램을 하나만 쓸 때에 사용했었고 현재는 사용하지 않는다.
- Load Time Binding
프로그램이 시작되고 메모리에 올라갈 때 물리적 주소가 결정이 된다. 컴파일 때에는 논리적인 주소만 결정이 된다.
프로그램을 시작할 때, 물리적 메모리가 500번지부터 비어있다면 논리적 메모리 주소 0번지를 물리적 메모리 500번지에 올린다.
- Run Time Binding
프로그램이 시작할 때 주소가 결정되는 부분은 똑같은데, 실행 도중 물리적 주소가 바뀔 수 있다.
논리적 메모리 주소 0번지가 물리적 메모리 주소 300번에 올라와 있다가, 실행 도중 경우에 따라서 메모리 300번지에서 쫓겨나고 300번지에 있던 내용이 700번지로 이동하는 방식이다.
현재 사용하는 시스템 방식이다.
Compile Time Binding과 Load Time Binding은 프로그램이 시작되고 주소가 바뀌지 않는다.
반면에 Run Time Binding은 CPU가 메모리 주소를 요청할 때마다 Binding을 체크해 주소 변환을 그때 그때 해줘야 하기 때문에 하드웨어 지원(MMU)이 필요하다.
CPU가 바라보는 주소는 Logical Address이다. CPU가 Instruction을 하려면 메모리 20번지 내용과 30번지 내용을 CPU로 읽어 들여서 더하는 연산을 한다.
그리고 실행이 되어서 메모리에 올라가더라도 Instruction 코드 안에 있는 Address는 20번지와 30번지로 바뀌지 않는다.
메모리에 올라갈 때 그 시작 위치는 바뀌지만 그 안에 있는 코드상의 주소는 그대로 Logical Address로 남을 수밖에 없다.
CPU가 메모리 몇 번지에 있는 내용을 달라고 요청하면, 그때 주소 변환을 통해 물리적 주소 내용을 읽어서 CPU에게 전달한다.
Run Time Binding은 그때그때마다 주소가 어디에 올라가는지를 주소 변환을 해줘야 한다.
필요한 부분만 메모리에 올라가고 필요 없는 내용은 디스크로 쫓겨나거나, 메모리에 올라가는 부분도 나뉘어 올라가거나 해야 하는데 이러한 일들을 OS가 한다.
우선, 메모리 관리 파트에서는 처음부터 설명하기 때문에 일단 프로그램의 논리적 주소가 통째로 물리적 주소로 올라간다는 가정하에 주소 변환을 알아보도록 하자.
✓ Compile Time Binding
물리적 메모리 주소가 컴파일 시 알려짐
시작 위치 변경 시 재컴파일
컴파일러는 절대 코드 (absolute code) 생성
✓ Load Time Binding
Loader의 책임하에 물리적 메모리 주소 부여
컴파일러가 재배치 가능 코드 (Relocatable code)를 생성한 경우 가능
✓ Execution Time Binding ( Run Time Binding)
수행이 시작된 이후에도 프로세스의 메모리 상 위치를 옮길 수 있음
CPU가 주소를 참조할 때마다 Binding을 점검 (Address Mapping Table)
하드웨어적인 자원이 필요 (e.g.. Base and Limit Registers, MMU)
● Memory-Management Unit (MMU)
CPU가 메모리 346번지에 있는 내용을 달라고 요청하는 것은
Logical Address로 요청을 하는 것이다.
기본적인 MMU에서는 레지스터 두 개(Relocation Register & Limit Register)를 가지고 변환하며 Base Register에다가 이 프로그램의 시작 위치를 저장해 놓는다.
그래서 주소 변환을 할 때 논리적 주소에다가 시작 위치를 더해서 물리적 주소를 얻게 된다.
좌측 하단에 있는 그림은 프로세스 1의 Logical Memory (Virtual Memory)를 나타내며 Physical Memory에는 14000번째부터 올라가 있는 상황이다.
CPU가 346번지의 정보를 요청했다면 이 논리적 메모리 주소를 물리적 메모리 주소로 변환시켜야 한다.
프로그램이 물리적 메모리에 올라가 있는 시작 위치가 14000번지이므로 시작 위치와 논리 주소를 더해주어 14346(14000+346)만큼 떨어진 내용의 위치를 읽어서 CPU에게 알려줘야 한다.
✓ MMU (Memory-Management Unit)
Logical Address를 Physical Address로 매핑해 주는 Hardware Device
✓ MMU Scheme
사용자 프로세스가 CPU에서 수행되며 생성해 내는 모든 주소값에 대해 Base Register (=Relocation Register)의 값을 더한다.
✓ User Program
Logical Address만을 다룬다.
실제 Physical Address를 볼 수 없으며 알 필요가 없다.
● Hardware Support for Address Translation (주소 변환을 위한 하드웨어)
Base Register 외에 한 가지 더 체크해야 할 사항은 Limit Register이다. Limit Register에는 프로그램의 크기를 저장하고 있다.
프로세스 1이 3000번지까지 갖고 있는 프로그램일 경우, 자신의 크기가 3000인데도 불구하고 4000번지를 요청하게 되면 해당 영역은 다른 프로그램 영역이다.
시작 위치에 4000번지를 더하면 프로세스 1의 메모리 영역을 벗어나기 때문이다. 남의 프로그램을 보는 것은 악의적인 행동! 막아야 한다!!
그러기 위해서는 프로그램 크기보다 더 큰 논리적 주소를 요청한 것인가 아닌가를 체크해야 한다.
Limit Register값을 넘어가는 주소를 요청하면 Trap이 걸린다.
Trap에 걸리면 하던 일을 멈추고 CPU 제어권이 운영체제에게 넘어간다.
그럼 운영체제는 Trap이 왜 걸렸는지 확인한 후 프로그램을 종료시킨다거나 하는 조치를 취한다.
반면에, 논리주소가 프로그램 크기 이내에 있는 요청이었다면 Base Register의 값을 더해서 주소 변환 후, 물리적 메모리의 데이터를 읽어서 CPU에게 전달한다.
사용자 프로그램은 Logical Address만 다루고 있다. 실제 Physical Adreess는 볼 수도 없고 알 필요도 없다. CPU도 Logical Address를 바라보고 있다.
Physical Address라는 건 요청이 되었을 때, MMU가 그때그때 주소 변환을 해서 얻게 되는 개념이다.