OS

[운영체제와 정보기술의 원리] 7장 메모리 관리

shininghyunho 2023. 12. 4. 18:07

32bit vs 64 bit

  • 32 bit 컴퓨터란 메모리를 테이블로 저장한느데 인덱스를 32 bit로 사용한다는 말.
  • 메모리 주소는 1byte 단위로 구분하므로 2^32 개의 인덱스와 1byte의 항목값을 가진 테이블이 존재.
  • 그래서 총 주소 공간은 2^32 * byte == 4 * 2^30 * byte 인데 2^10, 2^20, 2^30 은 각각 K,M,G 이므로,
    4GB 만큼의 주소공간을 표현이 가능함.
  • 그래서 32 bit 컴퓨터는 램 크기가 4GB를 넘을 수 없음.
  • 같은 계산 방식으로 64bit는 2^64 * byte == 16 * 2^60 * byte == 16 * 10^9 GB 이다.
  • 다시 32 bit 컴퓨터는 보통 4KB(2^12 byte)를 단위로 페이지라는 구역을 만들어 쓴다.
  • 32bit 중 페이지 하나당 12bit 하위 bit로 사용한다.

주소 바인딩

  • 프로세스는 독자적인 주소공간을 가짐. 이를 논리적 주소(logical address) 또는 가상 주소(virtual address)라고 함.
  • 프로세스마다 메모리를 독자적으로 쓴다고 생각하고 0번부터 주소를 할당받음.
  • 반대로 실제 메모리 주소는 물리적 주소(physical address)라고 함.
  • 물리적 주소 상단에는 커널주소가 하단에는 사용자 프로그램이 적재됨.
  • 프로세스의 논리적 주소는 메모리의 물리적 주소를 연결해야 사용이 가능한데
    이를 주소 바인딩(address binding)이라고 함.

주소 바인딩은 시기에 따라 3가지로 분류가 됨.

  • 컴파일 타임 바인딩(compile time binding) : 컴파일 시점에 물리적 메모리와 바인딩함.
    그러면 런타임 단계에서 해당 주소 위치가 변경되지 않고 고정되기에 절대 코드(absolute code)를 생성하는 방식임.
    물리적 메모리를 변경하려면 컴파일을 다시해야하기에 현대 시분할 시스템에선 맞지 않는 방식.
  • 로드 타임 바인딩(load time binding) : 프로그램이 시작될때 물리적 메모리가 바인딩됨.
    로더(loader, 사용자 프로그램을 메모리에 적재시키는 프로그램)에 의해 물리적 메모리 주소가 부여된다.
    해당 방식은 컴파일러가 재배치 가능 코드(relocatable code)를 생성한 경우에 가능한다.
  • 실행 시간 바인딩(run time binding) : 런타임 단계에서 물리적 주소가 할당된다.
    이 말은 프로그램이 실행이 된 후에도 물리적 주소가 변경될 수 있음을 의미한다.
    • CPU는 물리적 메모리를 찾기 위해 주소 매핑 테이블(address mapping table)을 참조한다.
    • 해당 테이블을 구현할때는 물리적 메모리 주소의 시작점을 의미하는 기준 레지스터(base register)
    • 논리적 주소의 크기(즉 최대 오프셋)을 의미하는 한계 레지스터(limit register)
    • 논리적 주소를 물리적 주소로 변환해주는 MMU(Memory Management Unit:메모리 관리 유닛) 이라는 하드웨어가 사용된다.

 

MMU 는 논리적 주소를 offset으로 사용하고 실제 물리적 주소의 시작점인 기준 레지스터를 터해주는 하드웨어 장치다.

위 예시에서는 논리주소가 123이어서 기준 레지스터 23000을 더한 23123이 실제 물리적 주소가 될것이다.

 

여기서 만약 논리적 주소가 엉뚱한 값으로 설정되어있으면 다른 프로세스의 주소영역에 접근할수 있게된다. 

