Logical vs. Physical Address
메모리에 '주소'를 통해서 접근
주소 종류 2가지
프로그램이 실행되면 독자적인 주소 공간이 형성된다.
프로그램마다 가지고 있는 메모리 주소 => 논리적 주소 (가상 주소라고도 함)
실제 물리적인 메모리의 주소 => 물리적 주소
메모리의 프로그램이 어디로 올라가느냐
- 물리적 메모리는 하나로, 0번지부터 통으로 관리
- 물리적 메모리 안에 아래부분에 운영체제 커널이 올라가 있고,
- 물리적 메모리 안에 상위부분에는 여러 프로그램들이 섞여서 올라가 있다.
프로그램마다 0번지부터 시작하는 독자적인 주소가 있지만, 실행이 되려면 물리적 메모리 주소 어딘가에 올라가야 하고, 그러면 주소가 바뀌게 된다. => 주소 변환 즉, 주소를 결정하는 건데 주소 바인딩 : 어떤 프로그램이 물리적인 메모리 주소 어디로 올라갈지 결정 하는 것.
- 주소는 언제 결정되는가? : (각 프로그램마다 갖고 있는 논리적 주소) => 물리적 주소
Symbolic Address
- 프로그래머가 프로그램을 만들 때, 숫자 주소를 가지고 프로그래밍 하진 않는다.
- 메모리 특정위치에 변수 값을 저장할 때, 메모리 몇번지에 저장할지 결정하지 않고 변수에 어떤 값을 저장하는 방식으로 함.
- 함수를 호출할 때, 몇번지로 jump해라 하지 않고, 함수 이름으로 함수 호출
즉, 프로그래머는 Symbol로 된 주소를 사용한다.
Symbolic address가 컴파일 되면 => Logical address 숫자로 된 주소로 바뀐다.(0번지부터 시작하는 그 프로그램만의 독자적인 주소) => 실행하기 위해서 Physical address로 변환
주소바인딩(Address Binding)
1. Compile and binding
주소변환이 compile 시에 이뤄짐
2. Load time binding
실행이 시작될 때
3. Execution time binding (= Run time binding)
프로그램이 시작된 이후 실행하다가
- 소스코드에는 Symbolic address로 표시
- 컴파일 돼서 실행파일에는 Logical address로 표시
- 0번지에 20번 메모리 주소랑 30번 메모리 주소랑 더하라고 나와있음.
- 20번 메모리에는 100이, 30번 메모리에는 330이 있음. 100+330
- 실행이 되려면 물리적인 메모리에 올라가야 한다. 물리적인 메모리의 주소가 결정되는 것을 주소 바인딩 이라고 한다.
아래는 주소 바인딩 시점에 따른 예시다.
Compile time binding
컴파일 시점에 이미 주소 바인딩이 되는 것.
그래서 실행파일 일 때 논리적인 주소가 아니라, 이미 물리적인 주소가 됨.
- 다른 주소는 많이 비어있엇도 항상 0번째부터 올려야 한다.
- 되게 비효율적임
- 예전에 컴퓨터 안에서 프로그램이 하나만 실행됐던 경우에는 이런 방법을 사용
Load time binding
프로그램이 시작돼서 메모리에 올라갈 때 주소 바인딩.
- 메모리를 봤더니 500이 비어있더라 ~ 논리적인 주소 0번지를 물리적인 메모리 500번지부터 올린다.
Run time binding
Load time binding처럼 실행시에 주소가 결정되는 것은 같다.
- 그런데 이 주소가 실행시에 바뀔 수 있다는 것
- 예) 메모리에서 쫓겨나서 돌아왔을 때 300번 메모리에 이미 다른 내용이 차지하면 비어있는 700번지에 올린다.
- 우리가 요즘 쓰는 컴퓨터 시스템은 Run time binding을 지원한다.
- cpu가 어떤 메모리 주소를 요청할 때마다 binding을 체크해야 한다. 이 내용이 물리적 메모리 어디에 올라가있는지 확인.
하드웨어인 cpu가 바라보는 주소는 logical address
- logical address가 실행돼서 메모리에 올라가더라도 instruction 코드 안 내용들은 안바뀜. 메모리의 시작위치만 바뀌지만 그 안에 코드상의 주소는 고스란히 logical address로 남는다.
- cpu가 instruction을 실행하겠다 하면, 20번지 내용과 30번지 내용을 더하려고 하면 그 내용을 cpu안으로 읽어들여야 하는데 그 주소가 logical address임.
- cpu가 매번 메모리의 몇번지에 있는 내용을 달라 요청을 하면 그때 주소 변환을 해서 물리적 메모리 주소 위치를 찾아서 그 내용을 cpu한테 전달.
- 그때그때마다 내용들이 어디에 올라가잇는지를 주소 변환을 새로 해주는 방법이 필요하다.
Memory-Management Unit(MMU)
MMU는 OS의 모듈이나 SW가 아니고 하드웨어를 말한다.
가정)
프로그램의 논리 주소가 통째로 물리적 메모리 주소로 올라가는 것을 가정 (현대에는 산발적으로 흩어짐)
기본적인 MMU
Dynamic Relocation
CPU가 메모리 346번지에 있는 내용을 달라 요청: logical address
=> 주소 변환이 필요함
=> 주소 변환을 해주는 하드웨어: MMU
=> 가장 간단한 변환: 레지스터 2개로!
- Relocation register
- Limit register
프로그램 p1이 cpu를 사용하고 있는 상황
- p1의 logical address(0번지~3000번지)
- 0번지부터 346번지 만큼 떨어져 있는 이 내용을 cpu가 요청한 것임.
그리고 p1 프로그램이 physical memory 상에는 14000번지 부터 올라가 있는 상황
주소 변환을 어떻게 해주면 되는가?
- relocation register, 즉, base register가 시작위치 (14000)을 저장한다.
- 주소 변환을 할 때는 논리주소(346)에다가 시작위치(14000)를 더해서 = 14346 번지 얻음
- p1 프로그램의 최대 크기는 3000번지까지 가지고 있음. => limit register가 담고 있음
- 이 프로그램이 만약 악의적인 프로그램이라면, 3000번지를 넘어서 4000번지 달라고 하면 다른 프로그램이 존재하는 메모리 위치를 요청하는 것.
Hadware Support for Address Translation
Some Terminologies
1.Dynamic Loading
Dynamin: 동적인
Loading: 메모리로 올리는 것
프로그램을 메모리에 올려야 실행이 됨. => 메모리에 동적으로 올린다.
- 프로세스 전체를 메모리에 미리 다 올리는 것이 아님.
- 보통 잘 쓰여지지 않는 코드들이 되게 많음
- 따라서, 해당 코드를 실행할 때 메모리에 올린다.
Dynamin Loading
은, 운영체제가 지원하는 기능이 아니라, 프로그래머가 직접 이런 코드를 작성하는 것 (근데, 라이브러리를 이용해서 프로그래머가 자세하게 어떻게 올리고 내리는지 작성은 x)
- 우리가 요즘 메모리에 올리고, 빼고 하는 paging 기법은 os차원에서 하는 것임.
- 요즘은 Dynamic Loding을 그 paging 기법이라고 가끔 의미하기도 함.
2.Overlays
Dynamic Loading과 차이점은?
- 역사적으로 다름
- 초창기에 메모리 크기가 워낙 작아서 프로그램 하나를 메모리에 올려놓는것 마저 불가능
- 프로그래머가 프로그램을 쪼개서 이쪽 부분 올려서 실행, 저쪽 부분 올려서 실행. 프로그래머가 수작업 => "Manual Overlay"
3.Swapping
프로세스를 메모리에서 하드디스크(backing store(=swap area))로 쫓아내는 것.
- 스케줄러 중 중기 스케줄러 에 의해 swap out 시킬 process 선정
- 주소 바인딩 중 Compile time binding 혹은 Load time binding 에서는 swap in 됏을 때 다른 메모리 주소가 비어있어도 원래 위치로 돌아가야 한다. 그래서 swapping의 장점이 극대화가 안돼. swapping이 효율적으로 동작하려면 Run time binding이 동작해야 한다. swap in 할 때 다른 메모리가 비어있다면 거기로 들어갈 수 있음.
- 원칙적으로 swapping의 swap out 개념은 프로그램을 구성하는 그 주소 공간이 전부다 쫓겨나는 것. (근데 요즘은 paging 기법도 있고,,, 해서 조금 연동돼서 쓰이기도 함)
4.Dynamic Linking
우리가 보통 코드를 작성한 다음에 컴파일 하고 Link해서 실행파일을 만든다.
Link: 여러 군데 존재하던 컴파일들 파일들을 묶어서 하나의 실행파일을 만드는 과정
내가 소스파일을 여러개 따로 코딩을 해서 Linking을 하기도 하고, 내가 작성하지 않은 함수지만 유용해서 불러다 쓰는 라이브러리들도 결국 Linking이 돼서 실행파일이 만들어지면 내 코드 안에 라이브러리 코드가 보통 포함이 되는 개념
1) Static linking
2) Dynamic linking
라이브러리 코드가 내 실행파일 안에 즉, 컴파일해서 실행파일이 만들어질 때 포함되지 않고 그 상태로 남아있다가 프로그램을 실행시키게 되면, 프로그램이 실행되다가 라이브러리 호출하는 곳에 도달하면 내 코드안에 라이브러리가 안들어있기 때문에 라이브러리를 찾아야 한다. stub 이라는 작은 코드로 위치를 찾는다.
dynamic linking을 해주는 라이브러리 => shared library
물리적인 메모리를 어떻게 관리할 것인가?
Allocation of Physical Memory
물리적인 메모리
- 낮은 주소 부분에 운영체제 커널 부분이 항상 상주
- 높은 주소 영역에는 사용자 프로그램이 올라감.
- 이 영역을 관리할 방법 2가지
- 1) Contiguous allocation (연속 할당)
: 프로그램이 메모리에 올라갈 때 통째로 올라가는 것.
- 주소변환이 비교적 간단했음
- 우리가 이전까지 배운 내용임
- 2) Noncontiguous allocation (불연속 할당)
: 프로그램을 구성하는 주소 공간을 잘게 쪼개서 일부는 저쪽에, 일부는 이쪽에
- 현대의 시스템에서 불연속 할당을 쓰고 있음.
- Paging: 프로그램의 주소 공간을 같은 크기의 page로 쪼개서 page 단위로 메모리 여기저기에 올리는 기법
- Segmentaion
- Paged Segmentation
1. Contiguous Allocation (연속 할당)
고정 분할 방식
사용자 프로그램이 들어갈 메모리 영역을 미리 partition으로 나눈다.
- 메모리의 조각이 너무 작아서 사용이 안되면 => 외부 조각
- 프로그램의 크기가 분할3번 보다 작으면 => 내부 조각
가변 분할 방식
프로그램을 미리 나누지 않는다.
- 프로그램이 실행될 때마다 차곡차곡 메모리에 올려놓는 방법.
- 프로그램 크기가 일정하지 않기 때문에 프로그램이 실행되고 종료되는 것에 따라 중간중간에 외부 조각이 생길 수 있음.
- 내부조각은 안생김.
프로그램이 실행될 때 Hole 중에서,가용 메모리 공간 중에서 어디다가 새로운 프로그램을 집어넣을 것인가 결정해야 한다. => Dynamic Storage-Allocation Problem
현재 실행할 프로그램의 크기가 n일 때 hole이 여러개가 있음.
방법 3가지
1) First-fit
: 제일 처음 발견되는 hole에다가 집어넣기
- 속도 측면에서 효과적
2) Best-fit
: hole을 다 살펴본 다음에 프로그램 크기랑 제일 근접한 hole에 올린다.
- 공간 이용률 측면에서 효과적
3) Worst-fit
: hole을 다 살펴본 다음에 가장 큰 hole에 프로그램 할당. (어리석음)
남은 Hole들을 한 곳에 몰아넣는 것
=> compection
- 외부 조각으로 생긱는 hole을 한곳으로 밀어서 아주 큰 hole을 만들기.
: hole이 작아서 프로그램이 못올라가는 문제를 해결할 수 있음.
run time binding
을 지원해야지만, 즉, 메모리 위치가 실행중일 때도 변경할 수 있는 기능이 지원되어야만 compection 가능.
어떤 최소한의 프로그램만 이동시켜서 큰 hole을 만드는게 좋음. 전체를 다 미는 것보다는 작은 이동을 통해서 큰 hole을 만드는 compection이 효율적이다. 간단한 문제가 아님. 어떤 프로그램을 움직일 것인지 결정하는 문제여서.
2. Noncontiguous Allocation (불연속 할당)
Paging 기법
프로그램을 구성하는 주소공간을 같은 page의 크기로 자른다.
page단위로 물리적인 메모리에 올려놓거나 backing store에 내려놓거나 하는것.
- 물리적 메모리에 사용자 프로그램을 들어갈 수 잇는 공간들도 똑같은 크기의, page 하나가 들어갈 수 있는 크기로 미리 잘라놓는다. => page frame
물리적인 메모리 공간을 page frame이라 부르고, page frame하나하나에는 page들이 올라갈 수 있는 상황.
paging 기법을 쓰면 문제 해결
- hole들의 크기가 균일하지 않아서 어디다 집어넣을 것인가? (hole)
- hole들을 한 곳에 밀어넣는 것들이 필요가 없어짐. (dynamic storage-allocation ploblem)
왜냐하면, 물리적 메모리에서 비어잇는 위치가 잇으면 page frame이기 때문에 프로그램의 어떤 page든지 비어있는 곳 아무곳이나 들어갈 수 있음.
대신, 이런 불연속 할당은 주소 변환이 복잡해진다.
- 시작 주소에서 더해서 주소변환 x
- 잘려진 각각의 page가 물리적인 메모리 어디에 올라가있는지를 확인해봐야 함. 즉, 주소변환을 page별로 해야함. address binding이 더 복잡해짐.
segmentation 기법
프로그램의 주소공간을 의미있는 단위로 자르는 것.
프로그램을 구성하는 주소공간이라는게 code
, data
, stack
의미있는 공간으로 구성.
- segmentation은 크게 cod segment, data segment, stack segment 이렇게 잘라서 각각의 segment를 필요시에 물리적 메모리에 다른 위치에 올려놓는다.
segment라는 건, 의미 단위이기 때문에 code, data, stack 말고도 code의 함수 단위로 쪼갤수도 있음.
- 각각의 함수를 segment로 잘라서 물리적 메모리의 다른 위치에 올릴 수 있음.
- 또, segment 단위로 주소변환도 해줘야함.
- segment는 의미 단위로 자른 것이기 때문에 크기가 균일하지 않음.
- 프로그램을 통째로 메모리에 올릴 때 발생했던 hole 문제들, 즉, Dynamic Storage-Allocation Propblem과 같은 문제가 발생할 수 있음.