Запись и чтение из файлового дескриптора 3 и / proc

Кольца ЦП являются наиболее четким различием

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

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

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

Почему в Linux не используются кольца 1 и 2: https://stackoverflow.com/questions/6710040/cpu-privivity-rings-why-rings-1-and-2-arent-used

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

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

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

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

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

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

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

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

Микросхема ЦП физически построена таким образом, что:

  • кольцо 0 может делать что угодно

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

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

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

    • не может изменять таблицы страниц: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work

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

      Это не позволяет одному процессу видеть память других процессов из соображений безопасности/простоты программирования.

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

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

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

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

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

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

      Что невозможно, так это то, что кольцо 3 дает себе разрешение на это, если у него его изначально не было.

      Linux всегда запрещает это. См. также: https://stackoverflow.com/questions/2711044/why-doesnt-linux-use-the-hardware-context-switch-via-the-tss

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

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

  • когда пользовательский процесс хочет, чтобы ядро ​​сделало для него что-то, например, запись в файл, он использует инструкцию, которая генерирует прерывание, такое как int 0x80 или системный вызов , чтобы сигнализировать ядро. x86-64 Пример системного вызова Linux hello world:

     .data
    Привет мир:
     .ascii "привет, мир\n"
    привет_мир_лен = . - Привет мир
    .текст
    .глобальный _start
    _Начало:
     /* написать */
    мов $1, %rax
    движение $1, %rdi
    мов $hello_world, %rsi
    мов $hello_world_len, %rdx
    системный вызов
    
     /* выход */
    мов $60, %rax
    движение $0, %rdi
    системный вызов
     

    скомпилируйте и запустите:

     как -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

  • Если программа пытается сделать что-то непослушное, например, записать в запрещенный регистр или адрес памяти (из-за подкачки), ЦП также вызывает некоторый обработчик обратного вызова ядра в кольце 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, чтобы попробовать ее, не убивая хост.

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

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

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

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

Подробнее см.: https://security.stackexchange.com/questions/129098/what-is-protection-ring-1

ARM

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

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

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

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

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

    .text
    .глобальный _start
    _Начало:
     /* написать */
    мов х0, 1
    ldr x1, = сообщение
    ldr x2, = len
    мов х8, 64
    СВК 0
    
     /* выход */
    мов х0, 0
    мов х8, 93
    СВК 0
    сообщение:
     .ascii "привет, системный вызов v8\n"
    Лен = . - сообщение
     

    GitHub вверх по течению.

    Протестируйте QEMU в Ubuntu 16.04:

     sudo apt-get install qemu-user gcc-arm-linux-gnueabihf
    arm-linux-gnueabihf-as -o hello.o привет.S
    arm-linux-gnueabihf-ld -о привет привет.о
    qemu-arm привет
     

    Вот конкретный пример «голого железа», который регистрирует обработчик SVC и выполняет вызов SVC.

  • EL2: гипервизоры, например Xen.

    Вводится с помощью инструкции hvc (вызов гипервизора).

    Гипервизор для ОС — это то же самое, что ОС для пользовательского пространства.

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

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

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

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

    Вводится с помощью инструкции smc (вызов в защищенном режиме)

Эталонная модель архитектуры ARMv8 DDI 0487C.a — Глава D1 — Модель программиста системного уровня AArch64 — Рисунок D1 -1 прекрасно иллюстрирует это:

enter image description here

Обратите внимание, что 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

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

0
30.01.2017, 00:50
3 ответа

Вы не закрыли этот файловый дескриптор, поэтому вы не освободили его ресурсы. Однако вы прочитали все, что когда-либо будет доступно в этом файловом дескрипторе. Будущие чтения сразу же получат EOF, но дескриптор файла не был закрыт, поэтому он все еще существует.

0
28.01.2020, 02:25

Файл «удаляется», когда больше нет записи каталога, указывающей на этот файл. В этом смысле файл может быть удален, но все еще существует на диске. Индекс для файла, а также его содержимое не удаляются, пока файл не будет закрыт.

Когда файл не имеет записи в каталоге, нет возможности открыть его напрямую. Однако все еще возможно дублировать существующие дескрипторы в этот файл, что и делает перенаправление на / proc / PID / fd / FD .

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

1
28.01.2020, 02:25

Вы все еще можете читать из файлового дескриптора. Проблема в том, что вы используете системный вызов «dup2», чтобы сделать вторую ссылку на файловый дескриптор, а файловый дескриптор имеет позицию. После вашей первой кошки позиция будет в конце данных. Вы можете читать данные понемногу (здесь я использую read в качестве примера для чтения строки за раз, обычно вы бы использовали read -r), и я использую perl как простой способ чтобы перемотать позицию к началу файла.

 $ exec 3<<<'This is some text
 > and some more
 > that is all folks'
 $ read <&3 ; echo "$REPLY"
 This is some text
 $ read <&3 ; echo "$REPLY"
 and some more
 $ perl -e 'sysseek(stdin,0,0);' <&3
 $ read <&3 ; echo "$REPLY"
 This is some text
3
28.01.2020, 02:25

Теги

Похожие вопросы