web-dev-qa-db-ja.com

APIとABIの違い

Linuxシステムプログラミングは初めてで、Linux System Programmingを読んでいる間にAPIとABIに出会いました。

APIの定義:

APIは、1つのソフトウェアがソースレベルで別のソフトウェアと通信するためのインターフェースを定義します。

ABIの定義:

APIはソースインターフェイスを定義しますが、ABIは特定のアーキテクチャ上の2つ以上のソフトウェア間の低レベルのバイナリインターフェイスを定義します。アプリケーションがそれ自体とどのように相互作用するか、アプリケーションがカーネルとどのように相互作用するか、そしてアプリケーションがライブラリとどのように相互作用するかを定義します。

プログラムはソースレベルでどのように通信できますか?ソースレベルとはとにかくソースコードに関連していますか?または、ライブラリのソースがメインプログラムに含まれますか?

私が知っている唯一の違いは、APIは主にプログラマーによって使用され、ABIは主にコンパイラーによって使用されることです。

171
Searock

APIは人間が使用するものです。ソースコードを書きます。プログラムを作成し、ライブラリ関数を使用する場合、次のようなコードを作成します。

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

そして、長整数パラメーターをとるメソッドlivenMyHills()があることを知る必要がありました。したがって、プログラミングインターフェイスとしては、すべてソースコードで表現されます。コンパイラは、これをこの特定のオペレーティングシステムでのこの言語の実装に適合する実行可能命令に変換します。そして、この場合、Audioユニットで低レベルの操作が行われます。そのため、一部のハードウェアでは特定のビットとバイトが噴出されます。そのため、実行時には、通常は表示されない多くのバイナリレベルのアクションが実行されます。

42
djna

API:アプリケーションプログラムインターフェイス

これは、アプリケーション/ライブラリから公開するパブリック型/変数/関数のセットです。

C/C++では、これはアプリケーションに付属するヘッダーファイルで公開します。

ABI:アプリケーションバイナリインターフェイス

これは、コンパイラがアプリケーションを構築する方法です。
物事を定義します(ただし、これに限定されません):

  • パラメーターが関数(レジスター/スタック)に渡される方法。
  • 誰がスタック(呼び出し元/呼び出し先)からパラメーターを削除します。
  • 戻り値が返される場所。
  • 例外の伝播方法。
281
Martin York

ほとんどの場合、これらの用語は、APIと互換性のない変更、またはABIと互換性のない変更という意味で使用されています。

APIの変更とは、基本的に、以前のバージョンでコンパイルされたコードが機能しなくなることです。これは、関数に引数を追加したか、ローカルコードの外部からアクセス可能なものの名前を変更したために発生する可能性があります。ヘッダーを変更し、.c/.cppファイル内の何かを強制的に変更するたびに、APIを変更しました。

ABIの変更は、バージョン1に対してすでにコンパイルされたコードがコードベース(通常はライブラリ)のバージョン2で動作しなくなる場所です。仮想メソッドをクラスに追加するのと同じくらい簡単なことはABI互換性がない可能性があるため、これは一般にAPI非互換の変更よりも追跡するのが難しいです。

ABIの互換性とその保持方法を理解するために、2つの非常に役立つリソースを見つけました。

42
jkerian

これは私の素人の説明です:

  • api-includeファイルだと思います。プログラミングインターフェイスを提供します
  • abi-カーネルモジュールを考えます。いくつかのカーネルで実行する場合、インクルードファイルなしで通信する方法に同意する必要があります。つまり、低レベルのバイナリインターフェイスとして
19
Anycorn

JavaでABIとAPIがどのように異なるかを具体的な例を挙げましょう。

ABIの互換性のない変更は、メソッドA#m()をString...引数の引数としてStringから取得することから変更した場合です。これはABI互換ではありませんです。これを呼び出しているコードを再コンパイルする必要がありますが、API互換です呼び出し元でコードを変更せずに再コンパイルすることで解決できます。

以下に例を示します。クラスAのJavaライブラリがあります

// 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を再コンパイルすると、これは修正され、すべてが再び機能します。

8
user7610

ApplicationBinaryInterface)オペレーティングシステムと組み合わせた特定のハードウェアプラットフォームの仕様。 APIの一歩先(ApplicationProgramInterface)、これはアプリケーションからオペレーティングシステムへの呼び出しを定義します。 ABIは、特定のCPUファミリのAPIとマシン言語を定義します。 APIはランタイムの互換性を保証しませんが、ABIはマシン言語、またはランタイム、フォーマットを定義するため、保証します。

