contents
Memory
•
메모리는 주소를 통해서 접근하는 매체
◦
Logical address(= virtual address)
◦
Physical address
•
프로그램이 실행되면 독자적인 주소 공간이 형성 → Logical address
Logical address(= virtual address)
•
프로세스마다 독립적으로 가지는 주소 공간
•
각 프로세스마다 0번지부터 시작
•
CPU가 보는 주소는 logical address
Physical address
•
메모리에 실제 올라가는 위치
•
실제로 프로그램이 실행되기 위해선 Physical address에 올라가야 함
•
물리적인 메모리는 하나로 0번지부터 통으로 관리
◦
물리적인 메모리 안에서, 아래에는 운영체제 커널, 상위 주소에는 여러 프로그램들이 섞여서 올라감
주소 바인딩
: 물리적인 메모리 주소를 결정하는 것
•
주소는 언제 결정되는가? (Logical → Physical)
Symbolic Address → Logical Address → WHEN → Physical address
◦
Symbolic Address : 사용자가 메모리의 주소값을 찾을 때, 숫자로 접근하는 것이 아닌 변수명으로 접근하게 된다.
◦
컴파일 이전에는 Symbolic address 상태
◦
프로그래머가 메모리를 사용하지만 프로그래머 입장에서는 숫자가 아닌 Symbol로 된 address를 사용하게 되고 이게 Complie이 되면 숫자로 된 주소로 바뀌게 된다.
◦
시간이 되면 물리적인 메모리에 올라가야 하니깐 주소 변환이 이루어져야 한다.
주소 바인딩을 하는 타이밍 3가지
1.
complie time binding
컴파일 시에 주소 변환
•
Physical address에 올릴 때에는 이미 결정되어 있는 주소로(Logical address) 올리게 된다.
•
다른 비어있는 메모리 공간이 있더라도 0번지부터 올라가게 됨
•
컴파일 타임에 미리 결정되기 때문에 대단히 비효율적인 방법
•
시작 위치가 변경되면 컴파일 다시해야 함(절대 코드이기 때문)
•
컴파일러는 절대 코드(absolute code) 생성
◦
절대 코드 : complie time binding은 Logical address가 그대로 물리적 메모리에 올라오기 때문에 절대 코드라고 불린다.
•
지금의 컴퓨터 시스템에서는 사용하지 않는 방법
◦
컴퓨터 내부에서 한 개의 시스템만 돌아가는 때에는 다른 프로그램이 같이 올라갈 일이 없어서 컴파일할 때 물리적인 메모리 주소를 결정해서 올리는 방법이 사용됨
2.
load time binding
프로그램이 시작되어서 메모리에 올라갈 때 물리적인 메모리 주소가 결정됨(Loader 책임하에 부여)
•
컴파일 타임에는 Logical address만 정해진 상태
•
실행시키게 되면 물리적인 메모리의 빈 공간(ex. 500)부터 0번지 메모리를 올린다.
•
컴파일러는 재배치 가능 코드(relocatable code)를 생성한다.
3.
execution time binding(= run time binding)
시작된 이후에도 물리적인 메모리 주소 변환
•
load time binding과 동일하게 실행시키면 물리적인 메모리 공간에 올라감
•
일단 물리적 메모리 공간에 바인딩(ex.300번지)이 되었다가 실행되는 도중에 해당 내용이 다른 주소 루트로 이동(ex.700번지)할 수 있다.
→ 경우에 따라서 메모리에서 쫓겨날 수 있고 다시 올릴 때는 비어 있는 메모리에 바인딩을 해서 올리게 된다.
•
CPU가 주소를 참조할 때마다 binding를 점검해야 한다.(address mapping table)
◦
내용이 메모리에 어디에 올라가 있는지 주소 변환을 그때그때 해야한다.
◦
하드웨어적인 지원 필요(MMU - 그때 그때 주소 변환을 해줌)
•
올라갈 때 시작 위치는 바뀌지만 컴파일된 주소 자체까지 바꿀 수 없다. → 아니면 바꾸기 위해서 재컴파일
•
위치는 바뀌지만 Logical한 주소 변경은 힘듦
•
명령문을 실행하려고 할 때 20, 30번지 메모리 내용을 CPU로 읽어들이려고 하는데 그 주소가 Logical Address
◦
CPU가 메모리 몇 번지에 있는 내용을 달라고 하면 그 때 주소 변환을 해서 물리적인 메모리 위치를 찾아서 그 내용을 읽어서 CPU한테 전달
MMU(Memory-Management Unit)
logical address를 physical address로 매핑해주는 Hardware device(주소 변환을 해주는 하드웨어 자원)
•
그때그때마다 내용들이 어디에 올라가 있는지 주소 변환을 새로해주는 방법이 없어서 하드웨어적인 지원이 필요
•
기본적인 MMU에서는 limit Register, base register를 가지고서 주소 변환을 합니다.
◦
Relocation(base) register는 프로그램의 시작 위치를 가지고 있음
◦
Limit Register는 프로그램의 크기를 가지고 있음
→ 이 프로그램이 만약 악의적인 프로그램이라서 본인의 크기가 3000임에도 4000번지에 있는 내용을 달라고 할 수 있다. 이런 경우 크기 범위를 벗어나는 곳에서 physical memory 위치가 잡히게 된다. 있지도 않은 주소 범위를 가지고 달라고 하면 다른 프로그램이 존재하는 메모리를 달라고 한 것
CPU가 메모리 346번지에 있는 값을 달라고 함 | logical address |
주소 변환을 해야 함 | MMU에서 진행 |
시작 위치(relocation register) + 논리적 주소 ⇒ 물리적 메모리 주소 | Physical memory |
•
MMU Scheme
사용자 프로세스가 CPU에서 수행되며 생성해내는 모든 주소값에 대해 base register(= relocation register)의 값을 더한다.
•
User Program
◦
logical address만을 다룬다.
◦
실제 physical address를 볼 수 없으며 알 필요가 없다. → 요청이 됐을 때 MMU가 변환을 해서 얻게 되는 개념
•
악의적인 메모리 접근 문제를 해결하기 위한 방법
CPU에서 logical address를 요구한다 |
해당 논리 주소가 프로그램 크기보다 큰 논리 주소(limit register의 값)를 요구한 건 아닌지 확인하기 |
만약 크다면, trap이 걸림. 운영체제에게 CPU 재제권이 넘어간다. trap이 걸린 이유를 확인함(악의적인 메모리 공간 넘어감) |
크기 이내의 값이라면, relocation register의 값을 더해서 주소변환을 한 다음에 physical memory 어딘가에 있는 내용을 읽어다가 CPU에게 전달 |
Some Terminologies
1.
Dynamic Loading(Loading = 메모리로 올리는 것)
프로세스 전체를 메모리에 미리 다 올리는 것이 아니라 해당 루틴이 불려질 때 메모리에 load하는 것
→ 프로그램 필요 일부를 올리는 방법
•
memory utilization의 향상
•
가끔씩 사용되는 많은 양의 코드의 경우 유용 → 계속 올리면 비효율적이기 때문에
◦
오류 처리 루틴
•
필요하면 올라가고 필요없으면 메모리에서 내려가는 것은 오리지널 Dynamic Loading의 개념은 아니다. → 운영체제가 해주는 Paging System에 의한 것(운영체제가 직접 관리)
•
운영체제의 특별한 지원없이 프로그램 자체에서 구현 가능(OS는 라이브러리를 통해 지원 가능) → 프로그래머가 Dynamic Loading를 해보도록 하는 개념
◦
Dynamic Loading를 프로그래머가 쉽게 하도록 운영체제가 라이브러리 형태로 Dynamic Loading를 지원해준다.
◦
Paging 기법과 Dynamic Loading의 개념은 다르다.
◦
프로그래머가 명시하지 않고 운영체제가 알아서 올려두고 쫓아내는 것도 Dynamic Loading에 섞어 쓰기도 한다.
•
라이브러리를 사용하기 때문에 사용자가 어떻게 올리고 내리는지는 굳이 알 필요가 없다.
2.
Dynamic Linking
Linking : 여러군데 존재하던 컴파일될 파일들을 묶어서 하나의 실행 파일을 만드는 과정
Static linking
•
라이브러리가 내 프로그램 실행 파일 코드에 포함
•
내 코드든, 라이브러리 코드든 내 프로그램 주소 공간 안에서 실행이 됨
•
실행 파일의 크기가 커짐
•
동일한 라이브러리를 각 프로세스가 메모리에 올리기 때문에 메모리 낭비가 심함.
Dynamic linking
•
실행 파일에는 라이브러리가 별도의 파일로 존재하고, 그 라이브러리 위치를 찾을 수 있는 작은 코드(stub, 포인터 역할)를 두고 라이브러리 자체는 포함하지 않는 것
•
프로그램이 실행되다가 라이브러리 호출하는 곳에 이르면 라이브러리가 어디 있는지 찾는다.
◦
필요하면 메모리에 올리고(디스크에서 가져옴), 이미 메모리에 올라와 있다면 해당 메모리 주소에 가서 이 라이브러리를 실행을 함
◦
이미 메모리에 올라와 있는 라이브러리는 메모리에 계속 올려두고 공유해서 사용이 가능해지게 됨
•
Dynamic linking를 해주는 라이브러리 = DLL = shared library(object)
•
운영체제의 도움 필요
3.
Overlays
메모리에 프로세스의 부분 중 실제 필요한 정보만을 올림(Dynamic Loading과 동일)
→ 초창기 컴퓨터 시스템의 메모리가 작았음, 프로그램 하나를 메모리에 올리는 것도 어려움
→ 큰 프로그램을 쪼개서 메모리에 올리는 방식으로 사용
•
운영체제의 지원없이 사용자에 의해 구현
•
초창기 시스템에서는 프로그래머가 수작업으로 코딩을 했다. (manual overlay) → 프로그래밍 매우 복잡
4.
Swapping
프로세스를 일시적으로 메모리에서 backing store(메모리에서 쫓아내진 것들을 저장하는 공간)로 쫓아내는 것 ⇒ 하드 디스크
•
backing store(= swap area)
◦
하드 디스크 : 많은 사용자의 프로세스 이미지를 담을 만큼 충분히 빠르고 큰 저장 공간
•
Swap In / Out
일반적으로 중기 스케줄러(Swapper)에 의해 swap out 시킬 프로세스 선정
◦
중기 스케줄러가 일부 프로그램을 골라서 통째로 메모리에서 디스크로 쫓아냄.
◦
어떤 프로그램을 쫓아내는가?
▪
priority가 낮은 프로세스
◦
Swapping 시스템이 지원이 되기 위해서는?
▪
Swapping이 효율적으로 동작하기 위해서는 Run time binding이 지원되어야 할 것
priority-based CPU scheduling algorithm
•
priority가 낮은 프로세스를 swapped out시킴
•
priority가 높은 프로세스를 메모리에 올려 놓음
Compile time 혹은 load time binding에서는 원래 메모리 위치로 swap in 해야 함
Execution time binding에서는 추후 빈 메모리 영역 아무 곳에나 올릴 수 있음
swap time은 대부분 transfer time(swap되는 양에 비례하는 시간)
◦
디스크는 디스크 헤더가 움직이는 시간이 대부분을 차지
Allocation of Physical Memory
물리적인 메모리를 어떻게 관리할 것인가?
•
낮은 주소 부분에 운영 체제 커널이 항상 상주
•
높은 주소 영역에는 사용자 프로그램들이 올라감
◦
사용자 프로그램들이 올라가는 영역을 관리하는 방법
1.
Contiguous allocation(연속 할당)
프로그램이 메모리에 올라갈 때 통째로 올라가게 됨
•
각각의 프로세스가 메모리의 연속적인 공간에 적재되도록 하는 것
2.
Noncontiguous allocation(불연속 할당)
프로그램을 구성하는 주소 공간을 잘게 쪼개는 게 가능
•
하나의 프로세스가 메모리의 여러 영역에 분산되어 올라감
•
현대의 시스템은 불연속 할당을 채택
•
여러 가지 기법
◦
Paging
▪
프로그램의 주소 공간을 같은 크기의 page로 잘라서 page 단위로 메모리 여기저기에 올라감
◦
Segmentation
◦
Paged Segmentation
Contiguous allocation(연속 할당)
1.
고정 분할(Fixed parition) 방식
프로그램이 들어갈 사용자 메모리 영역을 미리 파티션으로 나누어 놓는 것
•
낭비되는 조각이 발생하게 된다. → 외부 조각, 내부 조각
외부 조각(external fragmentation)
내부 조각(internal fragmentation)
•
융통성이 없음
◦
동시에 메모리에 load되는 프로그램의 수가 고정됨
◦
최대 수행 가능 프로그램 크기 제한
2.
가변 분할(Variable partition) 방식
프로그램이 들어갈 사용자 메모리 영역을 미리 나눠놓지 않는 것
•
프로그램이 실행될 때마다 차곡차곡 메모리에 올리는 방식
◦
프로그램의 크기를 고려해서 할당
◦
분할의 크기, 개수가 동적으로 변함
•
미리 메모리 공간을 나눠두지 않았기 때문에 내부 조각(internal fragmentation)이 생기지 않는다.
•
프로그램이 실행하다가 종료하면 홀 발생
홀(hole)
•
가용 메모리 공간 중 어디다가 새로운 프로그램을 집어 넣어야 하는가?
Dynamic Storage(Memory)-Allocation Problem
: 가변 분할 방식에서 size n인 요청을 만족하는 가장 적절한 hole를 찾는 문제
Dynamic Storage-Allocation Problem
1.
First-fit
Size가 n 이상인 것 중 최초로 찾아지는 hole에 할당
•
hole 중에서 가장 처음 발견되는 hole에다가 할당하는 것
2.
Best-fit
n 이상인 hole들 중에서 가장 작은 hole을 찾아서 할당(프로그램 크기와 가장 근접)
•
hole를 다 살펴본 다음에 프로그램 크기하고 가장 잘 맞는 hole에다가 프로그램을 올려두는 것
•
hole들을 탐색하는 시간 부담이 존재
3.
Worst-fit
가장 큰 hole에다가 프로그램을 할당
•
가장 큰 hole를 찾기 위해서 전체 hole들을 탐색해야 함
•
어리석인 방법 → 큰 홀에는 지금 실행하는 프로그램보다 더 큰 프로그램이 들어갈 수도 있다. 또한 지금 실행하는 프로그램에 적합한 hole이 있을텐데 큰 홀에다가 넣는 것은 적절하지 않다.
First-fit과 best-fit이 worst-fit보다 속도와 공간 이용률 측면에서 효과적인 것으로 알려져있다.
•
first-fit은 hole를 찾는 overhead가 적어서 좋다.
•
best-fit은 제일 적당한 hole를 찾아서 넣기 때문에 미래를 위해서 좋은 방식이다.
Compaction
external fragmentation 문제를 해결하는 한 가지 방법
•
사용 중인 메모리 영역을 한군데로 몰고 hole들을 다른 한 곳(중간중간 작은 hole)으로 몰아 큰 block를 만드는 것
•
hole이 작아서 사용을 못하는 문제를 해결 가능하다.
•
매우 비용이 많이 드는 방법
example) 디스크 조각 모음 → 파일의 데이터를 이동시킴
◦
compaction같은 경우는 ‘바인딩을 다 점검해야하고 하는 문제 발생', 프로그램 하나가 아니고 전체 프로그램의 바인딩에 관련된 문제가 된다.
•
최소한의 메모리 이동으로 compaction하는 방법(매우 복잡한 문제)
•
Compaction은 프로세스의 주소가 실행 시간에 동적으로 재배치 가능한 경우에만 수행될 수 있다.
◦
Run-time binding이 지원돼야지만 compaction이 가능하다.
현대의 프로그램은 연속 할당보다는 불연속 할당이다.
Paging
프로그램을 구성하는 주소 공간을 같은 크기의 페이지 단위로 자른 것이다. 페이지 단위로 물리적 메모리에 올려두거나 패킹 스테이지에 내려두는 것
•
물리적 메모리도 페이지 하나가 들어갈 수 있는 단위로 쪼개져 있음(페이지 프레임)
◦
페이지 프레임 하나 하나에는 페이지들이 올라갈 수 있음
•
hole들의 크기가 균일하지 않아서 생기는 문제, 홀들을 한 곳으로 모으는 솔루션이 필요가 없게 된다.
◦
어차피 물리적 메모리에서 비어있는 공간이 곧 페이지 프레임이 비어있는 것이기 때문에 프로그램의 어떤 페이지든지, 비어있는 위치 어디든 들어갈 수 있다.
•
불연속 할당을 하면 주소 변환이 복잡해진다.
◦
MMU에서 주소 변환을 할 때, 단지 시작 주소만 더해서 주소 변환을 하는 것이 아니고 짤려진 각각의 페이지가 물리적인 메모리 어디에 올라가있는지를 확인해야 한다.
◦
즉, 주소 변환을 페이지 별로 해야해서 주소 바인딩이 더 복잡해지는 게 특징
Paging
•
Process의 virtual memory를 동일한 사이즈의 page 단위로 나눔
•
virtual memory의 내용이 page 단위로 불연속적으로 저장됨
•
일부는 backing storage에, 일부는 physical memory에 저장
•
방법
◦
physical memory를 동일한 크기의 frame으로 나눔 → page frame
◦
logical memory를 동일 크기의 page로 나눔(frame과 같은 크기)
◦
모든 가용 frame들을 관리
◦
page table을 사용하여 logical address를 physical address로 변환
▪
단순히 Register를 사용하는 주소 변환으론 안되기 때문에 page table 사용
◦
external fragmentation 발생 안 함 → 같은 크기로 잘라놨기 때문에
◦
internal fragmentation 발생 가능
▪
페이지 단위로 virtual memory를 썰다보니깐 마지막에 가서는 페이지 하나보다 남는 자투리가 작을 수 있다.
▪
빈 공간이 약간은 생길 수 있기 때문에 internal fragmentation이 발생할 수 있다.
▪
페이지 크기를 잘게 썰게 되면 내부 조각의 영향력이 줄어든다.
Segmentation
어떤 의미있는 단위로 자르는 것
프로그램을 구성하고 있는 주소 공간이라는 것이 코드, 데이터, 스택이라는 의미있는 공간으로 구성이 되어 있다.
•
코드 Segmentation
◦
각각의 함수들(의미 단위)을 segment로 만들기도 가능
•
데이터 Segmentation
•
스택 Segmentation
각각의 Segment를 필요시에 물리적 메모리 다른 위치에 올려둘 수 있도록 함
•
이 때도 Segment 단위로 주소 변환을 해줘야 함
•
Segment는 의미 단위로 자른 것이기 때문에 크기가 균일하진 않다.
•
프로그램을 통째로 메모리에 올릴 때 Dynamic Storage-Allocation Problem이 Segmentation에서 발생하게 된다.