운영체제(9) - Virtual Memory(1)

‘운영체제’를 공부하여 정리한 내용입니다.

1. Background

유용성

  • 프로세스 전체가 메인 메모리 내에 올라오지 않더라도 실행이 가능하도록 한다.
    • 말인즉슨, 메인메모리 전체 크기가 프로세스의 크기보다 작아도 시스템이 프로그램을 돌릴 수 있도록 할 수 있다는 것이다.

필요성

  • 이 때까지는 현재 실행되고 잇는 코드는 반드시 메인 메모리에 존재해야 한다는 기본적인 요구 조건을 만족해야 했다.

  • 프로세스를 모두 메인 메모리에 올리는 것은, 비용을 매우 많이 드는 작업이며 아래와 같은 상황이 있다면 낭비가 될 수 있다.

    1. 프로세스 동작 가운데 거의 사용하지 않는 루틴
      • 오류 상황을 처리하는 Exception 코드 등
    2. 배열, 리스트 등의 자료구조를 사용할 때, 실제 사용하는 공간보다 많이 배치되어 있는 경우
    3. 프로그램 내의 어떤 option이나 루틴의 경우는 아주 가끔 사용된다.
  • 우리는 위와 같은 부분은 실제로는 딱히 메인 메모리에 없어도 된다는 것을 쉽게 알 수 있다.

    • 물론 필요할 때는 올려야 한다.

장점

  • 프로그램은 더 이상 메인 메모리의 크기에 제약 받지 않는다.
  • 각 사용자 프로그램이 더 작은 메모리를 차지하여, Multiprogramming에 많은 이점을 준디ㅏ.
    • 처리량 증가
    • 응답 시간 축소
  • Swap같은 기술을 사용할 때, 메모리에 실제로 차지하는 부분이 작아서 프로그램 실행 등과, 보조 메모리로의 후퇴가 빨라진다.

Virtual Memory

  • Virtual Memory는 위의 이점을 얻기 위한 추상적 개념으로, 실제의 Physical memory(메인 메모리) concepts와 사용자의 Logical Memory concepts를 분리할 수 있게 된다.
    • 이 덕분에 사용자는 실제보다 더 큰 메인메모리를 사용하는 것과 같은 환상을 느낄 수 있게 된다.
    • 실제로 이 기술은 작은 메인 메모리로 충분히 큰 공간의 Virtual Memory를 제공한다.

Virtual Address Space

  • 한 프로세스가 가진 일종의 논리적 공간이다.

    • 텍스트: 프로그램 코드(기계어)
    • 데이터: 전역 변수
    • Stack
    • Heap
  • 가상 공간에서는 이들이 연속적인 모습을 가지지만 실제로는 그렇지 않다는 것을 이제는 다 알것이다.

    • Logical Page를 Physical Page로 매핑하는 것은 MMU(Memory Management Unit)에서 해준다.
  • 이 때 Stack이나 Heap은 프로세스 사용 도중에 증가하는 영역이다.

    • 증가되지 않을 때는, 실제 Physical 공간을 요청할 필요가 없다.
    • 이런 부분은 그림에서는 자주 Heap 과 Stack의 사잇공간으로 표현되고, 이를 우리는 Sparse Address Space라고 표현한다.

추가적인 장점

  • 메모리 공유 작업을 효과적으로 하면서, User의 입장에서는 숨길 수 있다. (물리적인 공간은 공유하지만, 가상 공간을 따로 쓰는 것처럼 보이게 하여 사용자 인터페이스를 직관적으로 한다.)
    • 시스템 라이브러리를 공유할 때 효과적으로 진행할 수 있다.
    • 프로세스가 메모리의 특정 부분을 공유하게 할 수 있도록 한다.
  • 또한 Fork()등의 프로세스 복사 생성 작업에서 공유할 부분은 빠르게 공유하고, 필요한 부분의 물리 메모리만 차지하여 그 생성 속도를 이전보다 빠르게 하는 것이 가능하다.

2. Demand Paging

동의할 것

  • 앞서 Virtual Memory에 대해서 논의하면서 동의한 것은, 실제로는 프로세스의 모든 루틴이 실행 시간동안 모두 메인 메모리에 올라와 있지 않아도 된다는 점이었다.
  • 이는 다른 말로, 프로그램이 메인 메모리로 적재될 때는 초기에 필요한 것들만을 적재하는 방식으로 동작하는 것이 가능하다는 것이다.
    • 이를 다른 말로 Demand Paging이라고 표현한다.

