Unterschied zwischen API und ABI


192

Ich bin neu in der Linux-Systemprogrammierung und bin beim Lesen der Linux-Systemprogrammierung auf API und ABI gestoßen .

Definition der API:

Eine API definiert die Schnittstellen, über die eine Software auf Quellenebene mit einer anderen kommuniziert.

Definition von ABI:

Während eine API eine Quellschnittstelle definiert, definiert ein ABI die binäre Schnittstelle auf niedriger Ebene zwischen zwei oder mehr Softwareteilen in einer bestimmten Architektur. Es definiert, wie eine Anwendung mit sich selbst interagiert, wie eine Anwendung mit dem Kernel interagiert und wie eine Anwendung mit Bibliotheken interagiert.

Wie kann ein Programm auf Quellenebene kommunizieren? Was ist eine Quellenebene? Hat es sowieso mit Quellcode zu tun? Oder wird die Quelle der Bibliothek in das Hauptprogramm aufgenommen?

Der einzige Unterschied, den ich kenne, ist, dass API hauptsächlich von Programmierern und ABI hauptsächlich vom Compiler verwendet wird.


2
Mit der Quellenebene meinen sie so etwas wie eine Include-Datei, um Funktionsdefinitionen
verfügbar zu machen

Antworten:


49

Die API wird von Menschen verwendet. Wir schreiben Quellcode. Wenn wir ein Programm schreiben und eine Bibliotheksfunktion verwenden möchten, schreiben wir Code wie:

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

und wir mussten wissen, dass es eine Methode gibt livenMyHills(), die einen langen ganzzahligen Parameter benötigt. Als Programmierschnittstelle wird alles im Quellcode ausgedrückt. Der Compiler wandelt dies in ausführbare Anweisungen um, die der Implementierung dieser Sprache auf diesem bestimmten Betriebssystem entsprechen. In diesem Fall führt dies zu einigen Operationen mit niedrigem Pegel an einer Audioeinheit. So werden bestimmte Bits und Bytes auf einige Hardware gespritzt. Zur Laufzeit gibt es also viele Aktionen auf Binärebene, die wir normalerweise nicht sehen.


308

API: Anwendungsprogrammschnittstelle

Dies ist der Satz öffentlicher Typen / Variablen / Funktionen, die Sie aus Ihrer Anwendung / Bibliothek verfügbar machen.

In C / C ++ ist dies das, was Sie in den Header-Dateien verfügbar machen, die Sie mit der Anwendung liefern.

ABI: Application Binary Interface

So erstellt der Compiler eine Anwendung.
Es definiert Dinge (ist aber nicht darauf beschränkt):

  • Wie Parameter an Funktionen (Register / Stapel) übergeben werden.
  • Wer bereinigt Parameter vom Stapel (Anrufer / Angerufene).
  • Wo der Rückgabewert für die Rückgabe platziert wird.
  • Wie sich Ausnahmen verbreiten.

17
Dies ist wahrscheinlich die beste prägnante Erklärung dafür, was ein ABI ist, die ich je gesehen habe. gj!
TerryP

3
Ihr müsst entscheiden, ob diese Antwort kurz oder detailliert ist. :)
jrok

1
@jrok: Dinge können präzise und detailliert sein, sie schließen sich nicht gegenseitig aus.
Martin York

@LokiAstari, ist der ABI also nicht auch eine API?
Pacerier

4
@ Pacerier: Sie sind beide Schnittstellen. Aber sie befinden sich auf verschiedenen Abstraktionsebenen. Die API befindet sich auf der Ebene der Anwendungsentwickler. ABI befindet sich auf Compilerebene (irgendwo, wo ein Anwendungsentwickler niemals hingeht).
Martin York

47

Ich stoße meistens auf diese Begriffe im Sinne einer API-inkompatiblen Änderung oder einer ABI-inkompatiblen Änderung.

Bei einer API-Änderung funktioniert Code, der mit der vorherigen Version kompiliert worden wäre, im Wesentlichen nicht mehr. Dies kann passieren, weil Sie einer Funktion ein Argument hinzugefügt oder den Namen von etwas geändert haben, auf das außerhalb Ihres lokalen Codes zugegriffen werden kann. Jedes Mal, wenn Sie einen Header ändern und Sie gezwungen sind, etwas in einer .c / .cpp-Datei zu ändern, haben Sie eine API-Änderung vorgenommen.

Bei einer ABI-Änderung funktioniert Code, der bereits für Version 1 kompiliert wurde, nicht mehr mit Version 2 einer Codebasis (normalerweise einer Bibliothek). Dies ist im Allgemeinen schwieriger nachzuverfolgen als eine API-inkompatible Änderung, da etwas so Einfaches wie das Hinzufügen einer virtuellen Methode zu einer Klasse ABI-inkompatibel sein kann.

Ich habe zwei äußerst nützliche Ressourcen gefunden, um herauszufinden, was ABI-Kompatibilität ist und wie sie erhalten werden kann:


