Sqix

CS - OS - 07. 가상 메모리 본문

CS/OS

CS - OS - 07. 가상 메모리

Sqix_ow 2021. 11. 24. 02:18

리눅스를 기준으로 보면 프로세스 하나당 메모리는 4GB를 사용한다. 반면 우리 메모리는 16GB? 32GB? 이정도에 불과하다. 현재 우리가 통상 사용하는 컴퓨터는 폰 노이만 구조이므로 프로세스는 메모리를 할당받아 그 위에서 동작해야 하지만, 모든 프로세스를 전부 실제 메모리에 올리려면 비용이 너무 많이 소요된다. 그래서 우리는 가상 메모리를 사용한다.

가상 메모리

실제 메모리보다 많은 양의 메모리를 사용하는 것 처럼 보이는 기술. 실제로 순간 우리가 사용하는 메모리의 크기는 작다는 점에 착안하여 고안된 기술로, 프로세스 간 공간이 분리되어 특정 프로세스의 이슈가 발생하더라도 시스템 전체에 영향을 주지는 않는다.

 

Concept

  • 프로세스는 가상 주소를 사용하며 실제 해당 주소에서 데이터를 읽고 쓸 때만 물리 주소로 바꿔준다.
    • Virtual Address : 프로세스가 참조하는 가상 주소
    • Physical Address : 실제 메모리 주소
  • MMU 사용 -> CPU가 코드 실행 시, 가상 메모리 접근이 필요할 때 해당 주소를 물리 주소값으로 변환하는 장치
                       CPU는 가상 메모리를 다루고, 실제 주소 접근 시 MMU를 통해 물리 메모리에 접근

페이징(Paging System)

  • 페이징 개념
    • 크기가 동일한 페이지로 가상 주소 공간과 이에 매칭하는 물리 주소 공간 관리
    • 하드웨어 지원이 필요 (ex : Intel x86_64 CPU : 4KB, 2MB, 4MB, 1GB 지원)
    • 페이지 번호를 기반으로 가상 주소 / 물리 주소 매핑 정보를 기록하거나 사용
  • 프로세스 PCB에 Page Table 구조체를 가리키는 주소가 들어 있고, 여기에는 가상 - 물리 주소간 매핑 정보가 있다.
  • Page Table 구조체는 이전 프로세스에서 다뤘던 task_struct의 mm 부분(struct mm_struct)에서 관리된다.
  • 페이지 블록만큼의 공간을 사용하지 않을 경우 공간의 낭비(내부 단편화)가 발생한다

페이징에 필요한 페이징 프레임은 고정된 크기의 block이다(리눅스 기준 4KB). 페이지를 설정하게 되면 가상 주소 v는 v=(p,d)이며, 이때 p는 가상 메모리 페이지 번호, d는 p 안에서 참조하는 위치이다. 

v = (p, d) = (page number, offset)

만약 4KB를 예로 들면, 0-11bit는 Page Offset, 12 - 31은 Page Number가 될 것이다.

0   2   4   6   8   10  12  14  16  18  20  22  24  26  28  30  31
+----------------------------------------------------------------+
|                     |                                          |
|        PAGE         |                  PAGE                    |
|       OFFSET        |                 NUMBER                   |
|                     |                                          |
+----------------------------------------------------------------+
  • Page Table
    • 물리 주소에 있는 페이지 번호와 해당 페이지의 첫 물리 주소를 매핑해 놓은 표로 메모리에 할당되어 있다.
    • Page Number에 대한 실제 물리 주소에 Page Offset을 더해 실제 물리 주소를 알 수 있게 된다.
    • 현재 해당 페이지가 물리 주소에 할당되어 있는지 여부를 Valid-Invalid Bit를 통해 알 수 있다.

다중 단계 페이징 시스템

 

32bit 시스템에서 4KB 페이지를 위한 페이징 시스템은 위 그림과 같다. 그러나 이에 대한 페이지 테이블을 일괄적으로 전부 작성하게 되면 필요없는 페이지 정보까지 생성 / 관리되므로 공간이 낭비될 수 있다. 그래서 필요없는 페이지를 생성하지 않고 페이지별로 단계를 나누어 페이지 디렉토리별로 생성 / 관리하면 공간을 절약할 수 있다.

 

페이지별로 번호를 나타내는 bit를 구분해서 단계를 나눈다. 

  • CR3 레지스터 : 물리 메모리에 있는 페이지 디렉토리의 시작점 주소를 가리킨다.
  • 페이지 디렉토리
    • 각 작업마다 하나씩 할당되며, 10bit = 1024개의 엔트리를 가지고 엔트리는 하나의 페이지 테이블을 가리킨다.
  • 페이지 테이블
    • 1024개의 엔트리를 가지며 각 엔트리는 물리 메모리의 페이지 프레임 주소 정보를 갖는다.

2단계 페이징을 통해 접근할 수 있는 메모리는 1024 * 1024 * 4KB = 4GB이다.

 

물리 메모리에 접근하기 위해서는 CR3 레지스터가 페이지 디렉토리에 가서 페이지 테이블의 시작 주소를 찾고, 페이지 테이블에서 해당 페이지 정보를 가지는 v(p,d)를 찾고 이를 통해 물리 메모리의 데이터를 가져온다.

 

MMU와 TLB

 

