В чем разница между пользовательским режимом и режимом ядра в операционных системах?

107

В чем разница между пользовательским режимом и режимом ядра, почему и как вы активируете любой из них и каковы их варианты использования?

Alex
источник
1
Возможный дубликат В чем разница между пространством ядра и пространством пользователя?
Ciro Santilli 郝海东 郝海东 病 六四 事件 法轮功
1
@ CiroSantilli709 大 抓捕 六四 事件 法轮功 вопрос, заданный 7 лет назад, не может быть закрытым как дубликат вопроса, заданного 6 лет назад. Если они действительно дублируются, закрытие должно быть наоборот.
Сальвадор Дали
2
@SalvadorDali Привет, в настоящее время консенсус близок к «качеству»: meta.stackexchange.com/questions/147643/… Поскольку «качество» не поддается измерению, я просто использую голоса «за». ;-) Скорее всего, все сводится к тому, какой вопрос лучше всего подходит для новых ключевых слов Google в названии. Я рекомендую вам просто скопировать туда свой ответ, добавив внизу заявление об отказе от ответственности и ссылку на него, если он закрывается.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Ответы:

146
  1. Режим ядра

    В режиме ядра исполняемый код имеет полный и неограниченный доступ к базовому оборудованию. Он может выполнять любую инструкцию ЦП и ссылаться на любой адрес памяти. Режим ядра обычно зарезервирован для самых низкоуровневых и наиболее надежных функций операционной системы. Сбои в режиме ядра катастрофичны; они остановят весь компьютер.

  2. Пользовательский режим

    В пользовательском режиме исполняемый код не имеет возможности напрямую обращаться к оборудованию или справочной памяти. Код, работающий в пользовательском режиме, должен делегировать системные API-интерфейсы для доступа к оборудованию или памяти. Благодаря защите, обеспечиваемой такой изоляцией, сбои в пользовательском режиме всегда можно исправить. Большая часть кода, запущенного на вашем компьютере, будет выполняться в пользовательском режиме.

Читать далее

Понимание режима пользователя и ядра

рахул
источник
Интересно, когда процессор выполняет код операционной системы, в каком режиме находится процессор?
JackieLam
2
@JackieLam: он должен быть в режиме ядра.
Кадина
Итак, по сути, чтобы запустить процесс пользовательского пространства , он должен быть сопоставлен с пространством ядра ?
roottraveller 09
@rahul Я сомневаюсь, что эталонная память может быть получена в пользовательском режиме или что простейшее манипулирование данными приведет к дорогостоящему изменению режима в таком языке, как java.
maki XIE
48

Это два разных режима, в которых может работать ваш компьютер. До этого, когда компьютеры были похожи на большую комнату, если что-то выходит из строя - останавливает весь компьютер. Поэтому компьютерные архитекторы решили изменить это. Современные микропроцессоры аппаратно реализуют как минимум 2 различных состояния.

Пользовательский режим:

  • режим, в котором выполняются все пользовательские программы. У него нет доступа к оперативной памяти и оборудованию. Причина этого в том, что если бы все программы работали в режиме ядра, они могли бы перезаписывать память друг друга. Если ему необходимо получить доступ к любой из этих функций, он обращается к базовому API. Каждый процесс, запускаемый окнами, кроме системного, выполняется в пользовательском режиме.

Режим ядра:

  • режим, в котором выполняются все программы ядра (разные драйверы). У него есть доступ ко всем ресурсам и базовому оборудованию. Может быть выполнена любая инструкция ЦП и доступен любой адрес памяти. Этот режим зарезервирован для водителей, которые работают на самом нижнем уровне.

Как происходит переключение.

Переключение из пользовательского режима в режим ядра не выполняется процессором автоматически. CPU прерывается прерываниями (таймеры, клавиатура, ввод / вывод). При возникновении прерывания ЦП прекращает выполнение текущей запущенной программы, переключается в режим ядра, выполняет обработчик прерывания. Этот обработчик сохраняет состояние процессора, выполняет его операции, восстанавливает состояние и возвращается в пользовательский режим.

http://en.wikibooks.org/wiki/Windows_Programming/User_Mode_vs_Kernel_Mode

http://tldp.org/HOWTO/KernelAnalysis-HOWTO-3.html

http://en.wikipedia.org/wiki/Direct_memory_access

http://en.wikipedia.org/wiki/Interrupt_request

Сальвадор Дали
источник
Интересно, когда процессор выполняет код операционной системы, в каком режиме находится процессор?
JackieLam
1
@JackieLam: режим ядра
Apurv Nerlekar
10

