카테고리 없음

메모리 관리 07 - 워킹셋

묭묭.cpp 2024. 10. 16. 10:32

메모리 관리 07 - 워킹셋

워킹셋

  • 물리 메모리 내에 상주하는 가상 페이지의 부분집합

워킹셋의 종류

  • 프로세스 워킹셋
    • 단일 프로세스 내의 스레드에 의해 참조되는 페이지
  • 시스템 워킹셋
    • 상주하는 페이징 가능한 시스템 코드의 부분집합, 페이지드 풀, 시스템 캐시
  • 세션 워킹셋

어떤 페이지를 물리 메모리로 가져오고 유지하는 정책

요구 페이징

  • 메모리 관리자는 페이지를 메모리로 로딩하기 위해 클러스트링을 갖는 요구 페이징(Demand Paging) 알고리즘 사용
  • 페이지 폴트를 일으키면 페이지 폴트를 일으킨 페이지 앞뒤로 몇 개의 페이지를 더 읽어옴
    • 페이징 I/O 수를 최소화하려는 시도

페이징 요구 정책의 부작용

  • 프로세스의 처음 실행, 나중에 특정 시점에 실행을 다시 시작하는 경우
    • 많은 페이지 폴트가 발생 가능
  • 따라서 프로세스 시작 시점을 최적화하기 위해
    • 논리적 프리패처(Logical Prefetcher)라는 지능적인 프리패치 엔진을 가짐
  • 그 이상의 최적화는 슈퍼패치(SuperFetch)라는 컴포넌트에 의해 수행됨

논리적 프리패처

  • 프로세스 시작 과정에서 폴트 -> 폴트 -> 폴트가 연계하여 발생
    • 성능 저하
  • 한 번에 여러 페이지를 프리패칭함으로 시작 과정의 속도를 최적화

프리패처가 활성화되면

  • 메모리 관리자는 커널의 프리패처 코드에 페이지 폴트를 알려줌
  • 두 가지의 폴트 종류
    • 하드 폴트 : 페이지를 디스크부터 읽어와야 함
    • 소프트 폴트 : 이미 메모리에 있는 데이터를 프로세스 워킹셋에 추가하기만 하면 됨
  • 프리패처는 프로세스가 시작하는 처음 10초를 감시, 시스템 부팅은 30초
  • 이 시간에 유저 셸이 시작하고 추적이 실패하면 윈도우 서비스가 초기화된지 60초 후나 시스템이 시작한 120초 후 중 먼저 도달하는 시점까지 추적

커널 수집 추적 정보

  • NTFS 마스터 파일 테이블(MFT)
  • 메타데이터 파일
  • 참조된 파일
  • 참조된 디렉터리에서 일어난 폴트

추적 정보로 무엇을 하는가?

  • Svchost의 한 인스턴스에서 수행되는 슈퍼패치 서비스의 프리패처 컴포넌트로부터 요청을 대기
  • 지금부터 추적 데이터를 질의할 것이란 걸 슈퍼패치 서비스에게 알리기 위해 PrefetchTracesReady 이벤트를 시그널

슈퍼 패치 서비스는 무엇을 하는가?

  • 추적 데이터를 요청하기 위해 NtQuerySystemInformation 시스템 호출
  • 논리적 프리패처는 이전에 수집된 데이터와 결합해 추적 데이터를 후처리함
  • 그리고 %SystemRoot%\Prefetch 폴더에 파일로 저장
    • .pf 확장자

레디부트

  • 크고 효율적인 I/O 읽기를 만들고 RAM에 데이터를 저장함으로 I/O 동작의 최적화를 시도
  • 시스템 컴포넌트가 이 데이터를 필요로 할 때 저장된 RAM을 통해 이뤄짐
    • 기계적 디스크일 때 특히 효율적, SSD의 경우에도 유용함
  • 그러나 SSD의 경우 이득이 미미하므로 기본적으로 꺼져있음

시스템 부팅 혹은 애플리케이션 시작

  • 프리패칭을 수행할 기회를 얻음
  • 프리패처는 프리패치 디렉터리를 살펴보고 상황에 맞는 추적 파일을 찾음
  • 프리패처는 NTFS를 호출해 참조하는 MFT 메타파일을 프리패치
  • 참조되는 각 디렉터리 내용을 읽고, 마지막으로 참조된 파일을 염
  • 이때 프리패처는 메모리 관리자 함수 MmPrefetchPages를 호출해 추적 파일에 등록되어 있지만 아직 메모리에 없는 데이터와 코드를 읽음
  • 메모리 관리자는 모든 읽기 동작을 비동기적으로 시작하고 완료될 때까지 대기하고 애플리케이션을 시작

