Разница между API и ABI

194

Я новичок в системном программировании Linux, и я столкнулся с API и ABI, читая системное программирование Linux .

Определение API:

API определяет интерфейсы, с помощью которых одна часть программного обеспечения взаимодействует с другой на уровне источника.

Определение ABI:

Принимая во внимание, что API определяет исходный интерфейс, ABI определяет низкоуровневый двоичный интерфейс между двумя или более частями программного обеспечения в конкретной архитектуре. Он определяет, как приложение взаимодействует с самим собой, как приложение взаимодействует с ядром и как приложение взаимодействует с библиотеками.

Как программа может общаться на уровне источника? Что такое уровень источника? Это связано с исходным кодом в любом случае? Или источник библиотеки включается в основную программу?

Единственное отличие, которое я знаю, это то, что API в основном используется программистами, а ABI - компилятором.

Searock
источник
2
под уровнем источника они подразумевают что-то вроде включаемого файла для предоставления определений функций
Anycorn
1
Также см stackoverflow.com/q/2171177/632951
Pacerier

Ответы:

49

API - это то, что люди используют. Мы пишем исходный код. Когда мы пишем программу и хотим использовать библиотечную функцию, мы пишем такой код:

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

и нам нужно было знать, что существует метод livenMyHills(), который принимает длинный целочисленный параметр. Так что как интерфейс программирования все это выражается в исходном коде. Компилятор превращает это в исполняемые инструкции, которые соответствуют реализации этого языка в данной операционной системе. И в этом случае приводят к некоторым операциям низкого уровня на аудиоустройстве. Таким образом, определенные биты и байты выделяются на некотором оборудовании. Так что во время выполнения происходит множество действий на двоичном уровне, которые мы обычно не видим.

DJNA
источник
310

API: интерфейс прикладных программ

Это набор открытых типов / переменных / функций, которые вы выставляете из своего приложения / библиотеки.

В C / C ++ это то, что вы выставляете в заголовочных файлах, которые поставляются вместе с приложением.

ABI: двоичный интерфейс приложения

Вот как компилятор создает приложение.
Он определяет вещи (но не ограничивается ими):

  • Как параметры передаются в функции (регистры / стек).
  • Кто убирает параметры из стека (вызывающий / вызываемый).
  • Где возвращаемое значение помещается для возврата.
  • Как распространяются исключения.
Мартин Йорк
источник
17
Это, вероятно, лучшее краткое объяснение того, что такое ABI, которое я когда-либо видел; GJ!
TerryP
3
Вы, ребята, должны решить, является ли этот ответ кратким или подробным. :)
Jrok
1
@jrok: Вещи могут быть краткими и подробными, они не являются взаимоисключающими.
Мартин Йорк,
@LokiAstari, разве ABI на самом деле тоже не API?
Pacerier
4
@Pacerier: оба интерфейса. Но они на разных уровнях абстракции. API находится на уровне разработчика приложений. ABI находится на уровне компилятора (где-то разработчик приложений никогда не идет).
Мартин Йорк,
47

Я в основном сталкиваюсь с этими терминами в смысле API-несовместимых изменений или ABI-несовместимых изменений.

Изменение API, по сути, происходит там, где код, который был бы скомпилирован с предыдущей версией, больше не будет работать. Это может произойти, потому что вы добавили аргумент в функцию или изменили имя чего-то доступного за пределами вашего локального кода. Каждый раз, когда вы изменяете заголовок, и это заставляет вас что-то менять в файле .c / .cpp, вы вносите изменения в API.

Изменение ABI - это то, где код, который уже был скомпилирован для версии 1, больше не будет работать с версией 2 кодовой базы (обычно это библиотека). Как правило, сложнее отслеживать изменения, несовместимые с API, поскольку такая простая вещь, как добавление виртуального метода в класс, может быть несовместимой с ABI.

Я нашел два чрезвычайно полезных ресурса для выяснения, что такое совместимость с ABI и как ее сохранить:

jkerian
источник
4
+1 за указание на их взаимную исключительность. Например, введение Java ключевого слова assert является несовместимым с API, но совместимым с ABI изменением docs.oracle.com/javase/7/docs/technotes/guides/language/… .
Pacerier
Вы можете добавить в раздел ресурсов «3.6. Несовместимые библиотеки» по адресу tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html , в котором перечислены причины , которые могут вызвать изменение ABI.
Деми-Лун
20

Это мои объяснения дилетанта:

  • API - думать о includeфайлах. Они предоставляют программные интерфейсы.
  • ABI - думать о модуле ядра. Когда вы запускаете его на каком-то ядре, оно должно договориться о том, как общаться без включаемых файлов, то есть как низкоуровневый двоичный интерфейс.
Anycorn
источник
13

API-интерфейс совместно используемой библиотеки Linux с примером ABI

Этот ответ был извлечен из моего другого ответа: Что такое двоичный интерфейс приложения (ABI)? но я чувствовал, что это прямо отвечает и на этот вопрос, и что вопросы не являются дубликатами.

В контексте разделяемых библиотек наиболее важным следствием «наличия стабильного ABI» является то, что вам не нужно перекомпилировать свои программы после изменений библиотеки.

Как мы увидим в приведенном ниже примере, можно изменить ABI, нарушая работу программ, даже если API не изменился.

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

Компилируется и работает нормально с:

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

Теперь предположим, что для v2 библиотеки мы хотим добавить новое поле для mylib_mystrictвызываемого new_field.

Если мы добавили поле до того, old_fieldкак в:

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

и перестроить библиотеку, но нет main.out, тогда утверждение не удается!

Это потому что строка:

myobject->old_field == 1

сгенерировал сборку, которая пытается получить доступ к самой первой intструктуре, которая теперь new_fieldвместо ожидаемой old_field.

