Java'nın Yabancı İşlev ve Bellek (FFM) API'si, paylaşılan bir kitaplıktaki koda veya C veya Rust gibi bir programlama dilinde yazılmış DLL'ye erişmek için kullanılır. Ancak kodun bazı gereksinimleri karşılaması gerekir. Bu üç bölümlük makale serisi, bir Java uygulamasının kitaplığın işlevlerini nasıl çağırdığını, hangi hazırlıkların gerekli olduğunu ve hangi kurallara uyulması gerektiğini göstermek için C dilinde yazılmış bir demo kitaplığı kullanır.
Duyurudan sonra devamını okuyun
Rudolf Ziegaus bir yazılım geliştiricisi, Java eğitmeni ve IO Software GmbH'nin CEO'sudur. En sevdiği konular PKi, kriptografi ve düşük seviyeli programlamadır.
İlk bölümde C dilinde yazılmış bir paylaşımlı kütüphanenin Java'ya nasıl yükleneceği ve bu paylaşımlı kütüphanenin basit fonksiyonlarının nasıl çağrılacağı gösterildikten sonra şimdi daha karmaşık senaryolara geçiyoruz. Java'dan değiştirilebilir parametrelerle işlevlerin nasıl çağrılacağını ve dizilerin ve yapıların nasıl aktarılacağını gösterir.
Düzenlenebilir parametrelere sahip işlevler
Önceki örnekler yerel işlev çağrılarını basit tutuyordu. Java uygulaması basitçe parametreleri iletti ve döndürülen değeri kabul etti.
Sonraki örneklerde işler farklı görünüyor. İlk önce basit C işlevi gelir getVersion2işlevi kim seviyor getVersion Bölüm 1'den kitaplık sürümünü belirler. Yeni işlev sürüm numarasını bir değer olarak döndürmez, bunun yerine bir parametreyi değiştirir. C'de bir parametrenin değerini değil, adresini (referansla çağrılır) ileten bir uygulama tarafından çalışır. Bu yapı C'de şöyle görünür:
EXPORT void getVersion2(int* version);
Aşağıdaki Java kodu işlevi çağırır:
Duyurudan sonra devamını okuyun
int version;
getVersion2(&version);
THE & C'de fonksiyonun değişkenin adresini kullandığını belirtir. Java bu prosedüre izin vermez, dolayısıyla bir dönüş değeri gereklidir. Aşağıdaki Java yöntemi C işlevini referansla kullanır:
public int getVersion2() throws Throwable
{
MethodHandle method = getMethodHandle("getVersion2",
FunctionDescriptor.of(
ValueLayout.JAVA_INT,
ValueLayout.ADDRESS
));
try (Arena arena = Arena.ofConfined())
{
MemorySegment versionSeg =
arena.allocate(ValueLayout.JAVA_INT.byteSize());
method.invoke(versionSeg);
int version = versionSeg.get(ValueLayout.JAVA_INT, 0);
return version;
}
}
İlk olarak kod, serinin ilk bölümünde olduğu gibi yöntemi tekrar çağırır. getMethodHandle() AÇIK. Bildirim şunları tanımlar: FunctionDescriptor fonksiyon için getVersion2().
bilgi ValueLayout.ADDRESS parametre için C fonksiyonunun bir adres beklediğini gösterir.
Şimdi en ilginç kısım geliyor: Bir adresi aktarmak için Java uygulamasının dört baytlık bir bellek alanı (veri türü için) oluşturmak amacıyla FFM API'sini kullanması gerekir. int) rezerve edin. Bu, daha önce ilk bölümde açıkladığımız bir arenada gerçekleşir. Arenayı şununla oluştur: try-with-ressources-Beyanname sonrasında arenanın güvence altına alınmasını sağlar try-Blok otomatik olarak kapatılır ve içinde yönetilen bellek otomatik olarak serbest bırakılır. Yukarıdaki örnekte olduğu gibi farklı türde arenalar vardır. ofConfined oluşturulan uygulamanın yalnızca geçerli iş parçacığının belleğine erişebilmesini sağlar. Bir ile ofConfined() Bu nedenle oluşturulan Arena veya ona ayrılan bellek iş parçacığı açısından güvenli değildir.
Bir sonraki adım parametre için gerekli hafıza alanını belirlemektir. version atamak. Arenada bunun için bir yöntem var allocate(). Gerekli hafıza miktarı fonksiyon kullanılarak belirlenebilir. byteSize() değişken için. Değerin Java veri tipinin boyutunu temsil ettiği ve C veri tipi hakkında mutlaka bir şey söylemediği tekrar belirtilmelidir. C fonksiyonu bir olduğundan int-Parametreler, biz güvenliğin yanındayız int C'de her zaman dört bayt vardır. Şunda long-C'deki değer, ancak boyut platforma bağlıdır.
Bellek alanı bir ile temsil edilir MemorySegment yöntemi çağırırken gösterilir invoke C fonksiyonuna aktarılmalıdır.
Uygulama daha sonra sonucu okuyabilir. Bunu soruyor MemorySegment fonksiyon get ve ona bellek düzenini aktarır (bu durumda JAVA_INT) ve okuma için ofset MemorySegment. Örnekte ofset sıfırdır. Belirtiyorum JAVA_INT fonksiyon bir tane döndürür intuygulamanın daha fazla işleyebileceği değer.
Dizi parametreli işlevler
Bir sonraki görev prosedüre dayanır, ancak yalnızca bir değeri işlemekle kalmaz, aynı zamanda bir listeden ortalamayı da belirler. int-değerler. Bunu yapmak için yerel işleve bir dizi sağlamanız gerekir. int-geçiş değerleri:
public double calcAverage(int [] values) throws Throwable
{
MethodHandle calcAverage =
getMethodHandle("calcAverage"),
FunctionDescriptor.of(
ValueLayout.JAVA_DOUBLE, // return value
ValueLayout.ADDRESS, // data values
ValueLayout.JAVA_INT)); // number of elements
try(Arena arena = Arena.ofConfined())
{
long totalSize = ValueLayout.JAVA_INT.byteSize() * values.length;
MemorySegment valueSegment = arena.allocate(totalSize);
for (int i = 0; i < values.length; i++)
{
valueSegment.setAtIndex(ValueLayout.JAVA_INT, i, values[i]);
}
double result = (double) calcAverage.invoke(valueSegment,
values.length);
return result;
}
}
İlk olarak kod, dizinin toplam bellek boyutunu hesaplar (totalSize) ve gerekli hafızayı ayırır allocate(). Kod daha sonra yöntemle belleği ayırır. setAtIndex ilgili olan için MemorySegment. Dizinin her elemanı için çağrı yapılır.
Son olarak kod yöntemi çağırır invoke için MethodHandle ve diziyi ve uzunluğunu parametre olarak iletir. Son olarak C fonksiyonunun sonucunu döndürür.

Bir yanıt yazın