Nice programing

x86 페이징은 어떻게 작동합니까?

nicepro 2020. 10. 16. 08:06
반응형

x86 페이징은 어떻게 작동합니까?


이 질문은 주제에 대한 좋은 무료 정보의 공백을 채우기위한 것입니다.

나는 좋은 대답이 하나의 큰 SO 대답 또는 적어도 몇 가지 대답에 맞을 것이라고 믿습니다.

주요 목표는 완전한 초보자에게 매뉴얼을 직접 가져갈 수 있고 페이징과 관련된 기본 OS 개념을 이해할 수 있도록 충분한 정보를 제공하는 것입니다.

권장 지침 :

  • 답변은 초보자에게 친숙해야합니다.
    • 구체적이지만 단순화 된 예는 매우 중요합니다.
    • 표시된 개념의 응용 프로그램을 환영합니다.
  • 유용한 자료를 인용하는 것이 좋습니다.
  • OS에서 페이징 기능을 사용하는 방법에 대한 작은 설명도 환영합니다.
  • PAE 및 PSE 설명을 환영합니다.
  • x86_64 로의 작은 탈선도 환영합니다

관련 질문 및 내가 속이지 않는 이유 :


좋은 TOC 및 더 많은 내용이있는이 답변의 버전입니다 .

보고 된 오류를 수정하겠습니다. 큰 수정을가하거나 누락 된 측면을 추가하려면 자신의 답변을 작성하여 적절한 담당자를 확보하십시오. 사소한 편집 내용을 직접 병합 할 수 있습니다.

샘플 코드

최소 예 : https://github.com/cirosantilli/x86-bare-metal-examples/blob/5c672f73884a487414b3e21bd9e579c67cd77621/paging.S

프로그래밍의 다른 모든 것과 마찬가지로 이것을 실제로 이해하는 유일한 방법은 최소한의 예제를 가지고 노는 것입니다.

이것을 "어려운"주제로 만드는 것은 자신의 작은 OS를 만들어야하기 때문에 최소한의 예제가 크다는 것입니다.

인텔 매뉴얼

예제를 염두에 두지 않으면 이해할 수 없지만 가능한 한 빨리 매뉴얼에 익숙해 지도록 노력하십시오.

인텔은 인텔 매뉴얼 볼륨 3 시스템 프로그래밍 가이드-325384-056US 2015 년 9 월 4 장 "페이징"에서 페이징을 설명합니다.

특히 흥미로운 것은 그림 4-4 "32 비트 페이징을 사용하는 CR3 및 페이징 구조 항목의 형식"으로 주요 데이터 구조를 제공합니다.

MMU

페이징은 CPU의 MMU ( Memory Management Unit ) 부분에서 수행됩니다 . 다른 많은 것 (예 : x87 co-processor , APIC ) 과 마찬가지로 초기에는 별도의 칩으로 사용되었으며 나중에 CPU에 통합되었습니다. 그러나이 용어는 여전히 사용됩니다.

일반적인 사실들

논리 주소는 "일반"사용자 영역 코드에 사용되는 메모리 주소 (예 : 내용이다 rsi에서 mov eax, [rsi]).

첫 번째 세분화는이를 선형 주소로 변환 한 다음 페이징을 통해 선형 주소를 물리적 주소로 변환합니다.

(logical) ------------------> (linear) ------------> (physical)
             segmentation                 paging

대부분의 경우 실제 주소는 실제 RAM 하드웨어 메모리 셀을 인덱싱하는 것으로 생각할 수 있지만 다음과 같은 이유로 100 % 사실이 아닙니다.

페이징은 보호 모드에서만 사용할 수 있습니다. 보호 모드에서 페이징을 사용하는 것은 선택 사항입니다. 레지스터 PG비트 cr0가 설정된 경우 페이징이 켜집니다 .

페이징 대 세분화

페이징과 세분화의 주요 차이점은 다음과 같습니다.

  • 페이징은 RAM을 페이지라고하는 동일한 크기의 청크로 분할합니다.
  • 세분화는 메모리를 임의 크기의 청크로 분할합니다.

동일한 크기의 청크를 사용하면 작업을보다 쉽게 ​​관리 할 수 ​​있으므로 이것이 페이징의 주요 이점입니다.

페이징은 훨씬 더 대중적이되어 새로운 소프트웨어의 기본 작동 모드 인 64 비트 모드의 x86-64에서 분할 지원이 중단되었습니다. 여기서 IA32를 에뮬레이트하는 호환성 모드에서만 존재합니다.

