Sự khác biệt giữa API và ABI


192

Tôi chưa quen với lập trình hệ thống linux và tôi đã bắt gặp API và ABI khi đọc Lập trình hệ thống Linux .

Định nghĩa về API:

API xác định các giao diện mà một phần mềm giao tiếp với nhau ở cấp nguồn.

Định nghĩa của ABI:

Trong khi API xác định giao diện nguồn, ABI xác định giao diện nhị phân cấp thấp giữa hai hoặc nhiều phần mềm trên một kiến ​​trúc cụ thể. Nó định nghĩa cách ứng dụng tương tác với chính nó, cách ứng dụng tương tác với kernel và cách ứng dụng tương tác với các thư viện.

Làm thế nào một chương trình có thể giao tiếp ở mức nguồn? Một mức nguồn là gì? Nó có liên quan đến mã nguồn không? Hoặc nguồn của thư viện được bao gồm trong chương trình chính?

Sự khác biệt duy nhất tôi biết là API chủ yếu được sử dụng bởi các lập trình viên và ABI chủ yếu được sử dụng bởi trình biên dịch.


2
theo cấp độ nguồn, chúng có nghĩa là một cái gì đó như bao gồm tệp để hiển thị các định nghĩa hàm
Anycorn

Câu trả lời:


49

API là những gì con người sử dụng. Chúng tôi viết mã nguồn. Khi chúng ta viết một chương trình và muốn sử dụng một số chức năng thư viện, chúng ta viết mã như:

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

và chúng ta cần biết rằng có một phương thức livenMyHills(), trong đó có một tham số nguyên dài. Vì vậy, như một Giao diện lập trình, tất cả được thể hiện bằng mã nguồn. Trình biên dịch biến điều này thành các hướng dẫn thực thi phù hợp với việc thực hiện ngôn ngữ này trên hệ điều hành cụ thể này. Và trong trường hợp này dẫn đến một số hoạt động cấp thấp trên một đơn vị Âm thanh. Vì vậy, các bit và byte cụ thể được phun ra ở một số phần cứng. Vì vậy, trong thời gian chạy, có rất nhiều hành động cấp nhị phân đang diễn ra mà chúng ta thường không thấy.


308

API: Giao diện chương trình ứng dụng

Đây là tập hợp các loại / biến / hàm công khai mà bạn trưng ra từ ứng dụng / thư viện của bạn.

Trong C / C ++, đây là những gì bạn thể hiện trong các tệp tiêu đề mà bạn gửi cùng với ứng dụng.

ABI: Giao diện nhị phân ứng dụng

Đây là cách trình biên dịch xây dựng một ứng dụng.
Nó định nghĩa mọi thứ (nhưng không giới hạn):

  • Làm thế nào các tham số được truyền cho các chức năng (thanh ghi / ngăn xếp).
  • Ai dọn sạch các tham số từ ngăn xếp (người gọi / callee).
  • Trường hợp giá trị trả lại được đặt để trả về.
  • Làm thế nào ngoại lệ tuyên truyền.

17
Đây có lẽ là lời giải thích ngắn gọn nhất về ABI là gì, mà tôi từng thấy; gj
TerryP

3
Các bạn cần phải quyết định xem câu trả lời này là ngắn gọn hay chi tiết. :)
jrok

1
@jrok: Mọi thứ có thể ngắn gọn và chi tiết chúng không loại trừ lẫn nhau.
Martin York

@LokiAstari, vậy ABI có thực sự là một API không?
Pacerier

4
@Pacerier: Cả hai đều là giao diện. Nhưng chúng ở mức độ trừu tượng khác nhau. API ở cấp độ nhà phát triển ứng dụng. ABI ở cấp độ trình biên dịch (nơi mà một nhà phát triển ứng dụng không bao giờ đi).
Martin York

47

Tôi chủ yếu bắt gặp các thuật ngữ này theo nghĩa thay đổi không tương thích API hoặc thay đổi không tương thích ABI.

Thay đổi API về cơ bản là nơi mã sẽ được biên dịch với phiên bản trước sẽ không hoạt động nữa. Điều này có thể xảy ra vì bạn đã thêm một đối số cho một hàm hoặc thay đổi tên của một cái gì đó có thể truy cập bên ngoài mã địa phương của bạn. Bất cứ khi nào bạn thay đổi một tiêu đề và nó buộc bạn phải thay đổi một cái gì đó trong tệp .c / .cpp, bạn đã thực hiện thay đổi API.

Thay đổi ABI là nơi mã đã được biên dịch so với phiên bản 1 sẽ không còn hoạt động với phiên bản 2 của cơ sở mã (thường là thư viện). Điều này thường khó theo dõi hơn so với thay đổi không tương thích API vì một số thứ đơn giản như việc thêm một phương thức ảo vào một lớp có thể không tương thích ABI.

Tôi đã tìm thấy hai tài nguyên cực kỳ hữu ích để tìm ra khả năng tương thích ABI là gì và cách bảo quản nó:


