298 votes

Différence entre API et ABI

Je suis novice en matière de programmation de systèmes Linux et j'ai rencontré les notions d'API et d'ABI au cours de mes lectures. Programmation du système Linux .

Définition de l'API :

Une API définit les interfaces par lesquelles une pièce de logiciel communique avec un autre au niveau de la source.

Définition de l'ABI :

Alors qu'une API définit une source une interface source, une ABI définit l'interface binaire de bas niveau entre deux ou plus de logiciels sur une architecture particulière. Elle définit comment une application interagit avec elle-même, comment une application interagit avec le noyau, et comment une l'application interagit avec les bibliothèques.

Comment un programme peut-il communiquer au niveau de la source ? Qu'est-ce qu'un niveau source ? Est-il lié au code source d'une manière ou d'une autre ? Ou la source de la bibliothèque est incluse dans le programme principal ?

La seule différence que je connais est que l'API est surtout utilisée par les programmeurs et que l'ABI est surtout utilisée par un compilateur.

3 votes

Par niveau de source, ils veulent dire quelque chose comme un fichier d'inclusion pour exposer les définitions de fonctions

1 votes

424voto

Loki Astari Points 116129

API : Interface de programme d'application

Il s'agit de l'ensemble des types/variables/fonctions publics que vous exposez à partir de votre application/librairie.

En C/C++, c'est ce que vous exposez dans les fichiers d'en-tête que vous livrez avec l'application.

ABI : Interface binaire d'application

C'est ainsi que le compilateur construit une application.
Il définit des choses (sans s'y limiter) :

  • Comment les paramètres sont passés aux fonctions (registres/pile).
  • Qui nettoie les paramètres de la pile (appelant/callee).
  • Où la valeur de retour est placée pour le retour.
  • Comment les exceptions se propagent.

28 votes

C'est probablement le meilleur concis l'explication de ce qu'est un ABI, que j'ai jamais vue ; gj !

4 votes

Vous devez décider si cette réponse est concise. ou détaillée. :)

2 votes

@jrok : Les choses peuvent être concises et détaillées, elles ne sont pas mutuellement exclusives.

65voto

djna Points 34761

L'API est ce que les humains utilisent. Nous écrivons le code source. Lorsque nous écrivons un programme et que nous voulons utiliser une fonction de la bibliothèque, nous écrivons du code comme suit :

 long howManyDecibels = 123L;
 int ok = livenMyHills( howManyDecibels);

et nous avions besoin de savoir qu'il existe une méthode livenMyHills() qui prend un paramètre entier long. Ainsi, en tant qu'interface de programmation, tout est exprimé en code source. Le compilateur le transforme en instructions exécutables conformes à l'implémentation de ce langage sur ce système d'exploitation particulier. Et dans ce cas, cela se traduit par des opérations de bas niveau sur une unité audio. Donc, des bits et des octets particuliers sont envoyés vers du matériel. Donc, au moment de l'exécution, il y a beaucoup d'actions de niveau binaire qui se passent et que nous ne voyons pas habituellement.

60voto

jkerian Points 8328

Je rencontre le plus souvent ces termes dans le sens d'une modification incompatible avec l'API ou d'une modification incompatible avec l'ABI.

Un changement d'API signifie essentiellement que le code qui aurait été compilé avec la version précédente ne fonctionnera plus. Cela peut arriver parce que vous avez ajouté un argument à une fonction, ou changé le nom d'un élément accessible en dehors de votre code local. Chaque fois que vous modifiez un en-tête et que cela vous oblige à changer quelque chose dans un fichier .c/.cpp, vous avez effectué un changement d'API.

Un changement d'ABI signifie que le code qui a déjà été compilé avec la version 1 ne fonctionnera plus avec la version 2 d'une base de code (généralement une bibliothèque). Il est généralement plus difficile de suivre ce type de changement que les changements incompatibles avec l'API, car quelque chose d'aussi simple que l'ajout d'une méthode virtuelle à une classe peut être incompatible avec l'ABI.

J'ai trouvé deux ressources extrêmement utiles pour comprendre ce qu'est la compatibilité ABI et comment la préserver :

5 votes

+1 pour avoir souligné leur exclusivité mutuelle. Par exemple, l'introduction par Java du mot-clé assert est un changement incompatible avec l'API mais compatible avec l'ABI. docs.oracle.com/javase/7/docs/technotes/guides/language/ .

0 votes

Vous pourriez ajouter à vos ressources la section "3.6. Bibliothèques incompatibles" de tldp.org/HOWTO/Programme-Bibliothèque-HOWTO/bibliothèques-partages.html qui énumère les causes possibles d'un changement d'ABI.

52voto

Ciro Santilli Points 3341

Exemple d'API minimale exécutable de la bibliothèque partagée Linux par rapport à l'ABI

Cette réponse a été extraite de mon autre réponse : Qu'est-ce qu'une interface binaire d'application (ABI) ? mais j'ai estimé qu'elle répondait aussi directement à celle-ci, et que les questions ne sont pas redondantes.

Dans le contexte des bibliothèques partagées, l'implication la plus importante de "avoir une ABI stable" est que vous n'avez pas besoin de recompiler vos programmes après un changement de bibliothèque.

