사용자 공간과 커널 공간의 차이점은 무엇입니까?

사용자 공간과 커널 공간의 차이점은 무엇입니까?

커널이 사용자 프로그램(예: 시스템 호출)을 대신하여 실행될 때 커널 공간이 사용됩니까? 아니면 모든 커널 스레드(예: 스케줄러)의 주소 공간인가요?

첫 번째라면 일반 사용자 프로그램이 3GB(3GB + 1GB로 나눈 경우) 이상의 메모리를 가질 수 없다는 뜻인가요? 또한 이 경우 커널은 1GB의 커널 공간이 논리적으로 매핑되므로 높은 메모리의 페이지가 어떤 가상 메모리 주소에 매핑될 것인지에 대해 커널이 높은 메모리를 어떻게 사용합니까?

답변1

커널이 사용자 프로그램(예: 시스템 호출)을 대신하여 실행될 때 커널 공간이 사용됩니까? 아니면 모든 커널 스레드(예: 스케줄러)의 주소 공간인가요?

예 예.

더 나아가기 전에 기억에 관해 몇 마디 말해야 합니다.

기억은 두 가지 영역으로 나누어집니다.

  • 사용자 공간, 이는 일반 사용자 프로세스(즉, 커널을 제외한 모든 프로세스)가 실행되는 위치 집합입니다. 커널의 역할은 이 공간에서 실행되는 애플리케이션들이 서로 간섭하지 않고 머신에 간섭하지 않도록 관리하는 것이다.
  • 커널 공간, 커널의 코드와 데이터가 저장되고 실행되는 곳입니다.

사용자 공간에서 실행되는 프로세스는 제한된 메모리 부분에만 액세스할 수 있지만 커널은 모든 메모리에 액세스할 수 있습니다. 프로세스는 사용자 공간에서도 실행됩니다.아니요커널 공간에 접근할 수 있습니다. 사용자 공간 프로세스는커널의 작은 부분에만 접근커널을 통해 노출되는 인터페이스 -시스템 호출. 프로세스가 시스템 호출을 실행하면 소프트웨어 인터럽트가 커널로 전송되고 커널은 적절한 인터럽트 핸들러를 예약하고 핸들러가 완료된 후 작업을 계속합니다.

커널 공간 코드에는 "커널 모드"에서 실행되는 속성이 있으며, 이는 (일반적인 데스크톱 x86 컴퓨터에서) 필요한 것입니다.링 0에서 실행되는 호출 코드.일반적으로 x86 아키텍처에는 4개의 보호 링이 있습니다.. 링 0(커널 모드), 링 1(아마도 하이퍼바이저나 드라이버에 의해 사용됨), 링 2(아마도 드라이버에 의해 사용되지만 확실하지 않음). 링 3은 일반적인 애플리케이션이 실행되는 환경입니다. 실행 중인 응용 프로그램이 프로세서 명령의 하위 집합에 액세스할 수 있는 최소 권한 링입니다. 링 0(커널 공간)은 가장 특권적인 링이며 모든 기계 명령어에 액세스할 수 있습니다. 예를 들어, "일반" 응용 프로그램(예: 브라우저)은 x86 어셈블리 명령을 사용하여 lgdt전역 설명자 테이블을 로드 할 수 없으며 hlt프로세서를 중지할 수도 없습니다.

첫 번째라면 일반 사용자 프로그램이 3GB(3GB + 1GB로 나눈 경우) 이상의 메모리를 가질 수 없다는 뜻인가요? 또한 이 경우 커널은 1GB의 커널 공간이 논리적으로 매핑되므로 높은 메모리의 페이지가 어떤 가상 메모리 주소에 매핑될 것인지에 대해 커널이 높은 메모리를 어떻게 사용합니까?

이 질문에 대한 답변은 다음을 참조하세요.wag의 훌륭한 답변도착하다Linux에서 높은 메모리와 낮은 메모리란 무엇입니까?.

답변2

CPU 링이 가장 눈에 띄는 차이점입니다.

x86 보호 모드에서 CPU는 항상 4개의 링 중 하나에 있습니다. Linux 커널은 0과 3만 사용합니다.

  • 0은 커널을 의미합니다.
  • 3 사용자의 경우

이는 커널과 사용자 공간에 대한 가장 엄격하고 빠른 정의입니다.

Linux가 링 1과 링 2를 사용하지 않는 이유:https://stackoverflow.com/questions/6710040/cpu-privilege-rings-why-rings-1-and-2-arent-used

