Diferença entre API e ABI


192

Eu sou novo na programação de sistemas linux e me deparei com API e ABI enquanto lia a programação de sistemas Linux .

Definição da API:

Uma API define as interfaces pelas quais um software se comunica com outro no nível de origem.

Definição de ABI:

Enquanto uma API define uma interface de origem, uma ABI define a interface binária de baixo nível entre duas ou mais partes de software em uma arquitetura específica. Ele define como um aplicativo interage consigo mesmo, como um aplicativo interage com o kernel e como um aplicativo interage com as bibliotecas.

Como um programa pode se comunicar no nível da fonte? O que é um nível de fonte? Está relacionado ao código fonte de qualquer maneira? Ou a fonte da biblioteca é incluída no programa principal?

A única diferença que eu sei é que a API é usada principalmente por programadores e a ABI é usada principalmente pelo compilador.


2
pelo nível da fonte que quer dizer algo como arquivo de inclusão para expor definições de funções
Anycorn

Respostas:


49

A API é o que os humanos usam. Nós escrevemos código fonte. Quando escrevemos um programa e queremos usar alguma função da biblioteca, escrevemos códigos como:

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

e precisávamos saber que existe um método livenMyHills(), que requer um parâmetro inteiro longo. Portanto, como uma interface de programação, tudo é expresso no código fonte. O compilador transforma isso em instruções executáveis ​​que estão de acordo com a implementação desse idioma neste sistema operacional específico. E, nesse caso, resultam em algumas operações de baixo nível em uma unidade de áudio. Bits e bytes específicos são esguichos em algum hardware. Portanto, em tempo de execução, há muitas ações no nível binário em andamento que geralmente não vemos.


309

API: interface do programa de aplicativos

Este é o conjunto de tipos / variáveis ​​/ funções públicas que você expõe do seu aplicativo / biblioteca.

Em C / C ++, é isso que você expõe nos arquivos de cabeçalho que você envia com o aplicativo.

ABI: Interface Binária do Aplicativo

É assim que o compilador cria um aplicativo.
Ele define as coisas (mas não se limita a):

  • Como os parâmetros são passados ​​para as funções (registradores / pilha).
  • Quem limpa os parâmetros da pilha (chamador / chamado).
  • Onde o valor de retorno é colocado para retorno.
  • Como as exceções se propagam.

17
Esta é provavelmente a melhor explicação concisa do que é uma ABI que eu já vi; gj!
TerryP

3
Vocês precisam decidir se essa resposta é concisa ou detalhada. :)
JROK

1
@jrok: As coisas podem ser concisas e detalhadas, elas não são mutuamente exclusivas.
Martin York

@LokiAstari, a ABI também não é realmente uma API?
Pacerier 03/04

4
@ Pacerier: São ambas interfaces. Mas eles estão em diferentes níveis de abstração. A API está no nível do desenvolvedor do aplicativo. A ABI está no nível do compilador (em algum lugar que um desenvolvedor de aplicativos nunca vai).
Martin York

47

Eu geralmente encontro esses termos no sentido de uma alteração incompatível com a API ou uma alteração incompatível com a ABI.

Uma mudança na API é essencialmente onde o código que seria compilado com a versão anterior não funcionará mais. Isso pode acontecer porque você adicionou um argumento a uma função ou alterou o nome de algo acessível fora do seu código local. Sempre que você altera um cabeçalho e obriga a alterar algo em um arquivo .c / .cpp, você faz uma alteração na API.

Uma alteração ABI é onde o código que já foi compilado na versão 1 não funcionará mais com a versão 2 de uma base de código (geralmente uma biblioteca). Geralmente, é mais complicado acompanhar as alterações incompatíveis com a API, pois algo tão simples como adicionar um método virtual a uma classe pode ser incompatível com ABI.

Encontrei dois recursos extremamente úteis para descobrir o que é a compatibilidade ABI e como preservá-la:


4
+1 por apontar sua exclusividade mútua. Por exemplo, a introdução da palavra-chave assert por Java é uma alteração de API incompatível, mas compatível com ABI docs.oracle.com/javase/7/docs/technotes/guides/language/… .
Pacerier 27/08/14

Você pode adicionar à seção de recursos "3.6. Bibliotecas incompatíveis" de tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html , que lista o que pode causar uma alteração na ABI.
Demi-Lune

20

Estas são as minhas explicações para os leigos:

  • API - pense nos includearquivos. Eles fornecem interfaces de programação.
  • ABI - pense no módulo do kernel. Quando você o executa em algum kernel, ele precisa concordar em como se comunicar sem incluir arquivos, como interface binária de baixo nível.

13

Exemplo de API mínima executável da biblioteca compartilhada Linux vs ABI

Esta resposta foi extraída da minha outra resposta: O que é uma interface binária de aplicativo (ABI)? mas senti que ele responde diretamente a este também, e que as perguntas não são duplicadas.

No contexto de bibliotecas compartilhadas, a implicação mais importante de "ter uma ABI estável" é que você não precisa recompilar seus programas após a alteração da biblioteca.

Como veremos no exemplo abaixo, é possível modificar a ABI, interrompendo os programas, mesmo que a API não seja alterada.

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 executa bem com:

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

Agora, suponha que, para a v2 da biblioteca, desejemos adicionar um novo campo ao mylib_mystrictchamado new_field.

Se adicionamos o campo antes old_fieldcomo em:

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

e reconstruiu a biblioteca, mas não main.out, a declaração falha!

Isso ocorre porque a linha:

myobject->old_field == 1

gerou um assembly que está tentando acessar o primeiro intda estrutura, que agora está em new_fieldvez do esperado old_field.

Portanto, essa mudança quebrou a ABI.

Se, no entanto, adicionarmos new_fielddepois old_field:

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

