API ve ABI arasındaki fark


194

Linux sistem programlamasında yeniyim ve Linux Sistem Programlama'yı okurken API ve ABI ile karşılaştım .

API'nın tanımı:

Bir API, bir yazılım parçasının kaynak seviyesinde başka bir yazılımla iletişim kurduğu arayüzleri tanımlar.

ABI Tanımı:

Bir API bir kaynak arabirimi tanımlarken, ABI belirli bir mimarideki iki veya daha fazla yazılım parçası arasındaki düşük seviyeli ikili arabirimi tanımlar. Bir uygulamanın kendisiyle nasıl etkileşime girdiğini, bir uygulamanın çekirdekle nasıl etkileştiğini ve uygulamanın kütüphanelerle nasıl etkileştiğini tanımlar.

Bir program kaynak düzeyinde nasıl iletişim kurabilir? Kaynak seviyesi nedir? Zaten kaynak koduyla ilgili mi? Yoksa kütüphanenin kaynağı ana programa dahil edilir mi?

Biliyorum tek fark API çoğunlukla programcılar tarafından kullanılır ve ABI çoğunlukla derleyici tarafından kullanılır.


2
kaynak seviyesine göre, fonksiyon tanımlarını ortaya çıkarmak için dosya eklemek gibi bir şey kastediyorlar
Anycorn

Yanıtlar:


49

API insanların kullandığı yöntemdir. Kaynak kodu yazıyoruz. Bir program yazdığımızda ve bazı kütüphane işlevlerini kullanmak istediğimizde aşağıdaki gibi kod yazarız:

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

ve livenMyHills()uzun bir tamsayı parametresi alan bir yöntem olduğunu bilmemiz gerekiyordu . Yani bir Programlama Arayüzü olarak hepsi kaynak kodunda ifade edilir. Derleyici bunu, bu dilin bu işletim sisteminde uygulanmasına uygun yürütülebilir talimatlara dönüştürür. Ve bu durumda bir Audio ünitesinde bazı düşük seviyeli işlemlerle sonuçlanır. Bu nedenle, bazı bitler ve baytlar bazı donanımlarda fışkırır. Bu yüzden çalışma zamanında genellikle görmediğimiz çok sayıda İkili seviye eylemi var.


310

API: Uygulama Programı Arayüzü

Bu, uygulamanızdan / kitaplığınızdan gösterdiğiniz genel türler / değişkenler / işlevler kümesidir.

C / C ++ 'da uygulama ile birlikte gönderdiğiniz başlık dosyalarında ortaya koyduğunuz şey budur.

ABI: Uygulama İkili Arayüzü

Derleyici böyle bir uygulama oluşturur.
Bazı şeyleri tanımlar (ancak bunlarla sınırlı değildir):

  • Parametrelerin fonksiyonlara nasıl aktarıldığı (register / stack).
  • Yığındaki parametreleri kim temizler (arayan / arayan).
  • Dönüş değerinin iade için yerleştirildiği yer.
  • İstisnalar nasıl yayılır.

17
Bu muhtemelen bir ABI'nın ne olduğunu, şimdiye kadar gördüğüm en iyi kısa açıklamadır; gj!
TerryP

3
Bu cevabın kısa veya öz olup olmadığına karar vermeniz gerekiyor . :)
jrok

1
@jrok: İşler kısa ve öz olabilir, birbirlerini dışlamazlar.
Martin York

@LokiAstari, Yani ABI aslında bir API değil mi?
Pacerier

4
@Pacerier: Her ikisi de arayüz. Ancak farklı soyutlama düzeyindedirler. API uygulama geliştirici düzeyindedir. ABI derleyici düzeyindedir (bir uygulama geliştiricisinin asla gitmediği bir yer).
Martin York

47

Bu terimlerle çoğunlukla API ile uyumlu olmayan bir değişiklik veya ABI ile uyumlu olmayan bir değişiklik anlamında karşılaşıyorum.

Bir API değişikliği esasen önceki sürümle derlenen kodun artık çalışmadığı yerdir. Bunun nedeni, bir işleve bir bağımsız değişken eklediğiniz veya yerel kodunuz dışında erişilebilen bir şeyin adını değiştirdiğiniz için olabilir. Bir üstbilgiyi her değiştirdiğinizde ve bir .c / .cpp dosyasındaki bir şeyi değiştirmeye zorladığınız zaman, bir API değişikliği yaptınız.

Bir ABI değişikliği, sürüm 1'e karşı derlenmiş olan kodun artık bir kod tabanının (genellikle bir kütüphane) sürüm 2'si ile çalışmadığı yerdir. Bu, bir sınıfa sanal bir yöntem eklemek kadar basit bir şey ABI uyumlu olmadığından, API ile uyumlu olmayan değişiklikleri izlemek için genellikle daha zordur.