현재 링은 어떻게 결정되나요?

현재 링은 다음 조합으로 선택됩니다.

  • Privl전역 설명자 테이블: 각각 링을 인코딩하는 필드가 있는 GDT 항목의 메모리 내 테이블입니다 .

    LGDT 명령어는 주소를 현재 설명자 테이블로 설정합니다.

    또한보십시오:http://wiki.osdev.org/Global_Descriptor_Table

  • 세그먼트 레지스터 CS, DS 등은 GDT 항목의 인덱스를 가리킵니다.

    예를 들어, CS = 0GDT의 첫 번째 항목이 현재 코드 실행을 위해 활성화되어 있음을 나타냅니다.

각 반지는 무엇을 할 수 있나요?

CPU 칩의 물리적 구성은 다음을 허용합니다.

  • 링 0은 무엇이든 할 수 있습니다

  • 링 3은 여러 명령을 실행할 수 없으며 여러 레지스터에 쓸 수 없습니다. 특히 다음과 같습니다.

    • 반지는 바꿀 수 없어요! 그렇지 않으면 링 0으로 설정되어 링이 쓸모 없게 될 수 있습니다.

      즉, 현재세그먼트 설명자, 현재 링을 결정합니다.

    • 페이지 테이블을 수정할 수 없습니다.https://stackoverflow.com/questions/18431261/how-does-x86-paging-work

      즉, CR3 레지스터는 수정될 수 없으며, 페이징 자체가 페이지 테이블 수정을 방지합니다.

      보안/프로그래밍 용이성을 위해 이는 한 프로세스가 다른 프로세스의 메모리를 볼 수 없도록 합니다.

    • 인터럽트 핸들러를 등록할 수 없습니다. 이는 메모리 위치에 기록하여 구성되며 페이징을 통해 방지할 수도 있습니다.

      핸들러는 링 0에서 실행되며 보안 모델을 깨뜨립니다.

      즉, LGDT, LIDT 명령어는 사용할 수 없습니다.

    • in및 같은 IO 명령어는 실행될 수 없으므로 out임의의 하드웨어 액세스가 가능합니다.

      그렇지 않으면, 예를 들어 어떤 프로그램이 디스크에서 직접 읽을 수 있다면 파일 권한은 쓸모가 없게 됩니다.

      좀 더 정확히 말하자면, 감사합니다마이클 페이지: 운영 체제는 실제로 링 3에 대한 IO 명령을 허용할 수 있습니다. 이는 실제로 다음으로 인해 발생합니다.작업 상태 섹션.

      링 3이 처음에 허가를 받지 않았다면 링 3이 스스로 그렇게 할 수 있는 허가를 부여할 수 있는 방법은 없습니다.

      리눅스는 항상 그것을 허용하지 않습니다. 또한보십시오:https://stackoverflow.com/questions/2711044/why-doesnt-linux-use-the-hardware-context-switch-via-the-tss

프로그램과 운영 체제는 링 간에 어떻게 전환됩니까?

  • CPU가 켜지면 링 0에서 초기 프로그램 실행이 시작됩니다(괜찮지만 좋은 근사치입니다). 이 초기 프로그램을 커널로 생각할 수 있습니다(그러나 일반적으로그런 다음 여전히 링 0에 있는 커널의 부트로더를 호출합니다.).

  • 사용자 공간 프로세스가 커널이 자신을 위해 어떤 작업(예: 파일 쓰기)을 하길 원할 때 다음과 같은 인터럽트 생성 명령어를 사용합니다.int 0x80또는syscall커널에 신호를 보냅니다. x86-64 Linux 시스템 호출 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
    

    GitHub 업스트림.

    이런 일이 발생하면 CPU는 시작 시 커널에 등록된 인터럽트 콜백 핸들러를 호출합니다. 여기 하나 있어요핸들러 등록 및 사용에 대한 구체적인 베어메탈 예시.

    이 핸들러는 링 0에서 실행되고, 커널이 작업을 허용하는지 여부를 결정하고, 작업을 수행하며, 링 3에서 사용자 모드 프로그램을 다시 시작합니다. x86_64

  • 언제. . . 시스템 호출을 사용할 때 exec(또는 커널이시작할 것이다/init), 커널레지스터와 메모리 준비그런 다음 새로운 사용자 모드 프로세스는 진입점으로 점프하고 CPU를 링 3으로 전환합니다.

  • 또한 CPU는 프로그램이 금지된 레지스터나 메모리 주소(페이징으로 인해)에 쓰기와 같은 잘못된 작업을 시도하는 경우 링 0에서 특정 커널 콜백 핸들러를 호출합니다.

    그러나 사용자 모드가 문제가 있기 때문에 커널은 이번에는 프로세스를 종료하거나 신호로 경고를 보낼 수 있습니다.

  • 커널이 시작되면 정기적으로 인터럽트를 생성하는 고정 빈도로 하드웨어 시계를 설정합니다.

    이 하드웨어 시계는 실행 링 0에 대한 인터럽트를 생성하고 사용자 모드 프로세스가 깨어나도록 예약할 수 있습니다.

    이렇게 하면 시스템 호출을 하지 않더라도 프로세스를 예약할 수 있습니다.

