ความแตกต่างระหว่าง API และ ABI


194

ฉันใหม่เพื่อการเขียนโปรแกรมระบบลินุกซ์และฉันมาข้าม API และ ABI ในขณะที่อ่าน การเขียนโปรแกรมระบบลินุกซ์

คำจำกัดความของ API:

API กำหนดอินเทอร์เฟซซึ่งซอฟต์แวร์ชิ้นหนึ่งสื่อสารกับอีกส่วนหนึ่งในระดับแหล่งที่มา

คำจำกัดความของ ABI:

ในขณะที่ API กำหนดอินเทอร์เฟซของแหล่งที่มา ABI กำหนดอินเทอร์เฟซแบบไบนารีระดับต่ำระหว่างซอฟต์แวร์สองชิ้นขึ้นไปบนสถาปัตยกรรมเฉพาะ มันกำหนดว่าแอปพลิเคชันโต้ตอบกับตัวเองอย่างไรแอปพลิเคชันโต้ตอบกับเคอร์เนลและแอปพลิเคชันโต้ตอบกับไลบรารีอย่างไร

โปรแกรมสามารถสื่อสารในระดับแหล่งที่มาได้อย่างไร ระดับแหล่งที่มาคืออะไร? มันเกี่ยวข้องกับซอร์สโค้ดหรือไม่? หรือแหล่งที่มาของไลบรารีจะรวมอยู่ในโปรแกรมหลักหรือไม่

ข้อแตกต่างเดียวที่ฉันรู้คือ API ส่วนใหญ่จะใช้โดยโปรแกรมเมอร์และ ABI ส่วนใหญ่จะใช้โดยคอมไพเลอร์


2
โดยระดับที่มาพวกเขาหมายถึงสิ่งที่ต้องการรวมไฟล์เพื่อเปิดเผยคำจำกัดความของฟังก์ชั่น
Anycorn

คำตอบ:


49

API คือสิ่งที่มนุษย์ใช้ เราเขียนซอร์สโค้ด เมื่อเราเขียนโปรแกรมและต้องการใช้ฟังก์ชั่นห้องสมุดบางอย่างเราเขียนโค้ดเช่น:

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

และเราจำเป็นต้องรู้ว่ามีวิธีการlivenMyHills()ซึ่งใช้พารามิเตอร์จำนวนเต็มแบบยาว ดังนั้นในฐานะที่เป็นส่วนต่อการเขียนโปรแกรมมันทั้งหมดแสดงในซอร์สโค้ด คอมไพเลอร์แปลงสิ่งนี้ให้เป็นคำสั่งที่ใช้งานได้ซึ่งสอดคล้องกับการใช้ภาษานี้ในระบบปฏิบัติการนี้ และในกรณีนี้ส่งผลให้มีการทำงานระดับต่ำในหน่วยเสียง ดังนั้นบิตและไบต์บางอันจะถูกบีบอัดที่ฮาร์ดแวร์บางตัว ดังนั้นในรันไทม์มีการกระทำระดับ Binary จำนวนมากที่เรามักจะไม่เห็น


310

API: Application Program Interface

นี่คือชุดของประเภท / ตัวแปร / ฟังก์ชั่นสาธารณะที่คุณเปิดเผยจากแอปพลิเคชัน / ห้องสมุดของคุณ

ใน C / C ++ นี่คือสิ่งที่คุณเปิดเผยในไฟล์ส่วนหัวที่คุณจัดส่งพร้อมกับแอปพลิเคชัน

ABI: แอปพลิเคชันไบนารีอินเตอร์เฟส

นี่คือวิธีที่คอมไพเลอร์สร้างแอปพลิเคชัน
มันกำหนดสิ่งต่าง ๆ (แต่ไม่ จำกัด เฉพาะ):

  • พารามิเตอร์ถูกส่งผ่านไปยังฟังก์ชันอย่างไร (รีจิสเตอร์ / สแต็ก)
  • ใครเป็นผู้ทำความสะอาดพารามิเตอร์จากสแต็ก (ผู้โทร / callee)
  • ตำแหน่งที่ส่งคืนค่าสำหรับการส่งคืน
  • ข้อยกเว้นเผยแพร่อย่างไร

17
นี่อาจเป็นคำอธิบายสั้น ๆ ที่ดีที่สุดว่า ABI คืออะไรที่ฉันเคยเห็น GJ!
TerryP

3
พวกคุณต้องตัดสินใจว่าคำตอบนี้กระชับหรือละเอียด :)
jrok