ABI uyumluluğunun ne olduğunu ve nasıl korunacağını bulmak için son derece yararlı iki kaynak buldum:


4
Karşılıklı münhasırlıklarını göstermek için +1. Örneğin, Java'nın assert anahtar sözcüğünü tanıtması, API ile uyumsuz ancak ABI uyumlu bir değişikliktir docs.oracle.com/javase/7/docs/technotes/guides/language/… .
Pacerier

ABI değişikliğine neyin sebep olabileceğini listeleyen , tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html kaynaklarınızın "3.6. Uyumsuz Kütüphaneler" bölümüne ekleyebilirsiniz .
Demi-Lune

20

Bu benim layman açıklamalarım:

  • API - includedosyaları düşünün . Programlama arabirimleri sağlarlar.
  • ABI - çekirdek modülünü düşünün. Bazı çekirdeklerde çalıştırdığınızda, içerme dosyaları olmadan nasıl iletişim kurulacağına, yani düşük düzeyli ikili arabirime karar vermelidir.

13

Linux paylaşılan kütüphane minimal çalıştırılabilir API vs ABI örneği

Bu yanıt diğer cevabımdan alınmıştır: Uygulama ikili arayüzü (ABI) nedir? ama ben buna doğrudan cevap verdiğini ve soruların kopya olmadığını hissettim.

Paylaşılan kitaplıklar bağlamında, "kararlı bir ABI'ye sahip olmanın" en önemli anlamı, kitaplık değiştikten sonra programlarınızı yeniden derlemenize gerek olmamasıdır.

Aşağıdaki örnekte göreceğimiz gibi, API değişmemiş olsa bile, programları bozarak ABI'yi değiştirmek mümkündür.

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

Derleme ve iyi çalışır:

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

Şimdi, kütüphanenin v2'si için mylib_mystrictçağrılan yeni bir alan eklemek istediğimizi varsayalım new_field.

Alanı daha önce old_fieldolduğu gibi eklediysek :

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

ve kütüphaneyi yeniden inşa etti ama değil main.out, o zaman iddia başarısız olur!

Çünkü çizgi:

myobject->old_field == 1

intyapının ilkine erişmeye çalışan bir montaj oluşturmuştu , ki bu şimdi new_fieldbeklenen yerine old_field.

Bu nedenle bu değişiklik ABI'yi bozdu.

Ancak, biz eklemek new_fieldsonra old_field:

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

eski oluşturulan montaj hala intyapının ilkine erişir ve program hala çalışır, çünkü ABI'yi sabit tuttuk.

İşte bu örneğin GitHub'da tam otomatik bir sürümü .

Bu ABI'yi sabit tutmanın bir başka yolu, opak bir yapımylib_mystruct olarak davranmak ve alanlarına sadece yöntem yardımcıları aracılığıyla erişmek olurdu . Bu, ABI'yi sabit tutmayı kolaylaştırır, ancak daha fazla işlev çağrısı yapacağımız için performans yüküne neden olur.

API ve ABI karşılaştırması

Bir önceki örnekte, ekleme olduğunu ilginçtir new_fieldönce old_field, sadece API ABI kırdı ama.

Bunun anlamı, main.cprogramımızı kütüphaneye karşı derlemiş olsaydık, ne olursa olsun işe yarayacaktı.

Bununla birlikte, örneğin işlev imzasını değiştirmiş olsaydık, API'yı da kırmış olurduk:

mylib_mystruct* mylib_init(int old_field, int new_field);

çünkü bu durumda, main.cderlemeyi tamamen durduracaktır.

Anlamsal API ve programlama API'sı ABI

API değişikliklerini üçüncü bir türde de sınıflandırırız: anlamsal değişiklikler.

Örneğin,

myobject->old_field = old_field;

için:

myobject->old_field = old_field + 1;

o zaman bu ne API'yi ne de ABI'yi bozar, ama main.cyine de kırılır!

Bunun nedeni, programın dikkat çekici bir yönünden ziyade işlevin ne yapması gerektiğinin "insan tanımını" değiştirmiş olmamızdır.

Ben sadece bir anlamda yazılımın resmi doğrulamasının daha çok "anlamsal API" nın daha "programatik olarak doğrulanabilir bir API" ya taşındığına dair felsefi bir anlayışa sahiptim.

Anlamsal API ve Programlama API'sı

API değişikliklerini üçüncü bir türde de sınıflandırırız: anlamsal değişiklikler.

