본문 바로가기
Computer Science/운영체제

Operating System - Ch09. Main memory

by NCTP 2022. 6. 16.

본 글은 Operating System Concepts 10판을 기준으로 작성되었습니다...!

 

 

 

 

메모리는 아주 비싼 자원이다... 그래서 아주 잘 사용해야하고, 아껴 쓰고, 효율적으로 사용해야한다.

그렇다면 메모리 관리는 어떻게 해야할까? 

이에 대해서 Ch09에서 알아보자.

 


 

메모리 관리에 있어서 중요한 이슈들

 옛날 배치 시스템에서는 별 중요한 요소는 아니었다만... 멀티프로그래밍 환경이 찾아오면서 메모리 관리가 매우 중요해짐.

 

- 여러 프로세스에 대한 지원

- 실제 Physical memory보다 더 큰 용량을 가지는 프로세스도 지원할 수 있어야 한다.

- 메모리 자원 보호

- 메모리 자원 공유

- 성능

  

Virtual Memory (VM)

Virtual memory는 메모리를 만들어서 physical memory과 분리해서 사용하는 개념이다. 

 

- Logical address: CPU에서 정의한 메모리 주소

- Physical address: 메모리 장치에서 인식하는 물리적인 메모리 주소.

 

 

Address binding 주소 바인딩

데이터를 메모리에 저장하기 위해서 메모리 공간을 확보하고, 그 공간의 주소를 알아야 한다..

논리적 주소(Logical or Virtual memory)를 사용할 때, 논리적 주소만으로는 실제 메모리의 주소를 알 수 없다.

따라서 논리적 주소와 실제 메모리 주소를 연결시키는 작업이 필요한데, 이를 Address binding(주소 바인딩)이라고 한다.

 

다음 세 단계중 아무 때나 주소 바인딩을 할 수 있는데:

1. Compile time (컴파일 시): 컴파일 시에 정해진 메모리 주소에 데이터가 위치하게 됨. 만약 이때 정해진 메모리 주소를 바꾸고 싶다면, 재컴파일 해야함. 그렇지 않으면 메모리 주소를 변경하지 않는 것이 주류. 명시적이다. 컴파일 할 때도 오버헤드가 없고, 로드 때도, 실행 시에도 앞으로도 없음. 가장 큰 단점으로는 실행 주소가 정적이어서 유연하지 못하다는 것. 이런 구조는 메모리 낭비가 매우 심해진다.

2. Load time (로드 시): 로드가 되는 타이밍에 따라 데이터의 메모리 주소가 정해짐. 예를 들어 함수의 시작 주소로부터 상대적으로 10만큼 떨어져있는 주소에 데이터를 저장한다면, 해당 함수가 60이라는 주소에서 시작했을 때 70에 데이터를 저장한다. 유연하다. 컴파일 시 오버헤드가 적어 컴파일이 빠름. 그리고 실행 중에 오버헤드가 없어 빠르다. 대신 로드할 때 일괄적으로 처리하므로 로딩이 오래걸림. 프로그램이 매우 무거울 경우 로딩이 아주 오래걸릴 것. 

3. Execution time (실행 시): 실행할 때 시작 주소에 덧셈처리를 하여 그 주소에 데이터를 저장함. 컴파일, 로드 시 고려할 요소가 없어 오버헤드가 없음. 즉 로드와 컴파일이 빨라짐. 하지만 매번 실행할 때마다 주소를 새로 바인딩하는 연산을 수행해야함. 이 과정에서 오버헤드가 많이 발생함. 대부분의 운영체제가 실행 시 주소 바인딩을 함. CPU가 직접 주소에 덧셈 연산을 계속 하면 오버헤드가 엄청 발생하는데, 이를 해결하기 위해 하드웨어적으로 도움을 받음.

 

MMU(Memory Management Unit): MMU에서 Logical address를 physical address로 바꿔줌

 

 

Memory address transition: MMU가 logical -> physical, physical -> logical로 전환해 주는 동작

 

Contiguous Allocation  연속 할당

연속적으로 할당하기. 프로세스 하나를 메모리에 적재할 때, 프로세스의 메모리 공간을 그대로                                       Physical memory에 연속적으로 할당하는 것. (긴 막대를 자르지 않고 그대로 넣는 것.)             

(Logical address + relocation register = Physical address) Logical 주소에 relocation register를                                             더하면 짠, Physical address 완성

trap은 메모리 보호를 위해 발생함

