#TLB

지난 포스트에서 paging을 배웠다. 주소변환을 하는 과정에서 가상주소의 VPN를 물리주소의 PFN으로 변환하는데 필요한 page table이 필요하고 이를 접근하는데 속도가 느려지는 문제가 있었다. 이번엔 속도를 어떻게 향상시킬지를 알아보자.

🔗 TLB

주소변환을 빠르게 하기위해 우리는 translation-lookaside buffer 또는 TLB라고 부르는 것을 도입한다.TLB는 cpu에 구현된 mmu의 일부분이며 주소변환에 대한 캐시이다. 캐시는 자주접근하는 메모리를 cpu근처에 저장해놓고 사용하므로 메모리접근 속도를 높이는 것이다.

MMU는 주소공간을 지원해주기 위해 하드웨어가 정의하고 있는 것들 모두를 포함하고 있다. 앞에서 살펴본 page table, page entry의 포멧들을 cpu를 정하는데 그것도 mmu의 일부이다. 즉. 모든 메모리와 관련되서 cpu가 해줘야하는 기능들이 mmu이고 mmu는 기본적으로 하드웨어인데, 운영체제가 mmu를 정의를 잘 설정해줘야 의도대로 작동될 수 있다.

TLB를 제어하는 하드웨어부분의 동작을 수도코드로 나타내면 다음과 같다.

VPN = (VirtualAddress & VPN_MASK) >> SHIFT          // VPN을 가져온다.
(Success, TlbEntry) = TLB_Lookup(VPN)               // VPN이 TLB에 존재하는지 검사
if(Success == True)                                 // TLB 히트!
    if(CanAccess(TlbEntry.ProtectBits) == True)     // 해당 페이지에 대한 접근 권한검사
        offset = VirtualAddress & OFFSET_MASK       // offset 구함
        PhysAddr = (TlbEntry.PFN << SHIFT) | offset // offset과 PFN을 합쳐 PA구성
        Register = AccessMemory(PhysAddr)           // 메모리에 접근
    else
        RaiseException(PROTECTION_FAULT)
else                                                // TLB 미스!
    PTEAddr = PTBR + (VPN * sizeof(PTE)             // PTE가 있는 주소 구함
    PTE = AcessMemory(PTEAddr)                      // PTE접근
    if(PTE.Valid == False)                          // PTE 접근 권한검사
        RaiseException(SEGMENTATION_FAULT)
    else if(CanAccess(PTE.ProtectBits) == False)    
        RaiseException(PROTECTION_FAULT)
    else
        TLB_Insert(VPN, PTE.PFN, PTE.ProtectBits)   // TLB에 새로운 VPN entry삽입
        RetryInstruction()                          // 다시시도

Example

TLB 작동과정을 예제로 알아보자. 예제에서는 가상주소 100번지부터 10개의 4바이트 크기의 정수배열이 존재한다. 가상주소공간은 8비트이며, 페이지 크기는 16바이트이다. (페이지 크기로 offset은 4비트를 알 수 있고 따라서 PFN은 4비트다.)

a[0]부터 a[9]까지 TLB 히트,미스로 나타내보자.

a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
미스 히트 히트 미스 히트 히트 히트 미스 히트 히트

❓ TLB미스는 누가 처리할까?

한가지 방법은 하드웨어(즉 cpu)가 알아서 해주는거다. x86을 쓰는 인텔이나 amd가 이에 해당한다. CISC프로세서들이다. TLB미스가 나면 운영체제에게 묻지않고 알아서 해결한다. 자체적으로 페이지테이블 접근 능력이 필요하다. 반면 RISC는 (CISC보다 최근에 등장) TLB미스를 소프트웨어로 관리한다. 미스가 일어나면 시그널을 발생시켜 운영체제에서 트랩핸들러로 TLB를 갱신한다.

CISC는 page table base register(ptbr)로부터 구성된 형태는 multi-level page table(다음장에 나오는)인데, RISC는 cpu에서 page table을 집접 접근하는게 아니기 때문에 어떤 형태든 자유롭게 선택해서 구현이 가능하다.

🔗 TLB 내용

기본적으로 TLB는 Fully associative하다고 한다. 주소변환을 할때 TLB entry중에 빈공간이 있다면 어느곳이든 들어갈 수 있다. entry가 뒤죽박죽 되어있을텐데 하드웨어는 전체 TLB를 병렬로 찾는다. 언급했듯이 entry에는 VPN, PFN, 다른 비트들이 포함되어 있다.

위의 그림처럼 TLB가 있다면 두개의 entry가 각각 어떤 프로세스를 위한 항목인지 알 수가 없다. OS는 Context-switch할때 마다 flush해서 valid bit을 모두 0으로 채워야한다. 이는 오버해드가 크다. 따라서 주소공간 식별차 필드를 추가했다.

위의 경우 프로세스 두개가 같은 물리페이지를 공유하고 있다. 코드 페이지를 공유하는 것은 사용되는 물리페이지 수를 줄일 수 있으므로 유용하고 메모리 부하도 줄일 수 있다.

실제 TLB 구성

  • PFN
  • VPN
  • G (Global bit) : 모든 프로세스들이 공유한다.
  • ASID bits : Address space id, asid가 없으면 스케쥴링할때마다 TLB의 모든 내용을 flush해야한다.
  • C (Coherence bits) : 하드웨어에 어떻게 캐시되어있는지 판단
  • D (Dirty bit) : 페이지가 갱신되면 세팅
  • V (Valid bit) : VPN과 PFN이 유효한지

asid 비트수가 필요한 프로세스 id를 표현하려는 수보다 더 적은데?

해결하기 위한 방법 몇가지가 있다.

  1. pid를 asid비트수에 제한되도록 한다.
  2. asid는 프로세스 하나당 할당해주고 더 오버되면 asid가 없는것처럼 그냥 동작시킨다.
  3. 동적으로 할당한다. (현재 채택하는 방법)