1
@ jrok: สิ่งต่าง ๆ สามารถกระชับและละเอียดได้
Martin York

@LokiAstari ดังนั้น ABI ก็ไม่ได้เป็น API เช่นกัน?
Pacerier

4
@Pacerier: เป็นทั้งสองอินเตอร์เฟส แต่พวกเขาอยู่ในระดับที่แตกต่างกันของสิ่งที่เป็นนามธรรม API อยู่ที่ระดับนักพัฒนาแอปพลิเคชัน ABI อยู่ในระดับคอมไพเลอร์ (บางแห่งที่ผู้พัฒนาแอพพลิเคชั่นไม่เคยไป)
Martin York

47

ฉันพบคำเหล่านี้เป็นส่วนใหญ่ในแง่ของการเปลี่ยนแปลงที่เข้ากันไม่ได้กับ API หรือการเปลี่ยนแปลงที่เข้ากันไม่ได้กับ ABI

การเปลี่ยนแปลง API เป็นสิ่งที่รหัสที่จะรวบรวมกับเวอร์ชันก่อนหน้าจะไม่ทำงานอีกต่อไป สิ่งนี้อาจเกิดขึ้นได้เนื่องจากคุณเพิ่มอาร์กิวเมนต์ในฟังก์ชันหรือเปลี่ยนชื่อของสิ่งที่สามารถเข้าถึงได้นอกรหัสท้องถิ่นของคุณ เมื่อใดก็ตามที่คุณเปลี่ยนส่วนหัวและบังคับให้คุณเปลี่ยนบางสิ่งในไฟล์. c / .cpp คุณได้ทำการเปลี่ยนแปลง API แล้ว

การเปลี่ยนแปลง ABI คือตำแหน่งที่โค้ดที่ถูกคอมไพล์แล้วกับเวอร์ชัน 1 จะไม่ทำงานกับโค้ดเบสเวอร์ชัน 2 อีกต่อไป (โดยปกติจะเป็นไลบรารี) นี่เป็นเรื่องยากมากที่จะติดตามการเปลี่ยนแปลงที่เข้ากันไม่ได้กับ API เนื่องจากบางสิ่งที่ง่ายพอ ๆ กับการเพิ่มเมธอดเสมือนให้กับคลาสสามารถทำให้ ABI เข้ากันไม่ได้

ฉันได้พบสองแหล่งข้อมูลที่มีประโยชน์อย่างยิ่งสำหรับการค้นหาความเข้ากันได้ของ ABI คืออะไรและจะรักษาอย่างไร:


4
+1 สำหรับการชี้ให้เห็นถึงความพิเศษเฉพาะตัวของพวกเขา ยกตัวอย่างเช่นการแนะนำของ Java ของคำหลักยืนยันเป็น API-เข้ากันไม่ได้เลย ABI ได้เปลี่ยนแปลงdocs.oracle.com/javase/7/docs/technotes/guides/language/...
Pacerier

คุณสามารถเพิ่มลงในส่วนทรัพยากรของคุณ "3.6. ไลบรารีที่เข้ากันไม่ได้" ของtldp.org/HOWTO/Program-Library-HOWTO/shared-l Libraries.htmlซึ่งแสดงรายการสิ่งที่อาจทำให้เกิดการเปลี่ยนแปลง ABI
Demi-Lune

20

นี่คือคำอธิบายของคนธรรมดา:

  • API - คิดถึงincludeไฟล์ พวกเขามีอินเตอร์เฟซการเขียนโปรแกรม
  • ABI - คิดถึงโมดูลเคอร์เนล เมื่อคุณเรียกใช้บนเคอร์เนลบางตัวจะต้องยอมรับวิธีการสื่อสารโดยไม่รวมไฟล์เช่นอินเทอร์เฟซไบนารีระดับต่ำ

13

Linux shared library ตัวอย่าง API ที่ทำงานได้น้อยที่สุดเทียบกับ ABI