그래서 프로세스가 가질수 있는 최대 주소값, 논리적 주소는 0부터 시작하므로 프로세스의 크기를 레지스터로 저장한다.

이를 한계 레지스터(limit register)라고 한다.

 

그래서 논리적 주소는 먼저 한계 레지스터로 범위를 체크하고 넘는다면 트랩을 발생해 강제 종료시킨다.

 

메모리 할당 방식을 설명하기 앞서 사용할 용어를 먼저 정리함.

  • 동적 로딩(dynamic loading)
    • 프로그램 실행시 프로세스의 모든 내용을 메모리에 올리는게 아닌 일부만 올려둠.
    • 예외처리같은 코드들은 크기는 크지만 실제로 사용할 일이 적음.
    • 사용자 프로그램에서 구현도 가능하고 운영체제 라이브러리로도 사용 가능.
  • 동적 연결(dynamic linking)
    • 먼저 연결(linking)은 소스코드를 컴파일한 목적파일(object file)과 미리 컴파일된 라이브러리 파일(library file)을 묶어서 하나의 실행 파일로 생성하는 과정을 의미.
    • 여기서 동적 연결은 오브젝트 파일과 라이브러리 파일의 링킹을 프로그램 런타임에 진행하는것을 의미.
    • 반대로 컴파일 단계에서 링킹이 일어난다면 이는 동적연결(static linking)이라고 함.
    • 동적 연결에서는 여러 프로세스가 공유하는 라이브러리도 각자의 주소공간에 포함시켜 메모리 낭비가 큼.
    • 동적 연결에서 라이브러리르 사용할때 호출부분에 스텁을 사용.
    • 스텁을 통해 라이브러리가 메모리에 올라와 있지 않다면 적재해서 사용.
    • 이는 운영체제의 도움으로 가능함.
  • 중첩(overlays)
    • 프로그램의 주소공간을 분할해 실제 필요한 부분만 메모리에 적재하는것을 의미.
    • 의미는 동적로딩과 유사하지만 목적이 다름.
    • 중첩은 초창기 메모리가 너무 작아 한 프로세스도 메모리에 적재하지 못해
      프로세스는 일부분씩 메모리에 적재하여 실행함.
    • 동적연결은 멀티프로세싱 환경에서 더 많은 프로세스를 적재하기위한 기법임.
    • 중첩은 운영체제의 도움없이 사용자가 직접구현해서 굉장히 복잡한 작업이었음.
  • 스와핑(swapping)
    • 메모리가 부족해 프로세스의 주소공간 전체를 스왑 아웃(swap out)을 시키는것을 의미.
    • 스와 아웃은 디스크의 스왑 영역(백킹 스토어라고도 함)에 저장함.
    • 스왑 영역은 디스크에 존재하지만 잠시 프로세스를 저장하기위한 임시 공간임.
    • 스와핑이 일어나는것은 중기 스케줄러(medium-term scheduler)에 의해 진행됨.
    • 중기 스케줄러는 메모리에 너무 많은 프로세스가 올라와있다면 I/O 작업으로 인한 block 상태의 프로세스나
      그마저도 없다면 ready 상태의 프로세스 중에서 스왑영역으로 내쫒아 suspended 상태를 만듦.
    • 이후 다시 메모리가 여유로워지거나 사용해야하면 다시 스왑 인(swap in)을 진행.

 

물리적 메모리 할당 방식

지금 부터 물리적 메모리를 어떻게 할당한건지를 논의한다.

물리적 메모리는 크게 1개의 프로세스를 자르지 않고 통채로 메모리에 적재하는 연속할당 방식과

1개의 프로세스를 여러개의 조각으로 잘라 메모리에 이곳저곳 나눠서 적재하는 불연속 할당 방식이 있다.

 

연속할당 방식

프로세스를 메모리에 적재시, 주소공간을 쪼개지않고 한곳에 연속적으로 메모리에 적재하는 방식.

