티스토리 뷰

운영체제

[운영체제] 메모리와 페이징

Hani_Levenshtein 2021. 1. 1. 03:00

운영체제의 주된 기능인 프로세스 관리를 마무리하고 주기억장치 관리로 넘어가 볼까 합니다.

 

컴퓨터의 RAM은 항상 부족한 상태에 놓여있기 때문에 주기억장치 관리는 운영체제의 중요한 요소 중 하나입니다.


Loading

프로그램의 소스 코드는 컴파일러와 어셈블러를 거쳐 기계어로 번역됩니다.

링커는 SSD를 뒤져서 번역된 오브젝트가 실행되기 위한 라이브러리를 묶어줍니다.

링커에 의해 실행 파일이 생성되며, 생성된 파일은 로더에 의해 메모리에 적재됩니다.


Memory Structure

메모리로 적재된 프로세스는 크게 4가지 영역으로 구분됩니다.

 

code 영역에는 프로세서가 실행할 기계어 코드가 저장됩니다.

data 영역에는 코드에서 생성된 전역 변수, 정적 변수, 배열 그리고 구조체 등이 저장됩니다.

stack 영역에는 함수 내의 지역변수, 함수가 실행된 후 제자리로 돌아오기 위한 주소 등이 저장됩니다.

heap 영역에는 배열이나 구조체의 동적 할당처럼 사용자가 관리하는 메모리 영역입니다.

각 영역의 메모리 상 위치는 낮은 주소부터 code, data, heap 그리고 stack 순입니다.

그중에서 heap은 아래서 위로, stack은 위에서 아래로 쌓입니다.


MMU

로더가 SSD 내의 프로그램을 메모리로 올리기 전에 메모리의 어느 위치에 적재될 지도 결정을 해야 합니다.

적재될 메모리 상 위치를 결정하는 것은 프로세서와 메모리 사이에 위치하는 MMU(Memory Management Unit)입니다.

 

MMU 내부에는 Base Register, Limit Register, Relocation Register가 존재합니다.

Base Register에는 논리 주소의 범위 중에서 가장 첫 번째 값 + Relocation Register 값이 저장됩니다.

Limit Register에는 논리 주소의 범위 중에서 가장 마지막 값 + Relocation Register 값이 저장됩니다.

 

Base Register와 Limit Register사이의 값에 다른 프로세스가 존재하지 않고 물리 주소를 벗어나지 않는다면 프로세서는 MMU에게 보낸 논리 주소를 통해 물리 주소로 접근할 수 있게 됩니다.


Dynamic Loading

프로그램이 실행될 때 모든 코드를 사용하지는 않습니다.

필요한 부분만 필요할 때 적재함으로서 메모리를 쓸데없이 차지하는 것을 방지할 수 있습니다.

 

Dynamic Linking

모든 실행파일이 모두 다른 라이브러리를 사용하는 것은 아닙니다.

각 실행파일이 중복된 라이브러리를 사용할 가능성이 높으므로 Load전에 Link를 해놓지 않습니다.

일단 Load를 해놓고 정말 프로세스가 라이브러릴 사용해야 할 때, 라이브러리를 메모리에 올려놓습니다.

Link를 통해 메모리 상의 프로세스는 메모리 상의 라이브러리를 참조할 수 있습니다.

여러 프로세스가 해당 라이브러리를 요구할 때 Dynamic Linking이 힘을 발휘하게 됩니다. 

 

Swapping

항상 프로세스가 사용되고 있진 않습니다. 이런 프로세스가 자원을 점유하고 있는 것은 메모리의 낭비입니다.

따라서 저장 장치의 Backing Store로 잠깐 내려서(Swap-out) 메모리를 확보한 후, 해당 프로세스가 프로세서에 의해 요구되면 다시 메모리로 이동(Swap-in)시킵니다.

다시 이동할 때, 새로 위치할 메모리 공간이 MMU의 Relocation Register에 의해 지정되는 것은 동일합니다.


External fragmentation

프로세스가 메모리를 점유할 때, 메모리와 메모리 사이에 사용하지 않는 공간이 생깁니다.

이 공간을 Hole이라고 하는데 Hole의 용량이 많을수록 메모리를 비효율적으로 사용한다고 할 수 있습니다.

이를 Hole의 외부 단편화(External fragmentation)라고 합니다. 


메모리로 올라올 프로그램의 용량이 개별 Hole들의 용량보다 작을 경우

프로세스가 어느 Hole에 들어가야 가장 적절한지에 대한 알고리즘은 없기에 최적의 방법 역시 없습니다. 

Hole에 들어가더라도 여전히 Hole이 존재합니다.


메모리로 올라올 프로그램의 용량이 개별 Hole들의 용량보다 클 경우

Swapping을 통해 메모리를 확보해야 하지만 여전히 Hole이 존재합니다.

메모리로 올라올 프로그램의 용량이 개별 Hole들의 용량보다 크지만 Hole들의 용량 합보다 작을 경우

Compaction을 적용해봅시다.


