Несмотря на то, что большая часть кода ядра Linux написана на C, все еще есть много частей этого кода, которые очень специфичны для платформы, на которой он выполняется, и должны учитывать это.
Одним конкретным примером этого является виртуальная память, которая работает аналогичным образом на большинстве архитектур (иерархия таблиц страниц), но содержит конкретные детали для каждой архитектуры (например, количество уровней в каждой архитектуре, и эта цифра возрастала даже в x86 с введение новых более крупных чипов.) В коде ядра Linux представлены макросы для обработки этих иерархий, которые могут быть исключены компилятором на архитектурах, которые имеют меньше уровней таблиц страниц (так что код написан на C, но принимает детали архитектуры в рассмотрение.)
Многие другие области очень специфичны для каждой архитектуры и должны обрабатываться с помощью специфичного для арки кода. Большинство из них включает в себя код на ассемблере. Примеры:
Переключение контекста. Переключение контекста включает в себя сохранение значения всех регистров для процесса, подлежащего переключению, и восстановление регистров из сохраненного набора процесса, запланированного в ЦП. Даже количество и набор регистров очень специфичны для каждой архитектуры. Этот код обычно реализуется в сборке, чтобы обеспечить полный доступ к регистрам, а также обеспечить максимально быструю работу, поскольку производительность переключения контекста может быть критичной для системы.
Системные вызовы . Механизм, с помощью которого код пользовательского пространства может инициировать системный вызов, обычно зависит от архитектуры (а иногда даже от конкретной модели процессора, например, Intel и AMD вводили разные инструкции для этого, более старые процессоры могут не иметь этих инструкций, поэтому подробнее для тех, кто все еще будет уникальным.)
Обработчики прерываний . Сведения о том, как обрабатывать прерывания (аппаратные прерывания), обычно зависят от платформы и обычно требуют некоторого клея на уровне сборки для обработки конкретных соглашений о вызовах, используемых для платформы. Кроме того, примитивы для включения / отключения прерываний обычно зависят от платформы и также требуют кода сборки.
Инициализация : детали того, как должна происходить инициализация, также обычно включают детали, специфичные для платформы и часто требующие некоторого кода сборки для обработки точки входа в ядро. На платформах с несколькими процессорами (SMP) сведения о том, как подключить другие процессоры к сети, обычно также зависят от платформы.
Блокирующие примитивы . Реализация блокирующих примитивов (таких как спин-блокировки) обычно также включает в себя специфичные для платформы детали, поскольку некоторые архитектуры предоставляют (или предпочитают) разные инструкции ЦП для их эффективной реализации. Некоторые будут реализовывать атомарные операции, некоторые предоставят cmpxchg, который может атомарно протестировать / обновить (но потерпит неудачу, если другой писатель вошел первым), другие будут включать модификатор «lock» для инструкций процессора. Они также часто включают написание ассемблерного кода.
Вероятно, существуют другие области, в которых для ядра (или, в частности, для ядра Linux) необходим специфичный для платформы или архитектуры код. Если посмотреть на дерево исходных текстов ядра, то под arch/
и под ним есть специфичные для архитектуры поддеревья, include/arch/
где вы можете найти больше примеры этого.
Некоторые из них на самом деле удивляют, например, вы увидите, что число системных вызовов, доступных в каждой архитектуре, различно, и некоторые системные вызовы будут существовать в некоторых архитектурах, а не в других. (Даже на x86 список системных вызовов различается для 32-битного и 64-битного ядра.)
Короче говоря, есть много случаев, когда ядро должно знать, что специфично для платформы. Ядро Linux пытается абстрагировать большинство из них, поэтому алгоритмы более высокого уровня (например, как работают управление памятью и планирование) могут быть реализованы в C и работать одинаково (или в основном одинаково) на всех архитектурах.