메모리 관리
개념
관리 기법
메인 메모리는 운영체제를 위한 영역과 사용자 프로그램을 위한 영역으로 나뉜다. 다중 프로그래밍 시스템에서는 운영체제가 이 사용자 영역을 동적으로 세분화해, 여러 프로세스가 동시에 상주할 수 있도록 관리한다.
반입 정책
반입 정책은 메인 메모리에 적재할 다음 프로세스의 시점을 결정하는 방법이다. 요구 반입 기법은 운영체제나 시스템 프로그램, 사용자 프로그램의 참조 요구에 따라 메인 메모리에 적재하는 방식으로 오랫동안 쓰여왔다. 예상 반입 기법은 시스템의 요구를 미리 예측해 메모리에 선제적으로 적재하는 방식으로, 비교적 최근에 쓰이기 시작했다. 요청받은 페이지 외에 다른 페이지도 함께 들여오며, 탐색 시간과 회전 지연 시간을 갖는 보조기억장치의 특성을 고려한 정책이다.
배치 정책
배치 정책은 디스크에서 반입한 프로세스를 메인 메모리의 어느 위치에 저장할지 결정하는 방법으로, 최초 적합, 최적 적합, 최악 적합 등이 있다.
대치 정책
대치 정책은 재배치 기법의 하나로, 메인 메모리에 있는 어떤 프로세스를 내보낼지 결정한다.
메모리 해석에 대한 두 가지 관점
물리적 공간과 논리적 공간
메모리는 물리적 공간과 논리적 공간으로 나누어 볼 수 있다. **물리적 공간(물리적 주소)**은 실제 데이터나 프로그램이 저장되는 공간으로, 메모리 칩이나 디스크 공간에 생성된다. **논리적 공간(논리적 주소)**은 프로그래머가 프로그래밍에 사용하는 공간으로, 목적 코드가 저장되는 공간이나 프로그램이 사용하는 자료구조 등이 여기에 해당한다.
메모리 매핑
메모리 매핑은 이 논리적 주소와 물리적 주소를 연결하는 과정으로, 메모리 관리 장치(MMU, Memory Management Unit)라는 하드웨어에서 실행된다. 고정 분할, 동적 분할, 페이징, 세그먼트, 페이지화된 세그먼트 방식 등이 여기에 쓰인다.
메모리 관리 방식
메모리 관리 방식은 크게 두 가지다. 연속 메모리 할당은 프로그램을 적재하는 과정에서 메모리를 연속적으로 할당하는 방식이고, 분산 메모리 할당은 프로그램을 페이지나 세그먼트 단위로 나누어 여러 곳에 나누어 적재하는 방식이다.
메모리 방식 발전 과정
메모리 관리 방식은 시간이 지나며 다음처럼 발전해왔다. 초기 컴퓨터 시스템은 연속 메모리 할당 방식을 사용했다. 이후 메모리 영역을 여러 개의 고정된 크기로 나누어 프로세스에 제공하는 고정 분할 기법이 등장했지만, 이는 메모리 낭비(내부 단편화)를 유발했다. 이 문제를 해결하기 위해 각 프로세스의 크기에 맞춰 메모리를 나누는 동적 분할 기법이 제시되었고, 결국 분산 메모리 할당 방식이 자리 잡으며 오늘날의 가상 메모리 관리 기법으로 발전했다.
주소 바인딩
주소 바인딩은 논리적 주소를 물리적 주소로 변환하는 과정으로, 프로그램의 주소를 절대 주소로 바꾸어 메인 메모리의 고정된 부분에 적재한다. 이 바인딩은 컴파일 시점, 적재 시점, 수행 시점 중 하나에서 이루어진다.
컴파일 시점 바인딩은 프로세스가 메모리 내 어디에 적재될지 컴파일 과정에서 미리 알 수 있을 때, 컴파일러가 곧바로 물리적 주소를 생성하는 방식이다. 이렇게 메인 메모리의 실제 주소로 변환되는 과정을 절대재배치라 한다.
적재 시점 바인딩은 프로세스가 메모리의 어디에 적재될지 컴파일 시점에 알 수 없을 때 쓰인다. 이 경우 컴파일러는 재배치 가능한 상대 주소를 생성하는데, 상대 주소는 프로그램의 시작 주소를 0으로 놓고 만들어지므로 최종 바인딩은 적재 시점까지 미뤄진다. 시작 주소가 바뀌면 사용자 코드도 변화된 값을 반영하기 위해 다시 적재해야 한다.
수행 시점 바인딩은 프로세스가 실행되는 도중 메모리의 한 세그먼트에서 다른 세그먼트로 옮겨갈 때 바인딩을 수행 시점까지 미루는 방식이다. 오늘날의 운영체제는 대부분 이 방식으로 실행 시간에 바인딩을 수행한다.
동적 적재(Dynamic Loading)
동적 적재는 메모리 공간을 효율적으로 쓰기 위해 제안된 방식이다. 모든 루틴은 호출되기 전까지는 메모리에 적재되지 않고, 재배치 가능한 형태로 디스크에 저장되어 있다. 먼저 주 프로그램이 메모리에 적재되어 실행되고, 주 프로그램(호출 루틴)이 다른 루틴을 호출해야 할 때는 그 루틴이 메모리에 이미 적재되어 있는지 확인한다. 적재되어 있지 않다면 재배치 기능을 가진 연결적재기(로더)가 필요한 루틴을 메모리로 불러오면서 프로그램의 주소 테이블을 함께 갱신한다.
동적 적재의 장점은 분명하다. 사용하지 않는 루틴은 아예 적재하지 않고, 코드양이 많이 필요한 프로그램에서 특히 유용하다. 프로그램 전체의 양은 많아도 실제로 쓰이는 부분은 일부에 그치는 경우가 많아, 운영체제로부터 특별한 자원을 필요로 하지 않는다(다만 동적 연결적재기 자체는 제공되어야 한다).
중첩(Overlay)
중첩은 프로세스의 크기가 실제 메모리 크기로 제한되는 문제를 해결하기 위한 기법이다. 운영체제 영역과 메모리 공간의 일부에는 프로그램 실행에 반드시 필요한 명령어와 데이터만 저장하고, 중첩 구동기(오버레이 드라이버) 영역에는 실행 기간 동안 필요한 사용자 코드 등을 적재해 필요한 시점에 불러와 실행한다.
프로세스 교체
다중 프로그래밍 환경에서 프로세스는 사용자 프로그램이 끝날 때까지 메인 메모리에 머무는데, 이는 순환 할당 알고리즘이나 우선순위 기반 알고리즘에는 적합하지 않다. 따라서 할당이 끝나고 수행이 완료된 프로세스는 보조기억장치로 옮기고, 새로 시작할 프로세스를 불러들이는 프로세스 교체가 필요하다.
연속 메모리 할당
개념
연속 메모리 할당은 초기 컴퓨터 시스템에 적용되었던 메모리 관리 기법이다. 실행될 모든 프로그램은 필요한 만큼의 연속적인 메모리 공간을 할당받아 적재되며, 한 번에 전부 적재할 수 없으면 실행 자체가 불가능하므로 프로그램을 수정하거나 작은 모듈로 나누어 구성해야 한다.
단일 사용자 연속 메모리 할당
이 방식은 메모리 영역을 사용자를 위한 공간과 운영체제가 상주하는 공간으로 나눈다. 운영체제를 메모리의 하위에 두거나 상위에 두는 것 모두 가능하며, 흔히 운영체제 루틴이 담긴 부분(모니터)과 사용자 프로그램이 담긴 부분(사용자)으로 나뉜다. 이 구조에서는 사용자가 나머지 모든 메인 메모리에 대한 제어권을 가진다.
컴퓨터 주소 공간이 0000번지부터 시작하더라도, 사용자 프로그램의 실제 시작 주소는 0000번지가 아니라 기준 레지스터 값 이후이므로 기준 주소가 바뀌면 프로그램을 다시 적재해야 한다는 문제가 있다. 이를 해결하기 위해 사용자 프로그램을 기준 레지스터보다 낮은 상위 메모리에 적재하는 대신, 사용되지 않는 공간을 중간에 두어 운영체제와 사용자가 필요에 따라 유동적으로 활용하고, 수행 시점까지 바인딩을 미루는 방식을 쓴다.
메인 메모리보다 큰 프로그램을 실행할 수 있게 해주는 교체 기법도 있다. 단일 사용자 연속 메모리 할당 시스템이 프로세서 중심 작업을 처리할 때는 프로세스를 집중적으로 활용할 수 있지만, 입출력 작업이 자주 발생하는 경우에는 프로세서가 유휴 상태에 빠지는 일이 잦아진다.
고정 분할 다중 프로그래밍
고정 분할 다중 프로그래밍(MFT, Multiprogramming with a Fixed number of Tasks)은 IBM OS/360에서 쓰였지만 지금은 사용되지 않는 방식이다. 고정된 크기의 분할 영역에 프로세스(작업)를 각각 할당하며, 물리 주소는 분할 기준 레지스터(PBR) 값에 논리 주소를 더해 만든다. 이 방식의 성능은 분할의 개수에 따라 제한된다.
메모리 단편화는 메모리 구성 방식과 관계없이 모든 컴퓨터 시스템에서 발생하는데, 고정 분할 다중 프로그래밍에서는 사용자 작업의 크기가 지정된 분할에 정확히 들어맞지 않거나, 분할이 너무 작아 대기 중인 작업 중 어느 것도 맞지 않는 상황으로 나타난다. 이렇게 분할된 공간이 쓰이지 못한 채 남는 것이 내부 단편화다.
가변 분할 다중 프로그래밍
가변 분할 다중 프로그래밍(MVT, Multiprogramming with a Variable number of Tasks)은 고정된 경계를 없애고, 각 작업이 필요한 만큼만 메모리를 할당받는 방식이다. 각 프로세스(분할 영역)를 나타내기 위해 기준(재배치) 레지스터와 한계 레지스터를 사용하며, 한계 레지스터에는 프로세스 크기만큼의 영역이 저장된다. 메모리를 재배치할 때는 최초 적합, 최상 적합, 최악 적합 기법을 활용한다.
가변 분할 알고리즘은 외부 단편화 문제를 안고 있다. 프로세스들이 메모리에서 제거되고 새로운 프로세스가 적재되는 과정이 반복되면, 사용 가능한 공간이 점점 작게 나뉜다. 프로세스들이 연속된 메모리를 차지하는 과정에서 공백이 생기고, 이 공백들이 많은 수의 작은 조각으로 흩어질 수 있다. 분할 자체는 프로세스가 요구한 크기에 맞춰 만들어지므로 내부 단편화는 거의 없지만, 외부 단편화는 피하기 어렵다.
외부 단편화에 대응하는 방법은 두 가지다. 통합은 하나의 작업이 끝났을 때, 비어 있는 기억 장소가 다른 공백과 인접해 있는지 점검해 하나의 공백으로 합치는 과정이다. 압축은 메모리 내용을 적절히 옮겨, 사용 가능한 메모리를 하나의 큰 블록으로 만드는 방법이다.
버디 시스템
버디 시스템(Buddy System)은 자원 할당 과정에서 생기는 단편화 문제를 해결하기 위해 제안되었다. 큰 버퍼를 반복적으로 이등분해 작은 버퍼를 만들고, 가능할 때마다 인접한 자유 버퍼(Free Buffers)들을 다시 합치는 과정을 반복한다. 이렇게 나뉜 버퍼 한 쌍을 서로의 버디(Buddy)라고 부른다.