Multiple-partion allocation

 - Hole, 구멍: 말 그대로 구멍, 사용중인 메모리 사이에 사용 가능한 메모리가 작게 비어있는 경우임. 작은 구멍에 큰 막대를 넣을 수 없듯이, 작게 남아있는 메모리 공간을 큰 프로세스에게 할당해줄 수 없음. 그런데, 이 구멍들을 모아서 사용할 수 있다면? (다음 화에 계속), Fragmentation이라 하는데, 이 다음에 알아보자. 그래도 구멍보다 작은 프로세스는 로드가 가능함. 이런 경우, 여러 구멍들 중에 어느 구멍에 프로세스를 넣어야 Best 일까?

 

- First-fit: 처음 본 알맞은 구멍에 넣기

- Best-fit: 구멍의 크기와 할당하고자 하는 공간의 크기를 비교해서 가장 잘 맞는 구멍에 넣기

- Worst-fit: 제일 큰 구멍에 넣기.

 

Fragmentation 파편화/단편화

 - External Fragmentation 외부 파편화: Contiguous allocation에서 발생하는 Fragmentation, Hole들이 이곳저곳 있는 상황

 - Compaction: 외부 파편화의 해결방안 중 하나. 흩어진 구멍들을 재구성하는 것을 compaction이라고 한다. Secondary Storage에 구멍들을 모아서 연속적으로 만들어서 다시 메모리에 올린다. 이 과정에서 I/O가 두번 발생하는데, I/O는 느리게 동작하기 때문에 CPU utilization이 낮아진다. 따라서 Compaction이라는 것이 그렇게 좋은 방법은 아니다.

 

결국, contiguous allocation을 잘 안쓰고 Paging 기법을 쓴다.

Paging 페이징

  페이징의 기본 개념은,  logical memory상에 같은 크기의 블럭들로 메모리가 구분되어 있는데, 이를 Page라고 함. 그리고 이와 같은 크기로 physical memory에도 메모리를 동일한 단위로 분리한다. 이는 Frame이라고 한다.

  실제 physical memory에서는 연속적이지 않지만 logical memory에서는 연속적임. 이를 위해 페이지와 프레임을 매핑해주는데, 이 때  Page Table을 구축한다. 프로세스 당 하나의 page table을 가지고 있다.

  Frame이 틈틈히 비어있어도 다른 프로세스가 왔을 때 빈 Frame을 모으지 않고 프로세스의 Page에 매핑해서 사용하면 되기 때문에 CPU utilization이 높아진다.

 

Page tables

  운영체제에 의해 관리되며, page number과 frame number를 매핑해줌. page table을 통해 page에 매핑된 frame을 찾을 수 있음. 

 

Address translation

 - Logical address는 page number(p) 와 offset(d)를 가짐.

 - Page number는 page table의 index로서 관리된다. 페이지의 인덱스에 따라 페이지에 맞게 매핑되어 있는 프레임을 찾을 수 있고, 프레임에서 똑같은 offset만큼 시작 주소에서 떨어진 곳에서 찾고자 하는 데이터를 찾을 수 있다.

 

 

페이징을 하면 외부 단편화는 사라지지만, 약간의 내부 단편화가 발생한다.

Paging

Page Table 구현

  프로세스가 많이질수록 Page Table도 많아지고, 이 많은 양의 Page Table을 자그마한 MMU에 집어넣을 수는 없고, Memory에 넣으면 Memory에 두 번 접근하게 되어 오버헤드가 많이 발생한다. 따라서 Page Table를 잘 구현하는 것은 중요하다. 그럼 어떻게 Page Table를 잘 구현할 수 있을까?

  - 접근을 많이 하는 페이지만 MMU에 넣고 사용하면 효율적이지 않을까? 

  - 자주 쓰는 페이지도 있지만 잘 안쓰는 페이지도 있을 텐데... 모든 페이지를 모조리 관리해줄 필요가 있을까?

  - 캐시의 개념을 활용해보자.

   

TLB (Translation Look-aside Buffer)

   MMU 내부에 존재하는 TLB는 Associative memory라는 형태의 캐시로 되어있는데, 페이지 넘버를 받으면 그에 대응하는 프레임 넘버를 리턴해 메모리로 접근하는 식으로 동작한다. (cache hit) 만약 찾는 페이지 넘버와 대응하는 프레임 넘버가 존재하지 않으면, 직접 메모리에 가서 찾고 버퍼를 업데이트한다. cache hit하는 비율이 실제로는 99%가 넘는다고 한다.

 

TLB

Temporal locality vs Spatial locality

   - temporal locality: 한번 참조되면 조만간 다시참조될 확률이 높다.

   - spatial locality: 참조되면 그 주변에 있는 것들도 참조될 확률이 높다.

  TLB는 두 locality의 속성 모두 갖는다.

 

