Diferencia entre API y ABI


192

Soy nuevo en la programación de sistemas Linux y me encontré con API y ABI mientras leía la programación del sistema Linux .

Definición de API:

Una API define las interfaces mediante las cuales una pieza de software se comunica con otra en el nivel fuente.

Definición de ABI:

Mientras que una API define una interfaz fuente, una ABI define la interfaz binaria de bajo nivel entre dos o más piezas de software en una arquitectura particular. Define cómo una aplicación interactúa consigo misma, cómo interactúa una aplicación con el núcleo y cómo interactúa una aplicación con las bibliotecas.

¿Cómo se puede comunicar un programa a nivel fuente? ¿Qué es un nivel fuente? ¿Está relacionado con el código fuente de todos modos? ¿O la fuente de la biblioteca se incluye en el programa principal?

La única diferencia que sé es que la API es utilizada principalmente por los programadores y ABI es utilizada principalmente por el compilador.


2
por nivel de fuente significan algo así como incluir archivo para exponer definiciones de funciones
Anycorn

Respuestas:


49

La API es lo que usan los humanos. Escribimos el código fuente. Cuando escribimos un programa y queremos usar alguna función de biblioteca, escribimos código como:

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

y necesitábamos saber que hay un método livenMyHills(), que toma un parámetro entero largo. Entonces, como interfaz de programación, todo se expresa en código fuente. El compilador convierte esto en instrucciones ejecutables que se ajustan a la implementación de este lenguaje en este sistema operativo en particular. Y en este caso resultan en algunas operaciones de bajo nivel en una unidad de audio. Entonces, bits y bytes particulares se arrojan a chorros en algún hardware. Entonces, en tiempo de ejecución, hay muchas acciones de nivel binario que generalmente no vemos.


309

API: interfaz del programa de aplicación

Este es el conjunto de tipos / variables / funciones públicas que expone de su aplicación / biblioteca.

En C / C ++, esto es lo que expone en los archivos de encabezado que envía con la aplicación.

ABI: interfaz binaria de aplicación

Así es como el compilador construye una aplicación.
Define cosas (pero no se limita a):

  • Cómo se pasan los parámetros a las funciones (registros / pila).
  • Quién limpia los parámetros de la pila (llamante / llamado).
  • Donde se coloca el valor de retorno para el retorno.
  • Cómo se propagan las excepciones.

17
Esta es probablemente la mejor explicación concisa de lo que es un ABI, que he visto; gj!
TerryP

3
Ustedes deben decidir si esta respuesta es concisa o detallada. :)
jrok

1
@jrok: Las cosas pueden ser concisas y detalladas, no son mutuamente excluyentes.
Martin York

@LokiAstari, ¿Entonces ABI no es realmente una API también?
Pacerier

44
@Pacerier: Ambas son interfaces. Pero están en diferentes niveles de abstracción. API está en el nivel de desarrollador de aplicaciones. ABI está en el nivel del compilador (en algún lugar al que nunca va un desarrollador de aplicaciones).
Martin York

47

Principalmente encuentro estos términos en el sentido de un cambio incompatible con API, o un cambio incompatible con ABI.

Un cambio de API es esencialmente donde el código que se habría compilado con la versión anterior ya no funcionará. Esto puede suceder porque agregó un argumento a una función o cambió el nombre de algo accesible fuera de su código local. Cada vez que cambia un encabezado y lo obliga a cambiar algo en un archivo .c / .cpp, ha realizado un cambio de API.

Un cambio ABI es donde el código que ya se ha compilado contra la versión 1 ya no funcionará con la versión 2 de una base de código (generalmente una biblioteca). En general, es más difícil realizar un seguimiento que los cambios incompatibles con la API, ya que algo tan simple como agregar un método virtual a una clase puede ser incompatible con ABI.

He encontrado dos recursos extremadamente útiles para descubrir qué es la compatibilidad ABI y cómo preservarla:


44
+1 por señalar su exclusividad mutua. Por ejemplo, la introducción de Java de la palabra clave afirmar es un cambio incompatible con API pero compatible con ABI docs.oracle.com/javase/7/docs/technotes/guides/language/… .
Pacerier

Puede agregar a su sección de recursos "3.6. Bibliotecas incompatibles" de tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html , que enumera lo que puede causar un cambio de ABI.
Demi-Lune

20

Esta es mi explicación laica:

  • API: piense en los includearchivos. Proporcionan interfaces de programación.
  • ABI: piense en el módulo del núcleo. Cuando lo ejecuta en algún núcleo, tiene que acordar cómo comunicarse sin incluir archivos, es decir, como una interfaz binaria de bajo nivel.

13

Ejemplo de API mínima ejecutable de biblioteca compartida de Linux vs ABI

Esta respuesta se ha extraído de mi otra respuesta: ¿Qué es una interfaz binaria de aplicación (ABI)? pero sentí que también responde directamente a esta, y que las preguntas no son duplicados.

En el contexto de las bibliotecas compartidas, la implicación más importante de "tener un ABI estable" es que no necesita volver a compilar sus programas después de que la biblioteca cambie.

Como veremos en el ejemplo a continuación, es posible modificar la ABI, interrumpiendo los programas, aunque la API no haya cambiado.

C Principal

#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 y funciona bien 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

Ahora, supongamos que para v2 de la biblioteca, queremos agregar un nuevo campo al mylib_mystrictllamado new_field.

Si agregamos el campo antes old_fieldcomo en:

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

y reconstruyó la biblioteca pero no main.out, ¡entonces la afirmación falla!

Esto se debe a que la línea:

myobject->old_field == 1

había generado un ensamblado que intenta acceder al primero intde la estructura, que ahora es en new_fieldlugar del esperado old_field.

Por lo tanto, este cambio rompió el ABI.

Sin embargo, si agregamos new_fielddespués old_field:

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

entonces el antiguo ensamblado generado aún accede al primero intde la estructura, y el programa aún funciona, porque mantuvimos el ABI estable.

Aquí hay una versión completamente automatizada de este ejemplo en GitHub .

Otra forma de mantener esta ABI estable habría sido tratarla mylib_mystructcomo una estructura opaca , y solo acceder a sus campos a través de métodos auxiliares. Esto hace que sea más fácil mantener estable la ABI, pero incurriría en una sobrecarga de rendimiento ya que haríamos más llamadas a funciones.

API vs ABI

En el ejemplo anterior, es interesante notar que agregar el new_fieldanterior old_fieldsolo rompió la ABI, pero no la API.

Lo que esto significa es que si hubiéramos compilado nuestro main.cprograma contra la biblioteca, habría funcionado de todos modos.

Sin embargo, también habríamos roto la API si hubiéramos cambiado, por ejemplo, la firma de la función:

mylib_mystruct* mylib_init(int old_field, int new_field);

ya que en ese caso, main.cdejaría de compilarse por completo.

API semántica vs API de programación vs ABI

También podemos clasificar los cambios de API en un tercer tipo: cambios semánticos.

Por ejemplo, si hubiéramos modificado

myobject->old_field = old_field;

a:

myobject->old_field = old_field + 1;

entonces esto no habría roto ni API ni ABI, ¡pero main.caún así se rompería!

Esto se debe a que cambiamos la "descripción humana" de lo que se supone que debe hacer la función en lugar de un aspecto programáticamente notable.

Acabo de tener la idea filosófica de que la verificación formal del software en cierto sentido mueve más de la "API semántica" a una "API verificable programáticamente".

API semántica vs API de programación

También podemos clasificar los cambios de API en un tercer tipo: cambios semánticos.

La API semántica, por lo general, es una descripción en lenguaje natural de lo que se supone que debe hacer la API, generalmente incluida en la documentación de la API.

Por lo tanto, es posible romper la API semántica sin romper la compilación del programa.

