GNU/Linux >> Tutoriels Linux >  >> Linux

Un guide pour comprendre les bibliothèques de logiciels Linux en C

Les bibliothèques de logiciels sont un moyen de longue date, simple et judicieux de réutiliser le code. Cet article explique comment créer des bibliothèques à partir de zéro et les mettre à la disposition des clients. Bien que les deux exemples de bibliothèques ciblent Linux, les étapes de création, de publication et d'utilisation de ces bibliothèques s'appliquent à d'autres systèmes de type Unix.

Les bibliothèques d'échantillons sont écrites en C, ce qui est bien adapté à la tâche. Le noyau Linux est principalement écrit en C et le reste en langage assembleur. (Il en va de même pour les cousins ​​Windows et Linux tels que macOS.) Les bibliothèques système standard pour les entrées/sorties, la mise en réseau, le traitement des chaînes, les mathématiques, la sécurité, l'encodage des données, etc. sont également écrites principalement en C. Pour écrire une bibliothèque en C consiste donc à écrire dans le langage natif de Linux. De plus, C établit la marque des performances parmi les langages de haut niveau.

Plus de ressources Linux

  • Aide-mémoire des commandes Linux
  • Aide-mémoire des commandes Linux avancées
  • Cours en ligne gratuit :Présentation technique de RHEL
  • Aide-mémoire sur le réseau Linux
  • Aide-mémoire SELinux
  • Aide-mémoire sur les commandes courantes de Linux
  • Que sont les conteneurs Linux ?
  • Nos derniers articles Linux

