基于Linux内核标头中的功能的条件编译

时间:2019-06-06 22:27:38

标签: c linux linux-kernel backwards-compatibility perf

考虑以下情况:我正在使用导出到用户空间的Linux标头中的某些功能,例如<linux/perf_event.h>中的perf_event_open

由于成员已添加到perf_event_attr,例如perf_event_attr.cap_user_time,因此该API提供的功能已随着时间而改变。

如果这些新功能在本地可用,那么如何编写可编译和使用这些新功能的源代码,如果不使用和不使用它们,则如何优雅地回退呢?

尤其是如何在预处理器中检测这些东西是否可用?

我以这个perf_event_attr为例,但是我的问题很笼统,因为结构成员,新结构,定义和函数一直都在添加。

请注意,这里我仅考虑在同一进程上运行该进程的情况:如果要在一个主机上编译而在另一个主机上运行,​​则需要不同的技巧。

3 个答案:

答案 0 :(得分:2)

使用/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 :(得分:1)

您可能会遵循以下假设

  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失败。这样,您就可以不使用请求的功能来尝试其他实现。

简而言之,请静态检查功能而不是版本。如果失败,请动态尝试重试传统实现。

答案 2 :(得分:1)

除了其他答案之外。

如果您希望同时支持跨版本和跨发行版代码,则还应记住,有发行版(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,并了解何时以及在影响您的代码部分中到底发生了什么更改。