이는 다시 프로세스가 아닌 물리적 메모리를 고정된 크기로 나눌지 고정되지 않는 크기로 나눌지로 다시 나뉜다. 

  • 고정 분할 방식
    • 물리적 메모리를 미리 정해진 갯수만큼 분할해서 사용하는 방식.
    • 분할 크기를 동일하거나 조금식 다르게 하거나 할수 있음.
    • 하나의 분할에는 하나의 프로그램만 적재.
    • 그래서 미리 분할된 갯수만큼만 프로그램을 적재시킬수 있고
      프로그램의 최대 크기또한 분할의 크기를 넘을수 없기에 미리 정해짐.
    • 외부 조각과 내부 조각이 발생함.
    • 외부 조각(external fragmentation) : 외부 단편화라고도 함.
      • 분할이 너무 작을때, 분할보다 프로그램이 큰 경우 발생.
      • 분할의 외부에서 생기는 조각이기에 외부 조각이라고함.
      • 분할이 너무 작아 프로그램을 적재하지 못해 생기는 조각.
      • 어떠한 프로그램도 적재하지 못하는 공간임.
    • 내부 조각(internal fragmentation) : 내부 단편화라고도 함.
      • 분할이 너무 클때, 분할보다 프로그램이 작은 경우 발생.
      • 분할의 내부에서 생기는 조각이기에 내부 조각이라고함.
      • 분할에 프로그램을 적재하고 공간이 남게되는 공간.
      • 이미 한 분할에 프로그램이 적재되어 있기에 다른 프로그램을 적재할 수 없음.
  • 가변 분할 방식
    • 메모리에 적재되는 프로그램의 크기와 갯수에 따라 분할이 동적으로 변함.
    • 분할의 크기는 프로그램의 크기에 맞춰서 동적으로 생성하므로,
      분할이 커서 발생하는 내부조각은 없음.
    • 그러나 크기가 작은 프로그램이 할당되고 해제되면 작은 분할이 남게됨.
      그 분할에는 다른 프로그램이 할당되기 힘들어 외부조각이 발생.
    • 새로운 프로그램을 메모리에 적재할때 이미 여러가지 크기의 분할이 존재함.
      그렇다면 가용 공간 중에서 어디에 적재할지를 결정해야함.
      이를 동적 메모리 할당 문제(dynamic storage-allocation problem)라고함.
      • 최초 적합(first fit) : 프로그램을 적재할 분할을 메모리 상단부터 스캔함.
        최초 적합은 이름에서 알수있듯이 위에서부터 가장 먼저 적재할수 있는 분할에 할당.
        시간적인 측면에서는 가장 효율적인 방법.
        대신 분할 크기를 고려하지 않아 내부조각이 계속해서 발생.
      • 최적 적합(best fit) : 프로그램이 적재될 수 있는 가장 작은 분할에 할당.
        공간의 효율성을 극대화하기 위한 방법.
        그러나 전체 스캔(full scan)이 발생해 탐색에 오버헤드 발생.
      • 최악 적합(worst fit) : 프로그램이 적재 가능한 가장 큰 분할에 할당.
        풀 스캔을 통해 가장 큰 내부조각을 발생시키는 최악의 방법.
    • 가변분할 방식에서 발생하는 외부 조각을 해결하기위해
      사용중인 프로세스를 한곳으로 몰아 넣는 컴팩션(compaction)이라는 방법이 있음.
      • 컴팩션은 다수의 메모리 이동이 발생하는 작업이므로 오버헤드가 굉장히 큰 작업.
      • 그렇다고 메모리 이동이 최소가 되도록 이동할 프로세스를 선택하는건 매우 복잡한 계산임.
      • 컴팩션은 메모리가 런타임시에 이동되므로 런타임 바인딩 방식에서만 사용 가능.

불연속할당 방식

하나의 프로세스가 물리적 메모리 여러곳에 분산되어 적재가 됨.

 

프로세스를 여러가지 기준으로 나눌수 있음.

프로세스를 동일한 크기로 나누어 메모리에 올리는 페이징(paging) 방식.