신청

페이징은 최신 OS에서 프로세스 가상 주소 공간을 구현하는 데 사용됩니다. 가상 주소를 사용하면 OS는 다음과 같은 방식으로 단일 RAM에 둘 이상의 동시 프로세스를 맞출 수 있습니다.

  • 두 프로그램 모두 다른 프로그램에 대해 알 필요가 없습니다.
  • 두 프로그램의 메모리는 필요에 따라 늘어나거나 줄어들 수 있습니다.
  • 프로그램 간 전환이 매우 빠릅니다.
  • 한 프로그램은 다른 프로세스의 메모리에 액세스 할 수 없습니다.

페이징은 역사적으로 세그먼트 화 이후에 왔으며 가변 길이 세그먼트 대신 페이지의 고정 된 크기의 메모리 청크를 관리하는 것이 더 쉽기 때문에 Linux와 같은 최신 OS에서 가상 메모리 구현을 위해 대체로 대체되었습니다.

하드웨어 구현

보호 모드의 세분화 (세그먼트 레지스터를 수정하면 GDT 또는 LDT에서로드가 트리거 됨)와 마찬가지로 페이징 하드웨어는 메모리의 데이터 구조를 사용하여 작업 (페이지 테이블, 페이지 디렉터리 등)을 수행합니다.

이러한 데이터 구조의 형식은 하드웨어에 의해 고정 되지만 RAM에서 해당 데이터 구조를 올바르게 설정 및 관리하고이를 찾을 위치를 하드웨어에 알리는 것은 OS에 달려 cr3있습니다.

일부 다른 아키텍처는 페이징을 거의 완전히 소프트웨어의 손에 맡기므로 TLB 미스는 OS 제공 기능을 실행하여 페이지 테이블을 살펴보고 새 매핑을 TLB에 삽입합니다. 이로 인해 OS에서 페이지 테이블 형식을 선택할 수 있지만 x86과 같이 하드웨어가 다른 명령의 비 순차적 실행과 페이지 이동을 겹칠 가능성은 거의 없습니다 .

예 : 단순화 된 단일 레벨 페이징 체계

이것은 가상 메모리 공간을 구현하기 위해 x86 아키텍처 단순화 된 버전 에서 페이징이 작동하는 방법의 예입니다 .

페이지 테이블

OS는 다음 페이지 테이블을 제공 할 수 있습니다.

OS가 프로세스 1에 제공 한 페이지 테이블 :

RAM location        physical address   present
-----------------   -----------------  --------
PT1 + 0       * L   0x00001            1
PT1 + 1       * L   0x00000            1
PT1 + 2       * L   0x00003            1
PT1 + 3       * L                      0
...                                    ...
PT1 + 0xFFFFF * L   0x00005            1

OS가 프로세스 2에 제공 한 페이지 테이블 :

RAM location       physical address   present
-----------------  -----------------  --------
PT2 + 0       * L  0x0000A            1
PT2 + 1       * L  0x0000B            1
PT2 + 2       * L                     0
PT2 + 3       * L  0x00003            1
...                ...                ...
PT2 + 0xFFFFF * L  0x00004            1

어디:

  • PT1PT2: RAM에서 표 1 및 2의 초기 위치.

    샘플 값 : 0x00000000, 0x12345678, 등

    그 값을 결정하는 것은 OS입니다.

  • L: 페이지 테이블 항목의 길이.

  • present: 페이지가 메모리에 있음을 나타냅니다.

페이지 테이블은 RAM에 있습니다. 예를 들어 다음과 같이 위치 할 수 있습니다.

--------------> 0xFFFFFFFF


--------------> PT1 + 0xFFFFF * L
Page Table 1
--------------> PT1


--------------> PT2 + 0xFFFFF * L
Page Table 2
--------------> PT2

--------------> 0x0

두 페이지 테이블 모두 RAM의 초기 위치는 임의적이며 OS에 의해 제어됩니다. 중복되지 않도록하는 것은 OS에 달려 있습니다!

각 프로세스는 페이지 테이블을 직접 건드릴 수 없지만 OS에 페이지 테이블을 수정하도록 요청할 수 있습니다 (예 : 더 큰 스택 또는 힙 세그먼트 요청).