Comme nous allons le voir dans l'exemple ci-dessous, il est possible de modifier l'ABI, en cassant des programmes, même si l'API reste inchangée.

main.c

#include <assert.h>
#include <stdlib.h>

#include "mylib.h"

int main(void) {
    mylib_mystruct *myobject = mylib_init(1);
    assert(myobject->old_field == 1);
    free(myobject);
    return EXIT_SUCCESS;
}

mylib.c

#include <stdlib.h>

#include "mylib.h"

mylib_mystruct* mylib_init(int old_field) {
    mylib_mystruct *myobject;
    myobject = malloc(sizeof(mylib_mystruct));
    myobject->old_field = old_field;
    return myobject;
}

mylib.h

#ifndef MYLIB_H
#define MYLIB_H

typedef struct {
    int old_field;
} mylib_mystruct;

mylib_mystruct* mylib_init(int old_field);

#endif

Compile et fonctionne bien avec :

cc='gcc -pedantic-errors -std=c89 -Wall -Wextra'
$cc -fPIC -c -o mylib.o mylib.c
$cc -L . -shared -o libmylib.so mylib.o
$cc -L . -o main.out main.c -lmylib
LD_LIBRARY_PATH=. ./main.out

Maintenant, supposons que pour la version 2 de la bibliothèque, nous voulons ajouter un nouveau champ à mylib_mystruct appelé new_field .

Si nous avons ajouté le champ avant old_field comme dans :

typedef struct {
    int new_field;
    int old_field;
} mylib_mystruct;

et reconstruit la bibliothèque mais pas main.out alors l'assert échoue !

C'est parce que la ligne :

myobject->old_field == 1

a généré un assemblage qui essaye d'accéder au tout premier int de la structure, qui est maintenant new_field au lieu de l'attendu old_field .

Ce changement a donc cassé l'ABI.

Si, cependant, nous ajoutons new_field après old_field :

typedef struct {
    int old_field;
    int new_field;
} mylib_mystruct;

alors l'ancien assemblage généré accède toujours au premier int de la structure, et le programme fonctionne toujours, parce que nous avons gardé l'ABI stable.

Voici un version entièrement automatisée de cet exemple sur GitHub .

Une autre façon de garder cette ABI stable aurait été de traiter mylib_mystruct comme un structure opaque et n'accède à ses champs qu'au moyen d'aides de méthode. Il est ainsi plus facile de maintenir la stabilité de l'ABI, mais cela entraînerait un surcoût en termes de performances, car il y aurait davantage d'appels de fonctions.

API et ABI

Dans l'exemple précédent, il est intéressant de noter que l'ajout de l'élément new_field avant old_field a seulement cassé l'ABI, mais pas l'API.

Ce que cela signifie, c'est que si nous avions recompilé notre main.c contre la bibliothèque, ça aurait quand même fonctionné.

Mais nous aurions également brisé l'API si nous avions modifié, par exemple, la signature de la fonction :

mylib_mystruct* mylib_init(int old_field, int new_field);

puisque dans ce cas, main.c cesserait complètement de compiler.

API sémantique, API de programmation et ABI

Nous pouvons également classer les changements d'API dans un troisième type : les changements sémantiques.

Par exemple, si nous avions modifié

myobject->old_field = old_field;

à :

myobject->old_field = old_field + 1;

alors cela n'aurait brisé ni l'API ni l'ABI, mais main.c se briserait quand même !

Cela est dû au fait que nous avons modifié la "description humaine" de ce que la fonction est censée faire plutôt qu'un aspect perceptible par le programme.

J'ai juste eu l'intuition philosophique que vérification formelle des logiciels déplace en quelque sorte l'"API sémantique" vers une "API vérifiable par programme".

API sémantique et API de programmation

Nous pouvons également classer les changements d'API dans un troisième type : les changements sémantiques.

L'API sémantique est généralement une description en langage naturel de ce que l'API est censée faire, généralement incluse dans la documentation de l'API.

Il est donc possible de briser l'API sémantique sans briser la construction du programme elle-même.

Par exemple, si nous avions modifié

myobject->old_field = old_field;

à :

myobject->old_field = old_field + 1;

alors cela n'aurait brisé ni l'API de programmation, ni l'ABI, mais main.c l'API sémantique se briserait.

Il existe deux façons de vérifier par programme l'API du contrat :

  • tester un certain nombre de cas particuliers. C'est facile à faire, mais vous risquez toujours d'en manquer un.
  • vérification formelle . Plus difficile à faire, mais produit une preuve mathématique de l'exactitude, unifiant essentiellement la documentation et les tests d'une manière "humaine" / vérifiable par la machine ! Tant qu'il n'y a pas de bogue dans votre description formelle, bien sûr ;-)

Testé dans Ubuntu 18.10, GCC 8.2.0.

29voto

Anycorn Points 20521

Voici mes explications de profane :

  • API - pensez à include les fichiers. Ils fournissent des interfaces de programmation.
  • ABI - pensez au module du noyau. Lorsque vous l'exécutez sur un noyau, il doit se mettre d'accord sur la manière de communiquer sans fichiers d'inclusion, c'est-à-dire en tant qu'interface binaire de bas niveau.

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