Ядро Linux в комментариях

       

Сокращенное применение #if и #ifdef


Ядро Linux перенесено на множество платформ, и проблемы переносимости решались по мере их возникновения. Большинство кода для поддержки различных платформ завершается множеством препроцессорных заморочек, наподобие показанных ниже:

#if defined(SOLARIS) /* делать что-либо способом, принятым в Solaris ... */ #elif defined(HPUX) /* делать что-либо способом, принятым в HP-UX ... */ #elif defined (LINUX) /* делать что-либо общепринятым и правильным способом ... */ #else #error Неподдерживаемая платформа. #endif

Несмотря на то что пример связан с обеспечением переносимости кода в различных ОС, а задача Linux заключается в обеспечении переносимости кода в различных процессорах, принципы реализации совершенно аналогичны. Задействование препроцессора для такой задачи — ошибочное решение. Весь этот хаос лишь усложняет чтение и понимание кода. Что еще хуже, добавление поддержки новой платформы заставляет возвращаться к каждому блоку этого хлама (попробуйте еще отыскать их все!) и добавлению кусочков кода там и сям.

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

Нельзя не заметить четко прослеживающуюся аналогию между рассматриваемым решением и методологией использования объектов (или структур, заполненных указателями на функции, в С), которая применяется вместо множества операторов switch, реализующих различные варианты обработки. На определенном уровне проблемы и их решения весьма схожи.

Проблемы переносимости не ограничиваются платформами и центральными процессорами — в действие вступают также и компиляторы. Это еще одно место, позволяющее понять, почему предполагается, что Linux должен компилироваться в gcc. Нет необходимости предусматривать в коде блоки #if (либо #ifdef), обеспечивающие выбор того или иного компилятора, поскольку Linux предполагает существование только одного компилятора в мире. Основное использование #ifdef в коде ядра связано с поддержкой конструкций, которые могут не компилироваться. К таким конструкциям относится, например, код, тестирующий, определен ли макрос SMP, указывающий на необходимость поддержки многопроцессорных SMP-машин.



Содержание раздела