페이지는 4KB (12 비트)의 청크이며 주소가 32 비트이므로 각 페이지를 식별하는 데 20 비트 (20 + 12 = 32, 따라서 16 진수 표기법의 5 개 문자) 만 필요합니다. 이 값은 하드웨어에 의해 고정됩니다.

페이지 테이블 항목

페이지 테이블은 ... 페이지 테이블 항목의 테이블입니다!

테이블 항목의 정확한 형식은 하드웨어에 의해 고정 됩니다 .

이 단순화 된 예에서 페이지 테이블 항목에는 두 개의 필드 만 포함됩니다.

bits   function
-----  -----------------------------------------
20     physical address of the start of the page
1      present flag

따라서이 예에서 하드웨어 설계자는 L = 21.

대부분의 실제 페이지 테이블 항목에는 다른 필드가 있습니다.

메모리는 비트가 아닌 바이트로 주소를 지정할 수 있기 때문에 21 바이트로 정렬하는 것은 비현실적입니다. 따라서이 경우에는 21 비트 만 필요하더라도 하드웨어 설계자는 L = 32액세스 속도를 높이고 나중에 사용할 수 있도록 나머지 비트 만 남겨 두도록 선택할 수 있습니다. Lx86 의 실제 값 은 32 비트입니다.

단일 수준 체계의 주소 변환

페이지 테이블이 OS에 의해 설정되면 선형 주소와 물리적 주소 간의 주소 변환 은 하드웨어에 의해 수행 됩니다 .

OS가 프로세스 1을 활성화하려고 할 때 프로세스 1 에 대한 테이블의 시작 인 cr3을로 설정합니다 PT1.

프로세스 1이 선형 주소에 액세스하려는 경우 0x00000001페이징 하드웨어 회로는 OS에 대해 자동으로 다음을 수행합니다.

  • 선형 주소를 두 부분으로 나눕니다.

    | page (20 bits) | offset (12 bits) |
    

    따라서이 경우 우리는 다음을 갖게됩니다.

    • 페이지 = 0x00000
    • 오프셋 = 0x001
  • cr3그것을 가리 키기 때문에 페이지 표 1 을보십시오.

  • 0x00000페이지 부분이기 때문에 항목을보십시오 .

    하드웨어는이 항목이 RAM 주소에 있음을 알고 있습니다 PT1 + 0 * L = PT1.

  • 존재하기 때문에 액세스가 유효합니다.

  • 페이지 테이블에 의해, 페이지 번호의 위치 0x00000에 있습니다 0x00001 * 4K = 0x00001000.

  • 최종 물리적 주소를 찾으려면 오프셋을 추가하면됩니다.

      00001 000
    + 00000 001
      -----------
      00001 001
    

    00001페이지의 물리적 주소가 테이블에서 조회되고 001오프셋 이기 때문 입니다 .

    이름에서 알 수 있듯이 오프셋은 항상 페이지의 실제 주소에 추가됩니다.

  • 그런 다음 하드웨어는 해당 물리적 ​​위치에서 메모리를 가져옵니다.

같은 방식으로 프로세스 1에 대해 다음 번역이 발생합니다.

linear     physical
---------  ---------
00000 002  00001 002
00000 003  00001 003
00000 FFF  00001 FFF
00001 000  00000 000
00001 001  00000 001
00001 FFF  00000 FFF
00002 000  00002 000
FFFFF 000  00005 000

예를 들어 address 00001000액세스 할 때 페이지 부분은 00001페이지 테이블 항목이 RAM 주소 PT1 + 1 * L( 1페이지 부분 때문에)에 있음을 하드웨어가 알고 있으며 , 여기서 찾을 위치입니다.

OS가 프로세스 2로 전환하기를 원하면 2 cr3페이지를 가리 키기 만하면됩니다. 간단합니다!

이제 프로세스 2에 대해 다음 번역이 발생합니다.

linear     physical
---------  ---------
00000 002  00001 002
00000 003  00001 003
00000 FFF  00001 FFF
00001 000  00000 000
00001 001  00000 001
00001 FFF  00000 FFF
00003 000  00003 000
FFFFF 000  00004 000

동일한 선형 주소 는 내부 값에 따라서 만 다른 프로세스에 대해 다른 물리적 주소로 변환됩니다cr3 .

이러한 방식으로 모든 프로그램 은 정확한 물리적 주소에 대해 걱정하지 않고 데이터가에서 시작 0하고 끝날 것으로 예상 할 수 있습니다 FFFFFFFF.

페이지 폴트