4
+1 für den Hinweis auf ihre gegenseitige Ausschließlichkeit. Beispielsweise ist Javas Einführung des Schlüsselworts assert eine API-inkompatible und dennoch ABI-kompatible Änderung docs.oracle.com/javase/7/docs/technotes/guides/language/… .
Pacerier

Sie können Ihrem Ressourcenabschnitt "3.6. Inkompatible Bibliotheken" von tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html hinzufügen , in dem aufgeführt ist, was eine ABI-Änderung verursachen kann.
Demi-Lune

20

Dies sind meine Erklärungen für Laien:

  • API - denken Sie an includeDateien. Sie bieten Programmierschnittstellen.
  • ABI - Denken Sie an das Kernelmodul. Wenn Sie es auf einem Kernel ausführen, muss es sich darauf einigen, wie ohne Include-Dateien kommuniziert werden soll, dh als Low-Level-Binärschnittstelle.

13

Beispiel für eine minimal ausführbare API der gemeinsam genutzten Linux-Bibliothek im Vergleich zum ABI-Beispiel

Diese Antwort wurde aus meiner anderen Antwort extrahiert: Was ist eine Application Binary Interface (ABI)?aber ich hatte das Gefühl, dass es auch dieses direkt beantwortet und dass die Fragen keine Duplikate sind.

Im Zusammenhang mit gemeinsam genutzten Bibliotheken besteht die wichtigste Auswirkung eines "stabilen ABI" darin, dass Sie Ihre Programme nach dem Ändern der Bibliothek nicht neu kompilieren müssen.

Wie wir im folgenden Beispiel sehen werden, ist es möglich, den ABI zu ändern und Programme zu unterbrechen, obwohl die API unverändert bleibt.

Haupt c

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

#include "mylib.h"

int main(void) {
    mylib_mystrict *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

Kompiliert und läuft gut mit:

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

Nun sei angenommen , dass für v2 der Bibliothek, wir ein neues Feld hinzufügen möchten mylib_mystrictgenanntnew_field .

Wenn wir das Feld zuvor old_fieldwie folgt hinzugefügt haben :

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

und die Bibliothek wieder aufgebaut, aber nicht main.out , dann schlägt die Behauptung fehl!

Dies liegt daran, dass die Zeile:

myobject->old_field == 1

hatte eine Assembly generiert, die versucht, auf die allererste intStruktur zuzugreifen , die jetzt new_fieldanstelle der erwarteten istold_field .

Daher hat diese Änderung den ABI gebrochen.

Wenn wir jedoch new_fieldnachher hinzufügen old_field:

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

Dann greift die alte generierte Assembly immer noch auf die erste intStruktur zu, und das Programm funktioniert weiterhin, da wir den ABI stabil gehalten haben.

Hier ist ein vollautomatische Version dieses Beispiels auf GitHub .

Eine andere Möglichkeit, diesen ABI stabil zu halten, wäre die Behandlung mylib_mystructals undurchsichtige Struktur gewesen und nur über Methodenhelfer auf seine Felder zuzugreifen. Dies macht es einfacher, den ABI stabil zu halten, würde jedoch einen Leistungsaufwand verursachen, da wir mehr Funktionsaufrufe ausführen würden.

API gegen ABI

Im vorherigen Beispiel ist es interessant, dass die Zugabe new_fieldvorold_field , nur das ABI brach, aber nicht die API.

Was dies bedeutet, ist, dass wir unsere neu kompiliert hätten main.c Programm gegen die Bibliothek hätten, es trotzdem funktioniert hätte.

Wir hätten die API jedoch auch beschädigt, wenn wir zum Beispiel die Funktionssignatur geändert hätten:

mylib_mystruct* mylib_init(int old_field, int new_field);

da in diesem Fall main.c das Kompilieren ganz aufhören.

Semantische API gegen Programmier-API gegen ABI

Wir können API-Änderungen auch in einen dritten Typ einteilen: semantische Änderungen.

Zum Beispiel, wenn wir geändert hätten

myobject->old_field = old_field;

zu:

myobject->old_field = old_field + 1;

dann hätte dies weder API noch ABI kaputt gemacht, main.cwürde aber trotzdem kaputt gehen!

Dies liegt daran, dass wir die "menschliche Beschreibung" dessen, was die Funktion tun soll, geändert haben und nicht einen programmatisch wahrnehmbaren Aspekt.

Ich hatte gerade die philosophische Einsicht, dass die formale Verifizierung von Software in gewissem Sinne mehr von der "semantischen API" in eine "programmatisch überprüfbare API" verwandelt.

Semantische API vs Programmier-API

Wir können API-Änderungen auch in einen dritten Typ einteilen: semantische Änderungen.

Die semantische API ist normalerweise eine natürliche Beschreibung dessen, was die API tun soll, normalerweise in der API-Dokumentation enthalten.

Es ist daher möglich, die semantische API zu unterbrechen, ohne den Programmerstellung selbst zu unterbrechen.

Zum Beispiel, wenn wir geändert hätten

myobject->old_field = old_field;

zu:

myobject->old_field = old_field + 1;

dann hätte dies weder Programmier-API noch ABI kaputt gemacht, aber main.c die semantische API würde beschädigt.

Es gibt zwei Möglichkeiten, die Vertrags-API programmgesteuert zu überprüfen:

  • Testen Sie eine Reihe von Eckkoffern. Einfach zu machen, aber Sie könnten immer einen verpassen.
  • formale Überprüfung . Schwieriger zu machen, aber einen mathematischen Korrektheitsnachweis zu erbringen, der Dokumentation und Tests im Wesentlichen auf eine "menschlich" / maschinenprüfbare Weise vereinheitlicht! Solange deine formale Beschreibung natürlich keinen Fehler enthält ;-)