Процессор на компьютере под управлением Windows имеет два разных режима: пользовательский режим и режим ядра. Процессор переключается между двумя режимами в зависимости от того, какой тип кода выполняется на процессоре. Приложения работают в пользовательском режиме, а основные компоненты операционной системы - в режиме ядра. Хотя многие драйверы работают в режиме ядра, некоторые драйверы могут работать в пользовательском режиме.

Когда вы запускаете приложение в пользовательском режиме, Windows создает процесс для этого приложения. Процесс предоставляет приложению частное виртуальное адресное пространство и частную таблицу дескрипторов. Поскольку виртуальное адресное пространство приложения является частным, одно приложение не может изменять данные, принадлежащие другому приложению. Каждое приложение работает изолированно, и в случае сбоя приложения сбой ограничивается только этим приложением. На другие приложения и операционную систему сбой не влияет.

Помимо того, что виртуальное адресное пространство приложения пользовательского режима является частным, оно ограничено. Процессор, работающий в пользовательском режиме, не может получить доступ к виртуальным адресам, зарезервированным для операционной системы. Ограничение виртуального адресного пространства приложения пользовательского режима предотвращает изменение и возможное повреждение критически важных данных операционной системы приложением.

Весь код, работающий в режиме ядра, использует единое виртуальное адресное пространство. Это означает, что драйвер режима ядра не изолирован от других драйверов и самой операционной системы. Если драйвер режима ядра случайно записывает неправильный виртуальный адрес, данные, принадлежащие операционной системе или другому драйверу, могут быть скомпрометированы. В случае отказа драйвера режима ядра происходит сбой всей операционной системы.

Если вы являетесь пользователем Windows, пройдя по этой ссылке, вы получите больше.

Связь между пользовательским режимом и режимом ядра

Sangeen
источник
6

Кольца процессора - самое четкое различие

В защищенном режиме x86 ЦП всегда находится в одном из 4-х колец. Ядро Linux использует только 0 и 3:

  • 0 для ядра
  • 3 для пользователей

Это наиболее четкое и быстрое определение отношения ядра к пользовательской среде.

Почему Linux не использует кольца 1 и 2: Кольца привилегий ЦП: Почему кольца 1 и 2 не используются?

Как определяется текущее кольцо?

Текущее кольцо выбирается комбинацией:

  • глобальная таблица дескрипторов: таблица записей GDT в памяти, и каждая запись имеет поле, Privlкоторое кодирует кольцо.

    Инструкция LGDT устанавливает адрес текущей таблицы дескрипторов.

    См. Также: http://wiki.osdev.org/Global_Descriptor_Table

  • сегментные регистры CS, DS и т. д. указывают на индекс записи в GDT.

    Например, CS = 0означает, что первая запись GDT в настоящее время активна для исполняемого кода.

Что может делать каждое кольцо?

Чип ЦП физически построен так, чтобы:

  • кольцо 0 может все

  • кольцо 3 не может запускать несколько инструкций и записывать в несколько регистров, в первую очередь:

    • не может изменить свое собственное кольцо! В противном случае он мог бы установить для себя 0 звонков, и звонки были бы бесполезны.

      Другими словами, нельзя изменить дескриптор текущего сегмента , который определяет текущее кольцо.

    • Невозможно изменить таблицы страниц: как работает подкачка x86?

      Другими словами, невозможно изменить регистр CR3, а сама подкачка предотвращает изменение таблиц страниц.

      Это предотвращает просмотр одним процессом памяти других процессов по причинам безопасности / простоты программирования.

    • не может регистрировать обработчики прерываний. Они настраиваются путем записи в ячейки памяти, что также предотвращается подкачкой.

      Обработчики работают в кольце 0 и нарушают модель безопасности.

      Другими словами, нельзя использовать инструкции LGDT и LIDT.

    • не может выполнять инструкции ввода-вывода, такие как inи out, и, следовательно, иметь произвольный доступ к оборудованию.

      В противном случае, например, права доступа к файлам были бы бесполезны, если бы какая-либо программа могла напрямую читать с диска.

      Точнее, спасибо Майклу Петчу : на самом деле ОС может разрешить инструкции ввода-вывода на кольце 3, это фактически контролируется сегментом состояния задачи .

      Кольцо 3 не может дать себе на это разрешение, если оно изначально не имело.

      Linux всегда это запрещает. См. Также: Почему Linux не использует аппаратное переключение контекста через TSS?