Il existe également deux exemples de clients (un en C, l'autre en Python) pour accéder aux bibliothèques. Il n'est pas surprenant qu'un client C puisse accéder à une bibliothèque écrite en C, mais le client Python illustre qu'une bibliothèque écrite en C peut servir des clients d'autres langages.

Bibliothèques statiques ou dynamiques

Les systèmes Linux ont deux types de bibliothèques :

  • Une bibliothèque statique (ou archive de bibliothèque) est intégré dans un client compilé statiquement (par exemple, un client en C ou Rust) pendant la phase de liaison du processus de compilation. En effet, chaque client obtient sa propre copie de la bibliothèque. Un inconvénient important d'une bibliothèque statique apparaît si la bibliothèque doit être révisée (par exemple, pour corriger un bogue), car chaque client de bibliothèque doit être relié à la bibliothèque statique. Une bibliothèque dynamique, décrite ci-après, évite ce défaut.
  • Une bibliothèque dynamique (c'est-à-dire partagée) est marqué pendant la phase de liaison d'un programme client compilé statiquement, mais le programme client et le code de la bibliothèque restent par ailleurs déconnectés jusqu'à l'exécution - le code de la bibliothèque n'est pas intégré au client. Lors de l'exécution, le chargeur dynamique du système connecte une bibliothèque partagée à un client en cours d'exécution, que le client provienne d'un langage compilé statiquement, tel que C, ou d'un langage compilé dynamiquement, tel que Python. Par conséquent, une bibliothèque dynamique peut être mise à jour sans gêner les clients. Enfin, plusieurs clients peuvent partager une seule copie d'une bibliothèque dynamique.

En général, les bibliothèques dynamiques sont préférées aux bibliothèques statiques, bien qu'il y ait un coût en termes de complexité et de performances. Voici comment chaque type de bibliothèque est créé et publié :

  1. Le code source de la bibliothèque est compilé dans un ou plusieurs modules objet, qui sont des fichiers binaires pouvant être inclus dans une bibliothèque et liés à des clients exécutables.
  2. Les modules d'objets sont regroupés dans un seul fichier. Pour une bibliothèque statique, l'extension standard est .a pour "archives". Pour une bibliothèque dynamique, l'extension est .so pour "objet partagé". Les deux bibliothèques d'exemples, qui ont les mêmes fonctionnalités, sont publiées sous la forme de fichiers libprimes.a (statique) et libshprimes.so (dynamique). Le préfixe lib est utilisé pour les deux types de bibliothèque.
  3. Le fichier de la bibliothèque est copié dans un répertoire standard afin que les programmes clients puissent accéder à la bibliothèque en toute simplicité. Un emplacement typique pour la bibliothèque, qu'il soit statique ou dynamique, est /usr/lib ou /usr/local/lib; d'autres emplacements sont possibles.

Les étapes détaillées de création et de publication de chaque type de bibliothèque seront bientôt disponibles. Cependant, je vais d'abord présenter les fonctions C dans les deux bibliothèques.

Les fonctions de la bibliothèque d'exemples

Les deux bibliothèques d'exemples sont construites à partir des mêmes cinq fonctions C, dont quatre sont accessibles aux programmes clients. La cinquième fonction, qui est un utilitaire pour l'une des quatre autres, montre comment C prend en charge le masquage des informations. Le code source de chaque fonction est suffisamment court pour que les fonctions puissent être hébergées dans un seul fichier source, bien que plusieurs fichiers source (par exemple, un pour chacune des quatre fonctions publiées) soient une option.

Les fonctions de la bibliothèque sont élémentaires et traitent, de diverses manières, des nombres premiers. Toutes les fonctions attendent des valeurs entières non signées (c'est-à-dire non négatives) comme arguments :

  • Le is_prime la fonction teste si son seul argument est un nombre premier.
  • Les are_coprimes La fonction vérifie si ses deux arguments ont un plus grand diviseur commun (pgcd) de 1, qui définit les nombres premiers entre eux.
  • Les prime_factors fonction liste les facteurs premiers de son argument.
  • Le goldbach la fonction attend une valeur entière paire de 4 ou plus, énumérant la somme des deux nombres premiers à cet argument ; il peut y avoir plusieurs paires sommatrices. La fonction porte le nom du mathématicien du XVIIIe siècle Christian Goldbach, dont la conjecture selon laquelle tout entier pair supérieur à deux est la somme de deux nombres premiers reste l'un des plus anciens problèmes non résolus de la théorie des nombres.

La fonction utilitaire gcd réside dans les fichiers de bibliothèque déployés, mais cette fonction n'est pas accessible en dehors de son fichier contenant ; par conséquent, un client de bibliothèque ne peut pas invoquer directement le gcd une fonction. Un examen plus approfondi des fonctions C clarifie le point.

En savoir plus sur les fonctions C

Chaque fonction en C a une classe de stockage, qui détermine la portée de la fonction. Pour les fonctions, il y a deux options :

  • La classe de stockage par défaut pour les fonctions est extern , ce qui donne une portée globale à une fonction. Un programme client peut appeler n'importe quel extern fonction dans les bibliothèques d'échantillons. Voici la définition de la fonction are_coprimes avec un extern explicite :
    extern unsigned are_coprimes(unsigned n1, unsigned n2) {
      ...
    }
  • La classe de stockage static limite la portée d'une fonction au fichier dans lequel la fonction est définie. Dans les exemples de bibliothèques, la fonction utilitaire gcd est static :
    static unsigned gcd(unsigned n1, unsigned n2) {
      ...
    }

Ne fonctionne que dans le primes.c le fichier peut invoquer gcd , et seule la fonction are_coprimes le fait. Lorsque les bibliothèques statiques et dynamiques sont construites et publiées, d'autres programmes peuvent appeler un extern fonction telle que are_coprimes mais pas le static fonction gcd . Le static la classe de stockage masque donc le gcd fonction des clients de la bibliothèque en limitant la portée de la fonction aux autres fonctions de la bibliothèque.

Les fonctions autres que gcd dans le primes.c le fichier n'a pas besoin de spécifier une classe de stockage, qui serait par défaut extern . Cependant, il est courant dans les bibliothèques de rendre le extern explicite.

Le C fait la distinction entre les définitions de fonctions et les déclarations, ce qui est important pour les bibliothèques. Commençons par les définitions. C n'a que des fonctions nommées, et chaque fonction est définie avec :

  • Un nom unique. Deux fonctions d'un programme ne peuvent pas porter le même nom.
  • Une liste d'arguments, qui peut être vide. Les arguments sont typés.
  • Soit un type de valeur de retour (par exemple, int pour un entier signé 32 bits) ou void si aucune valeur n'est renvoyée.
  • Un corps entouré d'accolades. Dans un exemple artificiel, le corps pourrait être vide.

Chaque fonction d'un programme doit être définie exactement une fois.

Voici la définition complète de la fonction de bibliothèque are_coprimes :

extern unsigned are_coprimes(unsigned n1, unsigned n2) { /* definition */
  return 1 == gcd(n1, n2); /* greatest common divisor of 1? */
}

La fonction renvoie une valeur booléenne (0 pour faux ou 1 pour vrai), selon que les deux arguments entiers ont un plus grand diviseur commun de 1. La fonction utilitaire gcd calcule le plus grand diviseur commun des arguments entiers n1 et n2 .

Une déclaration de fonction, contrairement à une définition, n'a pas de corps :

extern unsigned are_coprimes(unsigned n1, unsigned n2); /* declaration */

La déclaration se termine par un point-virgule après la liste d'arguments ; il n'y a pas d'accolades entourant un corps. Une fonction peut être déclarée plusieurs fois dans un programme.

Pourquoi les déclarations sont-elles nécessaires ? En C, une fonction appelée doit être visible par son appelant. Il existe différentes manières de fournir une telle visibilité, en fonction de la complexité du compilateur. Un moyen sûr est de définir la fonction appelée au-dessus de son appelant lorsque les deux résident dans le même fichier :

void f() {...}     /* f is defined before being called */
void g() { f(); }  /* ok */

La définition de la fonction f pourrait être déplacé sous l'appel de la fonction g si f ont été déclarés au-dessus de l'appel :

void f();         /* declaration makes f visible to caller */
void g() { f(); } /* ok */
void f() {...}    /* easier to put this above the call from g */

Mais que se passe-t-il si la fonction appelée réside dans un fichier différent de celui de son appelant ? Comment les fonctions définies dans un fichier sont-elles rendues visibles dans un autre fichier, étant donné que chaque fonction doit être définie exactement une fois dans un programme ?

Ce problème affecte les bibliothèques, qu'elles soient statiques ou dynamiques. Par exemple, les fonctions des deux bibliothèques primes sont définies dans le fichier source primes.c , dont des copies binaires se trouvent dans chaque bibliothèque ; mais ces fonctions définies doivent être visibles pour un client de bibliothèque en C, qui est un programme séparé avec ses propres fichiers source.

Fournir une visibilité sur les fichiers est ce que les déclarations de fonction peuvent faire. Pour les exemples "primes", il existe un fichier d'en-tête nommé primes.h qui déclare les quatre fonctions à rendre visibles aux clients de la bibliothèque en C:

/** header file primes.h: function declarations **/
extern unsigned is_prime(unsigned);
extern void prime_factors(unsigned);
extern unsigned are_coprimes(unsigned, unsigned);
extern void goldbach(unsigned);

Ces déclarations servent d'interface en spécifiant la syntaxe d'invocation pour chaque fonction.

Pour la commodité du client, le fichier texte primes.h pourrait être stocké dans un répertoire sur le chemin de recherche du compilateur C. Les emplacements typiques sont /usr/include et /usr/local/include . Un client C serait #include ce fichier d'en-tête près du haut du code source du client. (Un fichier d'en-tête est ainsi importé dans la "tête" d'un autre fichier source.) Les fichiers d'en-tête C servent également d'entrée aux utilitaires (par exemple, bindgen de Rust ) qui permettent aux clients dans d'autres langages d'accéder à une bibliothèque C.

En résumé, une fonction de bibliothèque est définie exactement une fois mais déclarée partout où cela est nécessaire ; tout client de bibliothèque en C a besoin de la déclaration. Un fichier d'en-tête doit contenir des déclarations de fonction, mais pas des définitions de fonction. Si un fichier d'en-tête contenait des définitions, le fichier pourrait être inclus plusieurs fois dans un programme C, enfreignant ainsi la règle selon laquelle une fonction doit être définie exactement une fois dans un programme C.

Le code source de la bibliothèque

Vous trouverez ci-dessous le code source de deux bibliothèques. Ce code, le fichier d'en-tête et les deux exemples de clients sont disponibles sur mon site Web.

Les fonctions de la bibliothèque

#include <stdio.h>
#include <math.h>

extern unsigned is_prime(unsigned n) {
  if (n <= 3) return n > 1;                   /* 2 and 3 are prime */
  if (0 == (n % 2) || 0 == (n % 3)) return 0; /* multiples of 2 or 3 aren't */

  /* check that n is not a multiple of other values < n */
  unsigned i;
  for (i = 5; (i * i) <= n; i += 6)
    if (0 == (n % i) || 0 == (n % (i + 2))) return 0; /* not prime */

  return 1; /* a prime other than 2 or 3 */
}

extern void prime_factors(unsigned n) {
  /* list 2s in n's prime factorization */
  while (0 == (n % 2)) {  
    printf("%i ", 2);
    n /= 2;
  }

  /* 2s are done, the divisor is now odd */
  unsigned i;
  for (i = 3; i <= sqrt(n); i += 2) {
    while (0 == (n % i)) {
      printf("%i ", i);
      n /= i;
    }
  }

  /* one more prime factor? */
  if (n > 2) printf("%i", n);
}

/* utility function: greatest common divisor */
static unsigned gcd(unsigned n1, unsigned n2) {
  while (n1 != 0) {
    unsigned n3 = n1;
    n1 = n2 % n1;
    n2 = n3;
  }
  return n2;
}

extern unsigned are_coprimes(unsigned n1, unsigned n2) {
  return 1 == gcd(n1, n2);
}

extern void goldbach(unsigned n) {
  /* input errors */
  if ((n <= 2) || ((n & 0x01) > 0)) {
    printf("Number must be > 2 and even: %i is not.\n", n);
    return;
  }

  /* two simple cases: 4 and 6 */
  if ((4 == n) || (6 == n)) {
    printf("%i = %i + %i\n", n, n / 2, n / 2);
    return;
  }
 
  /* for n >= 8: multiple possibilities for many */
  unsigned i;
  for (i = 3; i < (n / 2); i++) {
    if (is_prime(i) && is_prime(n - i)) {
      printf("%i = %i + %i\n", n, i, n - i);
      /* if one pair is enough, replace this with break */
    }
  }
}

Ces fonctions servent d'eau au moulin de la bibliothèque. Les deux bibliothèques dérivent exactement du même code source, et le fichier d'en-tête primes.h est l'interface C pour les deux bibliothèques.

Construire les bibliothèques

Les étapes de construction et de publication des bibliothèques statiques et dynamiques diffèrent par quelques détails. Seules trois étapes sont nécessaires pour la bibliothèque statique et seulement deux autres pour la bibliothèque dynamique. Les étapes supplémentaires de construction de la bibliothèque dynamique reflètent la flexibilité supplémentaire de l'approche dynamique. Commençons par la bibliothèque statique.

Le fichier source de la bibliothèque primes.c est compilé dans un module objet. Voici la commande, avec le signe pourcentage comme invite système (les doubles signes pointus introduisent mes commentaires) :

% gcc -c primes.c ## step 1 static

Cela produit le fichier binaire primes.o , le module objet. Le drapeau -c signifie compiler uniquement.

L'étape suivante consiste à archiver le(s) module(s) objet(s) en utilisant Linux ar utilitaire :

% ar -cvq libprimes.a primes.o ## step 2 static

Les trois drapeaux -cvq sont l'abréviation de "create", "verbose" et "quick append" (au cas où de nouveaux fichiers doivent être ajoutés à une archive). Rappelons que le préfixe lib est standard, mais le nom de la bibliothèque est arbitraire. Bien sûr, le nom de fichier d'une bibliothèque doit être unique pour éviter les conflits.

L'archive est prête à être publiée :

% sudo cp libprimes.a /usr/local/lib ## step 3 static

La bibliothèque statique est maintenant accessible aux clients, dont des exemples sont à venir. (Le sudo est inclus pour garantir les droits d'accès corrects pour copier un fichier dans /usr/local/lib .)

La bibliothèque dynamique nécessite également un ou plusieurs modules objet pour le packaging :

% gcc primes.c -c -fpic ## step 1 dynamic

Le drapeau ajouté -fpic ordonne au compilateur de générer du code indépendant de la position, qui est un module binaire qui n'a pas besoin d'être chargé dans un emplacement mémoire fixe. Une telle flexibilité est essentielle dans un système de plusieurs bibliothèques dynamiques. Le module d'objet résultant est légèrement plus grand que celui généré pour la bibliothèque statique.

Voici la commande pour créer le fichier de bibliothèque unique à partir du ou des modules objet :

% gcc -shared -Wl,-soname,libshprimes.so -o libshprimes.so.1 primes.o ## step 2 dynamic

Le drapeau -shared indique que la bibliothèque est partagée (dynamique) plutôt que statique. Le -Wl flag introduit une liste d'options du compilateur, dont la première définit le soname de la bibliothèque dynamique , ce qui est obligatoire. Le soname spécifie d'abord le nom logique de la bibliothèque (libshprimes.so ) puis, en suivant le -o flag, le nom de fichier physique de la bibliothèque (libshprimes.so.1 ). L'objectif est de garder le nom logique constant tout en permettant au nom de fichier physique de changer avec les nouvelles versions. Dans cet exemple, le 1 à la fin du nom de fichier physique libshprimes.so.1 représente la première version de la bibliothèque. Les noms de fichiers logiques et physiques peuvent être identiques, mais la meilleure pratique consiste à avoir des noms distincts. Un client accède à la bibliothèque via son nom logique (dans ce cas, libshprimes.so ), comme je le préciserai sous peu.

L'étape suivante consiste à rendre la bibliothèque partagée facilement accessible aux clients en la copiant dans le répertoire approprié; par exemple, /usr/local/lib again:

% sudo cp libshprimes.so.1 /usr/local/lib ## step 3 dynamic

Un lien symbolique est maintenant établi entre le nom logique de la bibliothèque partagée (libshprimes.so ) et son nom de fichier physique complet (/usr/local/lib/libshprimes.so.1 ). Il est plus simple de donner la commande avec /usr/local/lib comme répertoire de travail :

% sudo ln --symbolic libshprimes.so.1 libshprimes.so ## step 4 dynamic

Le nom logique libshprimes.so ne doit pas changer, mais la cible du lien symbolique (libshrimes.so.1 ) peut être mis à jour selon les besoins pour les nouvelles implémentations de bibliothèque qui corrigent des bogues, améliorent les performances, etc.

La dernière étape (préventive) consiste à invoquer le ldconfig utilitaire qui configure le chargeur dynamique du système. Cette configuration garantit que le chargeur trouvera la bibliothèque nouvellement publiée :

% sudo ldconfig ## step 5 dynamic

La bibliothèque dynamique est maintenant prête pour les clients, y compris les deux exemples qui suivent.

Un client de bibliothèque C

L'exemple de client C est le testeur de programme, dont le code source commence par deux #include instructions :

#include <stdio.h>  /* standard input/output functions */
#include <primes.h> /* my library functions */

Les crochets angulaires autour des noms de fichiers indiquent que ces fichiers d'en-tête se trouvent sur le chemin de recherche du compilateur (dans le cas de primes.h , le répertoire /usr/local/include ). Sans ce #include , le compilateur se plaindrait des déclarations manquantes pour des fonctions telles que is_prime et prime_factors , qui sont publiés dans les deux bibliothèques. Soit dit en passant, le code source du programme de test n'a pas du tout besoin de changer pour tester chacune des deux bibliothèques.

En revanche, le fichier source de la bibliothèque (primes.c ) s'ouvre avec ces #include instructions :

#include <stdio.h>
#include <math.h>

Le fichier d'en-tête math.h est nécessaire car la fonction de bibliothèque prime_factors appelle la fonction mathématique sqrt dans la bibliothèque standard libm.so .

Pour référence, voici le code source du programme testeur :

Le programme testeur

#include <stdio.h>
#include <primes.h>

int main() {
  /* is_prime */
  printf("\nis_prime\n");
  unsigned i, count = 0, n = 1000;
  for (i = 1; i <= n; i++) {
    if (is_prime(i)) {
      count++;
      if (1 == (i % 100)) printf("Sample prime ending in 1: %i\n", i);
    }
  }
  printf("%i primes in range of 1 to a thousand.\n", count);

  /* prime_factors */
  printf("\nprime_factors\n");
  printf("prime factors of 12: ");
  prime_factors(12);
  printf("\n");
 
  printf("prime factors of 13: ");
  prime_factors(13);
  printf("\n");
 
  printf("prime factors of 876,512,779: ");
  prime_factors(876512779);
  printf("\n");

  /* are_coprimes */
  printf("\nare_coprime\n");
  printf("Are %i and %i coprime? %s\n",
         21, 22, are_coprimes(21, 22) ? "yes" : "no");
  printf("Are %i and %i coprime? %s\n",
         21, 24, are_coprimes(21, 24) ? "yes" : "no");

  /* goldbach */
  printf("\ngoldbach\n");
  goldbach(11);    /* error */
  goldbach(4);     /* small one */
  goldbach(6);     /* another */
  for (i = 100; i <= 150; i += 2) goldbach(i);

  return 0;
}

En compilant tester.c dans un exécutable, la partie délicate est l'ordre des drapeaux de lien. Rappelez-vous que les deux exemples de bibliothèques commencent par le préfixe lib , et chacun a l'extension habituelle :.a pour la bibliothèque statique libprimes.a et .so pour la bibliothèque dynamique libshprimes.so . Dans une spécification de liens, le préfixe lib et l'extension tombe. Un indicateur de lien commence par -l (L minuscule), et une commande de compilation peut contenir de nombreux indicateurs de lien. Voici la commande de compilation complète pour le programme testeur, en utilisant la bibliothèque dynamique comme exemple :

% gcc -o tester tester.c -lshprimes -lm

Le premier indicateur de lien identifie la bibliothèque libshprimes.so et le deuxième indicateur de lien identifie la bibliothèque mathématique standard libm.so .

L'éditeur de liens est paresseux, ce qui signifie que l'ordre des drapeaux de lien est important. Par exemple, l'inversion de l'ordre des spécifications de lien génère une erreur de compilation :

% gcc -o tester tester.c -lm -lshprimes ## danger!

Le drapeau qui lie à libm.so vient en premier, mais aucune fonction de cette bibliothèque n'est invoquée explicitement dans le programme du testeur ; par conséquent, l'éditeur de liens n'est pas lié au math.so bibliothèque. L'appel au sqrt la fonction de bibliothèque se produit uniquement dans les prime_factors fonction qui est maintenant contenue dans le libshprimes.so bibliothèque. L'erreur qui en résulte lors de la compilation du programme du testeur est :

primes.c: undefined reference to 'sqrt'

En conséquence, l'ordre des indicateurs de lien doit informer l'éditeur de liens que le sqrt fonction est nécessaire :

% gcc -o tester tester.c -lshprimes -lm ## -lshprimes 1st

L'éditeur de liens récupère l'appel à la fonction de bibliothèque sqrt dans le libshprimes.so bibliothèque et, par conséquent, fait le lien approprié vers la bibliothèque de mathématiques libm.so . Il existe une option plus compliquée pour la liaison qui prend en charge l'ordre lien-indicateur; dans ce cas, cependant, le moyen le plus simple consiste à organiser les indicateurs de lien de manière appropriée.

Voici quelques résultats d'une exécution du client testeur :

is_prime
Sample prime ending in 1: 101
Sample prime ending in 1: 401
...
168 primes in range of 1 to a thousand.

prime_factors
prime factors of 12: 2 2 3
prime factors of 13: 13
prime factors of 876,512,779: 211 4154089

are_coprime
Are 21 and 22 coprime? yes
Are 21 and 24 coprime? no

goldbach
Number must be > 2 and even: 11 is not.
4 = 2 + 2
6 = 3 + 3
...
32 =  3 + 29
32 = 13 + 19
...
100 =  3 + 97
100 = 11 + 89
...

Pour le goldbach fonction, même une valeur paire relativement petite (par exemple, 18) peut avoir plusieurs paires de nombres premiers qui s'additionnent (dans ce cas, 5 + 13 et 7 + 11). Ces multiples paires premières font partie des facteurs qui compliquent une tentative de preuve de la conjecture de Goldbach.

Conclusion avec un client Python

Contrairement à C, Python n'est pas un langage compilé statiquement, ce qui signifie que l'exemple de client Python doit accéder à la version dynamique plutôt qu'à la version statique de la bibliothèque primes. Pour ce faire, Python dispose de divers modules (standard et tiers) qui prennent en charge une interface de fonction étrangère (FFI), qui permet à un programme écrit dans un langage d'invoquer des fonctions écrites dans un autre. Python ctypes est une FFI standard et relativement simple qui permet au code Python d'appeler des fonctions C.

Toute FFI a des défis car les langages d'interface ont peu de chances d'avoir exactement les mêmes types de données. Par exemple, la bibliothèque primes utilise le type C unsigned int , que Python n'a pas ; les ctypes FFI mappe un C unsigned int à un Python int . Des quatre extern Fonctions C publiées dans la bibliothèque primes, deux se comportent mieux en Python avec des ctypes explicites configuration.

Les fonctions C prime_factors et goldbach avoir void au lieu d'un type de retour, mais ctypes par défaut remplace le C void avec le Python int . Lorsqu'elles sont appelées à partir du code Python, les deux fonctions C renvoient alors une valeur entière aléatoire (donc dénuée de sens) à partir de la pile. Cependant, ctypes peut être configuré pour que les fonctions renvoient None (type null de Python) à la place. Voici la configuration pour les prime_factors fonction :

primes.prime_factors.restype = None

Une instruction similaire gère le goldbach fonction.

La session interactive ci-dessous (en Python 3) montre que l'interface entre un client Python et la bibliothèque primes est simple :

>>> from ctypes import cdll

>>> primes = cdll.LoadLibrary("libshprimes.so") ## logical name

>>> primes.is_prime(13)
1
>>> primes.is_prime(12)
0

>>> primes.are_coprimes(8, 24)
0
>>> primes.are_coprimes(8, 25)
1

>>> primes.prime_factors.restype = None
>>> primes.goldbach.restype = None

>>> primes.prime_factors(72)
2 2 2 3 3

>>> primes.goldbach(32)
32 = 3 + 29
32 = 13 + 19

Les fonctions de la bibliothèque primes n'utilisent qu'un type de données simple, unsigned int . Si cette bibliothèque C utilisait des types compliqués tels que des structures, et si des pointeurs vers des structures étaient passés et renvoyés par des fonctions de bibliothèque, alors un FFI plus puissant que ctypes pourrait être préférable pour une interface fluide entre Python et C. Néanmoins, les ctypes montre qu'un client Python peut utiliser une bibliothèque écrite en C. En effet, la célèbre bibliothèque NumPy pour le calcul scientifique est écrite en C puis exposée dans une API Python de haut niveau.

La bibliothèque de nombres premiers simples et la bibliothèque NumPy avancée soulignent que C reste la lingua franca parmi les langages de programmation. Presque tous les langages peuvent parler à C et, via C, à tout autre langage qui parle à C. Python parle facilement à C et, comme autre exemple, Java peut faire de même lorsque Project Panama devient une alternative à Java Native Interface (JNI ).


Linux
  1. Un guide pratique pour apprendre awk

  2. Comprendre systemd au démarrage sous Linux

  3. Bibliothèques graphiques Linux 3D ?

  4. Guide du débutant sur la gestion des logiciels Linux avec RPM

  5. Utiliser une bibliothèque C dans Swift sous Linux

Comprendre les autorisations de fichiers Linux

Commandes Linux - Guide complet

Guide complet d'installation de Linux sur Chromebook

Un guide du débutant pour le dépannage du réseau sous Linux

Comprendre les processus sous Linux

Top 10+ des meilleurs logiciels de gestion de bibliothèque pour système Linux