배치 정책

  • 페이지 폴트를 내면 페이지를 물리 메모리 어디에 넣을 것인지 정해야 함
  • 이런 규칙을 배치 정책(Placement policy)라고 함
  • 페이지 프레임을 선택할 때 캐시의 불필요한 스레싱(thrashing)을 최소화하기 위해 CPU의 메모리 캐시 크기를 고려함

교체 정책(Replacement policy)

  • 페이지 폴트 발생 시 물리 메모리가 모두 사용 중일 때 제거할 페이지를 선택하는 정책
  • 일반적인 교체 정책
    • LRU(least recently used)
      • 대부분의 유닉스 버전에서 구현된 방법 - clock 알고리즘이라고도 함
      • 메모리 페이지가 언제 사용됐는지 추적
      • 가장 오랜시간 사용되지 않은 페이지를 제거
    • FIFO(first in, first out)
      • 사용빈도와 관계없이 가장 오래된 페이지를 제거

교체 정책의 전역 지역 구분

  • 전역 교체 정책
    • 프로세스에 상관없이 교체 가능
    • 가장 오래 메모리에 있었던 페이지를 찾음
    • 전체 운영체제에 영향을 줄 수 있음
  • 지역 교체 정책
    • 현재 프로세스가 소유한 페이지로 한정

윈도우는 두 가지를 조합하여 사용

  • 워킹셋이 한계 혹은 정리할 필요가 있을 때
  • 충분한 프리(Free) 페이지가 있다고 판단할 때까지 워킹셋에서 페이지를 제거

워킹셋 관리

  • 모든 프로세스는 최소 50 페이지 기본 워킹셋과 최대 345 페이지의 최대 워킹셋으로 시작
  • SetProcessWorkingSetSize 함수를 이용하여 프로세스 워킹셋 한계를 변경 가능
    • 효과는 미미함
    • 스케줄링 우선순위 상승 권한(SeIncreaseBasePriorityPrivilege)을 가져야 함
    • 하드 워킹셋 한계를 사용하게 프로세스를 구성하지 않았다면 무시됨
  • 즉, 프로세스가 과도하게 페이징을 일으키고 충분한 메모리가 존재하면 워킹셋의 한계를 증가시키게 허용함
    • 반대의 경우 또한 가능 줄일 수도 있음

워킹셋 상한선

5-17

페이지 폴트가 발생하면

  • 프로세스 워킹셋 한계와 가용 가능 메모리 양 검사
  • 조건을 만족하면 프로세스가 워킹셋 최대값까지 키우게 허용
  • 메모리가 부족하면 교체를 수행

윈도우는 변경된 페이지를 디스크에 기록하여 가용 메모리를 유지하려 함

  • 물리 메모리가 모자르게 되면
    • 밸런스 셋 관리자 시스템 스레드 컨텍스트에서 실행되는 워킹셋 관리자가 자동 워킹셋 정리를 시작
    • SetProcessWorkingSetSizeEx 함수를 통해 프로세스 워킹셋 정리를 수행 가능
  • 워킹셋 관리자가 사용 가능 메모리 검사, 어느 워킹셋이 정리될지를 결정
    • 메모리가 충분 - 얼마나 많은 페이지를 제거할 수 있는지 계산
    • 정리가 필요하면 최솟값 이상의 크기를 갖는 워킹셋을 찾음
    • 정리할 후보 프로세스 리스트를 최적화된 순서로 구성
  • 최솟값 이상을 사용하는 프로세스를 발견할 때 프로세스 워킹셋에서 제거할 페이지를 찾아 다른 용도로 사용하게 만듬
  • 여유 메모리가 너무 부족하면 최소한의 프리 페이지가 확보될 때까지 워킹셋에서 페이지를 제거해 나감

어떻게 최근에 엑세스되지 않은 페이지를 제거할까?

  • 하드웨어 PTE 내의 Accessed 비트를 검사해 수행
    • 이 비트가 꺼져있다면 오래된 것
    • 마지막 워킹셋 정리 검사를 한 이후로 페이지가 참조된 적 없음을 의미하는 카운트가 하나 증가함
    • 이 카운트(age of page)를 통해 제거될 후보 페이지를 찾는데 이용
  • Accessed 비트가 켜져 있다면?
    • 이 비트를 끄고 다음 페이지를 검사
    • 다음에 검사할 때 비트가 꺼져있음을 체크
    • 이런 검사는 원하는 만큼 페이지를 제거 혹은 한 바퀴 돌아 시작점으로 돌아올 때까지 전체 워킹셋을 대상으로 계산
    • 검사가 끝난 위치부터 다음번 검사가 시작됨

