CS/operating system

Memory Management: 메모리 관리 (1/3)

superbono 2021. 2. 16. 18:00

컴퓨터에서는 byte단위로 메모리 주소를 부여하기 때문에 32비트 주소 체계를 사용하면 2^32바이트 만큼의 메모리 공간에 서로 다른 주소를 할당할 수 있다. 컴퓨터 상의 주소는 32비트를 그대로 사용하지 않고 효율적인 운영을 위해 일련의 영역을 행정구역처럼 묶어서 사용한다. 보통 4kb(=2^12byte) 단위로 묶어서 페이지(page)를 만든다. 하나의 페이지 크기가 2^12이므로 페이지 내에서 바이트별 위치 구분을 위해서는 12비트가 필요하다. 따라서 총 32비트 주소 중 하위 12비트는 페이지 내에서의 주소를 나타내게 된다.

 

* Logical Address( = virtual address)

 - 프로세스마다 독립적으로 가지는 주소 공간

 - 각 프로세스마다 0번지부터 시작

 - CPU가 보는 주소는 logical address임

 

* Physical Address

 - 메모리에 실제 올라가는 위치 아래에는 커널, 상위에는 응용 프로그램

 

* 주소 바인딩: 어떤 프로그램이 물리적 주소 어디로 올라갈 지 결정하는 것 (어떤 프로세스의 논리적 주소가 물리적 주소 어디로 연결해주는거)

Symbloic Address(프로그래머 입장에서 사용하는 숫자주소가 아닌 주소) ->(컴파일) Logical Address ->(이 시점이 언제인가??? -> 주소 바인딩) Physical Address

 

1. 주소 바인딩

주소 바인딩은 프로그램이 적재되는 물리적 메모리의 주소가 결정되는 시기에 따라 세가지로 분류할 수 있다.

* Compile time binding (컴파일 될 때)  -> 절대코드를 생성하는 바인딩 방식

 - 물리적 메모리 주소(physical address)가 컴파일 시 결정된다.

 - 물리적 메모리의 위치를 변경하고 싶다면 재컴파일 해야함 (시작 위치를 변경할 때 재 컴파일 해야함) -> 따라서 비효율적이고 잘 사용되지 않음

 - 컴파일러는 절대 코드(absolute code) 생성함

 

* Load time binding (프로그램의 실행이 시작될 때에 물리적 메모리 주소 결정)

 - Loader(사용자 프로그램을 메모리에 적재시키는 프로그램)의 책임 하에 물리적 메모리 주소를 부여한다.

 - 프로그램이 종료될 때까지 물리적 메모리 상의 위치가 고정된다.

 - 컴파일러가 재배치 가능 코드(relocatable code)를 생성한 경우 가능하다 -> 비어있는 메모리에 올라갈 수 있는 코드 생성

 

* Execution time binding (=Run time binding) 

 - 수행이 시작된 후에도 프로세스의 메모리 상 위치를 옮길 수 있음

 - cpu가 주소를 참조할 때마다 binding을 점검 (address mapping table: 해당 데이터가 물리적 메모리의 어느 위치에 존재하는지) 

 - 하드웨어적인 지원이 필요하다 (e.g., base & limit registers, MMU(논리적 주소를 물리적 주소로 매핑해주는 하드웨어 장치))

 

MMU (Memory Management Unit)

logical addres를 hardware address로 매핑해주는 hardware device이다.

 

MMU Scheme

relocation register (= base register) : 접근할 수 있는 물리적 메모리의 최솟값

limit register : 논리적 주소의 범위( 그 프로그램이 기준 레지스터 값부터 접근할 수 있는 메모리의 범위), 현재 CPU에서 수행 중인 프로세스의 논리적 주소의 최댓값, 그 프로세스의 크기를 담고 있다. 한 번에 물리적 메모리에 여러가지 프로세스가 올라가는 다중 프로그래밍 환경에서 메모리 보안을 도와준다. 

wjqr

사용자 프로세스가 cpu에서 수행되며 생성해내는 모든 주소 값에 대해 base register(=relocation register)의 값을 더한다. relocation register(물리적, 시작위치 저장) 과 limit register로 주소 변환함

