새소식

인기 검색어

개발일기

가상 메모리

  • -

계획

오늘은 내내 놀았기 때문에 계획은 패스합니다 ^^;;

학습한 내용

가상 메모리

OS의 메모리 관리에 따라 프로그램 실행 양상과 컴퓨터 전체 성능이 좌우되기 때문에 중요한 영역이다.

 

OS에서 메모리를 관리할 때는 프로세스들을 메모리에 일렬로 할당할 수도 있고(연속 메모리 할당), 프로세스를 잘게 쪼개어 저장할 수도 있다(가상 메모리).

 

연속 메모리 할당

https://www.studytonight.com/operating-system/contiguous-memory-allocation-in-operating-system

연속 메모리 할당이란, 메모리에 프로세스들을 연속적으로 할당하는 걸 의미한다.

 

이때, 모든 프로세스를 메모리에 올린다면 메모리 공간이 부족하기 때문에 사용하지 않는 프로세스(ex. IO 작업을 기다리는 프로세스)는 임시로 Disk로 옮겨서 관리한다.

 

https://www.studytonight.com/operating-system/swapping-in-operating-system

 

그렇게 해서 빈 공간에 새로운 프로세스를 할당하는 방식을 Swapping이라고 부른다.

  • Swap Sapce : 프로세스들이 쫒겨나 있는 Disk 영역
  • Swap Out : 프로세스가 메모리에서 스왑 영역으로 옮겨지는 동작
  • Swap In : 프로세스를 스왑 영역에서 메모리로 재적재 하는 동작
  • free, top 명령어로 swap 영역의 크기를 확인할 수 있다

OS는 Swapping을 통해 메모리 주소 공간의 크기보다 더 큰 양의 프로세스들을 할당할 수 있다.

프로세스를 메모리에 할당하는 방법은 크게 3가지가 있다.

  • first fit : 선형 검색하며 프로세스 크기보다 빈 공간이 크기가 크다면 적재한다
  • best fit : 빈 공간 중 프로세스 크기와의 크기 차이가 가장 적은 공간에 적재한다
  • worst fit : 빈 공간 중 가장 큰 공간에 프로세스를 배치한다

연속 메모리 할당에는 고질적인 문제가 있다. 외부 단편화라는 현상인데, 프로세스들을 할당 / 해제를 반복하며 메모리 상의 프로세스와 프로세스 사이에 메모리 빈 공간이 존재하는 현상을 말한다. best fit은 그나마 적은 편이긴 하다.

 

이런 외부 단편화를 해결하기 위해선 Compaction(압축) 과정을 통해 프로세스들을 메모리에 재배치할 수 있지만, 그 과정 동안 오버헤드가 크다.
=> Java gc에서 비슷하게 힙 영역을 정리할 때 STW가 발생하고 이 STW가 Java 성능 문제의 주 원인 중 하나인 점을 생각해볼 수 있다

 

https://cstaleem.com/compaction

가상 메모리

https://en.wikipedia.org/wiki/Virtual_memory

가상 메모리는 프로그램을 페이지라는 단위로 잘개 쪼개 메모리에는 프로세스에서 필요한 페이지만 적재할 수 있도록 하는 기법이다.

 

필요한 페이지만 적재한다는 것은, 필요한 명령어와 데이터를 말하는 것이다. 프로그램은 명령어를 한 줄씩 읽으며 수행하기 때문에 현재 읽을 명령어와 그와 관련된 데이터만 알면 되기 때문이다.

 

가상 메모리는 연속 메모리 할당에서 발생하는 외부 단편화 문제를 해결하고, 여유 메모리보다 큰 프로그램을 적재할 수 있도록 만든다는 장점이 있다.

 

위에서 자를 수 있는 단위를 페이지라고 말했지만, 사실 페이지를 이용하는 페이징 말고 세그멘테이션이라는 기법도 존재한다. 현대 대부분의 OS는 페이징을 사용하기 때문에 페이지라고 말했다.

 

