다이어그램의 각 영역이 세그먼트입니까?
이들은 "세그먼트"라는 단어를 거의 완전히 다르게 사용합니다.
- x86 세그먼테이션 / 세그먼트 레지스터 : 최신 x86 OS는 32 비트 모드에서 모든 세그먼트가 동일한 base = 0과 limit = max를 갖는 플랫 메모리 모델을 사용합니다. 하드웨어는 64 비트 모드에서와 동일 하게 세그먼테이션을 수행합니다. . 64 비트 모드에서도 스레드 로컬 스토리지에 사용되는 FS 또는 GS는 제외합니다.
- 링커 / 프로그램 로더 섹션 / 세그먼트. ( ELF 파일 형식의 섹션과 세그먼트의 차이점은 무엇입니까 )
사용법은 공통된 기원을 갖습니다 . 세그먼트 메모리 모델 을 사용하는 경우 (특히 페이징 된 가상 메모리가없는 경우) 데이터 및 BSS 주소는 DS 세그먼트베이스에 상대적이며 SS베이스에 상대적으로 스택 및 CS 기본 주소.
따라서 세그먼트베이스를 기준으로 16 비트 또는 32 비트 오프셋을 변경하지 않고도 여러 다른 프로그램을 다른 선형 주소에로드하거나 시작 후 이동할 수 있습니다.
그러나 포인터와 관련된 세그먼트를 알아야하므로 "먼 포인터"등이 있습니다. (실제 16 비트 x86 프로그램은 종종 코드로 데이터에 액세스 할 필요가 없었으므로 64k 코드 세그먼트를 어딘가에, DS = SS가있는 또 다른 64k 블록을 사용할 수 있습니다. 또는 모든 세그먼트베이스가 동일한 작은 코드 모델).
x86 세그먼트 화가 페이징과 상호 작용하는 방법
32/64 비트 모드에서의 주소 매핑은 다음과 같습니다.
- segment : offset (오프셋을 보유하는 레지스터에 의해 암시되거나 명령 접두어로 재정의 된 세그먼트 기준)
- 32 또는 64 비트 선형 가상 주소 = 기본 + 오프셋. (Linux와 같은 플랫 메모리 모델에서는 포인터 / 오프셋 = 선형 주소도 있습니다. FS 또는 GS와 관련된 TLS에 액세스하는 경우는 제외)
TLB에 의해 캐시 된 페이지 테이블은 32 (레거시 모드), 36 (레거시 PAE) 또는 52 비트 (x86-64) 실제 주소에 선형으로 매핑됩니다. ( /programming/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the ).
이 단계는 선택 사항입니다. 제어 레지스터에서 비트를 설정하여 부팅 중에 페이징을 활성화해야합니다. 페이징이 없으면 선형 주소는 실제 주소입니다.
세분화 않는 것을주의 하지 당신이 하나의 프로세스 (또는 스레드)에서 가상 주소 공간 이상의 32 또는 64 비트를 사용하게 에만 오프셋 자신의 비트 수가있다로 플랫 (선형) 주소 공간의 모든 매핑되기 때문에. 16 비트 x86의 경우에는 그렇지 않았으며, 분할은 실제로 16 비트 레지스터와 오프셋이있는 64k 이상의 메모리를 사용하는 데 실제로 유용했습니다.
CPU는 세그먼트베이스를 포함하여 GDT (또는 LDT)에서로드 된 세그먼트 디스크립터를 캐시합니다. 포인터를 역 참조 할 때 포인터가있는 레지스터에 따라 세그먼트의 기본값은 DS 또는 SS입니다. 레지스터 값 (포인터)은 세그먼트베이스에서 오프셋으로 처리됩니다.
세그먼트베이스는 일반적으로 0이므로 CPU는이를 특수하게 처리합니다. 당신이 경우 또는 다른 관점에서, 이렇게 비 제로 세그먼트 기반을 가지고, 부하는 별도의 대기 시간이 있기 때문에 기본 주소가 적용되지 않는 추가 우회의 "특별한"(일반)의 경우.
리눅스가 x86 세그먼트 레지스터를 설정하는 방법 :
CS / DS / ES / SS의 기본 및 한계는 32 비트 및 64 비트 모드에서 모두 0 / -1입니다. 모든 포인터가 동일한 주소 공간을 가리 키기 때문에이를 플랫 메모리 모델이라고합니다.
(AMD CPU 아키텍트 는 PAE 또는 x86으로 페이징함으로써 훨씬 더 나은 방식으로 제공되는 비 실행 보호를 제외하고는 주류 OS가 어쨌든 그것을 사용하지 않았기 때문에 64 비트 모드에 플랫 메모리 모델을 적용 하여 세분화를 중화했습니다. 64 페이지 표 형식)
TLS (Thread Local Storage) : FS 및 GS는 long 모드에서 base = 0으로 고정 되지 않습니다 . (386 rep
을 사용 하여 새로워졌으며 ES를 사용 하는 -string 명령 조차 포함하지 않고 모든 명령에서 암시 적으로 사용되지 않았습니다 ). x86-64 Linux는 각 스레드의 FS 기본 주소를 TLS 블록의 주소로 설정합니다.
예를 들어 mov eax, [fs: 16]
16 바이트의 32 비트 값을이 스레드의 TLS 블록으로로드합니다.
CS 세그먼트 디스크립터는 CPU의 모드를 선택합니다 (16/32/64 비트 보호 모드 / 장기 모드). Linux는 모든 64 비트 사용자 공간 프로세스에 단일 GDT 항목을 사용하고 모든 32 비트 사용자 공간 프로세스에 다른 GDT 항목을 사용합니다. (CPU가 제대로 작동하려면 DS / ES도 유효한 항목으로 설정되어야하며 SS도 마찬가지입니다). 또한 권한 수준 (커널 (링 0) 대 사용자 (링 3))을 선택하므로 64 비트 사용자 공간으로 돌아갈 때에도 커널은 CS를 표준 대신 사용 iret
하거나 sysret
대신에 변경하도록 정렬해야합니다. 점프 또는 재시도 명령.
x86-64에서 syscall
진입 점은 swapgs
GS를 사용자 공간의 GS에서 커널로 뒤집는 데 사용됩니다.이 스레드는이 스레드의 커널 스택을 찾는 데 사용됩니다. (스레드 로컬 스토리지의 특수한 경우). 이 syscall
명령은 커널 포인터를 가리 키도록 스택 포인터를 변경하지 않습니다. 커널이 진입 점 1에 도달하면 여전히 사용자 스택을 가리키고 있습니다.
또한 이러한 디스크립터의 기본 / 제한이 긴 모드에서 무시 되더라도 CPU가 보호 모드 / 롱 모드에서 작동하려면 DS / ES / SS를 유효한 세그먼트 디스크립터로 설정해야합니다.
따라서 기본적으로 x86 세그먼트는 TLS 및 하드웨어에서 요구하는 필수 x86 osdev 작업에 사용됩니다.
각주 1 : 재미있는 역사 : AMD64 실리콘이 출시되기 몇 년 전부터 커널 개발자와 AMD 설계자 사이에 메일 링 메일 보관소가있어 디자인이 조정되어 syscall
사용할 수있었습니다. 자세한 내용은 이 답변의 링크를 참조 하십시오.