밸런스 셋 관리자

  • 워킹셋의 확장과 정리는 밸런스 셋 관리자라고 불리는 시스템 스레드 컨텍스트에서 일어남
    • KeBalanceSetManager 함수
  • 시스템 초기화 시 생성됨
  • 커널의 일부분이지만 워킹셋의 분석과 조정을 위해 메모리 관리자의 워킹셋 관리자를 호출함
    • MmWorkingSetManager

밸런스 셋 관리자는 두 개의 이벤트 객체를 대기함

  • 1초마다 주기적으로 시그널되는 이벤트
  • 워킹셋 조정 시점에 시그널되는 내부 이벤트
    • 페이지 폴트 횟수가 너무 잦거나 프리 리스트가 작을 때
    • 메모리가 넉넉하여 폴트된 페이지를 메모리에 넣어 워킹셋 크기를 점차적을 늘리게 함

1초 주기 타이머에 의해 깨어났을 때의 과정

  1. 시스템이 가상 안전 모드를 지원하면 안전한 커널이 주기적인 정리를 위해 호출됨
  2. IRP 크래딧을 조정하기 위한 루틴 호출하고 IRP 완료에 사용되는 프로세서별 룩 어사이드 리스트의 사용량을 최적화(IoAdjustIrpCredits)
    • 특정 프로세서가 과도한 I/O 부하에 놓이면 더 나은 확장성이 가능해짐
  3. 룩 어사이드 리스트를 검사하고 이들의 깊이를 조절하여 접근 시간을 개선 시키고 풀 사용량과 단편화를 줄임
  4. 윈도우 이벤트 트레이싱 버퍼 풀 크기를 조절하기 위한 호출을 하여 ETW 메모리 버퍼를 좀 더 효율적으로 사용
  5. 메모리 관리자의 워킹셋 관리자를 호출
  6. 잡에 대한 실행 시간을 강제
  7. 1초 타이머의 8번째마다 스왑퍼(swapper)라는 시스템 스레드를 깨움

스왑퍼(swapper)

  • 한 동안 실행시키지 않았던 커널 스택을 스왑아웃
  • 15초 동안 유저 모드에서 대기 상태에 있었던 스레드를 찾음
  • 그런 스레드가 있다면 해당 스레드의 커널 스택을 트랜지션 상태로 두어 물리 메모리를 회수
  • 그정도 기다린 스레드는 앞으로도 더 오랫동안 대기할 것이라는 원칙에 따름
  • 프로세스의 마지막 스레드의 커널 스택이 메모리에서 제거되었다면 이 프로세스는 완전히 스왑아웃 되었다 표시
  • 따라서 오랫동안 유휴 상태인 프로세스의 워킹셋은 0이됨

시스템 워킹셋

  • 시스템 주소 공간의 페이징 가능한 코드와 데이터는 전역 워킹셋을 통해 관리

시스템 전역 워킹셋

  • 시스템 캐시 워킹셋
    • 시스템 캐시에 상주하는 페이지 포함
  • 페이지드 풀 워킹셋
    • 페이지드 풀에 상주하는 페이지 포함
  • 시스템 PTE 워킹셋
    • 시스템 공간으로 매핑된 섹션 페이지 포함
    • 로딩된 드라이버, 커널 이미지의 페이징 가능한 코드와 데이터를 포함

메모리 통지 이벤트

윈도우는 메모리 자원이 모자를 경우 유저 모드 프로세스와 커널 모드 드라이버에게 통지

  • 메모리 자원 - 물리 메모리, 페이지드 풀, 넌페이지드 풀, 커밋 양
  • 이런 통지 정보는 메모리 사용량 결정을 위해 사용

유저 모드 프로세스가 통지 받을 수 있는 상황

  • 낮은 메모리나 높은 메모리 조건인 경우만 통지 받을 수 있음
  • 애플리케이션은 이런 상황에 통지를 원한다 명시함
    • CreateMemoryResourceNotification 함수를 호출 가능함
    • 이 때 얻어진 핸들은 모든 대기 함수에서 사용 가능함
    • 메모리가 낮거나 높아지면(조건에 맞음) 대기는 완료되고 스레드에게 통지됨
  • 스레드를 블록하지 않고 메모리 조건을 검사하고 싶다면
    • QueryMemoryResourceNotification 호출

드라이버의 경우

  • 메모리 관리자가 \KernelObjects 객체 관리자 디렉터리에 설정한 특정 이벤트 이름을 사용
  • 전역 네임드 이벤트 객체 중 하나를 메모리 관리자가 시그널함으로 통지가 이뤄지게 구현됨
  • 해당 메모리 조건이 탐지되면 적절한 이벤트가 시그널되고 대기 스레드가 일어남
반응형