【问题标题】:Conditional compilation based on functionality in Linux kernel headers基于 Linux 内核头文件中的功能的条件编译
【发布时间】:2019-06-06 22:27:38
【问题描述】:

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

此 API 提供的功能随着时间的推移而发生变化,因为成员已添加到 perf_event_attr,例如 perf_event_attr.cap_user_time

如果这些新功能在本地可用,我如何编写源代码来编译和使用它们,但如果它们不可用并且不使用它们,我该如何优雅地回退?

特别是,我如何在预处理器中检测这些东西是否可用?

我以perf_event_attr 为例,但我的问题是一般性的,因为结构成员、新结构、定义和函数一直在添加。

请注意,这里我只考虑进程在将要运行的同一系统上编译的情况:如果您想在一台主机上编译并在另一台主机上运行,​​则需要一组不同的技巧。

【问题讨论】:

  • 在最坏的情况下:autoconf
  • @AnttiHaapala - 是的,没错。这个项目中还没有引入autoconf,希望能避免。
  • 你没看到version这个字段吗?以下所有答案都不准确。
  • @0andriy - 我见过。我怎么能用它来解决这个问题?
  • @0andriy 你在perf_event_mmap_page.version 领域走来走去吗?在我看来,version 字段不能可靠地确定结构的变化。例如,thiscommit 添加了新字段,但似乎没有更改版本。另请注意,该问题并非特定于任何 Linux 内核类型,因此最终必须使用 Linux 内核版本,如下面的答案中所述。

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


【解决方案1】:

使用来自/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
}

【讨论】:

  • 不准确——注释字段version
  • @0andriy Inaccurate — note field version ?注意什么?什么领域?什么是不准确的?以什么方式?
  • @0andriy 直到 你不能清楚地解释你在说什么......你在说什么 - 你的 cmets 不准确。所有答案都可以帮助很好地解决此类问题,并且所有答案都非常准确。 如果您知道更多 - 撰写最佳答案。
  • 在提到的 perf 结构中有一个 version 字段。即使在一般情况下,将版本放入 IOCTL ABI 也是一种很好的做法。如果有人不能在盒子外面读一点,那很可惜。
【解决方案2】:

您可能会根据以下假设进行此操作

  1. 头文件中可用的功能与针对特定 Linux 版本记录的功能相对应。

  2. 执行时运行的内核对应编译时&lt;linux/version.h&gt;

理想情况下,我建议不要完全依赖这两个假设。

第一个假设失败主要是由于向后移植,例如在基于古老内核的企业 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。这允许您使用不使用请求的功能的替代实现重试。

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

【讨论】:

  • 不准确——注释字段version
  • @0andriy version 字段在哪个结构中?
  • 在主题启动者提到的perf_event_attr 中。
  • @0andriy struct perf_event_attr 没有名为 version 的字段。如果它有也没有任何意义,因为这个结构包含传递从用户空间到内核的信息。
  • 我好像没有仔细检查结构的名称。尽管如此,其中一个有version,另一个有size。它不会改变我的 cmets 的主要动机。
【解决方案3】:

除了其他答案。

如果您的目标是同时支持跨版本和跨发行版代码,您还应该记住,有些发行版 (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

并在您需要的地方与&gt;&gt;= 一起使用:

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

用于 Centos/RHEL 自定义内核支持。

附:当然,有必要检查适当的 Centos/RHEL 版本,并了解影响您的代码部分何时以及发生了什么变化。

【讨论】:

  • 条件RHEL_RELEASE_CODE &gt; RHEL_RELEASE_VERSION(7,2) 应该包含在#ifdef RHEL_RELEASE_CODE 中,这样在其他Linux 发行版上编译时就不会出现编译错误。尽管使用@Zulan 的答案中的CHECK_STRUCT_HAS_MEMBER 方法可能比检查RHEL_RELEASE_CODE 更容易,除非在某些情况下不起作用。
  • 不准确——注释字段version
  • @0andriy 我的回答有什么问题?请澄清。
  • 在提到的 perf 结构中有一个 version 字段。即使在一般情况下,将版本放入 IOCTL ABI 也是一种很好的做法。取决于它,一个人可能会在没有丑陋的#ifdefs 的情况下获得向后兼容性。
  • @0andriy 你说的用例太具体了。并非 Linux 内核中的每个数据结构都是“API”的一部分。所以我的回答很准确,也更常见。附言这样的#ifdefs没有丑陋,这绝对是正常的。
猜你喜欢
  • 1970-01-01
  • 2021-07-16
  • 1970-01-01
  • 2014-12-02
  • 2015-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多