물리적인 크기가 아닌 주소영역에 같은 의미끼리 나누어 적재하는 세그먼테이션(segmentation) 방식

세그먼테이션 방식으로 미리 나누어 두고 다시 동일한 크기로 나누어 적재하는 페이지든 세그먼테이션(paged segmentation) 방식이 있다.

 

현대의 대부분의 컴퓨터는 불연속할당 기법을 사용하므로 큰 카테고리로 설명함.

 

✅ 페이징(paging)

페이징은 프로세스의 주소 공간을 동일한 크기로 나눠(크기 단위를 페이지라고함)
물리적 메모리에 여러 공간에 분할해 적재하는 방식이다.

  • 주소 공간 전체를 메모리에 적재할 필요가 없이 일부는 백킹스토어에 일부는 디스크에 일부는 메모리에 올리는것이 가능.
  • 물리적 메모리를 페이지의 크기와 동일하게 분할하고 이를 프레임(frame)이라고 한다.
  • 프로세스의 페이지는 메모리 프레임 중 임의의 장소에 배치할 수 있기때문에,
    연속할당 기법에서 발생하는 동적 메모리 할당 문제가 생기지 않는다.
  • 한 프로세스의 주소공간을 나누는 페이지를 물리적 메모리의 프레임에 1:1 매칭이 된다.
    매칭되는 정보는 페이지 테이블(page table)에 저장된다.
    • 페이지 테이블에 엔트리 갯수는 해당 프로세스가 가지는 페이지의 갯수만큼 할당된다.
    • 페이지 테이블은 프로세스당 1개씩 생성이 된다.
  • 페이징 기법은 페이지와 프레임이 동일한 크기로 1:1 매칭이 되므로,
    분할의 크기가 작아서 프로그램을 닮을 수 없어서 발생한 외부 조각 문제가 발생하지 않는다.
  • 다만 프로세스의 크기가 정확히 페이지 크기의 배수가 되는게 아니므로,
    분할의 크기가 남아서 발생하는 내부 조각 문제는 발생한다.

주소 변환 기법

실제로 페이지 테이블을 어떻게 사용하는지 알아보자.

CPU는 논리적 주소(페이지)의 정보를 통해 물리적 주소(프레임)에 접근한다.

  • CPU는 (페이지 번호, 오프셋) 으로 (프레임 주소(base address), 오프셋) 을 찾게된다.
  • 프레임의 주소는 페이지 테이블의 페이지 번호를 통해 찾게된다.
    프레임의 주소는 물리적 메모리의 기준 주소, 즉 어디서부터 찾으라는 시작 주소(base address)가 들어있다.
  • 오프셋은 사용할 데이터가 페이지의 몇번째 위치하는지(프레임과 크기가 동일하므로 프레임의 몇번째 위치하는지)를 나타낸다.

페이지 테이블 구현

실제로 페이지 테이블이 어떻게 구현되어 있는지 보자.

 

운영체제는 페이지 테이블을 사용하기 위해 두개의 레지스터를 사용한다.

  • 페이지 테이블 기준 레지스터 (page table base register) : 페이지 테이블의 시작 위치가 저장되어있다.
  • 페이지 테이블 길이 레지스터 (page table length register) : 페이지 테이블의 길이가 저장되어있다. (메모리 침범 방지)

 

TLB

페이지 테이블을 통해서 물리적 메모리에 접근하려면 메모리에 크게 두번 접근해야한다.

  1. 페이지 테이블 기준 레지스터를 참조해 페이지 테이블 자체에 접근
  2. 페이지 테이블 번호를 참조해 테이블의 엔트리인 물리적 메모리 기준 주소 참조

즉 테이블 접근에 1번 물리적 주소 접근에 1번 총 2번 접근해야한다.

2번의 접근을 1번으로 줄이기 위해 TLB(Translation Look-aside Buffer) 라는 고속 주소 변환용 하드웨어 캐시를 사용하기도 한다. 

 

간단하게 말해 TLB 라는 캐시를 사용해 자주 쓰는 메모리 접근을 1회로 줄인것이다.

 