Por ejemplo, si hubiéramos modificado

myobject->old_field = old_field;

a:

myobject->old_field = old_field + 1;

entonces esto no habría roto ni la API de programación ni la ABI, pero main.cla API semántica se rompería.

Hay dos formas de verificar mediante programación la API del contrato:

  • prueba un montón de casos de esquina. Fácil de hacer, pero siempre puedes perderte uno.
  • la verificación formal . Es más difícil de hacer, pero produce una prueba matemática de corrección, esencialmente unificando la documentación y las pruebas en una forma "humana" / máquina verificable. Siempre que no haya un error en su descripción formal, por supuesto ;-)

Probado en Ubuntu 18.10, GCC 8.2.0.


2
La suya fue la respuesta que fue lo suficientemente detallada como para ayudarme a comprender la diferencia entre API y ABI. ¡Gracias!
Rakshith Ravi

9

( A plicación B de ciertas piezas I nterface) una especificación para una plataforma de hardware específica combinada con el sistema operativo. Es un paso más allá de la API ( A plicación P rograma I nterface), que define las llamadas desde la aplicación al sistema operativo. El ABI define la API más el lenguaje de máquina para una familia de CPU en particular. Una API no garantiza la compatibilidad del tiempo de ejecución, pero una ABI sí, porque define el lenguaje de la máquina, o el formato del tiempo de ejecución.

ingrese la descripción de la imagen aquí

Cortesía


9

Permítanme dar un ejemplo específico de cómo ABI y API difieren en Java.

Un cambio incompatible con ABI es si cambio un método A # m () de tomar Stringun argumento a String...argumento. Esto no es compatible con ABI porque debe volver a compilar el código que lo está llamando, pero es compatible con API, ya que puede resolverlo volviendo a compilar sin ningún cambio de código en la persona que llama.

Aquí está el ejemplo explicado. Tengo mi biblioteca Java con clase A

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

Y tengo una clase que usa esta biblioteca

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

Ahora, el autor de la biblioteca compiló su clase A, compilé mi clase Main y todo funciona bien. Imagina que viene una nueva versión de A

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

Si solo tomo la nueva clase A compilada y la dejo caer junto con la clase principal compilada anteriormente, obtengo una excepción al intentar invocar el método

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

Si recompilo Main, esto se arregla y todo vuelve a funcionar.


6

Su programa (código fuente) puede compilarse con módulos que proporcionan la API adecuada .

Su programa (binario) puede ejecutarse en plataformas que proporcionan una ABI adecuada .

La API restringe las definiciones de tipo, definiciones de función, macros, a veces variables globales que una biblioteca debe exponer.

ABI restringe lo que debe proporcionar una "plataforma" para que se ejecute su programa. Me gusta considerarlo en 3 niveles:

  • nivel de procesador: el conjunto de instrucciones, la convención de llamada

  • nivel de kernel: la convención de llamada del sistema, la convención especial de ruta de archivo (por ejemplo, los archivos /procy /sysen Linux), etc.

  • Nivel del sistema operativo: el formato del objeto, las bibliotecas de tiempo de ejecución, etc.

Considere un compilador cruzado llamado arm-linux-gnueabi-gcc. "arm" indica la arquitectura del procesador, "linux" indica el kernel, "gnu" indica que sus programas de destino usan la libc de GNU como biblioteca de tiempo de ejecución, diferente de la arm-linux-androideabi-gccque usa la implementación de la libc de Android.


1
Esta es una explicación muy sucinta de la diferencia entre ellos, y en una perspectiva muy singular.
Sajuuk

1

API- Application Programming Interfacees una interfaz de tiempo de compilación que el desarrollador puede usar para usar funciones que no son del proyecto, como la biblioteca, el sistema operativo, las llamadas principales en el código fuente

ABI[Acerca de] :Application Binary Interfacees unainterfaz de tiempo de ejecución que utiliza un programa durante la ejecución para la comunicación entre componentes en el código de máquina

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.