Anlamsal API, genellikle API belgelerinde yer alan ve API'nın ne yapması gerektiği konusunda doğal bir dil tanımıdır.

Bu nedenle, semantik API'yi program derlemesinin kendisini bozmadan kırmak mümkündür.

Örneğin,

myobject->old_field = old_field;

için:

myobject->old_field = old_field + 1;

bu ne programlama API'sini ne de ABI'yi bozar, ancak main.c semantik API bozulurdu.

Sözleşme API'sını programlı olarak kontrol etmenin iki yolu vardır:

  • bir grup köşe vakasını test edin. Yapması kolay, ama her zaman birini özleyebilirsiniz.
  • resmi doğrulama . Yapması daha zor, ancak matematiksel doğruluk kanıtı üretir, temelde belgeleri ve testleri "insan" / makine doğrulanabilir bir şekilde birleştirir! Elbette resmi açıklamanızda bir hata olmadığı sürece ;-)

Ubuntu 18.10, GCC 8.2.0'da test edilmiştir.


2
Saygılarımızla, API ve ABI arasındaki farkı anlamama yardımcı olacak kadar ayrıntılı cevap buydu. Teşekkür ederim!
Rakshith Ravi

9

( A pplication B inary I nterface) bir belirtim işletim sistemi ile bir araya belirli bir donanım platformu. Uygulamadan işletim sistemine yapılan çağrıları tanımlayan API'nın ( A pplication P rogram I nterface) bir adım ötesindedir . ABI, belirli bir CPU ailesi için API artı makine dilini tanımlar. Bir API, çalışma zamanı uyumluluğunu sağlamaz, ancak makine dili veya çalışma zamanı biçimini tanımladığı için ABI yapar.

resim açıklamasını buraya girin

Nezaket


9

ABI ve API'nin Java'da nasıl farklılaştığına dair özel bir örnek vereyim.

Bir ABI uyumsuz değişikliği, A # m () yöntemini Stringargüman olarak String...argüman olarak almaktan değiştirmem . Bu, ABI uyumlu değildir , çünkü çağıran kodu yeniden derlemeniz gerekir, ancak API uyumludur arayanda herhangi bir kod değişikliği olmadan yeniden derleyerek çözebileceğiniz için .

Aşağıda örnek verilmiştir. A sınıfı Java kütüphanem var

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

Ve bu kütüphaneyi kullanan bir sınıfım var

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

Şimdi, kütüphane yazarı A sınıfını derledi, Main sınıfımı derledim ve hepsi güzel çalışıyor. A'nın yeni bir versiyonunun geldiğini düşünün

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

Sadece yeni derlenmiş A sınıfını alıp daha önce derlenmiş olan Main sınıfı ile birlikte bırakırsam, yöntemi çağırmak için bir istisna alıyorum

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

Main'i yeniden derlersem, bu sorun giderilir ve her şey tekrar çalışır.


6

Programınız (kaynak kodu) uygun API sağlayan modüllerle derlenebilir .

Programınız (ikili program) uygun ABI sağlayan platformlarda çalışabilir .

API, kitaplığın göstermesi gereken tür tanımlarını, işlev tanımlarını, makroları, bazen genel değişkenleri kısıtlar.

ABI, programın çalışması için bir "platformun" ne sağlaması gerektiğini kısıtlar. 3 seviyede düşünmeyi seviyorum:

  • işlemci seviyesi - komut seti, çağrı kuralı

  • çekirdek düzeyi - sistem çağrısı kuralı, özel dosya yolu kuralı (örn. /procve /sysLinux'taki dosyalar) vb.

  • İşletim sistemi seviyesi - nesne biçimi, çalışma zamanı kitaplıkları vb.

Adlı bir çapraz derleyici düşünün arm-linux-gnueabi-gcc. "arm" işlemci mimarisini, "linux" çekirdeği, "gnu" hedef programlarının GNU'nun libc'sini çalışma zamanı kütüphanesi olarak arm-linux-androideabi-gcckullandığını, Android'in libc uygulamasını kullandığını gösterir.


1
bu, aralarındaki farkın çok özlü bir açıklamasıdır ve çok eşsiz bir bakış açısıyla.
Sajuuk

1

API- geliştirici tarafından kütüphane, işletim sistemi, kaynak kodunda çekirdek çağrılar gibi proje dışı işlevleri kullanmak için kullanılabilen Application Programming Interfacebir derleme zamanı arabirimidir.

ABI[Hakkında] - makine kodundaki bileşenler arasında iletişim için bir program tarafından kullanılanApplication Binary Interfacebir çalışma zamanı arabirimidir

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.