페이징
페이지가 프로그램을 잘게 자른 것이라면 프레임은 메모리를 잘게 자른 것이다. 그리고 페이지는 프레임에 할당된다.

 

프로그램이 색다른 맛이 여럿 섞인 케이크, 메모리가 케이크 상자라고 생각해 보자. 페이지는 케이크를 조각 별로 잘게 자른 것이고, 프레임은 상자에 칸을 여러 개 두어 각 칸에 어떤 맛의 케이크가 들어갈지 표기한 것이라고 할 수 있다.

 

https://www.paris.co.kr/product/new-%EB%A7%88%EC%9D%B4%EB%84%98%EB%B2%84%EC%9B%903/

페이지도 필요 여하에 따라 Swapping하는데, Page in / out이라고 부른다.

 

페이지는 외부 단편화를 해결하지만, 프로세스가 정확히 페이지 단위로 잘리지 않을 수 있기 때문에 내부 단편화라는 문제가 발생하기도 한다. (1002KB프로그램 4kb 페이지로 나누면 마지막 페이지에는 2kb가 빈 공간)

  • 페이지를 더 작은 단위로 세워 내부 단편화를 줄일 수 있다.

페이지 크기는 보편적으로 4KB를 사용한다. 페이지 크기가 크면 만약 읽고싶은 데이터가 1byte일지라도 4KB를 모두 읽어야 하는 문제가 발생한다. 반대로 페이지 크기가 너무 작으면 페이지 수만큼 디스크에서 읽어야 하기 때문에 Disk IO 비용이 커진다는 문제가 발생한다.

페이지 테이블

모든 프로세스는 페이지 테이블이라는 자료구조를 메모리에 가진다. 페이지 테이블은 페이지 번호, 프레임 번호를 짝지으며 다른 메타데이터들도 함께 저장한다.

 

https://wiki.osdev.org/Paging

 

페이지 테이블이 가지는 메타 데이터들 중 중요한 것들은 다음과 같다

  • 유효 비트 : 해당 페이지가 현재 메모리에 존재하는지
  • 보호 비트 : 페이지에 대한 읽기 / 쓰기 / 사용 권한
  • 참조 비트 : CPU가 이 페이지에 접근한 적이 있는지
  • 수정 비트 : 해당 페이지에 데이터를 썼는지 유무
    => JPA의 1차 캐시와 같은 느낌이라고 생각이 들었다.

유효 비트는 페이지 폴트에 대해 검사하는 역할을 한다. 페이지 폴트란, 현재 메모리에 페이지가 존재하지 않아 페이지 스왑 영역에서 메모리로 페이지 인 해야 하는 상황을 말한다.

 

페이지 테이블이 존재하기 때문에 CPU 입장에서는 메모리 주소가 순서대로 적혀있다고 느낀다. CPU는 순서대로 명령어를 실행하지만, 그때마다 필요한 정보는 페이지 테이블에서 원하는 프레임을 읽어오면 되기 때문이다.

 

이러한 장점이 있지만, 페이지 테이블의 존재 때문에 메모리를 2번 읽어야 하는 문제도 있다. CPU 입장에서는 메모리를 읽는 행위가 느린 행위이기 때문에 메모리를 2번 읽는다는 것은 성능적으로 문제가 있다.

 

https://www.geeksforgeeks.org/translation-lookaside-buffer-tlb-in-paging/

이 문제를 해결하기 위해 TLB. Translation Lookaside Buffer라는 걸 두어 해결한다. TBL는 CPU 곁(주로 MMU 내)에 두는 페이지 테이블 전용 캐시 메모리다. 참조 지역성에 근거해 최근 사용된 페이지 위주로 가져와 저장한다. TLB를 활용하면 TLB에서 페이지 폴트가 발생하지 않은 상황에서는 매번 매모리에 접근하지 않아도 된다.

  • MMU란 가상 주소를 물리 주소로 매핑해주는 HW 장치를 말한다
  • look-aside라는 용어는 이전에 캐싱 전략에서 한 번 다룬 단어이다. 저장소에서(느린) 데이터를 조사하기 전에 캐시(빠른)에 원하는 정보가 들어있는지 한 번 들여다 보고 없으면 그때 저장소에서 읽어오는 전략을 말한다.