링이 여러 개 있으면 무슨 소용이 있나요?

커널과 사용자 공간을 분리하면 두 가지 주요 이점이 있습니다.

  • 프로그램이 다른 프로그램을 방해하지 않는다는 것이 더 확실하기 때문에 프로그램을 만드는 것이 더 쉽습니다. 예를 들어, 사용자 공간 프로세스는 페이징으로 인해 다른 프로그램의 메모리를 덮어쓰는 것에 대해 걱정할 필요가 없으며 하드웨어를 다른 프로세스에 대해 유효하지 않은 상태로 만드는 것에 대해 걱정할 필요가 없습니다.
  • 더 안전합니다. 예를 들어, 파일 권한 및 메모리 분리는 해킹 애플리케이션이 은행 데이터를 읽는 것을 방지합니다. 물론 이는 커널을 신뢰한다고 가정합니다.

그것을 가지고 노는 방법?

나는 링을 직접 조작하는 좋은 방법이 될 베어메탈 설정을 만들었습니다.https://github.com/cirosantilli/x86-bare-metal-examples

불행하게도 나는 사용자 영역 예제를 만들 인내심이 없지만 사용자 영역이 가능하도록 페이지 매김 설정을 했습니다. 풀 리퀘스트를 보고 싶습니다.

또는 Linux 커널 모듈은 링 0에서 실행되므로 이를 사용하여 제어 레지스터 읽기와 같은 권한 있는 작업을 시도할 수 있습니다.https://stackoverflow.com/questions/7415515/how-to-access-the-control-registers-cr0-cr2-cr3-from-a-program-getting-segmenta/7419306#7419306

여기 하나 있어요편리한 QEMU + Buildroot 설정호스트를 죽이지 않고 시도해 보세요.

커널 모듈의 단점은 다른 kthread가 실행 중이어서 실험을 방해할 수 있다는 것입니다. 그러나 이론적으로는 커널 모듈로 모든 인터럽트 핸들러를 인계받아 시스템을 가질 수 있는데, 이는 실제로 흥미로운 프로젝트가 될 것입니다.

네거티브 루프

네거티브 링은 실제로 인텔 매뉴얼에서 참조되지 않지만 실제로 링 0 자체보다 더 많은 기능을 가진 CPU 모드가 있으므로 "네거티브 링" 지정에 적합합니다.

한 가지 예는 가상화에 사용되는 하이퍼바이저 패턴입니다.

자세한 내용은 다음을 참조하세요.

ARM에서는 링을 예외 수준이라고 부르지만 주요 아이디어는 동일합니다.

ARMv8에는 일반적으로 사용되는 4가지 예외 수준이 있습니다.

  • EL0: 사용자 영역

  • EL1: 커널(ARM 용어로 "하이퍼바이저").

    svc이전에 알려진 명령(SuperVisor 호출) 입력 사용swi 통합 조립 전, 이는 Linux 시스템 호출을 수행하는 데 사용되는 명령입니다. Hello World ARMv8 예:

    안녕하세요.S

    .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
    

    GitHub 업스트림.

    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 호출 수행.

  • EL2:관리 프로그램, 예를 들어.

    명령(HyperVisor 호출)을 사용하여 입력합니다 hvc.

    하이퍼바이저는 사용자 공간에 대한 운영 체제와 마찬가지로 운영 체제에 대한 역할을 합니다.

    예를 들어 Xen을 사용하면 동일한 시스템에서 Linux 또는 Windows와 같은 여러 운영 체제를 동시에 실행할 수 있으며 Linux가 사용자를 위해 수행하는 것처럼 보안 및 디버깅 용이성을 위해 운영 체제를 서로 격리합니다. 모드 프로그램.

    하이퍼바이저는 오늘날 클라우드 인프라의 핵심 부분입니다. 이를 통해 단일 하드웨어에서 여러 서버를 실행할 수 있어 하드웨어 활용도를 100%에 가깝게 유지하고 상당한 비용을 절약할 수 있습니다.

    예를 들어 AWS는 2017년까지 Xen을 사용했습니다.KVM으로의 마이그레이션이 뉴스를 만들다.

  • EL3: 또 다른 수준. 할 일 목록 예시.

    명령 입력 사용 smc(안전 모드 통화)