Demand Paging

  • Virtual Memory는 주로 Demand Paging기법을 사용해서 구현된다.

    • 이 때 우리는 Segment의 경우가 여러 페이지로 나뉘어 있다고 생각하고,
    • 실제 Physical address는 이 Demand Paging을 사용한다고 생각하면 된다.
  • 이는 어떻게 보면 Swapping 기법과 비슷하다.
    • 하지만 Swapping 기법의 경우는 전체 코드에 대해서 움직이게 하는 것이다.
    • 페이지 단위로 메인 메모리에 올리고 내리고 하는 이 방식에서는 그래서 Swapper(Swapping을 하는 주체)를 쓴다고 하지 않고, Pager를 쓴다고 표현한다.
    • 그리고 이런 방식으로 lazy하게 swap한다고 표현하는데, 이 Lazy라는 말은 미리 하지 않고, 필요할 때마다 한다는 의미로 사용된다.
      • 여기서도 Pager는 User가 페이지를 필요하다고 하면 그제서야 메모리로 적재한다.
      • 이 Lazy라는 단어는 컴퓨터 공학 전반적으로 자주 사용되는 용어이니 그 느낌을 잘 알아두면 좋다.
  • 위의 작업을 하기위해서는 pager는 프로세스의 어떤 page가 현재 메모리에 올라와있고, 어떤 것은 올라와있지 않은지(디스크에 있는지)를 확인해야 한다.

    • 이 작업을 위해서는 우리가 이전에 page에 대해서 논의할 때 보았던 valid/invalid bit를 사용할 수 있다.
      • valid: 메인메모리에 적재되어 있다.
      • invalid: 프로세스에는 포함되지만, 디스크에 머물러 있거나, 혹은 아예 프로세스 공간에 포함되지 않는 부분이라는 의미를 가진다.
        • 프로세스에는 포함되지만 디스크에 그 페이지가 있다면, 디스크 주소를 기록해두기도 한다.
      • 운영체제(8) - Memory Management Strategies(2) - Protection 참고
  • 만약 사용자가 프로세스의 4번 페이지(Logical Address)에 접근하고 싶은데 4번이 아직 메인 메모리에 올라와 있지 않은 상황을 가정해보자

    • 이 때 paging 하드웨어는 Logically 4번 페이지에 접근하기 위해서 page table을 확인하게 되고, 여기에는 분명 invalid라고 표시가 되어있을 것이다.
    • 이 때 Paging 하드웨어는 OS에게 page fault trap을 건다.
      • 필요한 page가 현재 메인 메모리에 올라와 있지 않다는 것을 알린다.
    • OS는 다음과 같은 작업을 해서, page fault를 처리한다.
      • 프로세스의 internal table(with PCB)을 검사해서 메모리 참조 주소가 유효한지 확인한다.
        • base & limit 등의 정보로 프로세스 전체 주소 영역을 검사했던 것과 비슷한 방법을 쓴다.
      • 무효하면 프로세스를 중단시키고, 유효하다면 디스크에서 해당하는 page를 가져와야 한다.
      • 우선 메인 메모리의 free frame을 찾는다.
      • 디스크에서 page를 가져와서 방금 찾은 free frame 공간에 적재한다.
      • page table을 업데이트 한다.
      • trap에 의해서 중단되었던 명령어를 다시 실행한다.
  • Pure Demand Paging

    • 처음에는 아예 아무런 page를 올리지 않고, 처음 명령어 실행때 page fault가 발생하도록 해서 거기서부터 pager가 움직여서 정말 필요한 page를 찾아서 메모리에 적재하도록 하는 방식이다.
  • 연속적으로 실행되는 명령어가 계속 다른 page를 요구하는 경우는 어떠한가?

    • 실제로 이런 상황이 발생하면 매우 퍼포먼스가 저하된다.
    • 하지만, Locality of reference라는 성질 덕분에 특정 명령어 집단은 특정 작은 부분을 한동안 집중적으로 참조한다는 것을 경험적으로 알고 있다.
    • 그렇기에 실제로는 문제로 이어지지는 않는다.
  • 요구되는 하드웨어 spec
    • page table
    • Secondaty memory: swap 작업을 위해서
      • 참고로 pager라고 우리가 명명하기는 했지만 그럼에도 디스크에서 메모리로 페이지를 적재하고 빼는 작업 자체는 swap-in, out으로 표현한다.
      • 헷갈리지 말자.

3. Copy-on-Write

상황 체크

  • fork()를 사용할 때
    • 실제로 부모 프로세스의 코드를 그대로 사용하는 자식 프로세스의 경우
      • 이 경우는 자식 프로세스만의 공간을 주고, 수정이 가해지는 데이터가 있는 경우는 부모와 같은 내용을 가지지만 아예 다른 복사된 페이지를 제공해 주어야 한다.
      • 이런 페이지는 Copy-on-Write 표시가 되어있어야 한다.
    • 복사된다음 exec()을 사용해서 다른 프로세스가 되는 경우
      • 이 경우는 아예 다른 코드를 올릴 것이기 때문에 부모와 같은 내용의 별도의 페이지를 지급할 필요가 없다.
      • 그래서 이 때는 부모와 모든 페이지를 공유하게 하고, exec()을 바로 불러서 다른 프로그램을 사상하는 프로세스가 되면 된다.
      • 이렇게 exec()을 사용한다고 가정하여 동작할 때는 vfork()라는 것을 사용한다.

Leave a Comment