TLB miss

  TLB에 찾고자 하는 것이 없다면,

  1. 하드웨어 차원에서 해결된다. MMU에서 해준다. (Intel x86)

  2. CPU 차원에서 해줄 수 없을 때는 OS 차원에서 해준다. 이 때는 오버헤드가 크게 발생한다. 

  따라서 대부분 MMU가 처리해준다.

 

  CPU를 점유하고 있던 프로세스가 내려가고 새로운 프로세스가 CPU를 점유했을 때 TLB는 리셋이 된다. TLB가 가진 page table을 싹 비우고 새 프로세스에 맞게 새로 채워나간다. 스레드 간의 문맥전환이 이루어질 때는 리셋하지 않는다.

 

Main Memory Protection

  Paging 방식에서는 페이지 테이블이 추가 비트를 통해 각 페이지를 가용/불가용 상태로 나눠서, 만약 5개의 페이지가 필요하다면 페이지 테이블에서 5개의 페이지까지 가용상태로 바꿔 사용한다. 

 

Page Table Entries (PTEs)

  페이지 테이블 한 칸에는 여러 추가 비트들이 존재한다.

  1. Valid bit (V): 유효한 페이지인지 아닌지

  2. Reference bit (R): 참조되고 있는 페이지인지 아닌지

  3. Modify bit (M): 변경되었는지 아닌지

  4. Protection bits (Prot): protection을 하기 위해 있는 추가 비트 (Read, Write, Execute 등의 상태를 저장)

  5. Frame number (FN): 프레임 넘버 저장

 

Page Table Structure 페이지 테이블 구조

1. Hierarchical paging 계층적 페이징

  페이징 테이블을 또 페이징을 해주는 방식이다. outer page table과 inner page table이 존재하게 된다.

  대부분  이 방식을 사용한다. outer page table에서 valid한 페이지라면 그 페이지 안의 inner page table을 활성화해서 사  용하고, outer page table에서 invalid한 페이지라면 그 페이지의 inner page table을 사용하지 않도록 해서 전부 일일이 관리하는 것보다 더 작은 페이지 테이블로 관리할 수 있다. 하지만 모든 페이지를 사용하고 모든 inner page tables를 사용하게 되는 최악의 상황에서는 원래보다 많은 자원을 소모하는데, 그런 경우는 거의 없으므로 좋은 방법이다.

Hierarchical paging

 

2. Hashed page table

 페이지 넘버를 Hash function을 돌려서 나온 값을 통해 physical memory로 접근한다.

3. Inverted page table

 각 프로세스가 페이지 테이블을 만드는 것이 아니라, 프레임 테이블 하나만 만들어서 physical memory 기준으로 메모리를 관리하는 방식이다.

 

Shared pages

  만약 여러 프로세스가 동일한 페이지를 사용한다면, 한번만 메모리에 할당을 하고, 여러 프로세스가 같이 같은 프레임을 공유해서 사용할 수 있다. 페이지 테이블을 사용하면서도 자원들을 공유하면서 physical memory를 아낄 수 있다.

 

shared pages

 

페이징의 장점

 - 피지컬 메모리 할당이 쉽다

 - 외부 단편화가 발생하지 않는다.

 - 하드 디스크로 잘 사용되지 않는 페이지를 옮겨서 메모리 효율을 높일 수 있다.

 - 데이터 쉐어가 쉽다.

 - 페이지 보호가 쉽다.

페이징의 단점:

 - 내부 단변화가 여전히 존재한다. (개선이 불가능하고, 오버헤드가 크지 않다 안고 간다.)

 - 메모리 참조에 오버헤드가 발생한다. (하나의 데이터에 접근하기 위해 메모리 접근을 두번 한다. TLB로 해결)

 - 페이지 테이블의 크기가 크다. (Hierarchical paging으로 해결)

Segmentation 분할

Segmentation은 프로세스의 Code segment, Data, Heap, Stack을 따로따로 구분해서 메모리에 할당하는 개념이다.

Segment number와 offset으로 Segment 테이블을 관리한다.

Sharing pages과 같은 개념으로 Sharing segments가 가능하다.

외부 단편화가 발생한다. 이를 해결하기 위해서, 여기서도 페이징을 사용한다.

 

Segmentation의 장점

- segment sharing 편하다.

- segment 테이블의 크기가 작다.

- locality가 좋아진다.

- segment 보호가 쉽다.

- 내부 단편화가 없다.

Segmentation의 단점

- 외부 단편화가 발생한다.

 

실제로 Segment와 paging을 함께 사용하는데, 이를 Pages segment, Segmentation with paging이라고 한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

댓글