이것ARMv8 아키텍처 참조 모델 DDI 0487C.a- D1 장 - AArch64 시스템 수준 프로그래머 모델 - 그림 D1-1은 이를 아름답게 보여줍니다.

여기에 이미지 설명을 입력하세요.

ARM의 출현으로 상황이 조금 바뀌었습니다.ARMv8.1 VHE(가상화 호스트 확장). 이 확장을 사용하면 커널이 EL2에서 효율적으로 실행될 수 있습니다.

여기에 이미지 설명을 입력하세요.

VHE는 대부분의 클라이언트가 Linux VM만 필요하기 때문에 KVM과 같은 Linux 커널 가상화 솔루션이 Xen을 능가했기 때문에 만들어졌습니다(위에서 언급한 AWS가 KVM으로 이동 참조). 상상할 수 있듯이 이 모든 것이 단일 가상 머신 프로젝트에서 KVM입니다. Xen보다 더 간단하고 잠재적으로 더 효율적입니다. 따라서 이제 호스트 Linux 커널은 이러한 상황에서 하이퍼바이저 역할을 합니다.

아마도 돌이켜 보면 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-emulate-a53-power-up

Ubuntu 18.10에서 테스트된 코드 조각입니다.

답변3

첫 번째라면 일반 사용자 프로그램이 3GB(3GB + 1GB로 나눈 경우) 이상의 메모리를 가질 수 없다는 뜻인가요?

예, 이는 일반 Linux 시스템의 경우입니다. 사용자와 커널 주소 공간을 완전히 독립적으로 만드는 "4G/4G" 패치 세트가 어느 시점에 떠돌고 있었지만(커널이 사용자 메모리에 액세스하기가 더 어려워지기 때문에 성능이 저하됨) 저는 그렇지 않습니다. 업스트림에 병합된 적이 없다고 생각합니다. x86-64가 상승함에 따라 관심이 약해집니다.

또한 이 경우 커널은 1GB의 커널 공간이 논리적으로 매핑되므로 높은 메모리의 페이지가 어떤 가상 메모리 주소에 매핑될 것인지에 대해 커널이 높은 메모리를 어떻게 사용합니까?

Linux가 작동했던 방식(주소 공간에 비해 메모리가 작은 시스템에서 여전히 작동함)은 전체 물리적 메모리가 주소 공간의 커널 부분에 영구적으로 매핑되는 것입니다. 이를 통해 커널은 다시 매핑하지 않고도 모든 물리적 메모리에 액세스할 수 있지만 분명히 많은 양의 물리적 메모리가 있는 32비트 시스템으로 확장되지는 않습니다.

그래서 낮은 메모리와 높은 메모리의 개념이 탄생했습니다. "낮은" 메모리는 커널 주소 공간에 영구적으로 매핑됩니다. "높은" 메모리에서는 그렇지 않습니다.

프로세서가 시스템 호출을 실행하면 커널 모드에서 실행되지만 여전히 현재 프로세스의 컨텍스트 내에서 실행됩니다. 따라서 현재 프로세스의 커널 주소 공간과 사용자 주소 공간에 직접 액세스할 수 있습니다(앞서 언급한 4G/4G 패치를 사용하지 않는다고 가정). 이는 사용자 공간 프로세스에 "높은" 메모리를 할당하는 데 문제가 없음을 의미합니다.

커널 목적으로 "높은" 메모리를 사용하는 것은 훨씬 더 문제입니다. 현재 프로세스에 매핑되지 않은 높은 메모리에 액세스하려면 임시로 커널의 주소 공간에 매핑되어야 합니다. 이는 추가 코드 및 성능 손실을 의미합니다.

관련 정보