Условная компиляция на основе функциональности в заголовках ядра Linux - PullRequest
3 голосов
/ 07 июня 2019

Рассмотрим случай, когда я использую некоторые функции из заголовков Linux, экспортируемых в пространство пользователя, например perf_event_open из <linux/perf_event.h>.

Функциональные возможности, предоставляемые этим API, со временем менялись, поскольку к perf_event_attr были добавлены элементы, например perf_event_attr.cap_user_time.

Как мне написать исходный код, который компилирует и использует эти новые функциональные возможности, если они доступны локально, но изящно отступает, если их нет и не использует их?

В частности, как я могу определить в препроцессоре, доступен ли этот материал?

Я использовал это perf_event_attr в качестве примера, но мой вопрос является общим, потому что члены структуры, новые структуры, определения и функции добавляются постоянно.

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

Ответы [ 3 ]

2 голосов
/ 07 июня 2019

Использовать макросы из /usr/include/linux/version.h:

#include <linux/version.h>

int main() {
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)
                                      // ^^^^^^ change for the proper version when `perf_event_attr.cap_user_time` was introduced
   // use old interface
#else
   // use new interface
   // use  perf_event_attr.cap_user_time
#endif
}
1 голос
/ 07 июня 2019

Только в дополнение к другим ответам.

Если вы стремитесь поддерживать как кросс-версию, так и кросс-дистрибутивный код, вам также следует помнить, что существуют дистрибутивы (Centos / RHEL), которые переносят некоторые недавние изменения из новых ядер в старое. Таким образом, вы можете столкнуться с ситуацией, в которой у вас будет LINUX_VERSION_CODE, равный некоторой старой версии ядра, но будут некоторые изменения (новые поля в структурах данных, новые функции и т. Д.) Из недавнего ядра. В таком случае этого макроса недостаточно.

Вы можете добавить что-то вроде (чтобы избежать ошибок препроцессора в случае, если это не дистрибутив Centos):

#ifndef RHEL_RELEASE_CODE
#define RHEL_RELEASE_CODE 0
#endif
#ifndef RHEL_RELEASE_VERSION
#define RHEL_RELEASE_VERSION(x,y) 1
#endif

И используйте его с > или >= там, где вам нужно:

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,3,0) || RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2)
...

для поддержки пользовательских ядер Centos / RHEL.

P.S. конечно, необходимо изучить соответствующие версии Centos / RHEL и понять, когда и что именно изменилось в тех разделах кода, которые затрагивают вас.

1 голос
/ 07 июня 2019

Вы можете пойти на это со следующими предположениями

  1. Функции, доступные в заголовочных файлах, соответствуют функциям, задокументированным для конкретной версии Linux.

  2. Ядро, работающее во время выполнения, соответствует <linux/version.h> во время компиляции

В идеале я предлагаю , а не , чтобы вообще полагаться на эти два предположения.

Первое предположение не выполняется главным образом из-за обратных портов, например, в корпоративных версиях Linux на основе древних ядер. Если вы заботитесь о разных версиях, вы, вероятно, заботитесь о них.

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

CHECK_STRUCT_HAS_MEMBER("struct perf_event_attr" cap_user_time linux/perf_event.h HAVE_PERF_CAP_USER_TIME)

CHECK_INCLUDE_FILES также может быть полезным.

Второе предположение может потерпеть неудачу по многим причинам, даже если двоичный файл не перемещается между системами; Например. обновить ядро, но не перекомпилировать бинарный файл или просто загрузить другое ядро. В частности, perf_event_open завершается с ошибкой EINVAL, если установлен зарезервированный бит. Это позволяет повторить попытку с альтернативной реализацией, не использующей запрошенную функцию.

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

...