프로세스 1이 존재하지 않는 페이지 내의 주소에 액세스하려고하면 어떻게됩니까?

하드웨어는 Page Fault Exception을 통해 소프트웨어에 알립니다.

그런 다음 수행해야 할 작업을 결정하기 위해 예외 처리기를 등록하는 것은 일반적으로 OS에 달려 있습니다.

테이블에없는 페이지에 액세스하는 것이 프로그래밍 오류 일 수 있습니다.

int is[1];
is[2] = 1;

그러나 다음과 같은 경우 Linux에서 허용되는 경우가있을 수 있습니다.

  • 프로그램이 스택을 늘리려 고합니다.

    주어진 가능한 범위에서 특정 바이트에 액세스하려고 시도하고 OS가 만족하면 해당 페이지를 프로세스 주소 공간에 추가합니다.

  • 페이지가 디스크로 스왑되었습니다.

    OS는 페이지를 RAM으로 되돌리려면 프로세스 뒤에서 몇 가지 작업을 수행해야합니다.

    OS는 이것이 나머지 페이지 테이블 항목의 내용을 기반으로 한 경우임을 발견 할 수 있습니다. 현재 플래그가 지워지면 페이지 테이블 항목의 다른 항목이 OS가 원하는대로 완전히 남아 있기 때문입니다.

    예를 들어 Linux에서 존재하는 경우 = 0 :

    • 페이지 테이블 항목의 모든 필드가 0이면 잘못된 주소입니다.

    • 그렇지 않으면 페이지가 디스크로 스왑되었으며 해당 필드의 실제 값이 디스크에서 페이지의 위치를 ​​인코딩합니다.

어쨌든 OS는 문제를 처리 할 수 ​​있도록 페이지 폴트를 생성 한 주소를 알아야합니다. 이것이 바로 멋진 IA32 개발자 cr2가 페이지 오류가 발생할 때마다 해당 주소 의 값을 설정하는 이유 입니다. 그런 다음 예외 처리기는 cr2주소를 얻기 위해 조사 할 수 있습니다 .

단순화

이 예제를 더 쉽게 이해할 수있는 현실 단순화 :

  • 모든 실제 페이징 회로는 공간을 절약하기 위해 다중 레벨 페이징을 사용하지만 이것은 단순한 단일 레벨 체계를 보여줍니다.

  • 페이지 테이블에는 20 비트 주소와 1 비트 존재 플래그라는 두 개의 필드 만 포함되었습니다.

    실제 페이지 테이블에는 총 12 개의 필드가 포함되어 있으므로 생략 된 기타 기능이 있습니다.

예 : 다단계 페이징 체계

단일 레벨 페이징 체계의 문제점은 너무 많은 RAM을 차지한다는 것입니다. 4G / 4K = 프로세스 1M 항목 입니다 . 각 항목의 길이가 4 바이트 인 경우 프로세스 당 4M 이됩니다 . 이는 데스크톱 컴퓨터에서도 너무 많은 양입니다. ps -A | wc -l현재 244 개의 프로세스를 실행 중이므로 약 1GB의 RAM이 필요합니다!

이러한 이유로 x86 개발자는 RAM 사용량을 줄이는 다단계 체계를 사용하기로 결정했습니다.

이 시스템의 단점은 액세스 시간이 약간 더 높다는 것입니다.

PAE가없는 32 비트 프로세서에 사용되는 간단한 3 레벨 페이징 체계에서 32 주소 비트는 다음과 같이 나뉩니다.

| directory (10 bits) | table (10 bits) | offset (12 bits) |

각 프로세스에는 하나의 페이지 디렉토리 만 연결되어 있어야하므로 2^10 = 1K단일 레벨 체계에 필요한 최소 1M보다 훨씬 더 나은 페이지 디렉토리 항목이 최소한 포함됩니다 .

페이지 테이블은 OS에서 필요한 경우에만 할당됩니다. 각 페이지 테이블에는 2^10 = 1K페이지 디렉토리 항목이 있습니다.

페이지 디렉토리는 ... 페이지 디렉토리 항목을 포함합니다! 페이지 디렉토리 항목은 테이블의 물리적 주소 대신 페이지 테이블의 RAM 주소를 가리키는 점을 제외하면 페이지 테이블 항목과 동일합니다 . 이러한 주소는 폭이 20 비트에 불과하므로 페이지 테이블은 4KB 페이지의 시작 부분에 있어야합니다.

