3 votes

Compilation conditionnelle basée sur la fonctionnalité des en-têtes du noyau Linux

Prenons le cas où j'utilise une fonctionnalité des en-têtes Linux exportée vers l'espace utilisateur, telle que perf_event_open de <linux/perf_event.h> .

La fonctionnalité offerte par cette API a évolué au fil du temps, au fur et à mesure que des membres ont été ajoutés à la base de données des perf_event_attr , tels que perf_event_attr.cap_user_time .

Comment puis-je écrire un code source qui compile et utilise ces nouvelles fonctionnalités si elles sont disponibles localement, mais qui se replie gracieusement si elles ne le sont pas et ne les utilise pas ?

En particulier, comment puis-je détecter dans le pré-processeur si ces éléments sont disponibles ?

J'ai utilisé cette perf_event_attr à titre d'exemple, mais ma question est d'ordre général, car des membres de structures, de nouvelles structures, des définitions et des fonctions sont ajoutés en permanence.

Notez que je ne considère ici que le cas où un processus est compilé sur le même système que celui sur lequel il s'exécutera : si vous voulez compiler sur un hôte et exécuter sur un autre, vous avez besoin d'un ensemble différent d'astuces.

2voto

Kamil Cuk Points 11578

Utilisez les macros de /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
}

1voto

Zulan Points 1216

Vous pouvez partir des hypothèses suivantes

  1. Les fonctionnalités disponibles dans les fichiers d'en-tête correspondent à celles documentées pour la version spécifique de Linux.

  2. Le noyau en cours d'exécution correspond à <linux/version.h> lors de la compilation

Idéalement, je suggère pas de s'appuyer sur ces deux hypothèses.

La première hypothèse échoue principalement en raison des rétroportages, par exemple dans les versions Linux d'entreprise basées sur d'anciens noyaux. Si vous vous souciez des différentes versions, vous vous en souciez probablement.

Je recommande plutôt d'utiliser les méthodes de vérification des membres de la structure et des fichiers inclus dans le système de construction, par exemple pour CMake :

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

CHECK_INCLUDE_FILES peut également s'avérer utile.

La deuxième hypothèse peut échouer pour de nombreuses raisons, même si le binaire n'est pas déplacé d'un système à l'autre ; par exemple, la mise à jour du noyau sans recompilation du binaire ou le simple démarrage d'un autre noyau. Plus précisément perf_event_open échoue avec EINVAL si un bit réservé est activé. Cela vous permet de réessayer avec une implémentation alternative n'utilisant pas la fonctionnalité demandée.

En bref, vérifiez statiquement la fonctionnalité au lieu de la version. Dynamiquement, essayer et réessayer l'ancienne implémentation si elle a échoué.

1voto

red0ct Points 1305

En complément des autres réponses.

Si vous souhaitez prendre en charge le code inter-version et inter-distro, vous devez également garder à l'esprit qu'il existe des distros (Centos/RHEL) qui transfèrent certaines modifications récentes des nouveaux noyaux vers les anciens. Il se peut donc que vous rencontriez une situation dans laquelle vous devrez LINUX_VERSION_CODE égal à une ancienne version du noyau, mais il y aura des changements (nouveaux champs dans les structures de données, nouvelles fonctions, etc. Dans ce cas, cette macro est insuffisante.

Vous pouvez ajouter quelque chose comme (pour éviter les erreurs de préprocesseur dans le cas où il ne s'agit pas d'une distro Centos) :

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

Et l'utiliser avec > o >= là où vous en avez besoin :

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

pour la prise en charge des noyaux personnalisés Centos/RHEL.

P.S. Bien sûr, il est nécessaire d'examiner une version appropriée de Centos/RHEL, et de comprendre quand et ce qui a changé exactement dans les sections du code qui vous concernent.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X