C 基于Linux内核头函数的条件编译

C 基于Linux内核头函数的条件编译,c,linux,linux-kernel,backwards-compatibility,perf,C,Linux,Linux Kernel,Backwards Compatibility,Perf,考虑这样一种情况:我正在使用从导出到用户空间的Linux头中的一些功能,例如从中导出的功能 随着时间的推移,此API提供的功能发生了变化,因为成员已添加到perf\u event\u attr,例如 如果这些新功能在本地可用,我如何编写编译和使用这些新功能的源代码,但如果它们不可用或不使用,我又如何优雅地回退 特别是,如何在预处理器中检测这些东西是否可用 我用这个perf_event_attr作为例子,但我的问题是一般性的,因为结构成员、新结构、定义和函数一直在添加 请注意,这里我只考虑在同一个

考虑这样一种情况:我正在使用从导出到用户空间的Linux头中的一些功能,例如从
中导出的功能

随着时间的推移,此API提供的功能发生了变化,因为成员已添加到
perf\u event\u attr
,例如

如果这些新功能在本地可用,我如何编写编译和使用这些新功能的源代码,但如果它们不可用或不使用,我又如何优雅地回退

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

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


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

使用
/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
}
#包括
int main(){

#如果LINUX\u版本\u代码,您可能会根据以下假设进行讨论

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

  • 执行期间运行的内核对应于编译期间的

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

    第一个假设失败的主要原因是后端口,例如在基于古老内核的企业Linux版本中。如果您关心不同的版本,您可能会关心它们

    相反,我建议使用检查结构成员的方法,并在生成系统中包含文件,例如,对于CMake:

    CHECK_STRUCT_HAS_MEMBER("struct perf_event_attr" cap_user_time linux/perf_event.h HAVE_PERF_CAP_USER_TIME)
    
    检查包含文件也很有用

    第二种假设可能由于多种原因而失败,即使二进制文件未在系统之间移动;例如,更新内核但不重新编译二进制文件或只是启动另一个内核。特别是
    perf_event_open
    如果设置了保留位,则会因
    EINVAL
    而失败。这允许您使用替代实现重试未使用请求的功能


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

    除了其他答案之外

    如果您的目标是同时支持跨版本和跨发行版代码,那么还应该记住有发行版(Centos/RHEL)它将最近的一些更改从新内核拉到旧内核。因此,您可能会遇到这样一种情况,即您的
    LINUX\u VERSION\u code
    与一些旧内核版本相同,但最近的内核会有一些更改(数据结构中的新字段、新函数等)。在这种情况下,此宏是不够的

    您可以添加以下内容(以避免在不是Centos发行版的情况下出现预处理器错误):

    并在需要时与
    =
    一起使用:

    #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,3,0) || RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2)
    ...
    
    对于Centos/RHEL自定义内核支持



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

    在最坏的情况下:autoconf@AnttiHaapala-是的,没错。在这个项目中还没有引入autoconf,希望避免它。您没有看到字段
    版本
    ?下面的所有答案都不准确。@0andriy-我已经看到了。我如何使用它来解决这个问题?@0andriy您是否正在浏览
    perf\u event\u mmap\u页面。version
    字段?在我看来,
    version
    字段并不能可靠地确定结构中的更改。例如,commit添加了新字段,但它没有使用em更改版本。还请注意,该问题并非特定于任何Linux内核类型,因此最终必须使用Linux内核版本,如以下答案中所述。条件
    RHEL_RELEASE_code>RHEL_RELEASE_version(7,2)
    可能应该包装在
    #ifdef RHEL_RELEASE_code
    中,以便在其他Linux发行版上编译时不会发生编译错误。尽管使用@Zulan的答案中的
    CHECK_STRUCT_HAS_MEMBER
    方法可能比检查
    RHEL_RELEASE_code
    更容易,除非在某些情况下这不起作用.不准确-请注意字段
    version
    @0andriy我的答案有什么问题?请澄清。在提到的性能结构中有一个
    version
    字段。即使在一般情况下,将一个版本放入IOCTL ABI也是一个很好的做法。根据这一点,人们可能会获得向后兼容,而不会出现丑陋的情况。#如果没有。@0andriy您正在谈论的并非Linux内核中的每个数据结构都是“API”的一部分。所以我的答案非常准确,而且更常见。另外,在这样的假设中没有丑陋之处,这是绝对正常的。不准确-注意字段
    version
    @0andriy
    version
    在主题启动者提到的
    perf\u event\u attr
    中哪个结构的
    字段。@0andriy
    struct perf\u event\u attr
    没有e一个名为
    version
    的字段。如果有,也没有意义,因为此结构包含从用户空间传递到内核的信息。似乎我没有仔细检查结构的名称。然而,其中一个有
    version
    ,另一个有
    size
    。这并没有改变我通信的主要动机nts.不准确-注意字段
    版本
    @0andriy
    不准确-注意字段版本
    ?注意什么?什么字段?什么不准确?以什么方式?@0andriy直到你不能清楚地阐明你在说什么-你的评论不准确。所有答案都可以很好地帮助解决此类问题,而且所有答案都非常准确如果你知道的更多,就用上面提到的句型写出最好的答案
    #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,3,0) || RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2)
    ...