Compaction

Hole을 없애기 위해서 프로세스가 사용하는 공간을 옮겨 서로 붙이는 것을 Compaction이라고 합니다.

Compaction은 Hole을 없애는 명확한 방법이긴 하지만 프로세스를 옮기는데 필요한 비용이 큽니다.

프로세스가 사라지고 생성될 때마다 Compaction이 일어나야 하니 번거롭습니다.

다른 방법을 찾아야겠습니다.

 


Paging

메모리 내의 프로세스의 위치를 옮기지 않고 Hole이 차지하는 용량을 활용할 수 있는 방법입니다.

 

프로세스를 일정한 크기로 나눠진 여러 개의 페이지, 메모리를 일정한 크기로 나눠진 여러 개의 프레임으로 고려합니다.

페이지와 프레임은 서로 크기가 동일하기 때문에 하나의 페이지는 하나의 프레임에 들어갈 수 있습니다.

페이징의 목적은 연속적인 페이지로 이루어진 프로세스를 불연속적인 프레임에 넣는 것입니다.

 

다만, 일반적인 방법으로 프로세스를 쪼개서 프레임에 넣으면 제대로 동작이 불가능할 것입니다.

쪼개진 프로세스에 의해 CPU가 참조할 주소가 정상적인 위치에 존재하지 않기 때문입니다.

페이징을 위해서 MMU가 다시 한번 등장해야 합니다.

 

CPU가 여러 개의 페이지로 쪼개진 프로세스를 참조하기 위해선 MMU의 역할이 중요합니다.

CPU가 보낸 논리주소는 RAM을 거치기 전에 MMU를 방문합니다.

 

논리주소의 상위비트는 Page Number(Logical Address / Page Size)가 됩니다.

논리주소의 하위비트는 Offset(Logical Address % Page Size)이 됩니다.

MMU는 Page Table을 보고 Page Number를 Frame Number로 변환시켜줍니다.

따라서 MMU는 물리 주소(Frame Number*Page Size+Offset)를 찾을 수 있습니다.


이해를 위해 CPU와 MMU를 분리했지만 사실 MMU는 CPU 내부에 존재합니다.

그리고 MMU내부에는 TLB(Translation Lookaside Buffer)라는 캐시가 담겨있으며 CPU로부터 논리 주소를 받아 해당 주소가 TLB 내에 존재하면 물리 주소를 반환하는 형식으로 메모리에 접근할 수 있습니다.

 

TLB에서 해당되는 주소가 없을 때는 Page Table에서 찾습니다. 

Page Table은 메모리에 있을 수도 있고 CPU에 있을 수도 있는데, 논리 주소를 받고 해당 주소가 Table 내에 존재하면 물리 주소를 반환하는 형식으로 메모리에 접근할 수 있습니다.

 

TLB와 Page Table은 캐시 개념이기 때문에 용량과 속도의 Trade-off 관계에 있습니다.

 

Page Table에도 없으면 Disk에서 찾으며, Page Table과 Disk에서 찾은 주소는 TLB에 쓰인 후 물리 주소로 변환됩니다.


Internal fragmentation

페이징을 위해 프로세스를 일정한 크기의 여러 개의 페이지로 나눈다고 했습니다.

하지만 프로세스가 해당 크기로 나누어 떨어지지 않을 경우 마지막 페이지는 프레임의 크기와 달라지게 됩니다.

남은 공간은 결국 Hole이 되는 내부 단편화가 발생하지만 Hole의 크기는 메모리 크기에 비해 무시할 정도로 작습니다.


Protection

OS가 컴퓨터 시스템에서 해야 할 중요한 일 중 하나는 해킹의 가능성을 차단하는 것입니다.

이를 위해 Page Table에 3개의 비트를 두어 특정 작업만 가능하도록 제어를 할 수 있습니다.

 

만약 허용되지 않은 작업이 명령으로 들어올 경우 Page Table이 CPU에게 인터럽트를 보냅니다.

CPU가 이런 인터럽트를 받으면 OS내의 ISR에게 달려가 해당 프로세스를 종료하도록 요청합니다.

따라서 정상적이지 않은 접근에 대한 차단을 통해 OS는 컴퓨터가 해킹당하는 것을 예방할 수 있습니다.


Sharing

하나의 프로그램 중 일부가 여러 번 메모리에 적재될 수 있습니다.

적재된 프로세스 중에서 code영역은 서로 겹칠 것이기 때문에 메모리의 낭비라고 할 수 있습니다. 

code영역이 변하지 않는 프로그램에 한하여 한번만 적재하여 서로 공유하여 이를 막을 수 있습니다. 

'운영체제' 카테고리의 다른 글

[운영체제] 가상 메모리  (0) 2021.01.23
[운영체제] 세그멘테이션  (0) 2021.01.15
[운영체제] 교착상태  (0) 2020.12.26
[운영체제] 프로세스 동기화  (0) 2020.12.24
[운영체제] CPU 스케쥴링  (0) 2020.12.24
댓글