cr3 이제 페이지 테이블 대신 현재 프로세스의 페이지 디렉토리에서 RAM 위치를 가리 킵니다.

페이지 테이블 항목은 단일 수준 체계에서 전혀 변경되지 않습니다.

페이지 테이블은 다음과 같은 이유로 단일 수준 체계에서 변경됩니다.

  • 각 프로세스에는 페이지 디렉토리 항목 당 하나씩 최대 1K 페이지 테이블이있을 수 있습니다.
  • 각 페이지 테이블은 1M 항목 대신 정확히 1K 항목을 포함합니다.

처음 두 레벨에서 10 비트를 사용하는 이유 12 | 8 | 12는 각 페이지 테이블 항목의 길이가 4 바이트이기 때문입니다. 그러면 페이지 디렉토리와 페이지 테이블의 2 ^ 10 항목이 4Kb 페이지에 잘 맞습니다. 이는 해당 목적을 위해 페이지를 할당하고 할당 해제하는 것이 더 빠르고 간단하다는 것을 의미합니다.

다단계 체계의 주소 변환

OS가 프로세스 1에 제공 한 페이지 디렉토리 :

RAM location     physical address   present
---------------  -----------------  --------
PD1 + 0     * L  0x10000            1
PD1 + 1     * L                     0
PD1 + 2     * L  0x80000            1
PD1 + 3     * L                     0
...                                 ...
PD1 + 0x3FF * L                     0

PT1 = 0x10000000( 0x10000* 4K) 에서 OS가 프로세스 1에 제공 한 페이지 테이블 :

RAM location      physical address   present
---------------   -----------------  --------
PT1 + 0     * L   0x00001            1
PT1 + 1     * L                      0
PT1 + 2     * L   0x0000D            1
...                                  ...
PT1 + 0x3FF * L   0x00005            1

PT2 = 0x80000000( 0x80000* 4K) 에서 OS가 프로세스 1에 제공 한 페이지 테이블 :

RAM location      physical address   present
---------------   -----------------  --------
PT2 + 0     * L   0x0000A            1
PT2 + 1     * L   0x0000C            1
PT2 + 2     * L                      0
...                                  ...
PT2 + 0x3FF * L   0x00003            1

어디:

  • PD1: RAM에서 프로세스 1의 페이지 디렉토리 초기 위치.
  • PT1PT2: RAM상의 프로세스 1에 대한 페이지 테이블 1 및 페이지 테이블 2의 초기 위치.

따라서이 예제에서 페이지 디렉토리와 페이지 테이블은 다음과 같이 RAM에 저장 될 수 있습니다.

----------------> 0xFFFFFFFF


----------------> PT2 + 0x3FF * L
Page Table 1
----------------> PT2

----------------> PD1 + 0x3FF * L
Page Directory 1
----------------> PD1


----------------> PT1 + 0x3FF * L
Page Table 2
----------------> PT1

----------------> 0x0

선형 주소를 0x00801004단계별로 번역 해 봅시다 .

cr3 = PD1, 방금 설명한 페이지 디렉토리를 가리킨다 고 가정합니다 .

바이너리에서 선형 주소는 다음과 같습니다.

0    0    8    0    1    0    0    4
0000 0000 1000 0000 0001 0000 0000 0100

다음과 같이 그룹화 10 | 10 | 12:

0000000010 0000000001 000000000100
0x2        0x1        0x4

다음을 제공합니다.

  • 페이지 디렉토리 항목 = 0x2
  • 페이지 테이블 항목 = 0x1
  • 오프셋 = 0x4

따라서 하드웨어는 페이지 디렉토리의 항목 2를 찾습니다.

페이지 디렉토리 테이블은 페이지 테이블이에 있습니다 0x80000 * 4K = 0x80000000. 이것은 프로세스의 첫 번째 RAM 액세스입니다.

페이지 테이블 항목이 0x1이므로 하드웨어는 페이지 테이블의 항목 1 0x80000000을보고 물리적 페이지가 주소에 있음을 알려줍니다 0x0000C * 4K = 0x0000C000. 이것은 프로세스의 두 번째 RAM 액세스입니다.

마지막으로 페이징 하드웨어가 오프셋을 추가하고 최종 주소는 0x0000C004.

번역 된 주소의 다른 예는 다음과 같습니다.

