简体   繁体   English

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

[英]Conditional compilation based on functionality in Linux kernel headers

Consider the case where I'm using some functionality from the Linux headers exported to user space, such as perf_event_open from <linux/perf_event.h> . 考虑其中我使用从出口到用户空间中的Linux头,一些功能的情况下,例如perf_event_open<linux/perf_event.h>

The functionality offered by this API has changed over time, as members have been added to the perf_event_attr , such as perf_event_attr.cap_user_time . 这个API提供的功能发生了变化随着时间的推移,随着成员已加入perf_event_attr ,如perf_event_attr.cap_user_time

How can I write source that compiles and uses these new functionalities if they are available locally, but falls back gracefully if they aren't and doesn't use them? 如果它们在本地可用,我如何编写编译和使用这些新功能的源代码,但如果它们不是并且不使用它们,则优雅地退回?

In particular, how can I detect in the pre-processor whether this stuff is available? 特别是,如何在预处理器中检测到这些东西是否可用?

I've used this perf_event_attr as an example, but my question is a general one because structure members, new structures, definitions and functions are added all the time. 我已经使用这个perf_event_attr作为例子,但我的问题是一般的问题,因为结构成员,新结构,定义和函数一直在增加。

Note that here I'm only considering the case where a process is compiled on the same system that it will run on: if you want to compile on one host and run on another you need a different set of tricks. 请注意,这里我只考虑在运行的同一系统上编译进程的情况:如果要在一个主机上编译并在另一个主机上运行,​​则需要一组不同的技巧。

Use the macros from /usr/include/linux/version.h : 使用/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
}

You might go into this with the following assumptions 你可以通过以下假设来讨论这个问题

  1. The features available in the header files correspond to those documented for the specific Linux version. 头文件中可用的功能对应于特定Linux版本中记录的功能。

  2. The kernel running during execution corresponds to <linux/version.h> during compilation 执行期间运行的内核在编译期间对应于<linux/version.h>

Ideally, I suggest not to rely on these two assumptions at all. 理想情况下,我建议根本不要依赖这两个假设。

The first assumption fails primarily due to backports, eg in enterprise Linux versions based on ancient kernels. 第一个假设失败主要是由于backports,例如在基于古代内核的企业Linux版本中。 If you care about different versions, you probably care about them. 如果你关心不同的版本,你可能会关心它们。

Instead, I recommend utilizing the methods for checking for struct members and include files in build system, eg for CMake: 相反,我建议使用方法检查struct成员并在构建系统中包含文件,例如对于CMake:

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

CHECK_INCLUDE_FILES can also be useful. CHECK_INCLUDE_FILES也很有用。

The second assumption can fail for many reasons, even if the binary is not moved between systems; 第二个假设可能由于多种原因而失败,即使二进制文件没有在系统之间移动; Eg updating the kernel but not recompiling the binary or simply booting another kernel. 例如,更新内核但不重新编译二进制文件或只是启动另一个内核。 Specifically perf_event_open fails with EINVAL if a reserved bit is set. 具体而言,如果设置了保留位,则perf_event_open失败并显示EINVAL This allows you to retry with an alternative implementation not using the requested feature. 这允许您使用不使用请求的功能的替代实现重试。

In short, statically check for the feature instead of the version. 简而言之,静态检查功能而不是版本。 Dynamically, try and retry the legacy implementation if it failed. 动态地,如果失败,请尝试重试旧版实现。

Just in addition to other answers. 除了其他答案。

If you're aiming for supporting both cross-version and cross-distro code, you should also keep in mind that there are distros (Centos/RHEL) which pull some recent changes from new kernels to old. 如果你的目标是支持跨版本和交叉发行版代码,你还应该记住,有一些发行版(Centos / RHEL)可以将新内核的最新更改从旧内核中删除。 So you may encounter a situation in which you'll have LINUX_VERSION_CODE equal to some old kernel version, but there will be some changes (new fields in data structures, new functions, etc.) from recent kernel. 因此,您可能会遇到LINUX_VERSION_CODE等于某个旧内核版本的情况,但最近的内核会有一些更改(数据结构中的新字段,新函数等)。 In such case this macro is insufficient. 在这种情况下,这个宏是不够的。

You can add something like (to avoid preprocessor errors in case it is not a Centos distro): 你可以添加类似的东西(以避免预处理器错误,如果它不是Centos发行版):

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

And use it with > or >= where you need: 并使用>>=您需要的地方:

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

for Centos/RHEL custom kernels support. 对于Centos / RHEL定制内核的支持。

PS of course it's necessary to examine an appropriate versions of Centos/RHEL, and understand when and what exactly has changed in the code sections that affect you. PS当然有必要检查适当版本的Centos / RHEL,并了解影响您的代码部分何时以及究竟发生了哪些变化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM