티스토리 뷰

반응형

안녕하세요 Pingu입니다.

 

지난 글에 이어 이번 글에서는 메모리를 사용할 때 고정크기로 할당하는 paging 기법의 문제점 중 하나인 page table로 인한 메모리 낭비를 줄이는 방법에 대해 알아봤습니다. 이번 글에서는 page table을 아예 Disk에 저장하여 메모리 낭비를 줄이는 방법을 알아보려고 합니다. 제가 공부할 때 참고하고 있는 책인 OSTEP에서는 Chapter 21 Swapping: Mechanisms 입니다!

Beyond Physical Memory: Mechanisms

지금까지 알아본 모든 메모리 관리 방법들은 모두 메모리의 크기보다 작은 프로세스의 경우만 알아봤었습니다. 그렇다면 만약 메모리의 크기보다 큰 프로세스를 실행하려면 어떻게 해야 할까요? 이를 위해서는 컴퓨터 구조에서 중요하게 다루는 memory hierarchy(메모리 계층 구조)에 추가적인 계층이 필요합니다. Paging 기법에서 모든 page는 메모리에 존재한다고 가정했었는데 더 큰 주소 공간을 가진 프로세스를 위해서 OS는 현재 사용 빈도가 낮은 page들을 메모리에서 해제하고 다른 곳에 저장해둘 공간이 필요합니다. 이 공간이 바로 Disk입니다. 즉 이를 추가한 메모리 계층 구조를 그려보면 아래와 같습니다.

위와 같이 메모리 계층 구조를 간단하게 그려볼 수 있습니다. 메모리 계층 구조에서 위로 갈수록 비싸지고 용량은 적고 빨라지며 아래로 갈수록 싸고 용량은 커지고 느려집니다. 이번 글에서는 page를 main memory(DRAM)에서 local storage(Disk)로 옮겨서 프로세스를 실행하는 방법에 대해 알아볼 예정입니다. 그런데 OS는 어떻게 크기는 크지만 느린 Disk를 활용하여 메모리 가상화를 잘 구현할 수 있을까요?

 

프로세스에 대해 하나의 큰 주소 공간을 제공하는 이유는 사용하기 편하기 때문입니다. 큰 주소 공간을 사용하면 프로세스를 위한 공간이 충분히 있는지 걱정할 필요가 없기 때문이죠. 이러한 것을 Disk의 Swap 공간과 함께 사용하면 OS는 동시에 실행되는 여러 프로세스에 대해 큰 가상 주소 공간을 제공할 수 있습니다. 멀티 프로세서가 발명되며 모든 page를 교체할 수 있는 능력이 요구되었습니다. 옛날 하드웨어들은 모든 프로세스에 필요한 page를 한 번에 저장할 수 없었기 때문입니다. 따라서 더 큰 메모리가 필요하게 되었고 이를 Disk의 도움을 받아 구현하는 방법을 이번 글에서 알아보겠습니다.

Swap Space

그럼 먼저 Swap 공간이 무엇이며 page를 어떻게 swap 공간에 저장하는지에 대해 알아보겠습니다. Paging 기법에서 page를 메모리가 아닌 Disk에 저장할 수 있는데 이렇게 page가 저장되는 Disk 공간을 swap 공간이라고 합니다. OS가 swap 공간을 page 크기 단위로 읽고 쓰기 위해서는 disk에서 page의 주소를 알아야 합니다. 또한 swap 공간의 크기는 특정 시점에 사용할 수 있는 최대 page 수를 결정합니다. 지금은 설명을 위해 swap 공간이 아주 크다고 가정하겠습니다.

