Использование плавающей точки в ядре Linux

84

Я читал книгу Роберта Лава «Разработка ядра Linux» и наткнулся на следующий отрывок:

Нет (простого) использования плавающей точки

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

В отличие от пользовательского пространства, ядро ​​не имеет возможности бесшовной поддержки плавающей запятой, поскольку оно не может легко перехватить само себя. Использование плавающей запятой внутри ядра требует ручного сохранения и восстановления регистров с плавающей запятой, помимо других возможных рутинных операций. Короткий ответ: не делайте этого! За исключением редких случаев, в ядре нет операций с плавающей запятой.

Я никогда не слышал об этих режимах «целое число» и «с плавающей запятой». Что они собой представляют и зачем они нужны? Существует ли это различие в основных аппаратных архитектурах (например, x86) или оно характерно для некоторых более экзотических сред? Что именно влечет за собой переход от целочисленного режима к режиму с плавающей запятой, как с точки зрения процесса, так и с точки зрения ядра?

NPE
источник
2
Прочтите внимательно en.wikipedia.org/wiki/X86 & en.wikipedia.org/wiki/MMX_%28instruction_set%29 & en.wikipedia.org/wiki/SSE4
Базиль Старынкевич
2
Книга немного сбивает с толку, говоря о «режиме». Целочисленные инструкции доступны всегда, но FPU можно отключить полностью или частично. Никакая полезная функция никогда не состояла полностью из операций FP, например, все инструкции управления считаются «целыми». Подробнее см. Ниже.
DigitalRoss
@DigitalRoss: Я согласен с терминологией. Кстати, спасибо за ответ, он прояснил ситуацию.
NPE
Было бы интересно узнать, из-за чего возникает желание выполнять операции с плавающей запятой в ядре. Заманчиво сказать «плохой дизайн» в смысле попытки сделать что-то в ядре, что должно быть сделано вне его, но, возможно, есть вещи, которые действительно должно делать ядро, когда использование FPU было бы инновационным решением?
Крис Стрэттон,
2
Поскольку никто об этом не упомянул, если вы используете FP (или SIMD) внутри ядра, вам нужно вызвать kernel_fpu_begin()/ kernel_fpu_end()до / после вашего кода, чтобы убедиться, что состояние FPU в пользовательском пространстве не повреждено. Это то, что mdделает код Linux для RAID5 / RAID6.
Питер Кордес

Ответы:

86

Потому как...

  • многие программы не используют числа с плавающей запятой или не используют ее ни на каком временном отрезке; и
  • сохранение регистров FPU и другого состояния FPU требует времени; следовательно

... ядро ​​ОС может просто выключить FPU. Presto, нет состояния для сохранения и восстановления, и, следовательно, более быстрое переключение контекста. (Это то, что имел в виду режим , это просто означало, что FPU был включен.)

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

Во время переключения контекста он знает, что нужно выполнить логику сохранения состояния. (А затем он может снова выключить FPU.)

Кстати, я считаю, что объяснение в книге причины, по которой ядра (и не только Linux) избегают операций FPU, ... не совсем точное. 1

Ядро может замкнуться в себе и делает это для многих вещей. (Таймеры, сбои страниц, прерывания устройств и т. Д.) Настоящая причина в том, что ядру не особенно нужны операции FPU, а также оно должно работать на архитектурах без FPU вообще. Следовательно, он просто избегает сложности и времени выполнения, необходимых для управления его собственным контекстом FPU, не выполняя операций, для которых всегда есть другие программные решения.

Интересно отметить, как часто состояние FPU нужно было бы сохранять, если бы ядро ​​хотело использовать FP . . . каждый системный вызов, каждое прерывание, каждое переключение между потоками ядра. Даже если время от времени требовалось ядро ​​FP, 2 вероятно , было бы быстрее сделать это в программном обеспечении.


1. То есть коренная ошибка.
2. Я знаю несколько случаев, когда программное обеспечение ядра содержит арифметическую реализацию с плавающей запятой . Некоторые архитектуры реализуют традиционные операции FPU на аппаратном уровне, но оставляют некоторые сложные операции IEEE FP программному обеспечению. (Подумайте: денормальная арифметика.) Когда случается какой-то странный угловой случай IEEE, они перехватывают программное обеспечение, которое содержит педантично правильную эмуляцию операций, которые могут перехватывать.

DigitalRoss
источник
16

В некоторых конструкциях ядра регистры с плавающей запятой не сохраняются при переключении задачи «ядра» или «системы». (Это связано с тем, что регистры FP имеют большой размер и для их сохранения требуется как время, так и пространство.) Поэтому, если вы попытаетесь использовать FP, значения будут случайным образом изменяться "пуф".

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

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

Горячие лижет
источник
В Linux используйте kernel_fpu_begin()/ kernel_fpu_end()до / после вашего кода, чтобы запустить сохранение / восстановление состояния FPU в пользовательском пространстве (и я предполагаю, что состояние FPU вашего ядра против упреждения).
Питер Кордес