Differenza tra API e ABI


194

Sono nuovo nella programmazione del sistema Linux e mi sono imbattuto in API e ABI durante la lettura di Programmazione di sistema Linux .

Definizione di API:

Un'API definisce le interfacce con cui un software comunica con un altro a livello di sorgente.

Definizione di ABI:

Mentre un'API definisce un'interfaccia di origine, un'ABI definisce l'interfaccia binaria di basso livello tra due o più parti di software su una particolare architettura. Definisce come un'applicazione interagisce con se stessa, come un'applicazione interagisce con il kernel e come un'applicazione interagisce con le librerie.

Come può un programma comunicare a livello sorgente? Che cos'è un livello sorgente? È comunque correlato al codice sorgente? O la fonte della libreria viene inclusa nel programma principale?

L'unica differenza che conosco è che l'API viene utilizzata principalmente dai programmatori e l'ABI viene utilizzata principalmente dal compilatore.


2
per livello sorgente significano qualcosa come includere il file per esporre le definizioni delle funzioni
Anycorn

Risposte:


49

L'API è ciò che gli umani usano. Scriviamo il codice sorgente. Quando scriviamo un programma e vogliamo usare alcune funzioni di libreria scriviamo codice come:

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

e dovevamo sapere che esiste un metodo livenMyHills(), che accetta un parametro intero lungo. Quindi come interfaccia di programmazione è tutto espresso in codice sorgente. Il compilatore trasforma questo in istruzioni eseguibili che sono conformi all'implementazione di questo linguaggio su questo particolare sistema operativo. E in questo caso comportano alcune operazioni di basso livello su un'unità audio. Quindi alcuni bit e byte particolari vengono schizzati in alcuni componenti hardware. Quindi in fase di esecuzione ci sono molte azioni a livello binario che normalmente non vediamo.


310

API: interfaccia del programma applicativo

Questo è l'insieme di tipi / variabili / funzioni pubbliche che esponi dalla tua applicazione / libreria.

In C / C ++ questo è ciò che esponi nei file di intestazione forniti con l'applicazione.

ABI: interfaccia binaria dell'applicazione

Ecco come il compilatore crea un'applicazione.
Definisce le cose (ma non è limitato a):

  • Come i parametri vengono passati alle funzioni (registri / stack).
  • Chi pulisce i parametri dallo stack (chiamante / chiamata).
  • Dove viene inserito il valore restituito per il reso.
  • Come si propagano le eccezioni.

17
Questa è probabilmente la migliore spiegazione concisa di cosa sia un ABI, che io abbia mai visto; gj!
TerryP

3
Ragazzi, dovete decidere se questa risposta è concisa o dettagliata. :)
jrok

1
@jrok: le cose possono essere concise e dettagliate e non si escludono a vicenda.
Martin York,

@LokiAstari, Quindi l'ABI non è in realtà anche un'API?
Pacerier,

4
@Pacerier: sono entrambe le interfacce. Ma sono a diversi livelli di astrazione. L'API è a livello di sviluppatore dell'applicazione. ABI è a livello di compilatore (da qualche parte uno sviluppatore di applicazioni non va mai).
Martin York,

47

Mi imbatto principalmente in questi termini nel senso di una modifica incompatibile API o di una modifica incompatibile ABI.

Una modifica API è essenzialmente quella in cui il codice che sarebbe stato compilato con la versione precedente non funzionerà più. Questo può accadere perché hai aggiunto un argomento a una funzione o hai cambiato il nome di qualcosa accessibile al di fuori del tuo codice locale. Ogni volta che cambi un'intestazione e ti costringe a cambiare qualcosa in un file .c / .cpp, hai apportato una modifica all'API.

Una modifica ABI è dove il codice che è già stato compilato rispetto alla versione 1 non funzionerà più con la versione 2 di una base di codice (di solito una libreria). In genere, è più difficile tenere traccia delle modifiche incompatibili con l'API poiché qualcosa di semplice come l'aggiunta di un metodo virtuale a una classe può essere incompatibile con l'ABI.

Ho trovato due risorse estremamente utili per capire cos'è la compatibilità ABI e come preservarla:


4
+1 per sottolineare la reciproca esclusività. Ad esempio, l'introduzione di Java della parola chiave assert è una modifica API incompatibile ma compatibile ABI docs.oracle.com/javase/7/docs/technotes/guides/language/… .
Pacerier,

È possibile aggiungere alla sezione delle risorse "3.6. Librerie incompatibili" di tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html , che elenca cosa può causare un cambiamento ABI.
Demi-Lune,

20

Queste sono le spiegazioni dei miei profani:

  • API: pensaci include file. Forniscono interfacce di programmazione.
  • ABI - pensa al modulo del kernel. Quando lo esegui su alcuni kernel, deve concordare su come comunicare senza includere file, ovvero come interfaccia binaria di basso livello.

13

API condivisa minima libreria condivisa Linux vs esempio ABI

Questa risposta è stata estratta dall'altra mia risposta: che cos'è un'applicazione binaria di interfaccia (ABI)?ma ho sentito che risponde direttamente anche a questo, e che le domande non sono duplicate.

Nel contesto delle librerie condivise, l'implicazione più importante di "avere un'ABI stabile" è che non è necessario ricompilare i programmi dopo che la libreria è cambiata.

Come vedremo nell'esempio seguente, è possibile modificare l'ABI, rompendo i programmi, anche se l'API è invariata.

main.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

Compila e funziona bene con:

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

Supponiamo ora che per v2 della libreria, vogliamo aggiungere un nuovo campo da mylib_mystrictchiamare new_field.

Se abbiamo aggiunto il campo prima old_fieldcome in:

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

e ricostruito la libreria ma non main.out, quindi l'asserzione fallisce!

Questo perché la linea:

myobject->old_field == 1

aveva generato un assembly che sta tentando di accedere al primo intdella struttura, che ora è new_fieldinvece previsto old_field.

Pertanto questo cambiamento ha rotto l'ABI.

Se, tuttavia, aggiungiamo new_fielddopo old_field:

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

quindi il vecchio assembly generato accede ancora al primo intdella struttura e il programma funziona ancora, perché abbiamo mantenuto l'ABI stabile.

Ecco una versione completamente automatizzata di questo esempio su GitHub .

Un altro modo per mantenere stabile questo ABI sarebbe stato quello di trattare mylib_mystructcome una struttura opaca , e accedere ai suoi campi solo attraverso i metodi di supporto. Ciò rende più semplice mantenere stabile l'ABI, ma comporterebbe un sovraccarico di prestazioni poiché faremmo più chiamate di funzione.

API vs ABI

Nell'esempio precedente, è interessante notare che l'aggiunta di new_fieldprima ha old_fieldsolo rotto l'ABI, ma non l'API.

Ciò significa che se avessimo ricompilato il nostro main.cprogramma con la libreria, avrebbe funzionato a prescindere.

Avremmo anche rotto l'API se avessimo cambiato ad esempio la firma della funzione:

mylib_mystruct* mylib_init(int old_field, int new_field);

poiché in quel caso, main.csmetterebbe di compilare del tutto.

API semantica vs API di programmazione vs ABI

Possiamo anche classificare le modifiche API in un terzo tipo: modifiche semantiche.

Ad esempio, se avessimo modificato

myobject->old_field = old_field;

per:

myobject->old_field = old_field + 1;

allora questo non avrebbe rotto né API né ABI, ma main.cavrebbe comunque rotto!

Questo perché abbiamo cambiato la "descrizione umana" di ciò che la funzione dovrebbe fare piuttosto che un aspetto evidente a livello di programmazione.

Ho appena avuto la visione filosofica che la verifica formale del software in un certo senso sposta di più l '"API semantica" in un "API verificabile a livello di programmazione".

API semantica vs API di programmazione

Possiamo anche classificare le modifiche API in un terzo tipo: modifiche semantiche.

L'API semantica, di solito è una descrizione in linguaggio naturale di ciò che l'API dovrebbe fare, di solito inclusa nella documentazione dell'API.

È quindi possibile interrompere l'API semantica senza interrompere la creazione del programma stesso.

Ad esempio, se avessimo modificato

myobject->old_field = old_field;

per:

myobject->old_field = old_field + 1;

allora questo non avrebbe rotto né l'API di programmazione, né l'ABI, ma main.c l'API semantica si sarebbe rotta.

Esistono due modi per controllare a livello di programmazione l'API del contratto:

  • prova un mucchio di valigie angolari. Facile da fare, ma potresti sempre perderne uno.
  • verifica formale . Più difficile da fare, ma produce prove matematiche di correttezza, essenzialmente unificando la documentazione e i test in modo "umano" / verificabile dalla macchina! Fintanto che non c'è un bug nella tua descrizione formale ovviamente ;-)

Testato in Ubuntu 18.10, GCC 8.2.0.


2
La tua è stata la risposta che è stata sufficientemente dettagliata per aiutarmi a capire la differenza tra API e ABI. Grazie!
Rakshith Ravi,

9

( A pplicazione B fabbrica di alcune componenti I nterface) Una specifica per una piattaforma hardware specifico in combinazione con il sistema operativo. È un passo oltre l'API ( A pplicazione P rogram I nterface), che definisce le chiamate dalla applicazione al sistema operativo. L'ABI definisce l'API più il linguaggio macchina per una particolare famiglia di CPU. Un'API non garantisce la compatibilità del runtime, ma un ABI lo fa, poiché definisce il linguaggio della macchina, o il runtime, il formato.

inserisci qui la descrizione dell'immagine

Cortesia


9

Vorrei fare un esempio specifico di come ABI e API differiscono in Java.

Una modifica incompatibile ABI è se cambio un metodo A # m () dall'aver preso Stringcome argomento a String...argomento. Questo non è compatibile ABI perché devi ricompilare il codice che lo chiama, ma è compatibile con l'API in quanto puoi risolverlo ricompilando senza alcuna modifica del codice nel chiamante.

Ecco l'esempio spiegato. Ho la mia libreria Java con classe A

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

E ho una classe che utilizza questa libreria

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

Ora, l'autore della biblioteca ha compilato la sua classe A, ho compilato la mia classe Main e tutto funziona bene. Immagina che arrivi una nuova versione di A.

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

Se prendo solo la nuova classe A compilata e la rilasciamo insieme alla classe Main precedentemente compilata, ottengo un'eccezione nel tentativo di invocare il metodo

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

Se ricompilo Main, questo è risolto e tutto funziona di nuovo.


6

Il tuo programma (codice sorgente) può essere compilato con moduli che forniscono l' API corretta .

Il tuo programma (binario) può essere eseguito su piattaforme che forniscono ABI corretto .

L'API limita le definizioni dei tipi, le definizioni delle funzioni, le macro, a volte le variabili globali che una libreria dovrebbe esporre.

ABI limita ciò che una "piattaforma" dovrebbe fornire per l'esecuzione del programma. Mi piace considerarlo in 3 livelli:

  • livello del processore: il set di istruzioni, la convenzione di chiamata

  • livello del kernel - la convenzione di chiamata del sistema, la convenzione del percorso del file speciale (ad es. file /proce /sysin Linux), ecc.

  • Livello del sistema operativo: il formato dell'oggetto, le librerie di runtime, ecc.

Considera un cross-compilatore chiamato arm-linux-gnueabi-gcc. "arm" indica l'architettura del processore, "linux" indica il kernel, "gnu" indica che i suoi programmi di destinazione usano libc di GNU come libreria di runtime, diversa dalla arm-linux-androideabi-gccquale usano l'implementazione libc di Android.


1
questa è una spiegazione molto succinta della differenza tra loro e in una prospettiva davvero unica.
Sajuuk,

1

API- Application Programming Interfaceè un'interfaccia di compilazione che può essere utilizzata dallo sviluppatore per utilizzare funzionalità non di progetto come libreria, sistema operativo, chiamate principali nel codice sorgente

ABI[Informazioni] -Application Binary Interfaceè un'interfaccia di runtime che viene utilizzata da un programma durante l'esecuzione per la comunicazione tra i componenti nel codice macchina

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.