Jestem nowy w programowaniu w systemie Linux i natknąłem się na API i ABI podczas czytania Linux System Programming .
Definicja API:
Interfejs API definiuje interfejsy, za pomocą których jedno oprogramowanie komunikuje się z innym na poziomie źródła.
Definicja ABI:
Podczas gdy interfejs API definiuje interfejs źródłowy, interfejs ABI definiuje interfejs binarny niskiego poziomu między dwoma lub więcej programami w określonej architekturze. Definiuje sposób interakcji aplikacji z samym sobą, sposób interakcji aplikacji z jądrem oraz sposób interakcji aplikacji z bibliotekami.
Jak program może komunikować się na poziomie źródłowym? Co to jest poziom źródłowy? Czy w ogóle jest to związane z kodem źródłowym? Czy źródło biblioteki zostaje włączone do programu głównego?
Jedyną różnicą, jaką znam, jest to, że API jest najczęściej używany przez programistów, a ABI jest głównie używany przez kompilator.
Odpowiedzi:
Interfejs API jest tym, czego używają ludzie. Piszemy kod źródłowy. Kiedy piszemy program i chcemy skorzystać z funkcji biblioteki, piszemy kod taki jak:
i musieliśmy wiedzieć, że istnieje metoda
livenMyHills()
, która wymaga długiego parametru liczby całkowitej. Jako interfejs programistyczny wszystko jest wyrażone w kodzie źródłowym. Kompilator zamienia to w instrukcje wykonywalne, które są zgodne z implementacją tego języka w tym systemie operacyjnym. I w tym przypadku powodują pewne operacje na niskim poziomie na urządzeniu audio. Tak więc niektóre bity i bajty są wyrzucane na jakiś sprzęt. Więc w czasie wykonywania dzieje się wiele akcji na poziomie binarnym, których zwykle nie widzimy.źródło
API: interfejs aplikacji
Jest to zestaw typów publicznych / zmiennych / funkcji udostępnianych przez aplikację / bibliotekę.
W C / C ++ ujawnia się to w plikach nagłówkowych dostarczanych z aplikacją.
ABI: Application Binary Interface
W ten sposób kompilator tworzy aplikację.
Definiuje rzeczy (ale nie ogranicza się do):
źródło
Najczęściej spotykam się z tymi terminami w znaczeniu zmiany niezgodnej z API lub zmiany niezgodnej z ABI.
Zmiana interfejsu API polega zasadniczo na tym, że kod, który zostałby skompilowany z poprzednią wersją, nie będzie już działał. Może się tak zdarzyć, ponieważ dodałeś argument do funkcji lub zmieniłeś nazwę czegoś dostępnego poza lokalnym kodem. Za każdym razem, gdy zmieniasz nagłówek, który zmusza cię do zmiany czegoś w pliku .c / .cpp, dokonałeś zmiany API.
Zmiana ABI polega na tym, że kod, który został już skompilowany z wersją 1, nie będzie już działał z wersją 2 bazy kodu (zwykle biblioteki). Jest to na ogół trudniejsze do śledzenia niż zmiana niezgodna z API, ponieważ coś tak prostego jak dodanie metody wirtualnej do klasy może być niezgodne z ABI.
Znalazłem dwa niezwykle przydatne zasoby, aby dowiedzieć się, czym jest kompatybilność ABI i jak ją zachować:
źródło
Oto moje wyjaśnienia dla laika:
include
plikach. Zapewniają interfejsy programowania.źródło
Przykład współdzielonej biblioteki Linuksa w Linuksie z minimalnym działaniem API vs ABI
Ta odpowiedź została wyodrębniona z mojej drugiej odpowiedzi: Co to jest interfejs binarny aplikacji (ABI)?ale czułem, że bezpośrednio odpowiada również na to pytanie i że pytania nie są duplikatami.
W kontekście bibliotek współdzielonych najważniejszą implikacją „posiadania stabilnego ABI” jest to, że nie trzeba ponownie kompilować programów po zmianach w bibliotece.
Jak zobaczymy w poniższym przykładzie, możliwe jest zmodyfikowanie ABI, łamanie programów, nawet jeśli API pozostaje niezmienione.
main.c
mylib.c
mylib.h
Kompiluje się i działa poprawnie z:
Załóżmy teraz, że dla v2 biblioteki chcemy dodać nowe pole do
mylib_mystrict
wywołanegonew_field
.Jeśli dodaliśmy pole wcześniej
old_field
jak w:i przebudował bibliotekę, ale nie
main.out
, to nie powiedzie się!Wynika to z faktu, że wiersz:
wygenerował zestaw, który próbuje uzyskać dostęp do pierwszej
int
struktury, która jest teraznew_field
zamiast oczekiwanejold_field
.Dlatego ta zmiana złamała ABI.
Jeśli jednak dodajemy
new_field
poold_field
:wtedy stary wygenerowany zestaw nadal uzyskuje dostęp do pierwszej
int
struktury, a program nadal działa, ponieważ utrzymaliśmy stabilność ABI.Oto w pełni zautomatyzowana wersja tego przykładu na GitHub .
Innym sposobem na utrzymanie stabilnego ABI byłoby traktowanie go
mylib_mystruct
jako nieprzezroczystej struktury i dostęp do jego pól tylko poprzez pomocników metod. Ułatwia to utrzymanie stabilności ABI, ale wiązałoby się to z dodatkowym obciążeniem wydajności, ponieważ wykonujemy więcej wywołań funkcji.API vs ABI
W poprzednim przykładzie warto zauważyć, że dodanie
new_field
wcześniejszegoold_field
tylko zepsuło ABI, ale nie API.Oznacza to, że gdybyśmy skompilowali nasz
main.c
program z biblioteką, działałby niezależnie.Zerwalibyśmy również interfejs API, gdybyśmy zmienili na przykład podpis funkcji:
ponieważ w takim przypadku
main.c
przestałby się całkowicie kompilować.Semantyczny interfejs API a programujący interfejs API a ABI
Możemy również klasyfikować zmiany API w trzecim typie: zmiany semantyczne.
Na przykład, gdybyśmy zmodyfikowali
do:
to nie zepsułoby ani API ani ABI, ale i tak by się zepsuło
main.c
!Jest tak, ponieważ zmieniliśmy „ludzki opis” tego, co funkcja ma robić, a nie aspekt zauważalny programowo.
Właśnie miałem filozoficzny wgląd, że formalna weryfikacja oprogramowania w pewnym sensie przenosi więcej z „semantycznego API” do bardziej „programowo weryfikowalnego API”.
Semantyczny interfejs API a programujący interfejs API
Możemy również klasyfikować zmiany API w trzecim typie: zmiany semantyczne.
Semantyczny interfejs API jest zwykle opisem w języku naturalnym tego, co powinien robić interfejs API, zwykle zawartym w dokumentacji interfejsu API.
Możliwe jest zatem zerwanie semantycznego interfejsu API bez naruszenia samej kompilacji programu.
Na przykład, gdybyśmy zmodyfikowali
do:
wtedy nie złamałoby to ani programowania API, ani ABI, ale
main.c
semantyczny API by się .Istnieją dwa sposoby programowego sprawdzenia interfejsu API umowy:
Testowane w Ubuntu 18.10, GCC 8.2.0.
źródło
( ASTOSOWANIE B inary I nterface) Opis specyficznych dla platformy sprzętowej połączeniu z systemem operacyjnym. Jest to jeden krok poza API ( TOSOWANIE P ROGRAM I nterface), który określa połączeń z aplikacji do systemu operacyjnego. ABI definiuje API oraz język maszynowy dla konkretnej rodziny procesorów. Interfejs API nie zapewnia zgodności środowiska wykonawczego, ale interfejs ABI tak, ponieważ definiuje język maszyny lub format środowiska wykonawczego.
Kurtuazja
źródło
Podam konkretny przykład, w jaki sposób ABI i API różnią się w Javie.
Zmiana niekompatybilna z ABI polega na zmianie metody A # m () z przejmowania
String
argumentu naString...
argument. Nie jest to zgodne z ABI, ponieważ musisz ponownie skompilować kod, który to wywołuje, ale jest kompatybilny z interfejsem API, ponieważ możesz go rozwiązać poprzez rekompilację bez żadnych zmian w kodzie wywołującym.Oto opisany przykład. Mam swoją bibliotekę Java z klasą A.
I mam klasę, która korzysta z tej biblioteki
Teraz autor biblioteki skompilował swoją klasę A, skompilowałem swoją klasę Main i wszystko działa dobrze. Wyobraź sobie, że nadchodzi nowa wersja A.
Jeśli po prostu wezmę nową skompilowaną klasę A i upuszczę ją razem z poprzednio skompilowaną klasą Main, otrzymam wyjątek przy próbie wywołania metody
Jeśli ponownie skompiluję Main, zostanie to naprawione i wszystko znów będzie działać.
źródło
Twój program (kod źródłowy) można skompilować z modułami, które zapewniają odpowiedni interfejs API .
Twój program (binarny) może działać na platformach, które zapewniają odpowiedni ABI .
Interfejs API ogranicza definicje typów, definicje funkcji, makra, czasem zmienne globalne, które biblioteka powinna ujawnić.
ABI ogranicza to, co „platforma” powinna zapewniać programowi do działania. Lubię rozważyć to na 3 poziomach:
poziom procesora - zestaw instrukcji, konwencja wywoływania
poziom jądra - konwencja wywołań systemowych, specjalna konwencja ścieżek plików (np.
/proc
i/sys
pliki w systemie Linux) itp.Poziom systemu operacyjnego - format obiektu, biblioteki wykonawcze itp.
Zastanów się nad kompilatorem krzyżowym o nazwie
arm-linux-gnueabi-gcc
. „uzbrojenie” oznacza architekturę procesora, „linux” oznacza jądro, „gnu” oznacza, że jego programy docelowe używają biblioteki GNU libc jako biblioteki wykonawczej, inne niż te,arm-linux-androideabi-gcc
które używają implementacji libc Androida.źródło
API
-Application Programming Interface
to interfejs czasu kompilacji , który może być wykorzystywany przez programistę do korzystania z funkcji niezwiązanych z projektem, takich jak biblioteka, system operacyjny, podstawowe wywołania w kodzie źródłowymABI
[About] -Application Binary Interface
jestinterfejsem wykonawczym używanym przez program podczas wykonywania komunikacji między komponentami w kodzie maszynowymźródło