[Embedded] 메모리에 대해
CS의 일환으로 메모리에 대해서 알아보자.
메모리 계층
메모리 계층은 레지스터, 캐시, 메모리, 저장장치로 구성되어있다.
아래 목록에서 위로 갈 수록 속도가 빠르며, 아래로 갈 수록 용량이 크다.
- 레지스터 : CPU안에 있는 메모리로, 기억 용량이 가장 작고, 휘발성이며, 속도가 가장 빠르다.
- 캐시 : 휘발성이며, 속도는 빠른편이며, 기억 용량이 적은편이다.
- 주 기억 장치 : 우리가 흔히 아는 RAM. 휘발성이며, 속도는 보통이며, 기억 용량도 보통인 편이다.
- 보조기억장치 : 우리가 흔히 아는 SSD, HDD. 비휘발성이며, 속도는 느리고, 기억 용량은 가장 많다.
RAM
램은 하드디스크로부터 일정량의 데이터를 복사해 저장하고, 이를 CPU에 전달하는 역할을 한다.
캐시
데이터를 미리 복사해 두는 임시 저장소이자 CPU와 같은 빠른장치와 메모리와 같은 비교적 느린 장치에서 병목 현상을 줄이기 위한 메모리이다.
RAM과 CPU간의 속도 차이가 많이 나기 때문에 이를 해결하기 위해서 L1, L2와 같은 캐싱 계층이 있다.
또한 이 캐시 메모리와 SSD와 같은 보조기억장치 사이의 RAM 또한 캐싱 계층이라고도 할 수 있다.
그리고 캐시 메모리에서 데이터를 찾는 행위와 관련한 단어로 캐시 히트, 캐시 미스가 있다.
- 캐시 히트 : 캐시 메모리에서 원하는 데이터를 찾았을 경우
- 캐시 미스 : 캐시 메모리에서 데이터를 찾지 못해 주 기억장치로 가서 데이터를 찾아오는 행위
웹 브라우저의 캐시
우리가 웹 브라우저에 접속하면 아이디를 선택했을 때 자동으로 비밀번호를 입력해주는 등 편리한 기능을 제공하는 경우가 많이 있다.
이것이 바로 웹 브라우저에서 캐시를 이용한 경우이다. 중복 요청 방지를 위해 사용되며 그 종류에 대해서 알아보자.
쿠키 : 만료 기한이 있는 키-값 형태 해시 저장소이다. 클라이언트 or 서버에서 만료기한을 정할 수 있는데 보통 서버에서 정한다. 쿠키를 설정할 때에는 document.cookie로 쿠키를 볼 수 없게 httponly 옵션을 설정해두자.
로컬 스토리지 : 만료 기한이 없는 키-값 형태 해시 저장소이다. 웹 브라우저를 닫아도 유지되고, 도메인 단위로 저장, 생성된다.
세션 스토리지 : 만료 기한이 없는 키-값 형태 해시 저장소이다. 탭 단위로 세션 스토리지를 생성하며, 탭을 닫으면 해당 데이터가 삭제된다.
메모리의 관리
메모리의 관리 기법에 대해서 알아보자.
가상메모리
프로그램이 실행되기 위해서는 RAM과 같은 주 기억 장치로 들어가야 한다.
하지만 실행될 프로그램이 여러개 이거나 주 기억 장치보다 클 경우에는 공간 부족으로 인해 실행이 제대로 되지 못할 수 있다.
그래서 당장에 필요한 부분만 주 기억장치에 저장하고, 나머지는 보조기억장치에 두고 동작하도록 해서 이런 문제를 해결한다.
즉, 프로그램이 동작함에 있어서 주 기억 장치만 쓰지 않고, 일부 보조 기억 장치를 사용하는 것을 가상 메모리라고 한다.
이 때, 가상 메모리를 사용하기 위해 주어진 주소를 가상 주소라고 하며, 실제 메모리에 있는 주소를 실제 주소라고 한다. 가상 주소는 메모리관리장치에 의해 실제 주소로 변환된다.
이렇게 실제주소 - 가상주소가 매핑된 가상 메모리는 페이지 테이블로 관리된다.
스와핑
만약 가상 메모리에는 존재하지만, 실제 메모리에는 없는 데이터가 발생할 경우 페이지 폴드가 발생한다. 이때, 메모리에서 사용하지 않는 영역은 보조기억장치로 보내고, 쓰고자 하는 부분을 다시 주 기억장치로 불러오는 과정을 스와핑이라고 한다.
스와핑의 과정은 아래와 같다.
- CPU가 메모리를 확인하여 해당 페이지가 없을 때 트랩을 발생시킨다.
- 운영체제는 CPU의 동작을 멈춘다.
- 운영체제는 페이지 테이블을 확인해 가상 메모리에 페이지가 존재하는지 확인한다. 없으면 프로세스를 중단하고 현재 물리 메모리에 비어있는 프레임이 있는지 찾는다. 없다면 스와핑이 발생한다.
- 비어 있는 프레임에 해당 페이지를 로드하고, 페이지 테이블을 최신화한다.
- 중단된 CPU를 다시 동작시킨다.
스레싱
메모리의 페이지 폴트율이 높은 현상이다.
메모리에 너무 많은 프로세스가 올라가서 스와핑이 많이 일어나면 발생하는 현상이다.
위에서 스와핑이 일어날 때 CPU의 동작이 잠시 멈춘다고 했다. 즉, 스와핑이 많이 일어날 수록 CPU가 멈추는 현상이 많이 일어나 CPU 이용률이 낮아지는 것이다.
여기서, CPU 이용률이 낮아지면, 운영체제는 가용성을 높이려고 프로세스를 메모리에 더 많이 올리게 되어 과부하가 일어난다.
이를 해결하기 위한 방법으로 메모리 용량 증가나 작업세트, PFF가 있다.
- 작업세트 : 과거 사용 이력인 지역성을 이용해 자주 사용하는 페이지 집합을 만들어서 미리 메모리에 로드하는 것
- PFF : 페이지 폴트 빈도에 상한선과 하한선을 만드는 방법
메모리 할당
가상 메모리와 더불어 메모리를 관리하는 두 번째 방법이다.
메모리 할당은 시작 메모리 위치와 크기를 기반으로 진행하는데, 이때, 연속 할당과 불연속 할당으로 나뉜다.
연속 할당
메모리에 연속적으로 공간을 할당하는 것이다.
프로세스를 잠시 int에 비유하면
서로 다른 int A, B, C가 있다고 하자. 그리고 int의 크기는 4바이트 이다.
0x0000부터 A B C 를 연속적으로 할당하면
- A : 0x0000 ~ 0x00FF의 메모리를 사용한다.
- B : 0x0100 ~ 0x01FF의 메모리를 사용한다.
- C : 0x0200 ~ 0x02FF의 메모리를 사용한다.
이렇게 되는 것이다. 프로세스도 마찬가지로 다른 프로세스를 연속적으로 메모리에 할당해서 사용할 수 있다.
그리고 고정 분할 방식과 가변 분할 방식이 있는데 이 또한 비유를 통해서 설명해보겠다.
고정 분할 방식
int a[100]을 선언하는 순간 int 100개의 용량을 미리 할당 및 고정해서 사용하는 것이다. 이 배열의 크기는 중간에 바꿀 수 없다!
프로세스도 마찬가지로 이렇게 관리할 수 있으며, 내부 단편화가 발생한다.
가변 분할 방식
배열을 C언어에서는 malloc과 free를 이용해서 길이를 늘리거나 줄일 수 있다.
프로세스도 malloc과 free를 이용하는 것은 아니지만 이렇게 동적으로 크기를 관리할 수 있으며 내부 단편화는 발생하지 않으나 외부 단편화는 발생할 수 있다.
불연속 할당
연속할당과는 정 반대로 링크드리스트마냥 서로 다른 메모리 주소에 불연속적으로 할당하는 것을 말한다. 이는 페이징 기법이 대표적이다.
- 페이징 : 동일한 크기의 페이지 단위로 나누어 메모리의 서로 다른 위치에 할당하는 방법이다. 주소 변환이 복잡해질 수 있으나 할당 크기가 균일하다.
페이지 교체 알고리즘
메모리는 한정되어 있으므로 페이지 교체가 많이 일어나게 된다.
앞서 말했듯이 이를 스와핑이라고하며, 스와핑은 되도록 많이 일어나지 않는게 좋다. 적절한 페이지 교체 알고리즘을 써서 관리를 해야한다. 종류를 알아보자.
오프라인 알고리즘
먼 미래에 참조되는 페이지와 현재 할당하는 페이지를 바꾸는 알고리즘이다.
하지만 우리가 미래에 참조되는 페이지를 알 수 없기 때문에 실제로 사용할 수 없는 알고리즘이다. 하지만 비교지표로 많이 사용된다.
FIFO
가장 먼저 영역에 들어온 페이지를 먼저 교체 하는 방식이다.
LRU
Least Recently Used라고 하여 참조가 가장 오래된 페이지를 바꾼다. 어떤 페이지가 오래됐는지 파악하기 위해서 페이지마다 계수기, 스택을 사용한다.
페이지가 들어올 때
- 스택에 해당 페이지가 있을 경우 : 스택의 가장 아래로 보낸다.
- 스택에 해당 페이지가 없을 경우 : 스택의 가장 위에 있는 페이지를 스택에서 pop하고, 새로 들어온 페이지를 스택의 가장 아래로 보낸다.
실제로 구현은 해시 테이블과 이중 연결 리스트로 구현한다.
NUR
Not Used Recently라고도 하며, 최근에 참조되었는지를 나타내는 비트를 0과 1로 둔다. 이때, 0은 최근에 참조되지 않았고, 1은 참조 되었음을 나타낸다.
시계 방향으로 돌면서 0을 찾고, 0을 찾은 순간 해당 프로세스를 교체하고 해당 부분을 1로 바꾼다.
그리고 일정 시간이 지날 경우 다시 0으로 바꾼다.
LFU
Least Frequently Used라고 하여, 참조 횟수가 가장 적은 페이지를 교체한다.