리눅스를 기준으로 보면 프로세스 하나당 메모리는 4GB를 사용한다. 반면 우리 메모리는 16GB? 32GB? 이정도에 불과하다. 현재 우리가 통상 사용하는 컴퓨터는 폰 노이만 구조이므로 프로셋스는 메모리를 할당받아 그 위에서 동박작해야 하지만, 모든 프로세스를 전부 실제 메모리에 올리려면 비용이 너무 많이 소요된다. 그래서 우리는 가상 메모리를 사용한다.
실제 메모리보다 많은 양의 메모리를 사용하는 것처럼 보이는 기술. 실제로 순간 우리가 사용하는 메모리의 크기는 작다는 점에 착안하여 고안된 기술로, 프로세스 간 공간이 분리되어 특정 프로세스의 이슈가 발생하더라도 시스템 전체에 영향을 주지는 않는다.
페이징에 필요한 페이징 프레임은 고정된 크기의 block이다(리눅스 기준 4KB). 페이지를 설정하게 되면 가상 주소 v는 v=(p,d)이며, 이때 p는 가상 메모리 페이지 번호,d는 p안에서 참조하는 위치이다.
v = (p,d) = (page number, offset)
만약 4KB를 예로 들면, 0-11bit는 Page Offset, 12-31은 Page Number가 될 것이다.
32bit 시스템에서 4KB페이지를 위한 페이징 시스템은 위 그림과 같다. 그러나 이에 대한 페이지 테이블을 일괄적으로 전부 작성하게 되면 필요없는 페이지 정보까지 생성 / 관리되므로 공간이 낭비될 수 있다. 그래서 필요없는 페이지를 생성하지 않고 페이지별로 단계를 나누어 페이지 디렉토리별로 생성 / 관리하면 공간을 절약할 수 있다.
페이지별로 번호를 나타내는 bit를 구분해서 단계를 나눈다.
2단계 페이징을 통해 접근할 수 있는 메모리는 102410244KB=4GB이다.
물리 메모리에 접근하기 위해서는 CR3 레지스터가 페이지 디렉토리에 가서 페이지 테이블의 시작 주소를 찾고, 페이지 테이블에서 해당 페이지 정보를 가지는 v(p,d)를 찾고 이를 통해 물리 메모리의 데이터를 가져온다.
TLB는 페이지 정보 캐시로, TLB에 가상 주소와 물리 주소를 매핑한 정보를 저장하여 MMU와 메모리 사이의 접근과정에서 발생하는 오버헤드를 줄이기 위한 하드웨어 장치이다. 전체적인 동작은 다음과 같다.
만약 프로세스 A와 B가 같은 프로그램을 사용한다면, 이 Code 영역은 같을 것이다.(static한 reentrant code라면). 그러면 하나의 code 영역을 여러 프로세스가 공유해서 메모리 낭비를 막을 수 있을 것이다.
다만, 해당 물리 공간에 write 작업을 할 때는 다른 프로세스가 사용하는 공유 자원의 값의 변경을 막기 위해 해당 영역만큼을 복사해서 Write 작업을 하는 프로세스가 가리키는 page pointer만 변경한다.
ReentrantLock란?
동기화된 메소드와 문장을 사용하여 액세스 할 수 있느 ㄴ암시적인 모니터 잠금 기능과 같은 기본적인 동작과 의미를 가진 reentrant 상호 간의 상호 배제된 상호 배제 잠금 기능을 의미한다.
이는 마치 뮤텍스와 많이 흡사한 모습을 띄고 있다.
즉, 상호 배타적 락이며 기본적으로 synchronized(동기화) 키워드를 이용한 암시적인 모니터와 같이 동작한다.
동기화에는 아래와 같은 내용들이 있는데 이 중 reentrantlock는 mutex라고 생각하면 쉽다.
상호 배제란, 임계 구역을 어느 시점에서 단지 한 개의 프로세스만이 사용할 수 있도록 하며, 다른 프로세스가 현재 사용 중인 임계 구역에 대하여 접근하려고 할 때 이를 금지하는 행위를 말합니다.
static : 값이 변하지 않는 변수
요구 페이징과 선행 페이징
-> 요구 페이징을 위한 페이지 교체 알고리즘이 필요하다.
페이지 폴트는 어떠한 페이지가 물리 메모리에 존재하지 않을 때 발생하는 인터럽트로, 운영체제는 해당 인터럽트가 발생하면 해당 페이지를 물리 메모리에 올리는 과정을 가진다.
과정을 보다시피 인터럽트(오버헤드 큼) -> 디스크 접근(오버헤드 큼) -> 메모리 접근(오버헤드 큼)의 과정이기 때문에 시간이 오래 걸린다. 만약 프로그램을 많이 띄워 놨는데, 거기서 페이지 폴트가 과하게 발생하면 과도하게 페이지 교체 작업이 일어나 실제로는 할 수 있는 작업이 없어질 수 있다. 이를 쓰레싱(Thrashing)이라고 한다.
메모리에 대한 접근은 최근에 사용했던 메모리 및 인근의 메모리에 접근할 가능성이 높다(메모리 지역성). 그래서 LRU 알고리즘이 OPT와 가장 유사한 경향성을 띈다.
가상 메모리를 서로 크기가 다른 논리적 단위인 세그먼트(segment)로 분할하는 기법으로, x86 real-mode에서는 CS(Code Segment), DS(Data Segment), SS(Stack Segment),ES(Extra Segment)로 구분하여 메모리를 접근한다.
Virtual Address v = (s,d)
페이징 시스템과 유사하게 가상 주소를 알면 세그먼트 번호를 통해 세그먼트 테이블에 접근하고, 세그먼트 테이블에서 세그먼트의 길이와 베이스 포인터 정보를 가져온 뒤 베이스 포인터에 오프셋만큼을 더하여 물리 주소를 구하고 메모리에서 접근을 하는 방식이다.