cpu가 특정 프로세스의 논리적 주소를 참조할고 할 때 mmu 기법은 그 주소값에 기준 레지스터: base register(=relocation register, 그 프로세스의 물리적 메모리 시작 주소를 가지고 있다.) 의 값을 더해 물리적 주소값을 얻어낸다. MMU 기법에서는 프로그램의 주소 공간이 물리적 메모리의 한 장소에 연속적으로 적재되는 것으로 가정한다. 따라서 그 프로그램의 물리적 메모리 상의 시작 주소만 알면 주소 변환이 쉽게 가능하다. MMU 기법에서 사용자 프로그램이나 CPU는 논리적 주소만을 다룰 뿐, 실제 물리적 주소는 상관 안한다. 

위의 그림에서 논리적 주소 346번지는 물리적 메모리의 시작 위치인 relocation register부터 요청된 위치가 얼마나 떨어져있는지를 알려주는 일종의 오프셋(offset) 개념으로 생각할 수 있다. 프로세스들은 모두 독립적인 논리적 주소공간을 가지므로 논리적 주소는 겹칠 수 있으나 물리적 주소는 겹쳐서는 안된다. MMU 기법에서는 문맥교환으로 인해 CPU에서 수행 중인 프로세스가 바뀔 때마다 relocation register의 값을 그 프로세스에 해당하는 값으로 재설정함으로서 각 프로세스에 맞는 논리적 주소값이 물리적 주소값을 찾아가도록 해준다. 

 

CPU가 요청한 프로세스의 논리적 주소값이 limit register 내에 저장된 그 프로세스의 크기보다 작은지 확인한다. 작다면 논리적 주소값에 relocation register 값을 더해 물리적 메모리 주소를 구한 다음 해당 물리적 메모리 주소에 접근하도록 허락한다. 한편 논리적 주소값이 limit register 값보다 크다면 trap(:소프트웨어 인터럽트)를 발생시킨다. 다른 프로세스의 주소 영역에 접근하려는 시도이기 때문에 강제 종료해야 하는 것이다. cpu 제어권은 os로 넘어감 os가 강제종료 시켜준다.

 

2. 메모리 관리

1. Dynamic Loading (동적 로딩)

프로그램 전체를 메모리에 미리 다 올리는 것이 아니라, 해당 루틴이 불려질 때 메모리에 load하는 것이다. 다중 프로그래밍(multi programming)환경에서 메모리 사용의 효율성을 높이기 위해 사용하는 방식이다. 프로그램의 상당 부분은 오류 처리 루틴 같은 가끔씩 사용되는 코드기 때문에 이러한 동적 로딩은 메모리 효율성을 향상시킨다. 운영체제의 특별한 지원 없이 프로그램 자체에서 구현이 가능하며 운영체제가 라이브러리를 통해 지원할 수도 있다. 프로그래머는 라이브러리를 사용한다. 명시적 직접 설계 프로그램이 올라갔다 쫓겨나는 paging 기법과는 다르다. 

* loading: 메모리로 올리는 것

 

2. Dynamic Linking (동적 연결)

* Linking :여러 군데에 존재하던 컴파일된 파일들을 묶어 하나의 실행파일을 만드는 것 / 프로그래머가 작성한 소스코드를 컴파일하여 생성된 목적파일(object file)과 이미 컴파일된 라이브러리 파일들을 묶어 하나의 실행 파일을 생성하는 과정

Linking을 실행 시간(execution time)까지 미루는 방법이다. 동적 연결과 반대되는 개념인 정적 연결은 프로그래머가 작성한 코드와 라이브러리 코드가 모두 합쳐져서 실행파일이 생성된다. 따라서 실행 파일의 크기가 상대적으로 크며 동일한 라이브러리를 각 프로세스가 개별적으로 메모리에 적재해야 하므로 물리적 메모리가 낭비된다. 예를 들어 printf 함수를 모든 프로세스가 다 메모리에 올려야 한다고 생각해보자 메모리의 낭비가 엄청날 것이다.

그에 반에 dynamic linking은 라이브러리 실행 시 연결(link)된다.