구체적으로 TLB가 어떻게 동작하는지 확인해보자.

  • 페이지 테이블에는 0번부터 페이지와 대응되는 모든 프레임 번호가 순차적으로 들어있다.
    그래서 페이지 번호를 인덱스로 사용해 테이블에 a번 페이지는 테이블 a번째에 위치해있다.
  • 이에반해 TLB는 모든 프레임 번호가 있는게 아니다.
    TLB는 히트율이 좋은 특정 프레임들만 저장해놓기 때문에,
    (페이지 번호, 프레임 번호) 를 엔트리로 저장해 놓고있다.
  • 그래서 CPU는 페이지 번호를 가지고 TLB를 풀 스캔하여 프레임 번호를 찾아야한다.
  • 대신 TLB는 병렬 탐색(parallel search)이 가능한 연관 레지스터(associative register)를 사용해
    빠르시간내에 프레임 번호를 찾을 수 있다.

계층적 페이징

포스팅 맨위에서 32bit 컴퓨터는 최대 4GB 메모리를 가질 수 있다고했다.

페이지의 크기를 4KB라고 한다면, 페이지의 갯수는 4GB/4KB = 1M 개가 된다.

1M 개의 페이지를 참조할 페이징 테이블의 크기는 프레임 번호를 담고 있는 테이블의 엔트리 크기 x 엔트리 갯수다.

프레임 번호를 담기위해 4byte를 쓴다면, 1M x 4byte = 4MB 의 페이지 테이블이 모든 프로세스마다 필요하다.

 

즉 프로세스가 많아질수록 메모리에는 페이지 테이블을 저장하는데 많은 공간을 쓴다는 말이다.

게다가 실제로 사용하는 프로그램은 메모리의 극히 일부분이 때문에,

사용하지도 않는 페이지까지 모두 테이블로 생성되어 공간낭비가 심하다.

 

그래서 페이징을 2단계로 나눈 2단계 페이징(two-level paging) 기법을 사용한다.

 

 

  • 2단계 페이징에서는 외부 페이지 테이블과 내부 페이지 테이블로 나뉜다.
  • 기본 페이지 테이징과는 다르게 사용하지 않는 주소공간은 만들지 않을 수 있다.
    외부 페이지 엔트리에 null 로 저장하면 굳이 해당 내부 페이지 테이블을 만들지 않는것이다.
  • 32 bit 컴퓨터 체계에서 페이지 크기가 4KB(2^12 byte)라면 오프셋(d) 에게 12 byte를 할당하고
    나머지 20 byte가 남게된다.
  • 이때 페이지 테이블을 한 페이지(4KB)에 담게된다면 4KB/4byte = 1K(2^10) 개의 오프셋을 가질것이다.
    따라서 내부 페이지 테이블은 1K(2^10)의 오프셋을 가져 10 byte를 사용하고,
    외부 페이지 테이블도 동일하게 10 byte를 사용한다.
  • 32 bit 가 아닌 64 bit 컴퓨터라면 페이지 테이블의 크기가 커져
    4단계 6단계 처럼 계층이 더욱 커질것이다.
  • 다단계 페이징을 사용해 사용하는 메모리의 테이블만 생성해 공간 효율성을 높힐 수 있다.
  • 그러나 페이지 테이블이 여러 단계로 생기므로 메모리 접근 횟수가 늘어나게 된다.
  • 하지만 TLB를 통해 캐시 히트율이 좋다면 메모리 접근 오버헤드는 크지 않는다.

역페이지 테이블

지금까지 설명한 페이지 테이블은 공간 효율성이 좋지 않다.

그 이유는 모든 프로세스의 페이지 테이블은 모든 페이지 항목을 갖기 때문이다.