위의 그림은 4 page를 저장할 수 있는 실제 메모리와 8 page를 저장할 수 있는 swap 공간이 있는 예입니다. 메모리를 보면 프로세스 0, 1, 2는 실제 메모리를 공유하고 있는 것을 볼 수 있습니다. 하지만 프로세스 3은 실제 메모리에는 존재하지 않습니다. 프로세스 3는 모든 page가 swap 공간에 존재하는데, 여기서 알 수 있는 점은 프로세스의 모든 page가 메모리에 존재하지 않을 수도 있다는 점입니다. 위와 같이 메모리와 swap 공간을 함께 사용하니 실제 메모리만 사용할 때 보다 더 큰 크기의 메모리를 가진 것처럼 사용할 수 있으며 그 결과 더 많은 프로세스를 실행할 수 있게 됩니다.

 

The Present Bit

그럼 이제 swap 공간에 page들을 저장할 수 있다는 것도 알았으니 어떤 page에 대해 주소변환을 하려고 할 때 page가 메모리에 있는지 swap 공간에 있는지도 알아야 하지 않을까요? 그리고 이를 Translate Lookaside Buffer(TLB)가 있는 시스템에서는 어떻게 처리해야 하는지 알아보겠습니다.

 

우선 기존의 방법을 다시 생각해보자면, 프로세스에서 가상 메모리 참조가 발생하고 가상 주소를 실제 메모리 주소로 변환해야 합니다. 이 과정을 위해 가상 주소에서 VPN(virtual page number)를 알아내고 이에 대한 정보가 TLB에 있는지 조회합니다. 존재한다면 바로 주소 변환을 해버리고 존재하지 않는다면 page table에 접근하여 PFN(page frame number)를 알아낸 뒤 offset을 더하여 실제 메모리 주소로 변환한 뒤 TLB에 이 정보를 저장합니다. 이 방법이 문제가 없었던 이유는 swap 공간이 없었기 때문입니다. 만약 swap 공간이 존재하는 시스템이라면 이 과정에서 page가 메모리에 존재하는지 swap 공간에 존재하는지를 알려주는 정보가 필요합니다. 이를 present bit이라는 곳에 기록하여 메모리에 존재하지 않는다면 page fault가 발생하고 page fault handler가 호출되어 실제 메모리에 page가 없으므로 swap 공간에서 찾도록 해야합니다.

The Page Fault

그럼 page가 메모리에 존재하지 않을 때 발생하는 page fault가 어떻게 처리되는지를 살펴보도록 하겠습니다. 우선 page가 disk로 swap 되어 메모리에 존재하지 않아 page fault가 발생하는 경우와 잘못된 접근에 의해 발생하는 segmentation fault는 실행이 되느냐 안 되느냐의 큰 차이가 있습니다. Page fault는 정상적인 접근으로 page를 disk에서 가져와서 다시 실행하지만 segmentation fault는 잘못된 접근이므로 프로세스를 중단합니다. 따라서 page fault가 발생하면 올바른 disk 주소로 이동하여 올바른 page를 메모리로 가져와야 합니다.

 

OS는 disk 주소에 대한 정보를 page의 PTE(Page Table Entry)에서 알아낼 수 있습니다. 이렇게 알아낸 주소로 page를 가져오면 메모리에 할당하고 page table의 present bit를 업데이트하여 page가 메모리에 존재한다는 것을 기억합니다. 그런 뒤 TLB에도 해당 정보를 업데이트하여 다음 접근부터는 TLB로 주소변환을 수행합니다.

 

Disk에서 page에 대한 정보를 가지고 올 때 즉 disk I/O가 진행 중인 동안에는 프로세스는 잠시 blocked 상태가 됩니다. 따라서 disk I/O가 진행중인 프로세스 외에 다른 프로세스를 해당 시간 동안 실행하고 있으면 됩니다. 이런 방식으로 주소변환이 느린 swap 방법을 멀티 프로세서 시스템에서 사용할 수 있는 것입니다.

What If Memory Is Full?

만약 메모리를 모두 사용 중인 상태 혹은 조금의 여유공간만 존재하는 상태에서 새로운 프로세스를 실행하고 싶다면 어떻게 할까요? OS는 현재 메모리에 존재하는 page들 중 일부를 메모리에서 해제하여 새로운 프로세스의 page를 위한 공간을 만들 수 있습니다. 이러한 것을 page-replacement policy(교체 정책)이라고 합니다.

 