라이브러리 호출 부분에 라이브러리 루틴의 위치를 찾기 위한 stub(스텁)이라는 작은 코드를 둔다. 라이브러리가 이미 메모리에 있으면 그 루틴의 주소로 가고(직접 참조) 없으면 디스크에서 읽어온다(메모리로 적재한 뒤 수행)

다수의 프로그램이 공통으로 사용하는 라이브러리를 메모리에 한번만 적재하므로 메모리 사용의 효율성을 높일 수 있다. 이러한 동적 연결 기법은 운영체제의 지원을 필요로 한다. 

 

3. Overlay (중첩)

프로세스의 주소 공간을 분할해 실제 필요한 부분만 메모리에 올리는 것이다. 프로세스의 크기가 메모리보다 클 때 유용하다. -> 옛날엔 메모리가 너무 작아서 쪼개서 올려야 했었다. 동적 로딩은 다중 프로그래밍 환경에서 메모리의 이용률을 향상시키기 위해 프로세스의 주소 공간 중 당장 필요한 것만 올리는 것이고, 중첩은 옛날에 메모리 크기가 프로세스 크기보다 작으니까 분할해서 올린다는 것이 차이점이라고 할 수 있다. 단일 프로세스가 메모리에 올라가기조차 힘든 시절이 있었다는 것이다.

운영체제의 지원 없이 사용자에 의해 구현되며 작업 공간의 메모리를 사용하던 초창기 시스템에서 수작업으로 프로그래머가 구현했다. -> dynamic loading은 라이브러리라도 지원해줘서 엄청 자세히 알 것 까진 없지만... 아무튼 수작업 중첩(manual overlay)라도고 부름 프로그래밍이 매우 복잡하다.

 

4. Swapping

물리적 메모리에 워드 프로세서 프로그램이 올라가 있는데 웹 브라우저 올리고 싶은데 메모리 공간이 없다면 p1을 스왑아웃 시키고 여유 공간에 웹브라우저 프로그램의 주소 공간을 스왑 인 시킨다.

메모리에 올라온 프로세스의 주소 공간 전체를 디스크의 스왑 영역(swap area, backing store)로 일시적으로 통째로 쫓아내는 것을 말한다. swap area란 디스크 내의 파일 시스템과는 별도로 존재한다. 파일 시스템은 전원이 나가도 그 내용이 유지되는 비휘발성 저장공간이지만, swap area는 프로세스가 수행 중인 동안에만 디스크에 일시적으로 저장하는 공간이다. 저장 기간이 상대적으로 짧다. 스와핑은 메모리에 존재하는 프로세스의 수 조절을 할 수 있다는 점에서 degree of multiprogramming을 조절한다고 말할 수 있다. 스와핑은 프로세스가 종료되어 그 주소공간을 디스크로 내쫓는 것이 아니라, 특정한 이유로 수행중인 프로세스의 주소 공간을 일시적으로 메모리에서 디스크로 내려놓는 것을 말한다. 이 때 메모리에서 디스크로 내쫓는 것을 swap out, 디스크에서 메모리로 올리는 것을 swap in이라고 말한다. 

 

swap in / swap out

일반적으로 중기 스케줄러(swapper: 메모리에 너무 많은 프로그램 올라오면 부담되니까 몇개를 메모리에서 디스크로 쫓아내는거)에 의해 swap out 시킬 프로세스를 선정한다.

priority based CPU scheduling algorithm

- 우선순위가 낮은 프로세스를 스왑 아웃 시킴

- 우선 순위가 높은 프로세스를 스왑 인 (메모리에 올려놓음)

 

compile time bindind, load time binding에서는 스왑 아웃 프로세스를 다시 스왑 인 시킬 때 원래 메모리 위치로 올려놔야 한다. 따라서 swapping이 효율적으로 반영된다고 할 수 없다. 반면 execution time binding에서는 추후 빈 메모리 영역 아무곳에나 올릴 수 있어서 효율적으로 반영할 수 있다. 얘는 300에서 쫓겨나도 700으로 올라가도 되는데 (주소가 바뀌어도 되니까) compile time, run time 얘넨 500에서 쫓겨나면 500으로 올라가야 함

swap time은 대부분 실제 데이터를 읽고 쓰는 전송시간(transfer time)이다(swap 되는 양에 비례하는 시간)