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_field
olduğ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
int
yapının ilkine erişmeye çalışan bir montaj oluşturmuştu , ki bu şimdi new_field
beklenen yerine old_field
.
Bu nedenle bu değişiklik ABI'yi bozdu.
Ancak, biz eklemek new_field
sonra old_field
:
typedef struct {
int old_field;
int new_field;
} mylib_mystruct;
eski oluşturulan montaj hala int
yapı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.c
programı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.c
derlemeyi 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.c
yine 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.