본문 바로가기
컴퓨터공학/운영체제

메모리 관리 전략(2)

by Daniel.kwak 2018. 10. 28.

목표

1.세그멘테이션에 대해서 알아본다.

2.페이징 기법에 대해서 알아본다.

3.메모리 관리 전략에 대한 차이를 알아본다.




세그멘테이션


배경

사용자가 인식하는 메모리의 모습과 실제 물리 메모리의 모양은 같지 않다. 물리적 특성을 고려하여 프로그래머가 메모리를 관리하는 방식은 효율적이진 않다. 그래서 프로그래머가 인지하는 메모리의 모습을 실제 물리 메모리의 모습으로 변환해주는 기법을 제공한다. 시스템은 메모리 관리하는데 있어서 더 많은 자유로운 선택을 할 수 있고, 프로그래머 또한 자연스러운 프로그래밍이 가능하다.


기본방법

프로그래머는 메모리를 일부는 명령어를 저장하고, 나머지는 데이터를 저장하는 바이트의 배열로 생각하는가? 아니다. 프로그래머들은 가변적인 길이를 가진 세그먼트의 집합 그리고 세그먼트 사이에는 어떤 순서도 존재하지 않는다고 생각한다.




프로그래머는 이러한 요소들이 적재되는 주소에 상관업이 '스택' , 'math 라이브러리' , '메인 프로그램' 이라 칭한다. 순서는 신경쓰지 않는것이다. 여기서 세그먼트의 길이는 프로그램의 목적에 따라 자동적으로 결정되게 된다. 각 세그먼트 내의 요소들은 "메인 프로그램의 1번째 명령문" , "심볼 테이블의 17번째 항목" 등과 같은 식의 '변위'로 생각된다.(여기서 변위의 의미는 위치와 양으로 보자)

세그멘테이션이란 위와 같이 프로그래머가 생각하는 모양을 그대로 지원하는 메모리 관리 기법이다. 프로그래머가 생각하는 논리 구조 공간은 세그먼트들의 집합으로 이루어진다. 각 세그먼트는 이름과 길이를 가진다. (프로그램에서 사용되는 주소는 세그먼트 이름과 오프셋의 두 부분으로 나누어 명기한다.) 구현을 쉽기 하기 위해, 내부에서는 세그먼트에 번호가 매겨지며 논리주소는 <세그먼트 번호, 오프셋> 구성으로 된다. C 컴파일러는 다음과 같은 세그먼트들을 만들어 낼 것이다. 


1.코드

2.전역 변수

3.메모리 할당을 위한 힙

4.각 쓰레드를 위한 스택

5.표준 C라이브러리


컴파일 타임에 링크되는 라이브러리는 별도의 세그먼트에 할당되어야 한다. 로더는 이런 세그먼트를 받아서 세그먼트마다 번호를 매긴다.


세그먼트 하드웨어

세그먼트 테이블의 각 항목은 세그먼트의 기준(base)과 세그먼트 한계(limit)을 가지고 있다. 세그먼트 기준은 세그먼트 시작 주소를 나타내며, 세그먼트 한계는 세그먼트 길이를 명시한다. 



이제 논리주소는 세그먼트 번호s와 세그먼트 내에서의 변위(offset)d 로 이루어진다. 세그먼트 번호 s는 세그먼트 테이블에 대한 색인이다. 변위d값은 0과 세그먼트 크기 사이의 값이여야 하며 그렇지 않을 경우 트랩이 발생한다. 이 변위가 범위 안에 있으면 세그먼트 기준+변위가 원하는 바이트의 실제 주소가 되는 것이다. 예시를 보자. 




0에서 4까지의 다섯개의 번호를 가진 세그먼트가 있다. 각 세그먼트는 물리 메모리에 저장되어 있고 세그먼트 테이블은 각 세그먼트별 항목을 가지고 있고, 각 항목은 메모리 내의 실제 주소의 시작(base)과 끝(limit)을 나타낸다. 세그먼트 2는 400B의 길이로, 4300번지부터 시작된다. 즉, 세그먼트 2 내의 53번째 바이트에 대한 참조는 4300+53 = 4353번지로 사상된다. 