왜 그런지는 정확히는 알수 없지만 내 생각(+챗gpt)에는 다음과 같은 이유가 있을거같다.

  1. 운영체제는 해당 프로세스가 어떤 페이지를 참조할지 알수없음.
    프로세스가 실행하다가 특정 주소를 참조할텐데 운영체제는 이를 사전에 전부 알수가 없을것이다.
    그래서 모든 페이지를 참조할 수 있게 페이지 테이블을 구성한다. (내 생각엔 이게 가장 큰 이유)
  2. 페이지 폴트시 해당 페이지에 대한 자리가 미리 참조하고 있다면 빠르게 접근 가능.
  3. 문맥교환시 각 프로세스마다 페이지 테이블의 크기가 다르다면 매번 새롭게 할당하는데 오버헤드가 큼.

또한 프로세스마다 독립적인 가상주소간을 가져야하기 때문에 프로세스마다 다른 페이지 테이블을 갖는다.

이러한 이유들 때문에 페이징은 공간 효율성이 좋진않다.

 

그렇다면 모든 페이지를 참조하니까 하나의 공통된 테이블이 있으면 어떨까?

그게 역페이지 테이블이다.

 

  • 이름이 역페이지인 이유는 페이지 테이블의 인덱스가 가상 주소가 아닌 실제 물리 주소이기 때문이다.
  • 즉 물리적인 주소 순서대로 테이블이 구성되어 있고,
    테이블 항목안에 프로세스 번호와 페이지 번호가 있는것이다. 
  • 다만 페이지를 찾기 위해서 페이지 테이블의 모든 항목을 풀 스캔해야하므로
    굉장히 오랜 시간이 걸린다.
  • 그래서 역페이지 테이블은 메모리에 유지하지 않고 연관 레지스터에 보관해 병렬탐색을 통해 시간 효율성을 높힌다.

공유 페이지

그냥 페이지 자체를 공유할수도 있다.

공유 페이지는 공유 코드를 담고 있다.

  • 공유 코드는 동기화 문제로 인해 읽기 전용(read-only) 특성을 갖고있다.
  • 재진입 가능 코드(re-entrant code) 또는 순수 코드(pure code)라고도 불린다.

위 그림에 예에서는 프로세스 1,2,3이 코드 1,2,3을 공유 코드로 사용하고 있다.

반대로 독립적인 페이지는 사유 페이지(private page)라고 불린다.

 

메모리 보호

  • 페이지 테이블 항목에는 물리적인 주소값이 저장되어있다.
  • 해당 주소값 이외에도 보호비트(protection bit) 유효-무효 비트(valid-invalid bit)도 포함되어있다.
  • 보호 비트 : 해당 주소를 어떻게 처리할수있는지에 대한 비트다. read, write, read-only 같은 권한이 써져있다.
    (프로세스의 주소영역은 이미 다른 프로세스로부터 접근이 불가능하므로 누가접근하는지는 몰라도 된다. 어차피 본인 프로세스만 접근이 가능하니까)
  • 유효-무효 비트 : 해당 프레임이 메모리에 올라와있는지 여부다. 무효 표시로 되어있다면 디스크가 백킹 스토어에 있을수 있다.

✅세그먼테이션

  • 페이징은 주소공간을 모두 물리적인 크기로 나눴다. (물리적인 크기 단위)
  • 이와 반대로 동일한 의미를 갖는 부분으로 나눈것이 세그먼테이션이다. (논리적인 의미 단위)
  • 주소영역의 코드, 데이터, 힙, 스택 을 하나의 세그먼트로 볼수도 있고,
    주소영역 전체를 하나의 세그먼트로도 볼 수 있다. (동일한 의미로 생각하면)

페이징과 유사하게 주소영역을 나누어서 메모리에 올리지만, 어떻게 나누는지가 다르다.
세그먼트는 논리적인 구분 단위.

 

세그먼테이션은 어떻게 가상주소를 물리적인 주소로 변환할까?

  • 세그먼트는 크기가 의미 단위이기 때문에 크기를 따로 적어줘야한다.
  • 해당 세그먼트 대응하는 물리 주소의 시작 주소값인 기준점(base)와
    주소의 끝을 의미하는 한계점(limit)이 페이지 테이블에 있어야한다.
    (페이징에서는 크기가 모두 동일해 limit가 필요 없었다.)