então, o antigo assembly gerado ainda acessa o primeiro intda estrutura e o programa ainda funciona, porque mantivemos a ABI estável.

Aqui está um versão totalmente automatizada deste exemplo no GitHub .

Outra maneira de manter essa ABI estável seria tratar mylib_mystructcomo uma estrutura opaca e acessar apenas seus campos por meio de auxiliares de método. Isso facilita a manutenção da ABI estável, mas acarretaria uma sobrecarga de desempenho, pois faríamos mais chamadas de função.

API vs ABI

No exemplo anterior, é interessante notar que adicionar o new_fieldantesold_field quebrou apenas a ABI, mas não a API.

O que isso significa é que, se tivéssemos recompilado nosso main.c programa contra a biblioteca, ele teria funcionado independentemente.

No entanto, também teríamos quebrado a API, se tivéssemos alterado, por exemplo, a assinatura da função:

mylib_mystruct* mylib_init(int old_field, int new_field);

pois nesse caso, main.c pararia de compilar completamente.

API semântica vs API de programação vs ABI

Também podemos classificar as alterações da API em um terceiro tipo: alterações semânticas.

Por exemplo, se tivéssemos modificado

myobject->old_field = old_field;

para:

myobject->old_field = old_field + 1;

então isso não main.cquebraria a API nem a ABI, mas ainda quebraria!

Isso ocorre porque alteramos a "descrição humana" do que a função deve fazer, em vez de um aspecto perceptível programaticamente.

Acabei de ter a visão filosófica de que a verificação formal de software , de certo modo, move mais a "API semântica" para uma "API programaticamente verificável".

API semântica vs API de programação

Também podemos classificar as alterações da API em um terceiro tipo: alterações semânticas.

A API semântica, geralmente é uma descrição da linguagem natural do que a API deve fazer, geralmente incluída na documentação da API.

Portanto, é possível quebrar a API semântica sem interromper a construção do programa.

Por exemplo, se tivéssemos modificado

myobject->old_field = old_field;

para:

myobject->old_field = old_field + 1;

então isso não main.cquebraria nem a API de programação nem a ABI, mas a API semântica seria interrompida.

Há duas maneiras de verificar programaticamente a API do contrato:

  • teste vários casos de canto. Fácil de fazer, mas você sempre pode perder uma.
  • verificação formal . Mais difícil de fazer, mas produz provas matemáticas de correção, essencialmente unificando documentação e testes de uma maneira "humana" / verificável por máquina! Contanto que não exista um bug em sua descrição formal, é claro ;-)

Testado no Ubuntu 18.10, GCC 8.2.0.


2
A sua foi a resposta que foi detalhada o suficiente para me ajudar a entender a diferença entre API e ABI. Obrigado!
Rakshith Ravi 15/09/19

9

( A APLICAÇÃO B inary eu nterface) Uma especificação de uma plataforma de hardware específico combinado com o sistema operativo. É um passo para além do API ( A APLICAÇÃO P rogram eu nterface), que define as chamadas a partir da aplicação para o sistema operativo. A ABI define a API mais a linguagem da máquina para uma família de CPU específica. Uma API não garante compatibilidade com o tempo de execução, mas uma ABI, porque define o formato da linguagem da máquina ou do tempo de execução.

insira a descrição da imagem aqui

Cortesia


9

Deixe-me dar um exemplo específico de como a ABI e a API diferem em Java.

Uma alteração incompatível com a ABI é se eu mudar um método A # m () de tomar a Stringcomo argumento para String...argumento. Isso não é compatível com ABI porque você precisa recompilar o código que está chamando isso, mas é compatível com a API, pois você pode resolvê-lo recompilando sem nenhuma alteração de código no chamador.

Aqui está o exemplo explicitado. Eu tenho minha biblioteca Java com classe A

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

E eu tenho uma classe que usa esta biblioteca

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

Agora, o autor da biblioteca compilou sua classe A, compilei minha classe Main e está tudo funcionando bem. Imagine uma nova versão de A

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

Se eu apenas pegar a nova classe compilada A e soltá-la junto com a classe compilada anteriormente Main, eu recebo uma exceção na tentativa de chamar o método

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

Se eu recompilar Main, isso está corrigido e tudo está funcionando novamente.


6

Seu programa (código fonte) pode ser compilado com módulos que fornecem API adequada .

Seu programa (binário) pode ser executado em plataformas que fornecem ABI adequadas .

A API restringe definições de tipos, definições de funções, macros, às vezes variáveis ​​globais que uma biblioteca deve expor.

A ABI restringe o que uma "plataforma" deve fornecer para que seu programa seja executado. Eu gosto de considerá-lo em 3 níveis:

  • nível do processador - o conjunto de instruções, a convenção de chamada

  • nível do kernel - a convenção de chamada do sistema, a convenção especial do caminho do arquivo (por exemplo, os arquivos /proce /sysno Linux), etc.

  • Nível do SO - o formato do objeto, as bibliotecas de tempo de execução, etc.

Considere um compilador cruzado chamado arm-linux-gnueabi-gcc. "arm" indica a arquitetura do processador, "linux" indica o kernel, "gnu" indica que seus programas de destino usam a libc do GNU como biblioteca de tempo de execução, diferente da arm-linux-androideabi-gccimplementação da libc do Android.


1
esta é uma explicação muito sucinta da diferença entre eles e em uma perspectiva muito única.
Sajuuk

1

API- Application Programming Interfaceé uma interface de tempo de compilação que pode ser usada pelo desenvolvedor para usar funcionalidades que não são do projeto, como biblioteca, SO, chamadas principais no código-fonte

ABI[About] -Application Binary Interfaceé umainterface de tempo de execução usada por um programa durante a execução da comunicação entre componentes no código da máquina

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.