페이징

세그멘테이션은 프로세스가 적재되는 물리 주소 공간이 연속적이지 않아도 적재를 허용한다. 반면 페이징은 이러한 이점을 제공하는 또 하나의 메모리 기법이다. 페이징은 외부 단편화를 방지하고, 단편화에 따른 압축 작업이 필요 없지만, 세그멘테이션은 그렇지 않다. 또한 페이징은 스왑 아웃되는 다양한 크기의 세그먼트를 예비 저장장치에 저장해야 하는 심각한 문제도 해결한다. 


물리 메모리는 프레임이라 불리는 같은 크기의 블록으로 나뉜다. 반면, 논리 메모리는 페이지라 불리는 같은 크기의 블록으로 나뉜다. 프로세스가 실행되면서 프로세스의 페이지는 파일 시스템이나 예비저장장치로부터 주 메모리 프레임으로 적재되게 된다. 페이징 하드웨어를 살펴보자. 




CPU에서 나오는 모든 주소는 페이지 번호(p) 와 페이지 변위(d:offset) 두 개의 부분으로 나뉜다. 페이지 번호는 페이지 테이블에 엑세스 할 때 사용되며 페이지 테이블은 주 메모리에서 각 페이지가 점유하는 주소를 가지고 있다. 이제 이 페이지 주소에 페이지 변위를 더하면 원하는 물리 주소가 된다. 


페이지 크기

페이지 크기 역시 하드웨어가 결정한다. 대개의 경우 512B ~ 16MB 사이이며 2의 제곱으로 증가한다. 만약 논리 주소 공간의 크기가 2의m승이고 , 페이지의 크기가 2의 n승이라면 논리 주소의 상위 m-n비트는 페이지 번호를 나타내며, 하위  n비트는 페이지 변위를 나타낸다. 논리 주소가 16, 페이지 크기가4, 물리 메모리 크기가 32인 예를 보자.




논리 주소3은 페이지 0, 변위 3이고 실제 주소는 5*4 + 3 이므로 23번째의 물리 메모리에 저장되어 있다.


페이징 그 자체는 동적 재배치이다. 모든 논리 주소는 페이징 하드웨어에 의해 물리 주소로 사상되기 때문이다. 그래서 페이징 기법을 사용하면 외부 단편화는 발생하지 않는다. 놀고있는 모든 프레임이 프로세스에게 할당이 되기 때문인데, 이 때문에 내부 단편화가 발생한다. 할당이 항상 프레임의 정수 배로 할당되기 때문이다. 

따라서 n+1번째 프레임은 거의 모든 프레임에서 내부 단편화가 발생한다. 평균적으로는 프로세스당 반 페이지 정도의 내부 단편화가 예상된다. 작은 페이지 크기가 유리할 수도 있는데, 이는 결국 페이지 테이블이 커지는 문제를 야기한다. 한편 디스크 입장에서는 페이지 크기가 클수록 효율적이다. 일반적 추세는 페이지 크기가 프로세스, 자료, 주 메모리와 함께 커짐에 따라 같이 커져왔다. 

 주의할 것은 페이지 메모리 시스템에서 물리 메모리의 크기는 프로세스의 최대 논리적인 크기와는 다르는 것이다. 페이징 기법은 CPU주소 포인터 크기가 제한하는 것보다 더 큰 물리 메모리를 사용할 수 있게 한다. -> 왜 그런지 찾아볼 것.

 한 프로세스가 실행되기 위해 도착하면 페이지 몇 개 분에 해당하는지를 조사한다. 각 사용자 페이지는 한 프레임씩을 필요로 하므로, N개의 프레임을 모두 사용할 수 있다면 이 프레임들은 현재 프로세스에게 할당된다. 이렇게 프로세스의 첫 페이지가 할당된 프레임이 적재되고, 그 프레임 번호가 페이지 테이블에 기록된다. 마찬가지로 다음 페이지는 또 다른 프레임에 적재되고, 그 프레임 번호가 페이지 테이블에 기록되며 이 과정이 모든 페이지가 프레임에 할당될 때 까지 반복된다.