คำตอบนี้ถูกดึงออกมาจากคำตอบอื่น ๆ ของฉัน: application binary interface (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_mystrictnew_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จะหยุดรวบรวมทั้งหมด

Semantic API กับการเขียนโปรแกรม API เทียบกับ ABI

นอกจากนี้เรายังสามารถจัดประเภทการเปลี่ยนแปลง API ในประเภทที่สาม: การเปลี่ยนแปลงทางความหมาย

ตัวอย่างเช่นถ้าเรามีการปรับเปลี่ยน

myobject->old_field = old_field;

ถึง:

myobject->old_field = old_field + 1;

จากนั้นสิ่งนี้จะไม่แตก API หรือ ABI แต่main.cจะยังพัง!

นี่เป็นเพราะเราเปลี่ยน "คำอธิบายของมนุษย์" ในสิ่งที่ฟังก์ชั่นควรจะทำแทนที่จะเป็นแง่มุมที่เห็นได้ชัดโดยทางโปรแกรม

ฉันเพิ่งเข้าใจปรัชญาว่าการตรวจสอบอย่างเป็นทางการของซอฟต์แวร์ในแง่หนึ่งย้าย "semantic API" ไปสู่ ​​"API ที่ตรวจสอบได้ทางโปรแกรม" มากกว่า

Semantic API กับ Programming 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 ขอบคุณ!
Rakshith Ravi

9

( pplication B inary ฉัน nterface) ข้อมูลจำเพาะสำหรับแพลตฟอร์มฮาร์ดแวร์เฉพาะรวมกับระบบปฏิบัติการ เป็นขั้นตอนเดียวนอกเหนือจาก API ( A pplication P rogram I nterface) ซึ่งกำหนดการโทรจากแอปพลิเคชันไปยังระบบปฏิบัติการ ABI กำหนด API พร้อมภาษาเครื่องสำหรับตระกูล CPU เฉพาะ API ไม่รับประกันความเข้ากันได้ของรันไทม์ แต่ ABI ทำเช่นนั้นเพราะจะกำหนดรูปแบบภาษาเครื่องหรือรันไทม์

ป้อนคำอธิบายรูปภาพที่นี่

มารยาท


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 class ของฉันและมันก็ทำงานได้ดี ลองนึกภาพรุ่นใหม่ของ A มา

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

ถ้าฉันเพิ่งนำคลาสที่คอมไพล์ใหม่ A มาวางกับคลาสหลักที่คอมไพล์ก่อนหน้านี้ฉันจะได้รับข้อยกเว้นในการพยายามเรียกเมธอด

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

ถ้าฉันคอมไพล์หลักใหม่สิ่งนี้จะได้รับการแก้ไขและทั้งหมดจะทำงานอีกครั้ง


6

โปรแกรมของคุณ (ซอร์สโค้ด) สามารถรวบรวมกับโมดูลที่จัดเตรียมAPIที่เหมาะสม API

โปรแกรม (ไบนารี) ของคุณสามารถทำงานบนแพลตฟอร์มที่ให้ABIที่เหมาะสม ABI

API จำกัด คำจำกัดความประเภทคำจำกัดความฟังก์ชันแมโครบางครั้งตัวแปรโกลบอลที่ไลบรารีควรเปิดเผย

ABI จำกัด สิ่งที่ "แพลตฟอร์ม" ควรจัดให้มีเพื่อให้โปรแกรมของคุณทำงาน ฉันชอบที่จะพิจารณาใน 3 ระดับ:

  • ระดับโปรเซสเซอร์ - ชุดคำสั่งการประชุมที่เรียก

  • ระดับเคอร์เนล - แบบแผนการเรียกของระบบระเบียบแบบพา ธ ไฟล์พิเศษ (เช่น/procและ/sysไฟล์ใน Linux) เป็นต้น

  • ระดับ OS - รูปแบบวัตถุ, ไลบรารีรันไทม์และอื่น ๆ

พิจารณา compiler arm-linux-gnueabi-gccข้ามชื่อ "arm" หมายถึงสถาปัตยกรรมโปรเซสเซอร์ "linux" หมายถึงเคอร์เนล "gnu" หมายถึงโปรแกรมเป้าหมายที่ใช้ libc ของ GNU เป็นไลบรารีรันไทม์ซึ่งแตกต่างจากการarm-linux-androideabi-gccใช้งาน libc ของ Android


1
นี่เป็นคำอธิบายที่ชัดเจนเกี่ยวกับความแตกต่างระหว่างพวกเขาและในมุมมองที่ไม่เหมือนใคร
Sajuuk

1

API- Application Programming Interfaceเป็นอินเทอร์เฟซเวลาคอมไพล์ซึ่งนักพัฒนาสามารถใช้เพื่อใช้งานฟังก์ชันที่ไม่ใช่โครงการเช่นไลบรารี่, OS, คอร์คอร์ในรหัสที่มา

ABI[เกี่ยวกับ] -Application Binary Interfaceเป็นส่วนต่อประสานรันไทม์ซึ่งโปรแกรมใช้ระหว่างการเรียกใช้การสื่อสารระหว่างส่วนประกอบในรหัสเครื่อง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.