linear    10 10 12 split   physical
--------  ---------------  ----------
00000001  000 000 001      00001001
00001001  000 001 001      page fault
003FF001  000 3FF 001      00005001
00400000  001 000 000      page fault
00800001  002 000 001      0000A001
00801008  002 001 008      0000C008
00802008  002 002 008      page fault
00B00001  003 000 000      page fault

페이지 디렉토리 항목 또는 페이지 테이블 항목이없는 경우 페이지 폴트가 발생합니다.

OS가 다른 프로세스를 동시에 실행하려는 경우 두 번째 프로세스에 별도의 페이지 디렉터리를 제공하고 해당 디렉터리를 별도의 페이지 테이블에 연결합니다.

64 비트 아키텍처

64 비트는 여전히 현재 RAM 크기에 비해 너무 많은 주소이므로 대부분의 아키텍처는 더 적은 비트를 사용합니다.

x86_64는 48 비트 (256TiB)를 사용하고 레거시 모드의 PAE는 이미 52 비트 주소 (4PiB)를 허용합니다.

48 비트 중 12 개는 이미 오프셋을 위해 예약되어 있으며 36 비트를 남깁니다.

2 단계 접근 방식을 사용하는 경우 최상의 분할은 두 개의 18 비트 수준입니다.

그러나 이는 페이지 디렉토리에 2^18 = 256K항목이있어서 너무 많은 RAM을 차지 한다는 것을 의미합니다. 32 비트 아키텍처의 경우 단일 레벨 페이징에 가깝습니다!

따라서 64 비트 아키텍처는 더 많은 페이지 수준 (일반적으로 3 또는 4)을 생성합니다.

x86_64는 9 | 9 | 9 | 12체계 에서 4 개의 레벨을 사용 하므로 상위 레벨은 2^9상위 레벨 항목 만 차지 합니다.

PAE

물리적 주소 확장.

32 비트에서는 4GB RAM 만 지정할 수 있습니다.

이것은 대형 서버에 대한 제한 사항이되기 시작하여 Intel은 Pentium Pro에 PAE 메커니즘을 도입했습니다.

이 문제를 해결하기 위해 인텔은 4 개의 새로운 주소 라인을 추가하여 64GB를 처리 할 수있었습니다.

PAE가 켜져 있으면 페이지 테이블 구조도 변경됩니다. 변경되는 정확한 방법은 PSE가 켜져 있거나 꺼져있는 날씨에 따라 다릅니다.

PAE는 PAE비트를 통해 켜고 끕니다 cr4.

총 주소 지정 가능 메모리가 64GB이더라도 개별 프로세스는 최대 4GB까지만 사용할 수 있습니다. 그러나 OS는 다른 4GB 청크에 다른 프로세스를 배치 할 수 있습니다.

PSE

페이지 크기 확장.

페이지 길이는 4K 대신 4M (또는 PAE가 켜져있는 경우 2M)이 될 수 있습니다.

PSE는 PAE비트를 통해 켜고 끕니다 cr4.

PAE 및 PSE 페이지 테이블 체계

PAE와 PSE가 활성 상태이면 다른 페이징 수준 체계가 사용됩니다.

  • PAE 및 PSE 없음 : 10 | 10 | 12

  • PAE 및 PSE 없음 : 10 | 22.

    22 비트는 4Mb 주소이므로 22는 4Mb 페이지 내의 오프셋입니다.

  • PAE 및 PSE 없음 : 2 | 9 | 9 | 12

    9가 10 대신 두 번 사용되는 설계 이유는 이제 항목이 더 이상 32 비트에 맞지 않는데, 이는 모두 20 개의 주소 비트와 12 개의 의미있는 또는 예약 된 플래그 비트로 채워졌습니다.

    그 이유는 20 비트가 더 이상 페이지 테이블의 주소를 나타내는 데 충분하지 않기 때문입니다. 프로세서에 4 개의 추가 와이어가 추가 되었기 때문에 이제 24 비트가 필요합니다.

    따라서 디자이너는 항목 크기를 64 비트로 늘리기로 결정했으며 단일 페이지 테이블에 맞추려면 항목 수를 2 ^ 10 대신 2 ^ 9로 줄여야합니다.

    시작 2는 페이지 디렉토리를 가리키고 32 비트 선형 주소를 채우 므로 PDPT (Page Directory Pointer Table)라고하는 새로운 페이지 레벨 입니다. PDPT는 폭이 64 비트입니다.

    cr3이제 4GB 메모리에 있어야하고 효율성을 처리하기 위해 32 비트 배수로 정렬되어야하는 PDPT를 가리 킵니다. 이것은 이제 cr320이 아닌 27 개의 유효 비트를 가지고 있음을 의미합니다 : 32 배수의 경우 2 ^ 5 * 2 ^ 27 첫 4GB의 2 ^ 32를 완료합니다.

  • PAE 및 PSE : 2 | 9 | 21

    디자이너는 한 페이지에 맞추기 위해 9 비트 너비의 필드를 유지하기로 결정했습니다.

    이렇게하면 23 비트가 남습니다. PDPT가 PSE없이 PAE 케이스로 일관되게 유지하기 위해 2를 남겨두면 오프셋을 위해 21이 남습니다. 이는 페이지 너비가 4M가 아니라 2M임을 의미합니다.

