APIとABIの違い


192

私は、Linuxシステムのプログラミングに新しいですし、読みながら、私はAPIとABIに出くわした のLinuxシステムプログラミングを

APIの定義:

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

ABIの定義:

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

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

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


2
ソースレベルでは、関数定義を公開するためのインクルードファイルのようなものを意味します
Anycorn

回答:


49

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

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

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


308

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

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

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

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

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

  • パラメーターが関数(レジスター/スタック)に渡される方法。
  • スタック(呼び出し元/呼び出し先)からパラメーターをクリーンアップするユーザー。
  • 戻り値が返される場所。
  • 例外がどのように伝播するか。

17
これはおそらく、私が今まで見た中で、ABIが何であるかについての最も簡潔な説明です。gj!
TerryP 2010

3
この答えが簡潔詳細かを判断する必要があります。:)
jrok

1
@jrok:物事は簡潔かつ詳細にすることができ、相互に排他的ではありません。
マーティンヨーク

@ LokiAstari、ABIも実際にはAPIではないのですか?
Pacerier 2015

4
@Pacerier:どちらもインターフェースです。しかし、それらは異なる抽象化レベルにあります。APIはアプリケーション開発者レベルです。ABIはコンパイラーレベルです(アプリケーション開発者が行くことのない場所)。
マーティンヨーク

47

私は主に、APIと互換性のない変更、またはABIと互換性のない変更という意味でこれらの用語に遭遇します。

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

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

私は、ABI互換性とは何か、およびそれをどのように保持するかを理解するための2つの非常に役立つリソースを見つけました。


4
彼らの相互の排他性を指摘するための+1。たとえば、Javaのassertキーワードの導入は、API互換ではないがABI互換の変更docs.oracle.com/javase/7/docs/technotes/guides/language/…です。
Pacerier 2014

tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.htmlのリソースセクション「3.6。互換性のないライブラリ」に追加すると、ABIの変更の原因となる可能性のあるものがリストされます。
Demi-Lune

20

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

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

13

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_mystrictcalled に追加するとし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_fieldbefore を追加するとold_field、ABIだけが破壊され、APIは破壊されないことに注意してください。

これが意味することは、もし私たちが main.c、ライブラリに対してプログラムを、関係なく機能したということです。

また、たとえば関数のシグネチャを変更した場合、APIも壊れていました。

mylib_mystruct* mylib_init(int old_field, int new_field);

その場合、 main.cコンパイルは完全に停止します。

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

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

たとえば、変更した場合

myobject->old_field = old_field;

に:

myobject->old_field = old_field + 1;

その後、これはAPIもABIも壊れませんでしたmain.cが、それでも壊れます!

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

ソフトウェアの正式な検証は、ある意味で「セマンティックAPI」から「プログラムで検証可能なAPI」へと移行するという哲学的な洞察を得たところです。

セマンティックAPIとプログラミング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でテスト済み。


2
あなたの答えは、APIとABIの違いを理解するのに十分に詳細な答えでした。ありがとうございました!
Rakshith Ravi

9

A pplication B inary I nterface)A仕様のオペレーティング・システムと組み合わせた特定のハードウェアプラットフォームのため。これは、API(越えて一の工程であるA pplication P rogram Iオペレーティング・システムへのアプリケーションからの呼び出しを定義nterface)。ABIは、APIと特定のCPUファミリのマシン言語を定義します。APIはランタイム互換性を保証しませんが、ABIは機械語またはランタイムの形式を定義するため、ABIは保証します。

ここに画像の説明を入力してください

礼儀


9

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

ABI互換性のない変更は、メソッドA#m()をa 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を再コンパイルすると、これは修正され、すべてが再び機能します。


6

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

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

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

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

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

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

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

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


1
これは両者の違いを非常に簡潔に説明したもので、非常にユニークな見方をしています。
Sajuuk、

1

API- Application Programming Interfaceであり、コンパイルによって使用されることができる時間のインタフェース開発者ライブラリ、OS、コアコールなどの非プロジェクト機能を使用するためにソースコードは

ABI[概要] -実行時にプログラムがマシンコードのコンポーネント間の通信に使用Application Binary Interfaceするランタイムインターフェイスです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.