세그먼테이션의 테이블 구성은 어떨까?

세그먼테이션도 페이징과 유사하게 세그먼트 테이블 기준 레지스터(segment-table base register)와

세그먼트 테이블 길이 레지스터(segment-table length register)가 있다.

  • 세그먼트 테이블 기준 레지스터(STBR) : 해당 세그먼트 테이블이 메모리 어느 위치에 있는지 나타낸다.
  • 세그먼트 테이블 길이 레지스터(STLR) : 테이블의 몇개의 세그먼트가 있는지 나타낸다.

CPU는 다음과 같은 순서로 주소 변환을 시도한다.

  1. 세그먼트 번호가 STLR 보다 작은지 확인한다. 크다면 트랩을 발생시킨다.
  2. 오프셋 값이 limit 보다 작은지 확인한다.
  3. 그렇지 않다면 base 접근한뒤 offset 위치에 해당 주소값이 존재한다.

세그먼트는 페이징과 부분을 나누는 의미외에 유사한점이 많다.

  • 세그먼테이션도 보호비트와 유효비트가 있다. (페이징과 동일하게 접근 권한과 메모리에 있는지 유무다.)
  • 공유 메모리도 사용이 가능하다.

 

각각의 테이블을 보면 동일한 주소를 가리키고 있다.

 

그밖에 세그먼테이션의 특징

  • 세그먼테이션은 페이징에 비해 보안성이 좋다.
    주소 공간을 공유하는 상황에서 하나의 페이지에는 공유 메모리와 사유 메모리가 같이 공존할 수 있다.
    하지만 의미 단위로 분리하는 세그먼테이션은 공유 메모리와 사유 메모리가 섞일일이 없다.
  • 하지만 세그먼트의 크기는 가변적이기 때문에 외부 조각이 발생한다.
    작은 세그먼트 조각이 해제되면 다른 큰 세그먼트들을 할당할 수가 없기 때문이다.
    앞서 살펴봤던 메모리 가변분할 방식에서 나왔던 가용공간 할당 문제가 생기는것이다.
    first fit, best fit 등이 있었다.

✅페이지드 세그먼테이션

페이징은 한 페이지에 공유 메모리와 사유 메모리가 같이 적재되는 보안 문제가 있었다.

세그먼트는 가변적인 크기로 인해 외부 조각이 발생하는 문제가 생긴다.

 

그래서 프로그램 입장에서는 세그먼트를 사용하는것처럼 보이지만

실제로 물리적 메모리에 적재할때는 페이지을 사용하는

페이지드 세그먼테이션이 있다.

해당 방법을 사용하면 메모리는 논리적 단위인 세그먼트로 분리가 되므로 보안 문제가 해결되고,

물리적으로는 동일한 크기의 페이지로 적재되므로 외부 조각 문제도 해결된다.

 

주소 변환 방법은 크게 생각하면

세그먼트 생성 -> 세그먼트에 해당하는 페이지로 변환 -> 페이지를 통해 실제 메모리 접근이다.

 

구체적으로 살펴보면

  • 세그먼트 번호를 STBR와 더해서 세그먼트 테이블에 접근한다.
  • 해당 항목에는 세그먼트 길이와 페이지 테이블 base(시작주소)가 있다.
  • offset이 세그먼트 길이보다 작은지 확인한다.
  • 세그먼트의 오프셋을 다시 페이지 번호와 페이지의 오프셋으로 나눈다.
  • 이제 페이지 테이블 base와 페이지 번호를 더해 페이지 테이블에 접근한다.
  • 페이지 테이블 항목에는 실제 물리주소 프레임의 base가 담겨있다.
  • 프레임 base와 오프셋을 더해 실제 주소값에 접근한다.

말 그대로 세그먼트를 이용해 페이지 번호를 구한뒤 물리적인 주소에 접근하는 것이다.