TLB

TLB (Translation Lookahead Buffer)는 페이징 주소를위한 캐시입니다.

캐시이기 때문에 연관성 수준과 같은 CPU 캐시의 많은 디자인 문제를 공유합니다.

이 섹션은 4 개의 단일 주소 항목이있는 단순화 된 완전 연관 TLB를 설명합니다. 다른 캐시와 마찬가지로 실제 TLB는 일반적으로 완전히 연관되지 않습니다.

기본 기능

선형 주소와 물리적 주소 간의 변환이 발생하면 TLB에 저장됩니다. 예를 들어, 4 개 항목 TLB는 다음 상태에서 시작됩니다.

  valid   linear   physical
  ------  -------  ---------
> 0       00000    00000
  0       00000    00000
  0       00000    00000
  0       00000    00000

>대체 할 현재 항목을 나타냅니다.

페이지 선형 주소 00003가 실제 주소로 변환 된 00005후 TLB는 다음과 같습니다.

  valid   linear   physical
  ------  -------  ---------
  1       00003    00005
> 0       00000    00000
  0       00000    00000
  0       00000    00000

과의 두 번째 변환 이후 0000700009이된다 :

  valid   linear   physical
  ------  -------  ---------
  1       00003    00005
  1       00007    00009
> 0       00000    00000
  0       00000    00000

이제 00003다시 번역해야하는 경우 하드웨어는 먼저 TLB를 찾고 단일 RAM 액세스로 해당 주소를 찾습니다 00003 --> 00005.

물론 00000유효한 항목이 00000키로 포함되어 있지 않기 때문에 TLB에 없습니다 .

교체 정책

TLB가 채워지면 이전 주소를 덮어 씁니다. CPU 캐시와 마찬가지로 교체 정책은 잠재적으로 복잡한 작업이지만 간단하고 합리적인 휴리스틱은 최근에 가장 적게 사용 된 항목 (LRU)을 제거하는 것입니다.

LRU를 사용하여 상태에서 시작 :

  valid   linear   physical
  ------  -------  ---------
> 1       00003    00005
  1       00007    00009
  1       00009    00001
  1       0000B    00003

추가하면 다음 0000D -> 0000A이 제공됩니다.

  valid   linear   physical
  ------  -------  ---------
  1       0000D    0000A
> 1       00007    00009
  1       00009    00001
  1       0000B    00003

TLB를 사용하면 TLB 수준 당 하나의 액세스 권한이 필요하므로 TLB를 사용하면 번역 속도가 빨라 집니다. 즉, 간단한 32 비트 체계에서는 2 개, 64 비트 아키텍처에서는 3 개 또는 4 개입니다.

TLB는 일반적으로 CAM (content-addressable memory)이라고하는 고가의 RAM 유형으로 구현됩니다. CAM은 하드웨어에 연관 맵을 구현합니다. 즉, 키 (선형 주소)가 주어진 구조가 값을 검색합니다.

매핑은 RAM 주소에서도 구현할 수 있지만 CAM 매핑에는 RAM 매핑보다 훨씬 적은 항목이 필요할 수 있습니다.

예를 들어 다음과 같은지도가 있습니다.

  • 키와 값 모두 20 비트 (단순 페이징 체계의 경우)
  • 한 번에 최대 4 개의 값을 저장해야합니다.

4 개의 항목이있는 TLB에 저장할 수 있습니다.

linear   physical
-------  ---------
00000    00001
00001    00010
00010    00011
FFFFF    00000

그러나 이를 RAM으로 구현 하려면 2 ^ 20 개의 주소가 필요합니다 .

linear   physical
-------  ---------
00000    00001
00001    00010
00010    00011
... (from 00011 to FFFFE)
FFFFF    00000