그림에서 볼 수 있듯이, 페이징의 가장 큰 특징은 메모리에 대한 프로그래머의 인식과 실제 내용이 다르다는 것이다. 프로그래머는 메모리가 연속된 공간이며 메모리에는 이 프로그램만 있다고 생각하지만, 실제로는 프로그램은 여러 곳에 프레임 단위로 분산되어 있고, 많은 다른 프로그램도 올라와 있다. 논리 주소는 물리 주소로 매핑되며, 이 매핑과정은 프로그래머에게는 안 보이고 OS가 컨트롤한다. 따라서 이용자 프로세스는 자신의 것이 아닌 메모리에는 접근할 수 없다. 

OS는 메모리를 관리하기 위해 물리 메모리의 할당에 대해 파악하고 있어야 한다. 즉 어느 프레임이 할당되어 있고, 어느 프레임이 사용 가능한지, 총 프레임은 몇 개나 되는지 등을 알아야 하며 이러한 정보는 일반적으로 프레임 테이블에 저장된다. (프레임 당 하나의 항목을 가지며 놀고 있는지, 할당되어 있는지, 어느 프로세스의 어느 페이지에 할당되었는지) 

 또한 OS는 프로세스의 논리 주소를 정확한 물리 주소로 매핑해야 하는데, 이를 위해 각 사용자에 대해 페이지 테이블의 복사본을 유지한다. 이 복사본은 시스템 콜을 처리하기 위해 사용자 프로그램의 물리 주소를 알아낼 때 사용되며, 따라서 페이징은 Context Switching 시간을 증가시킨다. 


공유 페이지

페이징의 또 다른 장점은 코드를 쉽게 공유할 수 있다는 것이다. 특히 시분할 환경에서 중요한데, 40명의 사용자를 지원하는 시스템을 생각해보자. 각각의 사용자는 문서 편집 프로그램을 실행하려고 한다. 문서 편집기가 150KB의 코드와 50KB의 데이터 공간으로 구성되어 있다면 40명의 사용자를 지원하기 위해서는 8,000KB메모리 공간이 필요하다. 그러나 재진입 가능 코드라면 다음 그림과 같이 공유할 수 있다. 




위 그림은 한 페이지의 크기가 50KB인 세 페이지 짜리 편집기를 공유하고 있는 세 프로세스의 모습이다. 

재진입 가능 코드는 수행하는 동안 절대 변하지 않는다. 따라서 두 개나 그 이상의 프로세서들이 동시에 같은 코드를 수행하는게 가능하다. 이렇게 되면 단 하나의 편집기 코드 사본이 물리 메모리에 올라오면 된다. 각각의 사용자의 페이지 테이블은 같은 편집기 코드를 페이지 테이블에 사상한다. 그러나 데이터는 서로 다른 페이지 프레임에 사상된다. 따라서 40명의 사용자를 지원하기 위해 하나의 편집기 코드 사본 + 50KB 크기의 40개 데이터 프레임만 있으면 된다. (8000->2150)

물론 공유를 위해서는 코드는 재진입을 보장해야 한다. OS에서는 읽기 전용만으로는 코드의 정확성을 보장할 수 없으므로 보호해주어야 한다. 프로세스들 사이의 메모리 공유는 쓰레드에서의 주소 공간 공유나, 프로세스간 상호 통신 방법으로도 가능하며 어떤 OS에서는 메모리 공유를 페이지 공유로 하기도 한다. 



참고

Operating System : 9th

'컴퓨터공학 > 운영체제' 카테고리의 다른 글

메모리 관리 전략(1)  (0) 2018.10.27
프로세스 동기화(뮤텍스,세마포어,임계구역)  (0) 2018.10.26
DeadLock(교착상태)  (0) 2018.10.25
CPU 스케쥴링 알고리즘  (0) 2018.10.24
프로세스와 쓰레드의 차이점  (0) 2018.10.24
쓰레드  (0) 2018.10.24