TLB는 페이지 정보 캐시로, TLB에 가상 주소와 물리 주소를 매핑한 정보를 저장하여 MMU와 메모리 사이의 접근과정에서 발생하는 오버헤드를 줄이기 위한 하드웨어 장치이다. 전체적인 동작은 다음과 같다.

  1. CPU에서 Virtual Address 요청
  2. TLB에 존재하는 주소면 바로 물리 주소 접근 후 데이터 반환 / 아닌 경우 MMU를 통해 CR3 접근
  3. CR3을 통해 메모리의 페이지 테이블에 접근해서 만약 존재하는 페이지 주소이면 물리 주소 MMU 전달
    존재하지 않는 페이지 주소이면 Disk에서 이를 찾아서 Page Table에 write하고 MMU에 주소 전달
  4. MMU에서는 물리 주소 획득 이후 TLB 캐싱
  5. TLB 캐싱 이후 메모리의 물리 주소에 접근해서 데이터를 획득하고 이를 CPU에 전달

운영체제의 메모리 낭비 방지

 

만약 프로세스 A와 B가 같은 프로그램을 사용한다면, 이 Code 영역은 같을 것이다(static한 reentrant code라면). 그러면 하나의 code 영역을 여러 프로세스가 공유해서 메모리 낭비를 막을 수 있을 것이다. 

 

다만, 해당 물리 공간에 대해 write 작업을 할 때는 다른 프로세스가 사용하는 공유 자원의 값의 변경을 막기 위해 해당 영역만큼을 복사해서 Write 작업을 하는 프로세스가 가리키는 page pointer만 변경한다.

 

페이지 폴트

 

요구 페이징과 선행 페이징

  • 선행 페이징 : 프로세스와 관련된 모든 데이터를 메모리에 올려놓고 실행
  • 요구 페이징 : 프로세스의 실행 도중 필요한 데이터를 필요한 시점에만 메모리에 적제하고, 
                      더 이상 필요하지 않은 페이지 프레임은 교체하여 다시 저장매체에 저장

-> 요구 페이징을 위한 페이지 교체 알고리즘이 필요하다.

 

페이지 폴트는 어떠한 페이지가 물리 메모리에 존재하지 않을 때 발생하는 인터럽트로, 운영체제는 해당 인터럽트가 발생하면 해당 페이지를 물리 메모리에 올리는 과정을 가진다.

과정을 보다시피 인터럽트(오버헤드 큼) -> 디스크 접근(오버헤드 큼) -> 메모리 접근(오버헤드 큼)의 과정이기 때문에 시간이 오래 걸린다. 만약 프로그램을 많이 띄워 놨는데, 거기서 페이지 폴트가 과하게 발생하면 과도하게 페이지 교체 작업이 일어나 실제로는 할 수 있는 작업이 없어질 수 있다. 이를 쓰레싱(Thrashing)이라고 한다.

 

페이지 교체 정책

  • FIFO : 가장 먼저 들어온 페이지를 내리는 것
  • OPT : 최적 페이지 교체 알고리즘으로, 향후 가장 오랫동안 사용하지 않을 페이지를 내리는 것으로, 이는 프로그램 실행에 대한 예측이 불가능하므로 사실상 불가능하다.
  • LRU(Least Recently Used) : 가장 오래 전에 사용된 페이지를 교체하는 알고리즘
  • LFU(Least Frequently Used) : 가장 적게 사용된 페이지를 교체하는 알고리즘
  • NUR(Not Used Recently) : 최근에 사용되지 않은 페이지를 교체하는 기법으로, 각 페이지마다 참조 비트(R), 수정 비트(M)을 두어 사용되었으면 1, 그렇지 아니하면 0으로 부여한 뒤 (R,M) = (0,0) - (0,1) - (1,0) - (1,1) 순으로 교체

메모리에 대한 접근은 최근에 사용했던 메모리 및 그 인근의 메모리에 접근할 가능성이 높다(메모리 지역성). 그래서 LRU 알고리즘이 OPT와 가장 유사한 경향성을 띈다.

세그멘테이션

가상 메모리를 서로 크기가 다른 논리적 단위인 세그먼트(segment)로 분할하는 기법으로, x86 real-mode에서는 CS(Code Segment), DS(Data Segment), SS(Stack Segment), ES(Extra Segment)로 구분하여 메모리를 접근한다. 

 

세그먼트 가상 주소

 

Virtual Address v = (s,d)

    - s = 세그먼트 번호

    - d = 블록 내의 세그먼트 오프셋

 

페이징 시스템과 유사하게 가상 주소를 알면 세그먼트 번호를 통해 세그먼트 테이블에 접근하고, 세그먼트 테이블에서 세그먼트의 길이와 베이스 포인터 정보를 가져온 뒤 베이스 포인터에 오프셋만큼을 더하여 물리 주소를 구하고 메모리에서 접근을 하는 방식이다.

 

물리 메모리가 원하는 연속된 크기의 메모리를 제공해주지 못하는 경우 외부 단편화가 발생한다.

'CS > OS' 카테고리의 다른 글

CS - OS - 08. File System  (0) 2021.11.24
CS - OS - 06. Thread  (0) 2021.11.23
CS - OS - 05. Inter Process Communication(IPC)  (0) 2021.11.19
CS - OS - 04. 프로세스와 컨텍스트 스위칭  (0) 2021.11.19
CS - OS - 03. 인터럽트  (0) 2021.11.18
Comments