which would be even more expensive than using a TLB.

Invalidating entries

When cr3 changes, all TLB entries are invalidated, because a new page table for a new process is going to be used, so it is unlikely that any of the old entries have any meaning.

The x86 also offers the invlpg instruction which explicitly invalidates a single TLB entry. Other architectures offer even more instructions to invalidated TLB entries, such as invalidating all entries on a given range.

Some x86 CPUs go beyond the requirements of the x86 specification and provide more coherence than it guarantees, between modifying a page table entry and using it, when it wasn't already cached in the TLB. Apparently Windows 9x relied on that for correctness, but modern AMD CPUs don't provide coherent page-walks. Intel CPUs do, even though they have to detect mis-speculation to do so. Taking advantage of this is probably a bad idea, since there's probably not much to gain, and a big risk of causing subtle timing-sensitive problems that will be hard to debug.

Linux kernel usage

The Linux kernel makes extensive usage of the paging features of x86 to allow fast process switches with small data fragmentation.

In v4.2, look under arch/x86/:

  • include/asm/pgtable*
  • include/asm/page*
  • mm/pgtable*
  • mm/page*

There seems to be no structs defined to represent the pages, only macros: include/asm/page_types.h is specially interesting. Excerpt:

#define _PAGE_BIT_PRESENT   0   /* is present */
#define _PAGE_BIT_RW        1   /* writeable */
#define _PAGE_BIT_USER      2   /* userspace addressable */
#define _PAGE_BIT_PWT       3   /* page write through */

arch/x86/include/uapi/asm/processor-flags.h defines CR0, and in particular the PG bit position:

#define X86_CR0_PG_BIT      31 /* Paging */

Bibliography

Free:

  • rutgers-pxk-416 chapter "Memory management: lecture notes"

    Good historical review of memory organization techniques used by older OS.

Non-free:

  • bovet05 chapter "Memory addressing"

    Reasonable intro to x86 memory addressing. Missing some good and simple examples.


Here's a very short, high-level answer:

An x86 processor operates in one of several possible modes (roughly: real, protected, 64-bit). Each mode can use one of several possible memory addressing models (but not every mode can use every model), namely: real-mode addressing, segmented addressing, and flat-linear addressing.

In the modern world, only flat-linear addressing in protected or 64-bit mode are relevant, and the two modes are essentially the same, with the main difference being the size of the machine word and thus the addressable amount of memory.

Now, the memory addressing mode gives meaning to the memory operands of the machine instructions (such as mov DWORD PTR [eax], 25, which stores a 32-bit (aka dword) integer of value 25 into the memory whose address is stored in the eax 32-bit register). In flat-linear addressing, this number in eax is allowed to run over a single, contiguous range, from zero up to the maximal value (in our case that's 232 − 1).

However, flat-linear addressing can be either paged or not paged. Without paging, the address directly refers to physical memory. With paging, the processor's memory management unit (or MMU) transparently feeds the desired address (now called a virtual address) into a lookup mechanism, the so-called page tables, and obtains a new value, which is interpreted as a physical address. The original operation now operates on this new, translated address in physical memory, even though the user only ever sees the virtual address.

The key benefit of paging is that the page tables are managed by the operating system. Thus the operating system can modify and replace the page tables arbitrarily, such as when "switching tasks". It can keep a whole collection of page tables, one for each "process", and whenever it decides that a particular process is going to run on a given CPU, it loads the process's page tables into that CPU's MMU (each CPU has its own set of page tables). The result is that each process sees its own virtual address space which looks the same regardless of which physical pages were free when the OS had to allocate memory for it. It never knows about the memory of any other process, since it cannot access physical memory directly.

Page tables are nested tree-like data structures stored in normal memory, written by the OS but read directly by hardware, so the format is fixed. They're "loaded" into the MMU by setting a special CPU control register to point to the top-level table. The CPU uses a cache called a TLB to remember lookups, so repeated accesses to the same few pages are much faster than scattered accesses, for TLB-miss reasons as well as the usual data cache reasons. It's common to see the term "TLB entry" used to refer to page table entries even when they aren't cached in the TLB.

And in case you worry that a process might just disable paging or try and modify the page tables: This is not allowed, since x86 implements privilege levels (called "rings"), and user code executes at a privilege level that's too low to allow it to modify the CPU's page tables.

참고URL : https://stackoverflow.com/questions/18431261/how-does-x86-paging-work

반응형