프로세스의 크기가 커지면 페이지 테이블 크기도 자연히 커진다. 예를 들어 16GB 짜리 프로세스가 존재하고, 페이지 크기가 4KB라면, 페이지 수가 4000000개가 되어야 한다.

 

 

모든 페이지 테이블 엔트리를 메모리에 유지하는 것은 메모리 낭비이기 때문에 페이징 테이블을 관리하는 다단계 페이지 테이블 multilevel page table이 등장했다. 쉽게 말해서 페이지 테이블들을 관리하는 페이지 테이블인 것이다.

다만, 페이지 테이블 계층이 늘어날 수록 페이지 폴트시 메모리 접근해야 하는 횟수가 늘어난다는 단점이 있다.

요구 페이징

페이지는 기본적으로 필요한 것들만 메모리에 존재해야 한다. 필요한 페이지만 적재하는 기법을 요구 페이징이라고 부른다.

 

요구 페이징을 안정적으로 동작시키려면 페이지 교체프레임 할당 문제를 해결해야 한다.

 

페이지 교체시 어떤 것을 교체할 지 정하려면 페이지 교체 알고리즘이 필요한데, 페이지 폴트가 적을 수록 좋은 알고리즘이다. 이는 Page In / Out 횟수를 줄여 Disk IO를 줄일 수 있기 때문이다.

 

페이지 교체 알고리즘에는 다음과 같은 방법들이 있다.

  • FIFO : 가장 먼저 올라온 페이지부터 내쫒는다
  • 최적 페이지 교체 : 앞으로 사용될 빈도가 가장 낮은 페이지를 내쫒는다 (현실 가능성 x)
  • LRU : 가장 사용 빈도가 낮은 페이지를 내쫒는다

스래싱

https://www.scaler.com/topics/thrashing-in-os/

페이지 폴트를 결정하는 요소 중에는 프레임 수도 존재한다. 프레임 수가 적으면 그만큼 적재할 수 있는 페이지 수도 적어지기 때문이다.

 

스래싱이란 프로세스가 실제 실행되는 시간보다 페이징에 더 많은 시간을 소요해서 성능을 저해하는 문제를 말한다.

 

만약 메모리에 프로세스 수가 많아 프로세스 당 사용할 수 있는 프레임 수가 적게 배정받는다면, 페이지 폴트 때문에 성능이 낮아질 수 있다. 프로세스 수가 늘어났을 때 성능이 낮아지는 문제는 Context Switching 때문만은 아니라는 말이다.

 

이 문제는 물리 메모리를 Scale up 해서 해결할 수 있는 부분이긴 하다.

 

이때 프로세스에 프레임을 할당하는 방식에도 여러 기법을 적용해볼 수 있다.

  • 균등 할당 : 프로세스 마다 정해진 프레임을 할당한다.
  • 비례 할당 : 프로세스 크기에 따라 프레임을 할당한다.

프로세스 크기가 크다고 해서 프레임을 많이 필요로 하는 것은 아니기 때문에 직접 실행해 봐야 안다. 따라서 다음과 같은 기법들이 등장했다.

  • 작업 집합 모델 : 프로세스가 일정 기간 참조한 페이지들을 기억해서 자주 사용된 집합 만큼만 프레임을 할당한다.
  • 페이지 폴트 빈도 : 페이지 폴트율의 상한선과 하한선을 정해놓고 폴트율이 높으면 프레임을 추가 할당하고, 낮다면 회수한다.
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.