enter image description here

礼儀

7
Premraj

プログラム(ソースコード)は、適切なAPIを提供するモジュールを使用してコンパイルできます。

プログラム(バイナリ)は、適切なABIを提供するプラットフォームで実行できます。

APIは、型定義、関数定義、マクロ、ライブラリが公開するグローバル変数を制限します。

ABIは、プログラムを実行するために「プラットフォーム」が提供するものを制限します。私は3つのレベルでそれを検討したいです:

  • プロセッサレベル-命令セット、呼び出し規約

  • カーネルレベル-システムコール規則、特別なファイルパス規則(Linuxの/procおよび/sysファイルなど)など。

  • OSレベル-オブジェクト形式、ランタイムライブラリなど.

arm-linux-gnueabi-gccという名前のクロスコンパイラを検討してください。 「arm」はプロセッサアーキテクチャ、「linux」はカーネル、「gnu」はAndroidのlibc実装を使用するarm-linux-androideabi-gccとは異なり、ターゲットプログラムがランタイムライブラリとしてGNUのlibcを使用することを示します。

4
ZhouZhuo

Linux共有ライブラリの最小実行可能APIとABIの例

この回答は、他の回答から抽出されました: アプリケーションバイナリインターフェイス(ABI)とは何ですか? しかし、この回答にも直接回答し、質問は重複していないと感じました。

共有ライブラリのコンテキストでは、「安定したABIを持つ」ことの最も重要な意味は、ライブラリの変更後にプログラムを再コンパイルする必要がないことです。

以下の例で見るように、APIが変更されていなくても、ABIを変更してプログラムを破壊する可能性があります。

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_mystructopaque struct として扱い、メソッドヘルパーを介してそのフィールドにのみアクセスすることです。これにより、ABIを安定させやすくなりますが、より多くの関数呼び出しを行うとパフォーマンスのオーバーヘッドが発生します。

API vs ABI

前の例では、new_fieldの前にold_fieldを追加すると、ABIのみが破損し、APIは破損しないことに注意してください。

これが意味するのは、ライブラリに対してmain.cプログラムを再コンパイルしていた場合、それは関係なく機能するということです。

ただし、たとえば関数のシグネチャを変更した場合は、APIも破損します。

mylib_mystruct* mylib_init(int old_field, int new_field);

その場合、main.cはコンパイルを完全に停止するためです。

セマンティックAPI vsプログラミングAPI vs ABI

APIの変更は、セマンティックの変更という3番目のタイプに分類することもできます。

たとえば、変更した場合

myobject->old_field = old_field;

に:

myobject->old_field = old_field + 1;

この場合、APIもABIも壊れませんでしたが、main.cはまだ壊れます。

これは、プログラムで認識できる側面ではなく、関数が実行することになっている「人間の説明」を変更したためです。

ソフトウェアの正式な検証 ある意味で、「セマンティックAPI」をより「プログラム的に検証可能なAPI」に移行させるという哲学的な洞察がありました。

セマンティックAPI vsプログラミングAPI

APIの変更は、セマンティックの変更という3番目のタイプに分類することもできます。

通常、セマンティックAPIは、APIが行うことを想定した自然言語の記述であり、通常はAPIドキュメントに含まれています。

したがって、プログラムビルド自体を壊すことなく、セマンティックAPIを壊すことができます。

たとえば、変更した場合

myobject->old_field = old_field;

に:

myobject->old_field = old_field + 1;

その場合、これはプログラミングAPIもABIも破損しませんが、main.cセマンティックAPIは破損します。

プログラムでコントラクトAPIをチェックする方法は2つあります。

  • 多数のコーナーケースをテストします。簡単にできますが、いつでも見逃すかもしれません。
  • 正式な検証 。困難ですが、正確性の数学的な証明を作成し、ドキュメントとテストを「人間」/機械検証可能な方法に本質的に統合します!もちろん、正式な説明にバグがない限り;-)

Ubuntu 18.10、GCC 8.2.0でテスト済み。

enter image description here

Application programming interface (API)-最高レベルの抽象化によって表されます。このAPIは、アプリケーションをライブラリやコアOSにインターフェースします。

Application Binary Interface (ABI)は、低レベルのデータ型や呼び出し規約などの事実をカバーし、多くのプログラムのフォーマットも定義します。主に、システムコールはこのレベルで定義されます。さらに、このタイプのインターフェイスにより、同じABIを使用するOS全体でさまざまなアプリケーションやライブラリの移植性が実現します。

続きを読む こちら

1
yoAlex5