4
+1 để chỉ ra tính độc quyền lẫn nhau của họ. Ví dụ: việc giới thiệu từ khóa khẳng định của Java là một tài liệu thay đổi không tương thích với API nhưng không tương thích với ABI docs.oracle.com/javase/7/docs/technotes/guides/lingu/ tựa .
Pacerier

Bạn có thể thêm vào phần tài nguyên của mình "3.6. Thư viện không tương thích" của tldp.org/HOWTO/Program-L Library- HOWTO / shared-l library.html , trong đó liệt kê những gì có thể gây ra thay đổi ABI.
Demi-Lune

20

Đây là lời giải thích cư sĩ của tôi:

  • API - nghĩ về includecác tập tin. Họ cung cấp giao diện lập trình.
  • ABI - nghĩ về mô-đun hạt nhân. Khi bạn chạy nó trên một số kernel, nó phải đồng ý về cách giao tiếp mà không bao gồm các tệp, tức là giao diện nhị phân cấp thấp.

13

Thư viện chia sẻ Linux có thể chạy API tối thiểu so với ví dụ ABI

Câu trả lời này đã được trích xuất từ ​​câu trả lời khác của tôi: Giao diện nhị phân ứng dụng (ABI) là gì? nhưng tôi cảm thấy rằng nó cũng trả lời trực tiếp câu hỏi này và các câu hỏi không trùng lặp.

Trong ngữ cảnh của các thư viện dùng chung, ý nghĩa quan trọng nhất của việc "có ABI ổn định" là bạn không cần phải biên dịch lại các chương trình của mình sau khi thư viện thay đổi.

Như chúng ta sẽ thấy trong ví dụ dưới đây, có thể sửa đổi ABI, phá vỡ các chương trình, mặc dù API không thay đổi.

C chính

#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

Biên dịch và chạy tốt với:

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

Bây giờ, giả sử rằng đối với v2 của thư viện, chúng tôi muốn thêm một trường mới để mylib_mystrictgọi new_field.

Nếu chúng ta đã thêm trường trước old_fieldnhư trong:

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

và xây dựng lại thư viện nhưng không main.out, sau đó khẳng định thất bại!

Điều này là do dòng:

myobject->old_field == 1

đã tạo ra hội đồng đang cố gắng truy cập vào đầu tiên intcủa cấu trúc, mà bây giờ new_fieldthay vì dự kiến old_field.

Do đó, sự thay đổi này đã phá vỡ ABI.

Tuy nhiên, nếu chúng tôi thêm new_fieldsau old_field:

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

sau đó, hội đồng được tạo cũ vẫn truy cập vào intcấu trúc đầu tiên và chương trình vẫn hoạt động, bởi vì chúng tôi giữ cho ABI ổn định.

Đây là phiên bản hoàn toàn tự động của ví dụ này trên GitHub .

Một cách khác để giữ ABI ổn định này sẽ là xử lý mylib_mystructnhư một cấu trúc mờ và chỉ truy cập vào các trường của nó thông qua các trình trợ giúp phương thức. Điều này giúp cho việc giữ ABI ổn định dễ dàng hơn, nhưng sẽ phải chịu chi phí hoạt động khi chúng tôi thực hiện nhiều cuộc gọi chức năng hơn.

API so với ABI

Trong ví dụ trước, thật thú vị khi lưu ý rằng việc thêm new_fieldtrước đó old_field, chỉ phá vỡ ABI chứ không phá vỡ API.

Điều này có nghĩa là, nếu chúng tôi đã biên dịch lại main.cchương trình của chúng tôi chống lại thư viện, nó sẽ hoạt động bất kể.

Tuy nhiên, chúng tôi cũng đã phá vỡ API nếu chúng tôi đã thay đổi ví dụ chữ ký hàm:

mylib_mystruct* mylib_init(int old_field, int new_field);

vì trong trường hợp đó, main.csẽ ngừng biên dịch hoàn toàn.

API ngữ nghĩa và API lập trình so với ABI

Chúng tôi cũng có thể phân loại các thay đổi API theo loại thứ ba: thay đổi ngữ nghĩa.

Ví dụ: nếu chúng tôi đã sửa đổi

myobject->old_field = old_field;

đến:

myobject->old_field = old_field + 1;

sau đó, điều này sẽ phá vỡ cả API và ABI, nhưng main.cvẫn sẽ phá vỡ!

Điều này là do chúng tôi đã thay đổi "mô tả con người" về những gì chức năng được cho là làm thay vì khía cạnh đáng chú ý về mặt lập trình.

Tôi chỉ có cái nhìn sâu sắc về mặt triết học rằng việc xác minh chính thức phần mềm theo nghĩa chuyển nhiều hơn "API ngữ nghĩa" thành một "API có thể kiểm chứng bằng lập trình" hơn.

API ngữ nghĩa và API lập trình

Chúng tôi cũng có thể phân loại các thay đổi API theo loại thứ ba: thay đổi ngữ nghĩa.

API ngữ nghĩa, thường là một mô tả ngôn ngữ tự nhiên về những gì API phải làm, thường được bao gồm trong tài liệu API.

