답변:
커널이 사용자 프로그램, 즉 시스템 호출을 대신하여 실행될 때 커널 공간이 사용됩니까? 아니면 모든 커널 스레드의 주소 공간입니까 (예 : 스케줄러)?
그렇습니다.
더 나아 가기 전에 메모리에 대해 이것을 언급해야합니다.
메모리 가져 오기는 두 가지 영역으로 나뉩니다.
사용자 공간에서 실행되는 프로세스는 제한된 메모리 부분에만 액세스 할 수있는 반면 커널은 모든 메모리에 액세스 할 수 있습니다. 사용자 공간에서 실행중인 프로세스도 커널 공간에 액세스 할 수 없습니다 . 사용자 공간 프로세스는 커널 에 의해 노출 된 인터페이스 ( 시스템 호출) 를 통해 커널의 작은 부분에만 액세스 할 수 있습니다 . 프로세스가 시스템 호출을 수행하면 소프트웨어 인터럽트가 커널로 전송 된 다음 적절한 인터럽트 핸들러를 디스패치하고 핸들러가 완료된 후에 작업을 계속합니다.
커널 공간 코드에는 "커널 모드"에서 실행되는 속성이 있습니다 (일반 데스크탑 -x86- 컴퓨터에서) . 링 0에서 실행되는 코드를 호출합니다 . 일반적으로 x86 아키텍처에는 4 가지 보호 링이 있습니다 . 링 0 (커널 모드), 링 1 (가상 컴퓨터 하이퍼 바이저 또는 드라이버에 의해 사용될 수 있음), 링 2 (드라이버에 의해 사용될 수 있음). 링 3은 일반적인 응용 프로그램이 실행되는 것입니다. 가장 낮은 권한을 가진 링이며, 실행되는 응용 프로그램은 프로세서 명령의 하위 집합에 액세스 할 수 있습니다. 링 0 (커널 공간)은 가장 특권이있는 링이며 모든 시스템 지침에 액세스 할 수 있습니다. 예를 들어, 브라우저와 같은 "일반"응용 프로그램은 x86 어셈블리 명령어를 사용할 수 없습니다.lgdt
글로벌 디스크립터 테이블을로드하거나 hlt
프로세서를 정지합니다.
첫 번째 경우, 일반 사용자 프로그램이 3GB 이상의 메모리를 가질 수 없다는 것을 의미합니까 (구분이 3GB + 1GB 인 경우)? 또한 1GB의 커널 공간이 논리적으로 매핑되므로 고용량 메모리의 페이지가 어떤 가상 메모리 주소에 매핑됩니까?
-1
은 하이퍼 바이저 용 이라고 생각 했습니까? en.wikipedia.org/wiki/Protection_ring
CPU 링은 가장 분명한 차이점입니다
x86 보호 모드에서 CPU는 항상 4 개의 링 중 하나에 있습니다. Linux 커널은 0과 3 만 사용합니다.
이것은 커널 대 사용자 영역에 대한 가장 어렵고 빠른 정의입니다.
리눅스가 링 1과 2를 사용하지 않는 이유 : https : //.com/questions/6710040/cpu-privilege-rings-why-rings-1-and-2-arent-used
전류 링은 어떻게 결정됩니까?
현재 링은 다음의 조합으로 선택됩니다.
글로벌 디스크립터 테이블 : GDT 엔트리의 메모리 내 테이블이며, 각 엔트리는 Privl
링을 인코딩 하는 필드 를 갖는다 .
LGDT 명령어는 주소를 현재 설명자 테이블로 설정합니다.
세그먼트는 GDT에서 엔트리의 인덱스를 가리키는 CS, DS 등을 등록한다.
예를 들어, CS = 0
GDT의 첫 번째 항목이 현재 실행 코드에 대해 활성화되어 있음을 의미합니다.
각 반지는 무엇을 할 수 있습니까?
CPU 칩은 물리적으로 다음과 같이 구성됩니다.
링 0은 무엇이든 할 수 있습니다
링 3은 여러 명령을 실행할 수 없으며 여러 레지스터에 쓸 수 있습니다.
자신의 반지를 변경할 수 없습니다! 그렇지 않으면 자체가 링 0으로 설정 될 수 있으며 링은 쓸모가 없습니다.
즉, 현재 링을 결정하는 현재 세그먼트 디스크립터를 수정할 수 없습니다 .
페이지 테이블을 수정할 수 없습니다 : https://stackoverflow.com/questions/18431261/how-does-x86-paging-work
즉, CR3 레지스터를 수정할 수 없으며 페이징 자체가 페이지 테이블 수정을 방지합니다.
이것은 보안 / 편의 프로그래밍 이유 때문에 한 프로세스가 다른 프로세스의 메모리를 보지 못하게합니다.
인터럽트 핸들러를 등록 할 수 없습니다. 메모리 위치에 쓰면 구성되며 페이징으로도 방지됩니다.
처리기는 링 0에서 실행되며 보안 모델을 손상시킵니다.
즉, LGDT 및 LIDT 명령어를 사용할 수 없습니다.
같은 IO 지침을 할 수 없어 in
하고 out
, 따라서 임의의 하드웨어에 액세스 할 수 있습니다.
그렇지 않으면, 예를 들어, 프로그램이 디스크에서 직접 읽을 수있는 경우 파일 권한이 쓸모가 없습니다.
Michael Petch 덕분에 더 정확하게 는 OS가 링 3에서 IO 명령을 허용하는 것이 실제로 가능하며, 이는 실제로 작업 상태 세그먼트에 의해 제어됩니다 .
불가능한 것은 링 3이 처음에 그것을 가지고 있지 않은 경우 링 3에 권한을 부여하는 것입니다.
리눅스는 항상 그것을 허용하지 않습니다. 참조 : https : //.com/questions/2711044/why-doesnt-linux-use-the-hardware-context-switch-via-the-tss
프로그램과 운영 체제는 링간에 어떻게 전환됩니까?
CPU가 켜지면 링 0에서 초기 프로그램을 실행하기 시작합니다 (물론 근사치입니다). 이 초기 프로그램을 커널이라고 생각할 수 있습니다 (그러나 일반적으로 링 0으로 커널을 호출하는 부트 로더입니다).
유저 랜드 프로세스가 커널이 파일에 쓰는 것과 같은 일을하기를 원할 때, 커널과 같은 인터럽트를 발생 시키 int 0x80
거나syscall
신호를 보내는 명령을 사용합니다 . x86-64 Linux syscall hello world 예제 :
.data
hello_world:
.ascii "hello world\n"
hello_world_len = . - hello_world
.text
.global _start
_start:
/* write */
mov $1, %rax
mov $1, %rdi
mov $hello_world, %rsi
mov $hello_world_len, %rdx
syscall
/* exit */
mov $60, %rax
mov $0, %rdi
syscall
컴파일하고 실행하십시오.
as -o hello_world.o hello_world.S
ld -o hello_world.out hello_world.o
./hello_world.out
이 경우 CPU는 부팅시 커널이 등록한 인터럽트 콜백 핸들러를 호출합니다. 다음은 핸들러를 등록하고 사용 하는 구체적인 베어 메탈 예제입니다 .
이 핸들러는 링 0에서 실행되며, 커널이이 조치를 허용하는지, 조치를 수행하고 링 3에서 userland 프로그램을 다시 시작할지 여부를 결정합니다. x86_64
때 exec
시스템 호출을 사용하는 (또는 커널이 때 시작한다/init
), 커널은 레지스터와 메모리 준비 새로운 유저 랜드 공정을, 다음의 엔트리 포인트로 점프 3 링하는 CPU 스위치
프로그램이 금지 된 레지스터 또는 메모리 주소에 쓰기 (페이징 때문에)와 같은 잘못된 작업을 시도하면 CPU는 링 0에서 일부 커널 콜백 핸들러를 호출합니다.
그러나 사용자 영역이 좋지 않기 때문에 커널은 이번에 프로세스를 종료하거나 신호와 함께 경고를 줄 수 있습니다.
커널이 부팅 될 때 일정한 고정 주파수로 하드웨어 클록을 설정하여 주기적으로 인터럽트를 생성합니다.
이 하드웨어 클럭은 링 0을 실행하는 인터럽트를 생성하고 어떤 사용자 프로세스가 깨어날 지 예약 할 수 있습니다.
이런 식으로 프로세스가 시스템 호출을하지 않더라도 스케줄링이 발생할 수 있습니다.
여러 개의 고리가있는 점은 무엇입니까?
커널과 사용자 영역을 분리하면 두 가지 주요 이점이 있습니다.
어떻게 놀아?
링을 직접 조작하는 좋은 방법이어야하는 베어 메탈 셋업을 만들었습니다 : https://github.com/cirosantilli/x86-bare-metal-examples
불행히도 userland 예제를 만들려는 인내심은 없었지만 페이징 설정까지 갔으므로 userland가 가능해야합니다. 풀 요청을보고 싶습니다.
또는 Linux 커널 모듈은 링 0에서 실행되므로이를 사용하여 권한있는 작업을 시도 할 수 있습니다 (예 : 제어 레지스터 읽기) : https://stackoverflow.com/questions/7415515/how-to-access-the-control-registers -cr0-cr2-cr3-from-a-get-getting-segmenta / 7419306 # 7419306
다음은 편리한 QEMU + Buildroot 설정 으로 호스트를 종료하지 않고 사용해 볼 수 있습니다.
커널 모듈의 단점은 다른 kthread가 실행 중이고 실험을 방해 할 수 있다는 것입니다. 그러나 이론적으로 커널 모듈로 모든 인터럽트 핸들러를 인수하고 시스템을 소유 할 수 있습니다. 실제로는 흥미로운 프로젝트 일 것입니다.
네거티브 링
음수 링은 실제로 인텔 설명서에서 참조되지 않지만 실제로 링 0보다 더 많은 기능을 가진 CPU 모드가 있으므로 "음의 링"이름에 적합합니다.
한 가지 예로 가상화에 사용되는 하이퍼 바이저 모드가 있습니다.
자세한 내용은 https://security.stackexchange.com/questions/129098/what-is-protection-ring-1을 참조하십시오.
팔
ARM에서는 링을 예외 수준이라고하지만 주요 아이디어는 동일하게 유지됩니다.
ARMv8에는 다음과 같이 일반적으로 사용되는 4 가지 예외 수준이 있습니다.
EL0 : 유저 랜드
EL1 : 커널 (ARM 용어에서 "감독자").
Linux 시스템 호출을 수행하는 데 사용되는 명령 인 이전 통합 어셈블리svc
로 알려진 명령 (SuperVisor Call) 과 함께 입력되었습니다 . 안녕하세요 세계 ARMv8 예제 :swi
.text
.global _start
_start:
/* write */
mov x0, 1
ldr x1, =msg
ldr x2, =len
mov x8, 64
svc 0
/* exit */
mov x0, 0
mov x8, 93
svc 0
msg:
.ascii "hello syscall v8\n"
len = . - msg
Ubuntu 16.04에서 QEMU를 사용하여 테스트하십시오.
sudo apt-get install qemu-user gcc-arm-linux-gnueabihf
arm-linux-gnueabihf-as -o hello.o hello.S
arm-linux-gnueabihf-ld -o hello hello.o
qemu-arm hello
다음은 SVC 핸들러 를 등록하고 SVC 호출을 수행하는 구체적인 베어 메탈 예제입니다 .
hvc
지침 (HyperVisor Call) 과 함께 입력했습니다 .
하이퍼 바이저는 OS에 대한 것이며 OS는 사용자 영역에 있습니다.
예를 들어 Xen을 사용하면 동일한 시스템에서 Linux 또는 Windows와 같은 여러 OS를 동시에 실행할 수 있으며 Linux가 사용자 프로그램에 대해하는 것처럼 보안 및 디버그 용이성을 위해 OS를 서로 분리합니다.
하이퍼 바이저는 오늘날 클라우드 인프라의 핵심 부분입니다. 단일 서버에서 여러 서버를 실행하여 하드웨어 사용량을 항상 100 %에 가깝게 유지하고 많은 비용을 절약 할 수 있습니다.
EL3 : 또 다른 수준. TODO 예.
smc
지침 과 함께 입력 (보안 모드 호출)
ARMv8 아키텍처 참조 모델 DDI 0487C.a - 장 D1 - AArch64 시스템 레벨 프로그래머 모델 - 그림 D1-1 아름답게이 보여
가늠자의 이점으로 인해 ARM이 음수 수준을 필요로하지 않고 x86보다 권한 수준에 대해 더 나은 명명 규칙을 갖는 방법에 유의하십시오. 0은 낮고 3은 높습니다. 높은 레벨은 낮은 레벨보다 더 자주 생성되는 경향이 있습니다.
현재 EL은 다음 MRS
명령 으로 쿼리 할 수 있습니다 . https://stackoverflow.com/questions/31787617/what-is-the-current-execution-mode-exception-level-etc
ARM은 칩 영역을 절약하는 기능이 필요하지 않은 구현을 허용하기 위해 모든 예외 수준을 제시 할 필요는 없습니다. ARMv8 "예외 수준"에서는 다음과 같이 말합니다.
구현에 모든 예외 레벨이 포함되지 않을 수 있습니다. 모든 구현에는 EL0 및 EL1이 포함되어야합니다. EL2 및 EL3은 선택 사항입니다.
예를 들어 QEMU는 기본적으로 EL1이지만 EL2 및 EL3은 명령 행 옵션으로 활성화 할 수 있습니다. https://stackoverflow.com/questions/42824706/qemu-system-aarch64-entering-el1-when-emulating-a53-power-up
코드 조각은 우분투 18.10에서 테스트되었습니다.
첫 번째 경우, 일반 사용자 프로그램이 3GB 이상의 메모리를 가질 수 없다는 것을 의미합니까 (구분이 3GB + 1GB 인 경우)?
예, 이것은 일반적인 리눅스 시스템의 경우입니다. 한 시점에 사용자와 커널 주소 공간을 완전히 독립적으로 만드는 "4G / 4G"패치 세트가 있었지만 (커널이 사용자 메모리에 액세스하기 어렵 기 때문에 성능 비용으로) 생각하지 않습니다. 그들은 상류로 합병되었고 x86-64의 상승으로 관심이 줄어 들었습니다.
또한 1GB의 커널 공간이 논리적으로 매핑되므로 고용량 메모리의 페이지가 어떤 가상 메모리 주소에 매핑됩니까?
리눅스가 작동하는 방식 (주소 공간에 비해 메모리가 작은 시스템에서 여전히 작동)은 전체 물리적 메모리가 주소 공간의 커널 부분에 영구적으로 매핑되는 방식이었습니다. 이를 통해 커널은 다시 매핑하지 않고도 모든 물리적 메모리에 액세스 할 수 있었지만 물리적 메모리가 많은 32 비트 시스템으로 확장되지 않습니다.
따라서 낮은 메모리와 높은 메모리라는 개념이 탄생했습니다. "낮은"메모리는 커널 주소 공간에 영구적으로 매핑됩니다. "높은"메모리는 아닙니다.
프로세서가 시스템 호출을 실행 중이면 커널 모드에서 실행 중이지만 여전히 현재 프로세스의 컨텍스트에서 실행 중입니다. 따라서 현재 프로세스의 커널 주소 공간과 사용자 주소 공간에 직접 액세스 할 수 있습니다 (위에서 언급 한 4G / 4G 패치를 사용하지 않는 경우). 이는 "높은"메모리가 사용자 프로세스에 할당되는 데 아무런 문제가 없음을 의미합니다.
커널 목적으로 "높은"메모리를 사용하는 것이 더 문제가됩니다. 현재 프로세스에 매핑되지 않은 높은 메모리에 액세스하려면 커널의 주소 공간에 임시로 매핑되어야합니다. 이는 추가 코드와 성능 저하를 의미합니다.