이러한 교체 정책을 효율적으로 하지 않으면 어떤 문제가 발생할까요? 엄청나게 자주 사용되는 page들이 있었는데 이를 새로운 프로세스가 실행되며 모두 swap 공간으로 이동했다고 가정해보겠습니다. 엄청 자주 접근되는 page인데도 불구하고 disk에 존재하게 되어 이는 엄청난 성능 저하로 이어질 것입니다. 따라서 교체 정책을 효율적으로 할 수 있는 방법에 대해서도 알아봐야 합니다. 이에 대해서는 다음 글에서 알아보도록 하겠습니다.

Page Fault Control Flow

그럼 이제 page fault의 처리 과정이 어느 정도 그려지시나요? 그럼 이를 간단하게 코드로 나타내 보겠습니다. 먼저 page fault가 발생했을 때 하드웨어가 하는 일을 코드로 표현해보겠습니다.

// VPN 추출
VPN = (VirtualAddress & VPN_MASK) >> SHIFT
// 추출한 VPN에 대한 주소변환 정보가 TLB에 있는지 확인
(Success, TLBEntry) = TLB_Lookup(VPN)
// TLB에 있으면
if (Success == True)
    if (CanAccess(TLBEntry.ProtectBits) == True)
        Offset = VirtualAddress & OFFSET_MASK
        PhyAddr = (TLBEntry.PFN << SHIFT) | OFFSET
        Register = AccessMemory(PhysAddr)
    else
        RaiseException(PROTECTION_FAULT)
// TLB에 없으면
else
    // Page Table에 접근하여 page 위치 알아온다
    PTEAddr = PTBR + (VPN * sizeof(PTE))
    PTE = AccessMemory(PTEAddr)
    if (PTE.Valid == False)
        RaiseException(SEGMENTATION_FAULT)
    else
        if (CanAccess(PTE.ProtectBits) == False)
            RaiseException(PROTECTION_FAULT)
        // Present Bit가 True라면 (메모리에 page table 존재)
        else if (PTE.Present == True)
            TLB_Insert(VPN, PTE.PFN, PTE.ProtectBits)
            RetryInstruction()
        // Present Bit가 False라면 (Swap 공간에 page table 존재)
        else if (PTE.Present == False)
            // Page Fault 발생
            RaiseException(PAGE_FAULT)

위의 코드에서 다시 알 수 있듯 주소 변환에 대한 정보가 TLB에 없고 page table이 유효한데 메모리에 없을 때 page fault가 발생합니다. 그럼 이렇게 발생한 page fault를 OS에서 처리하는 과정을 코드로 보겠습니다.

// Page를 가지고 와서 할당할 메모리 공간을 찾는다
PFN = FindFreePhysicalPage()
if (PFN == -1)
    PFN = EvictPage()
// Disk에서 Page Table 데이터를 가지고 온다
DiskRead(PTE.DiskAddr, PFN)
// Present Bit를 True로 수정
PTE.present = True
PTE.PFN = PFN
RetryInstruction()

이러한 처리 과정을 통해 Page Fault를 처리할 수 있습니다.

Summary

이번 글에서는 실제로 존재하는 메모리보다 더 많은 메모리를 사용하는 방법인 swap 방법에 대해 알아봤습니다. Present Bit의 존재로 현재 접근하고자 하는 page가 메모리에 존재하는지 swap 공간에 존재하는지 알 수 있었으며 swap 공간에 존재할 때 발생하는 page fault를 처리하는 방법에 대해서도 알아봤습니다. 이러한 방법은 disk를 활용하는 방법이기 때문에 많이 느릴 수 있다는 단점이 존재했습니다. 다음 글에서는 메모리에서 swap 공간으로 보낼 page를 결정하는 방법에 대해 알아보도록 하겠습니다!

 

감사합니다.

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함