Как программы и операционные системы переходят между кольцами?

  • когда ЦП включен, он запускает начальную программу в кольце 0 (ну, вроде как, но это хорошее приближение). Вы можете считать эту начальную программу ядром (но обычно это загрузчик, который затем вызывает ядро, все еще находящееся в кольце 0 ).

  • когда процесс пользовательской среды хочет, чтобы ядро ​​что-то сделало для него, например, запись в файл, он использует инструкцию, которая генерирует прерывание, например int 0x80или,syscall чтобы сигнализировать ядру. Пример системного вызова Linux x86-64 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 вверх по течению .

    Когда это происходит, ЦП вызывает обработчик обратного вызова прерывания, который ядро ​​зарегистрировало во время загрузки. Вот конкретный пример baremetal, который регистрирует обработчик и использует его .

    Этот обработчик работает в кольце 0, которое решает, разрешит ли ядро ​​это действие, выполняет действие и перезапускает программу пользовательского пространства в кольце 3. x86_64

  • когда используется execсистемный вызов (или когда ядро запускается/init ), ядро подготавливает регистры и память для нового пользовательского процесса, затем переходит к точке входа и переключает ЦП на кольцо 3

  • Если программа пытается сделать что-то непослушное, например запись в запрещенный регистр или адрес памяти (из-за разбиения на страницы), CPU также вызывает некоторый обработчик обратного вызова ядра в кольце 0.

    Но поскольку пользовательская среда была непослушной, ядро ​​могло на этот раз убить процесс или выдать ему предупреждение с сигналом.

  • Когда ядро ​​загружается, оно устанавливает аппаратные часы с некоторой фиксированной частотой, которые периодически генерируют прерывания.

    Эти аппаратные часы генерируют прерывания, которые запускают кольцо 0, и позволяют ему планировать, какие процессы пользовательского пространства должны пробудиться.

    Таким образом, планирование может выполняться, даже если процессы не выполняют никаких системных вызовов.

Какой смысл иметь несколько колец?

Разделение ядра и пользовательской среды дает два основных преимущества:

  • легче создавать программы, так как вы более уверены, что одна не будет мешать другой. Например, одному процессу пользовательской среды не нужно беспокоиться ни о перезаписи памяти другой программы из-за разбиения на страницы, ни о переводе оборудования в недопустимое состояние для другого процесса.
  • это более безопасно. Например, права доступа к файлам и разделение памяти могут помешать хакерскому приложению прочитать данные вашего банка. Это, конечно, предполагает, что вы доверяете ядру.

Как с этим поиграться?

Я создал "голый металл", который должен быть хорошим способом прямого управления кольцами: https://github.com/cirosantilli/x86-bare-metal-examples

К сожалению, у меня не хватило терпения сделать пример пользовательского пространства, но я дошел до настройки разбиения на страницы, так что пользовательское пространство должно быть возможным. Я бы хотел увидеть запрос на перенос.

В качестве альтернативы модули ядра Linux работают в кольце 0, поэтому вы можете использовать их для опробования привилегированных операций, например, чтения регистров управления: Как получить доступ к регистрам управления cr0, cr2, cr3 из программы? Получение ошибки сегментации

Вот удобная установка QEMU + Buildroot, чтобы попробовать ее, не убивая хоста.

Обратной стороной модулей ядра является то, что работают другие потоки kthread, которые могут помешать вашим экспериментам. Но теоретически вы можете взять на себя все обработчики прерываний с вашим модулем ядра и владеть системой, что на самом деле было бы интересным проектом.

Отрицательные кольца

Хотя отрицательные кольца фактически не упоминаются в руководстве Intel, на самом деле существуют режимы ЦП, которые имеют дополнительные возможности, чем само кольцо 0, и поэтому хорошо подходят для названия «отрицательное кольцо».

Одним из примеров является режим гипервизора, используемый в виртуализации.

Подробнее см .:

РУКА

В ARM вместо этого кольца называются уровнями исключений, но основные идеи остаются прежними.

В ARMv8 существует 4 уровня исключений, которые обычно используются как:

  • EL0: пользовательское пространство

  • EL1: ядро ​​(«супервизор» в терминологии ARM).

    Вводится с помощью svcинструкции (SuperVisor Call), ранее известной как swi до унифицированной сборки , которая используется для выполнения системных вызовов Linux. Привет, мир, пример ARMv8:

    привет.

    .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 вверх по течению .

    Протестируйте это с помощью QEMU на Ubuntu 16.04:

    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: гипервизоры , например Xen .

    Вступил с hvcинструкцией (HyperVisor Call).

    Гипервизор для ОС - то же самое, что ОС для пользователя.

    Например, Xen позволяет одновременно запускать несколько операционных систем, таких как Linux или Windows, в одной и той же системе, и он изолирует операционные системы друг от друга для обеспечения безопасности и простоты отладки, как это делает Linux для программ пользовательского уровня.

    Гипервизоры являются ключевой частью сегодняшней облачной инфраструктуры: они позволяют нескольким серверам работать на одном оборудовании, поддерживая использование оборудования почти на 100% и экономя много денег.

    AWS, например, использовала Xen до 2017 года, когда о его переходе на KVM стало известно в новостях .

  • EL3: еще один уровень. Пример TODO.

    Вошел с smcинструкцией (Secure Mode Call)