Getestet in Ubuntu 18.10, GCC 8.2.0.


2
Ihre Antwort war detailliert genug, um den Unterschied zwischen API und ABI zu verstehen. Danke dir!
Rakshith Ravi

9

( A pplication B inary I nterface) eine Spezifikation für eine spezifische Hardware - Plattform mit dem Betriebssystem kombiniert. Es ist ein Schritt über den API ( A pplication P rogram I nterface), welche die Anrufe von der Anwendung auf das Betriebssystem definiert. Der ABI definiert die API sowie die Maschinensprache für eine bestimmte CPU-Familie. Eine API stellt keine Laufzeitkompatibilität sicher, ein ABI jedoch, da sie die Maschinensprache oder das Laufzeitformat definiert.

Geben Sie hier die Bildbeschreibung ein

Höflichkeit


9

Lassen Sie mich ein konkretes Beispiel geben, wie sich ABI und API in Java unterscheiden.

Eine ABI-inkompatible Änderung ist, wenn ich eine Methode A # m () von a Stringals Argument in String...Argument ändere . Dies ist nicht ABI-kompatibel, da Sie den Code, der das aufruft, neu kompilieren müssen. Es ist jedoch API-kompatibel, da Sie es durch Neukompilieren ohne Codeänderungen im Aufrufer beheben können.

Hier ist das Beispiel. Ich habe meine Java-Bibliothek mit Klasse A.

// Version 1.0.0
public class A {
    public void m(String string) {
        System.out.println(string);
    }
}

Und ich habe eine Klasse, die diese Bibliothek verwendet

public class Main {
    public static void main(String[] args) {
        (new A()).m("string");
    }
}

Jetzt hat der Bibliotheksautor seine Klasse A zusammengestellt, ich habe meine Klasse Main zusammengestellt und alles funktioniert gut. Stellen Sie sich vor, eine neue Version von A kommt

// Version 2.0.0
public class A {
    public void m(String... string) {
        System.out.println(string[0]);
    }
}

Wenn ich nur die neue kompilierte Klasse A nehme und sie zusammen mit der zuvor kompilierten Klasse Main ablege, wird beim Versuch, die Methode aufzurufen, eine Ausnahme angezeigt

Exception in thread "main" java.lang.NoSuchMethodError: A.m(Ljava/lang/String;)V
        at Main.main(Main.java:5)

Wenn ich Main neu kompiliere, ist dies behoben und alles funktioniert wieder.


6

Ihr Programm (Quellcode) kann mit Modulen kompiliert werden, die die richtige API bereitstellen .

Ihr Programm (binär) kann auf Plattformen ausgeführt werden, die eine ordnungsgemäße ABI bereitstellen .

Die API beschränkt Typdefinitionen, Funktionsdefinitionen, Makros und manchmal globale Variablen, die eine Bibliothek verfügbar machen sollte.

ABI schränkt ein, was eine "Plattform" für die Ausführung Ihres Programms bereitstellen soll. Ich betrachte es gerne in 3 Ebenen:

  • Prozessorebene - der Befehlssatz, die Aufrufkonvention

  • Kernel-Ebene - die Systemaufrufkonvention, die spezielle Dateipfadkonvention (z. B. /procund /sysDateien unter Linux) usw.

  • Betriebssystemebene - das Objektformat, die Laufzeitbibliotheken usw.

Stellen Sie sich einen Cross-Compiler mit dem Namen vor arm-linux-gnueabi-gcc. "arm" gibt die Prozessorarchitektur an, "linux" gibt den Kernel an, "gnu" gibt an, dass seine Zielprogramme die libc von GNU als Laufzeitbibliothek verwenden, anders als arm-linux-androideabi-gccdie libc-Implementierung von Android.


1
Dies ist eine sehr prägnante Erklärung des Unterschieds zwischen ihnen und in einer sehr einzigartigen Perspektive.
Sajuuk

1

API- Application Programming Interfaceist eine Schnittstelle zur Kompilierungszeit , die vom Entwickler verwendet werden kann , um nicht projektbezogene Funktionen wie Bibliothek, Betriebssystem und Kernaufrufe im Quellcode zu verwenden

ABI[Info] -Application Binary Interfaceist eine Laufzeitschnittstelle , die von einem Programm während der Ausführung für die Kommunikation zwischen Komponenten im Maschinencode verwendet wird

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.