Do đó, có thể phá vỡ API ngữ nghĩa mà không phá vỡ bản dựng chương trình.

Ví dụ: nếu chúng tôi đã sửa đổi

myobject->old_field = old_field;

đến:

myobject->old_field = old_field + 1;

sau đó, điều này sẽ không phá vỡ API lập trình, cũng không phải ABI, nhưng main.cAPI ngữ nghĩa sẽ phá vỡ.

Có hai cách để lập trình kiểm tra API hợp đồng:

  • kiểm tra một loạt các trường hợp góc. Dễ làm, nhưng bạn luôn có thể bỏ lỡ một.
  • xác minh chính thức . Khó hơn để làm, nhưng tạo ra bằng chứng toán học về tính chính xác, về cơ bản thống nhất tài liệu và kiểm tra thành một cách có thể kiểm chứng "con người" / máy! Miễn là không có lỗi trong mô tả chính thức của bạn ;-)

Đã thử nghiệm trong Ubuntu 18.10, GCC 8.2.0.


2
Câu trả lời của bạn đủ chi tiết để giúp tôi hiểu sự khác biệt giữa API và ABI. Cảm ơn bạn!
Rakshith Ravi

9

( Một pplication B inary tôi nterface) Một đặc điểm kỹ thuật cho một nền tảng phần cứng cụ thể kết hợp với hệ điều hành. Đây là một bước tiến về API ( Một pplication P rogram tôi nterface), trong đó xác định các cuộc gọi từ các ứng dụng cho hệ điều hành. ABI định nghĩa API cộng với ngôn ngữ máy cho một họ CPU cụ thể. API không đảm bảo khả năng tương thích thời gian chạy, nhưng ABI thì có, bởi vì nó xác định ngôn ngữ máy hoặc định dạng thời gian chạy.

nhập mô tả hình ảnh ở đây

Phép lịch sự


9

Hãy để tôi đưa ra một ví dụ cụ thể về cách ABI và API khác nhau trong Java.

Một thay đổi không tương thích ABI là nếu tôi thay đổi một phương thức A # m () từ lấy một Stringđối số thành String...đối số. Điều này không tương thích với ABI vì bạn phải biên dịch lại mã đang gọi đó, nhưng nó tương thích với API vì bạn có thể giải quyết nó bằng cách biên dịch lại mà không có bất kỳ thay đổi mã nào trong trình gọi.

Dưới đây là ví dụ đánh vần. Tôi có thư viện Java của mình với lớp A

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

Và tôi có một lớp sử dụng thư viện này

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

Bây giờ, tác giả thư viện đã biên soạn lớp A của họ, tôi đã biên soạn lớp chính của mình và tất cả đều hoạt động tốt. Hãy tưởng tượng một phiên bản mới của A đến

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

Nếu tôi chỉ lấy lớp A đã biên dịch mới và thả nó cùng với lớp được biên dịch trước đó, tôi sẽ có một ngoại lệ khi cố gắng gọi phương thức

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

Nếu tôi biên dịch lại Main, cái này đã được sửa và tất cả đều hoạt động trở lại.


6

Chương trình của bạn (mã nguồn) có thể được biên dịch với các mô-đun cung cấp API phù hợp .

Chương trình của bạn (nhị phân) có thể chạy trên các nền tảng cung cấp ABI thích hợp .

API hạn chế định nghĩa kiểu, định nghĩa hàm, macro, đôi khi các biến toàn cục mà thư viện nên trưng ra.

ABI hạn chế những gì một "nền tảng" sẽ cung cấp cho bạn để chương trình chạy trên đó. Tôi thích xem xét nó ở 3 cấp độ:

  • cấp bộ xử lý - tập lệnh, quy ước gọi

  • cấp độ kernel - quy ước gọi hệ thống, quy ước đường dẫn tệp đặc biệt (ví dụ: /proc/syscác tệp trong Linux), v.v.

  • Cấp độ hệ điều hành - định dạng đối tượng, thư viện thời gian chạy, v.v.

Hãy xem xét một trình biên dịch chéo có tên arm-linux-gnueabi-gcc. "arm" biểu thị kiến ​​trúc bộ xử lý, "linux" biểu thị kernel, "gnu" biểu thị các chương trình mục tiêu của nó sử dụng libc của GNU làm thư viện thời gian chạy, khác với arm-linux-androideabi-gccviệc sử dụng triển khai libc của Android.


1
đây là một lời giải thích rất ngắn gọn về sự khác biệt giữa chúng và trong một viễn cảnh rất độc đáo.
Sajuuk

1

API- Application Programming Interfacelà giao diện thời gian biên dịch có thể được nhà phát triển sử dụng để sử dụng chức năng phi dự án như thư viện, HĐH, các cuộc gọi cốt lõi trong mã nguồn

ABI[Giới thiệu] -Application Binary Interfacelàgiao diện thời gian chạy được sử dụng bởi một chương trình trong khi thực hiện để liên lạc giữa các thành phần trong mã máy

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.