Поэтому это изменение сломало ABI.

Если, однако, мы добавим new_fieldпосле old_field:

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

тогда старая сгенерированная сборка все еще обращается к первой intструктуре, и программа все еще работает, потому что мы сохранили ABI стабильным.

Вот полностью автоматизированная версия этого примера на GitHub .

Еще один способ сохранить стабильность этого ABI состоял бы в том, чтобы рассматривать его mylib_mystructкак непрозрачную структуру и обращаться к ее полям только через помощников методов. Это облегчает поддержание стабильности ABI, но может повлечь за собой снижение производительности, поскольку мы выполняем больше вызовов функций.

API против ABI

В предыдущем примере интересно отметить, что добавление new_fieldранее old_fieldтолько нарушало ABI, но не API.

Это означает, что если бы мы перекомпилировали нашу main.cпрограмму для библиотеки, она работала бы независимо.

Однако мы бы также нарушили API, если бы изменили, например, сигнатуру функции:

mylib_mystruct* mylib_init(int old_field, int new_field);

так как в этом случае main.cперестанет компилироваться вообще.

Семантический API против API программирования против ABI

Мы также можем классифицировать изменения API по третьему типу: семантические изменения.

Например, если мы изменили

myobject->old_field = old_field;

чтобы:

myobject->old_field = old_field + 1;

тогда это не сломало бы ни API, ни ABI, но main.cвсе равно сломалось бы!

Это потому, что мы изменили «человеческое описание» того, что должна делать функция, а не программно заметный аспект.

У меня только что было философское понимание, что формальная проверка программного обеспечения в некотором смысле перемещает больше «семантического API» в более «программно проверяемый API».

Семантический API против API программирования

Мы также можем классифицировать изменения API по третьему типу: семантические изменения.

Семантический API, как правило, представляет собой естественное описание того, что должен делать API, обычно включается в документацию API.

Поэтому возможно нарушить семантический API, не нарушая саму сборку программы.

Например, если мы изменили

myobject->old_field = old_field;

чтобы:

myobject->old_field = old_field + 1;

тогда это не нарушило бы ни API программирования, ни ABI, но main.cсемантический API сломался бы.

Существует два способа программной проверки API контракта:

  • проверить кучу угловых случаев. Это легко сделать, но вы всегда можете пропустить один.
  • формальная проверка . Сложнее сделать, но производит математическое доказательство правильности, по существу объединяя документацию и тесты в «человеческий» / машинно проверяемый способ! Пока, конечно, в вашем официальном описании нет ошибки ;-)

Протестировано в Ubuntu 18.10, GCC 8.2.0.

Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
2
Ваш ответ был достаточно подробным, чтобы помочь мне понять разницу между API и ABI. Спасибо!
Ракшит Рави
9

( РИМЕНЕНИЕ В Инары я nterface) спецификацию для конкретной аппаратной платформы в сочетании с операционной системой. Это один шаг за пределы API ( РИМЕНЕНИЕ Р rogram Я nterface), который определяет вызовы от приложения к операционной системе. ABI определяет API плюс машинный язык для определенного семейства процессоров. API не гарантирует совместимость во время выполнения, но ABI делает, потому что он определяет машинный язык или формат времени исполнения.

введите описание изображения здесь

учтивость

Premraj
источник
9

Позвольте мне привести конкретный пример того, как ABI и API отличаются в Java.

ABI несовместимое изменение, если я изменю метод A # m () с принятия Stringв качестве аргумента String...аргумента. Это не совместимо с ABI, потому что вы должны перекомпилировать код, который вызывает это, но это API-совместимый, поскольку вы можете решить его, перекомпилировав без каких-либо изменений кода в вызывающей стороне.

Вот пример, изложенный. У меня есть библиотека Java с классом A

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

И у меня есть класс, который использует эту библиотеку

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

Теперь, автор библиотеки скомпилировал их класс A, я скомпилировал свой класс Main, и все это прекрасно работает. Представьте себе новую версию A

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

Если я просто возьму новый скомпилированный класс A и урону его вместе с ранее скомпилированным классом Main, я получу исключение при попытке вызвать метод

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

Если я перекомпилирую Main, это исправлено, и все снова работает.

user7610
источник
6

Ваша программа (исходный код) может быть скомпилирована с модулями, которые предоставляют надлежащий API .

Ваша программа (двоичная) может работать на платформах, которые обеспечивают надлежащий ABI .

API ограничивает определения типов, определения функций, макросы, иногда глобальные переменные, которые должна представлять библиотека.

ABI ограничивает то, что «платформа» должна предоставлять вашей программе для запуска. Мне нравится рассматривать это в 3 уровнях:

  • уровень процессора - набор инструкций, соглашение о вызовах

  • Уровень ядра - конвенция системного вызова конвенция специального пути к файлу (например, /procи /sysфайлы в Linux) и т.д.

  • Уровень ОС - формат объекта, библиотеки времени выполнения и т. Д.

Рассмотрим кросс-компилятор с именем arm-linux-gnueabi-gcc. «arm» обозначает архитектуру процессора, «linux» обозначает ядро, «gnu» указывает, что его целевые программы используют libc GNU в качестве библиотеки времени выполнения, в отличие от arm-linux-androideabi-gccиспользуемой реализации libc в Android.

ZhouZhuo
источник
1
это очень краткое объяснение различия между ними, и в очень уникальной перспективе.
Саджук
1

API- Application Programming Interfaceэто интерфейс времени компиляции, который может использоваться разработчиком для использования не проектных функций, таких как библиотека, ОС, вызовы ядра в исходном коде

ABI[About] -Application Binary Interfaceэтоинтерфейс времени выполнения, который используется программой во время выполнения для связи между компонентами в машинном коде

yoAlex5
источник