ARMv8 Architecture Reference Model DDI 0487C.a - Глава D1 - В AArch64 уровневой системы программист Модель - Рисунок D1-1 иллюстрирует это красиво:

введите описание изображения здесь

Ситуация с ARM немного изменилась с появлением расширений хоста виртуализации ARMv8.1 (VHE) . Это расширение позволяет ядру эффективно работать в EL2:

введите описание изображения здесь

VHE был создан, потому что решения виртуализации в ядре Linux, такие как KVM, завоевали популярность по сравнению с Xen (см., Например, переход AWS на KVM, упомянутый выше), потому что большинству клиентов нужны только виртуальные машины Linux, и, как вы можете себе представить, все в одном В проекте KVM проще и потенциально более эффективно, чем Xen. Итак, теперь ядро ​​хоста Linux действует как гипервизор в этих случаях.

Обратите внимание на то, что ARM, возможно, благодаря преимуществу ретроспективного анализа, имеет лучшее соглашение об именах для уровней привилегий, чем x86, без необходимости в отрицательных уровнях: 0 является нижним и 3 высшим. Более высокие уровни, как правило, создаются чаще, чем более низкие.

Текущий EL можно запросить с помощью MRSинструкции: каков текущий режим выполнения / уровень исключения и т. Д.?

ARM не требует наличия всех уровней исключений, чтобы позволить реализациям, которым не нужна функция для экономии области чипа. ARMv8 «Уровни исключения» говорит:

Реализация может не включать все уровни исключений. Все реализации должны включать EL0 и EL1. EL2 и EL3 не являются обязательными.

QEMU, например, по умолчанию имеет значение EL1, но EL2 и EL3 можно включить с помощью параметров командной строки: qemu-system-aarch64, ввод el1 при эмуляции включения питания a53

Фрагменты кода протестированы на Ubuntu 18.10.

Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
1
Поскольку этот вопрос не относится к какой-либо ОС inи outдоступен для звонка 3. TSS может указывать на таблицу разрешений ввода-вывода в текущей задаче, предоставляя доступ для чтения / записи ко всем или определенным портам.
Майкл Петч
Конечно, вы устанавливаете биты IOPL в значение 3, тогда программа кольца 3 имеет полный доступ к порту, а разрешения TSS IO не применяются.
Майкл Петч
@MichaelPetch, спасибо, я этого не знал. Я обновил ответ.
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
5

Я собираюсь нанести удар в темноте и предположить, что вы говорите о Windows. Короче говоря, режим ядра имеет полный доступ к оборудованию, а пользовательский режим - нет. Например, многие, если не большинство драйверов устройств, написаны в режиме ядра, потому что им нужно контролировать более тонкие детали своего оборудования.

См. Также эту вики-книгу .

Марк Рушаков
источник
2
Это важно для вас как программиста, потому что ошибки ядра, как правило, наносят гораздо больший ущерб, чем вы, возможно, привыкли. Одна из причин различия между ядром и пользователем заключается в том, что ядро ​​может отслеживать / контролировать критические системные ресурсы и защищать каждого пользователя от других. Это немного упрощено, но все же полезно, чтобы напомнить себе, что ошибки пользователя часто раздражают, но ошибки ядра, как правило, приводят к отказу всей машины.
Адам Лисс,
3

Другие ответы уже объясняли разницу между пользовательским режимом и режимом ядра. Если вы действительно хотите вдаваться в подробности, вы должны получить копию Windows Internals , отличную книгу, написанную Марком Руссиновичем и Дэвидом Соломоном, в которой описывается архитектура и внутренние детали различных операционных систем Windows.

Дирк Фольмар
источник
2

какой

По сути, разница между режимами ядра и пользователя не зависит от ОС и достигается только путем ограничения выполнения некоторых инструкций только в режиме ядра с помощью аппаратной конструкции. Все другие цели, такие как защита памяти, могут быть выполнены только с помощью этого ограничения.

Как

Это означает, что процессор живет либо в режиме ядра, либо в пользовательском режиме. Используя некоторые механизмы, архитектура может гарантировать, что всякий раз, когда она переключается в режим ядра, код ОС выбирается для запуска.

Зачем

Имея эту аппаратную инфраструктуру, они могут быть достигнуты в обычных ОС:

  • Защита пользовательских программ от доступа ко всей памяти, чтобы программы, например, не перезаписывали ОС,
  • предотвращение выполнения пользовательскими программами конфиденциальных инструкций, таких как те, которые изменяют границы указателя памяти ЦП, например, чтобы программы не нарушали границы своей памяти.
Али Асгари
источник