色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

調(diào)用一些C本機代碼來介紹外部函數(shù)和內(nèi)存API

jf_ro2CN3Fa ? 來源:Denys Makogon ? 2023-02-21 17:28 ? 次閱讀

隨著 JDK 19 在未來幾周*內(nèi)發(fā)布,是時候討論巴拿馬(Panama)項目了,更具體地說,是新的外部函數(shù)和內(nèi)存 API,它簡化了 Java 和本機代碼之間的互操作性。

本文使用一個簡單的基于 Java 的“Hello World”應(yīng)用程序調(diào)用一些 C 本機代碼來介紹外部函數(shù)和內(nèi)存 API。

準備

要使用 Foreign Function & Memory API 和示例代碼,請先下載 JDK 19(build 24 或更高版本)。

項目概述

巴拿馬項目旨在為 JVM 和用其他語言(如 C/C++)編寫的本機代碼之間搭建橋梁。包含以下 3 個部分:

外部函數(shù)和內(nèi)存 API:JEP 424

Jextract 工具

Vector API:JEP 338

外部函數(shù)和內(nèi)存 API 提供一些重要的抽象:

內(nèi)存段及其地址:一組 API 類,用于處理本機內(nèi)存和指向它的指針;

內(nèi)存布局和描述符:用于模擬外部類型(結(jié)構(gòu)、原語)和函數(shù)描述符的 API;

內(nèi)存會話:管理一個或多個內(nèi)存資源生命周期的抽象;

鏈接器和符號查找:一組用于執(zhí)行向下和向上調(diào)用的 API 類;

段分配器:一種用于在內(nèi)存會話中分配內(nèi)存段的 API。

Hello World 程序

對巴拿馬了解得越深,就越會發(fā)現(xiàn)擁有一個好的介紹是至關(guān)重要的,這樣就不會錯過重要的概念、技術(shù)和方法。

本文將介紹鏈接器(Linker),并簡要介紹 SymbolLookup 方法和本機內(nèi)存管理 ( MemorySession )。上面描述的這三個主要組件是構(gòu)建塊,用于更深入地開發(fā)由 Java 和本機代碼組成的程序。

鏈接器

從技術(shù)角度來看,鏈接器是兩個二進制接口之間的橋梁:JVM 和 C/C++ 本機代碼,也稱為 C ABI。

JDK 19 為所有流行的平臺提供了一組 C ABI 實現(xiàn):

publicstaticLinkergetSystemLinker(){
returnswitch(CABI.current()){
caseWin64->Windowsx64Linker.getInstance();
caseSysV->SysVx64Linker.getInstance();
caseLinuxAArch64->LinuxAArch64Linker.getInstance();
caseMacOsAArch64->MacOsAArch64Linker.getInstance();
};
}

在 JDK 術(shù)語中,鏈接器是特定于平臺的 C ABI 實現(xiàn)的一個實例。鏈接器提供一組方法來執(zhí)行向下調(diào)用和向上調(diào)用,其中:

downcall 是從高級子系統(tǒng)發(fā)起的事件。在我們的例子中是 JVM 到較低級別的子系統(tǒng),如操作系統(tǒng)內(nèi)核或者一些 Java 代碼調(diào)用一些本機代碼。稍后將通過外部函數(shù)和內(nèi)存 API 說明這一點。

upcall 例如一些本機代碼調(diào)用一些 Java 代碼。

雖然鏈接器就像電話一樣,想打電話給誰,只需撥入正確的電話號碼即可。符號查找方法就像通訊錄,只需提供要打電話的人正確的信息即可。

要執(zhí)行向下調(diào)用,需要提供調(diào)用的(本機)函數(shù)的描述符、通過符號查找分配的本機地址,以及用于創(chuàng)建調(diào)用本機函數(shù)的方法句柄對應(yīng)的鏈接器。

從 Java 實現(xiàn)經(jīng)典的 C 風(fēng)格的 Hello World:

intprintf(constchar*__restrict,...)

Java 中的 C 語言風(fēng)格的“Hello World”

要編寫使用本機 printf 函數(shù)的基于 Java 的“Hello World”應(yīng)用程序,我們需要:

1. 找到 native 函數(shù)的地址

首先,我們需要搜索 printf 函數(shù)的本機內(nèi)存地址:

Linkerlinker=Linker.nativeLinker();
SymbolLookuplinkerLookup=linker.defaultLookup();
SymbolLookupsystemLookup=SymbolLookup.loaderLookup();

SymbolLookupsymbolLookup=name->
systemLookup.lookup(name).or(()->linkerLookup.lookup(name));

OptionalprintfMemorySegment=symbolLookup.lookup("printf");

從技術(shù)上講,查找可能會失敗,因此需要提供適當?shù)腻e誤處理。

2. 構(gòu)建正在調(diào)用的函數(shù)的描述符

一旦知道了 C printf 所在的位置,就需要定義由結(jié)果類型和接受的參數(shù)組成的 printf 描述符。值得一提的是,像 printf 這樣的本機函數(shù)稱為可變參數(shù)函數(shù)。在 Java 中,接受可變參數(shù)集的方法稱為具有可變參數(shù)的方法。

為了簡化,我們可以為 printf 定義 FunctionDescriptor 的簡化版本:

FunctionDescriptorprintfDescriptor=FunctionDescriptor.of(JAVA_INT,ADDRESS);

注意 :從 Java 運行時的角度來看,C 指針背后的值類型無關(guān)緊要,因為 C 指針的內(nèi)存布局不保存類型,而是平臺固定的 32/64 位值。

一個描述符定義了一個返回值類型為 int 的函數(shù),它的參數(shù)是一個指針。假設(shè)一個描述符幾乎對應(yīng)于它在 stdio.h 中的 C 定義,因為它定義了一個標準函數(shù),而 printf 是一個可變參數(shù)函數(shù)。

通過值布局(Value Layout)在 Java 中對 C 類型建模

在 Java 中,值布局用于對與基本數(shù)據(jù)類型的值關(guān)聯(lián)的內(nèi)存布局建模,例如整數(shù)類型(有符號或無符號)和浮點類型。JAVA_INT 和 ADDRESS 都是對應(yīng)的 C 類型的值布局。

JAVA_INT :

//ValueLayout.OfInt.class
OfIntJAVA_INT=newOfInt(ByteOrder.nativeOrder()).withBitAlignment(32);

這是值布局的一個實例,它的載體是 int.class。通過這種布局,鏈接器被指示在 C int32和具有運營商類 int.class 的相應(yīng) Java int 類型之間創(chuàng)建橋梁。

ADDRESS:

//ValueLayout.OfAddress.class
OfAddressADDRESS=newOfAddress(ByteOrder.nativeOrder())
.withBitAlignment(ValueLayout.ADDRESS_SIZE_BITS);

ADDRESS 是一個值布局,其中對應(yīng)的 C 類型是一個指向變量的指針,載體是MemoryAddress.class。

3. 從函數(shù)的本機內(nèi)存地址構(gòu)建方法句柄

使用 C printf 本機地址及其函數(shù)描述符,我們現(xiàn)在可以為 C printf 創(chuàng)建一個方法句柄:

MethodHandleprintfMethodHandle=symbolLookup.lookup("printf").map(
addr->linker.downcallHandle(addr,printfDescriptor)
).orElse(null);

上面的代碼創(chuàng)建了 C print 的可執(zhí)行引用,簡而言之:一個方法句柄,來自 printf 的本機內(nèi)存地址及其函數(shù)描述符。

注意 :方法句柄是對底層方法、構(gòu)造函數(shù)、字段或類似低級操作的類型化、可執(zhí)行引用,具有參數(shù)或返回值的可選轉(zhuǎn)換。

現(xiàn)在已經(jīng)解釋了必要的概念,我們可以擴展 downcalls 和 upcalls 的定義:

downcall 是通過由本機函數(shù)地址及其 Java 版本的函數(shù)描述符形成的 MethodHandle調(diào)用本機函數(shù)。

upcall 是通過 MethodHandle 調(diào)用一些用 Java 編寫的代碼,該 MethodHandle 轉(zhuǎn)換為本機內(nèi)存段,然后可以將其作為函數(shù)指針傳遞給本機函數(shù)。

4. 分配本機內(nèi)存

我們需要以某種方式將 Java 對象綁定到本機內(nèi)存段,以確保 C printf 可以訪問它們。

C 中的內(nèi)存分配和釋放內(nèi)存都很痛苦,因為開發(fā)人員可能會忘記分配或釋放內(nèi)存,這會導(dǎo)致程序泄漏或因分段錯誤而崩潰。

另一方面,Java 依靠垃圾收集器來分配和釋放內(nèi)存。但是巴拿馬的外部函數(shù)和內(nèi)存 API 是在堆外分配內(nèi)存,有助于分配堆外內(nèi)存,這是任何本機互操作的關(guān)鍵部分!

外部函數(shù)和內(nèi)存 API 允許開發(fā)人員分配和訪問內(nèi)存段、它們的地址以及位于堆上或堆外的連續(xù)內(nèi)存區(qū)域的形狀。所有分配的內(nèi)存段都綁定到特定的內(nèi)存會話 ( MemorySession )。內(nèi)存會話的實例提供一組 API 來分配本機內(nèi)存段。考慮一個內(nèi)存會話,就像一個統(tǒng)一的內(nèi)存分配工具,比如 C malloc。MemorySession 實現(xiàn)了 AutoClosable 接口,它使用 try-with-resources 結(jié)構(gòu)極大地簡化了取消分配。

外部函數(shù)和內(nèi)存 API 提供了不止一種分配內(nèi)存段的正確方法。一種可能的本機內(nèi)存分配方法是 SegmentAllocator,它類似于 MemorySession:

try(varmemorySession=MemorySession.openConfined()){
SegmentAllocatorallocator=SegmentAllocator.newNativeArena(memorySession);
varcStringFromAllocator=allocator.allocateUtf8String("HelloWorld"+"
");
varcStringFromSession=memorySession.allocateUtf8String("HelloWorld"+"
");
}

簡單起見,這個“Hello World”應(yīng)用程序?qū)⑹褂?MemorySession 作為內(nèi)存段分配工具。

最后,要調(diào)用 C printf,我們需要使用 MemorySession 在內(nèi)存會話中分配 const char * 內(nèi)存段,并將其傳遞給 C printf 函數(shù):

MemorySegmentcString=memorySession.allocateUtf8String(str+"
");

使用分配的內(nèi)存段,我們可以調(diào)用函數(shù):

privatestaticintprintf(Stringstr,MemorySessionmemorySession)throwsThrowable{
Objects.requireNonNull(printfMethodHandle);
varcString=memorySession.allocateUtf8String(str+"
");
return(int)printfMethodHandle.invoke(cString);
}

publicstaticvoidmain(String[]args)throwsThrowable{
varstr="HelloWorld";
try(varmemorySession=MemorySession.openConfined()){
System.out.println(printf(str,memorySession));
}
}

5. 小結(jié)

到目前為止,我們了解到內(nèi)存會話 ( MemorySession ) 或段分配器 ( SegmentAllocator ) 是執(zhí)行內(nèi)存分配的關(guān)鍵 API。應(yīng)使用 try-with-resources 聲明內(nèi)存會話以實現(xiàn)隱式內(nèi)存釋放。分配內(nèi)存段有多種選擇——通過段分配器或直接通過內(nèi)存會話。鏈接器、符號查找對象、值和內(nèi)存布局以及方法句柄都是靜態(tài)對象。

總結(jié)

本文概述了外部函數(shù)和內(nèi)存 API,并研究了如何從 Java 調(diào)用簡單的 C 函數(shù)。

好消息是開發(fā)人員可以依靠 jextract 工具來處理大部分外部函數(shù)和內(nèi)存機制。

使用外部函數(shù)和內(nèi)存 API 從 Java 調(diào)用本機代碼時需要解決幾個問題:

獲取本機庫及其對應(yīng)的頭文件。

在 Java 中構(gòu)建函數(shù)描述符 ( FunctionDescriptor )。

查找函數(shù)符號的本機內(nèi)存地址,并為其創(chuàng)建方法句柄。

創(chuàng)建一個相關(guān)的方法句柄并確認它已經(jīng)正確創(chuàng)建(例如,如果本機庫不在系統(tǒng)路徑中,查找將失敗并且返回一個方法句柄將為空)。

決定應(yīng)用程序?qū)⑷绾畏峙鋬?nèi)存段:通過段分配器或內(nèi)存會話。確保內(nèi)存分配技術(shù)在應(yīng)用程序的整個代碼庫中保持一致。

代碼清單

packagecom.java_devrel.samples.panama.part_1;
importjava.lang.foreign.*;
importjava.lang.invoke.MethodHandle;
importjava.util.Objects;
importstaticjava.lang.foreign.ValueLayout.ADDRESS;
importstaticjava.lang.foreign.ValueLayout.JAVA_INT;
publicclassPrintfSimplified{
privatestaticfinalLinkerlinker=Linker.nativeLinker();
privatestaticfinalSymbolLookuplinkerLookup=linker.defaultLookup();
privatestaticfinalSymbolLookupsystemLookup=SymbolLookup.loaderLookup();
privatestaticfinalSymbolLookupsymbolLookup=name->systemLookup.lookup(name).or(()->linkerLookup.lookup(name));
privatestaticfinalFunctionDescriptorprintfDescriptor=FunctionDescriptor.of(JAVA_INT.withBitAlignment(32),ADDRESS.withBitAlignment(64));
privatestaticfinalMethodHandleprintfMethodHandle=symbolLookup.lookup("printf").map(addr->linker.downcallHandle(addr,printfDescriptor)).orElse(null);
privatestaticintprintf(Stringstr,MemorySessionmemorySession)throwsThrowable{
Objects.requireNonNull(printfMethodHandle);
varcString=memorySession.allocateUtf8String(str+"
");
return(int)printfMethodHandle.invoke(cString);
}
publicstaticvoidmain(String[]args)throwsThrowable{
varstr="helloworld";
try(varmemorySession=MemorySession.openConfined()){
System.out.println(printf(str,memorySession));
}
}
}






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2972

    瀏覽量

    104855
  • JVM
    JVM
    +關(guān)注

    關(guān)注

    0

    文章

    158

    瀏覽量

    12238
  • C++語言
    +關(guān)注

    關(guān)注

    0

    文章

    147

    瀏覽量

    7008
  • printf函數(shù)
    +關(guān)注

    關(guān)注

    0

    文章

    31

    瀏覽量

    5899

原文標題:巴拿馬項目:打通 JVM 與 Native 代碼

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    C語言函數(shù)調(diào)用過程中的內(nèi)存變化解析

    調(diào)用實例,內(nèi)存視角,反匯編代碼探討C 語言函數(shù)調(diào)用
    的頭像 發(fā)表于 12-11 16:21 ?3779次閱讀

    C語言使用函數(shù)調(diào)用的知識點

    C語言使用函數(shù)調(diào)用,我們再熟悉不過了,但是函數(shù)調(diào)用在內(nèi)存中究竟發(fā)生了什么真的清楚嗎?只有搞清楚內(nèi)存
    發(fā)表于 09-07 11:47 ?852次閱讀

    如何查看及更改函數(shù)/函數(shù)塊的調(diào)用環(huán)境

    模塊化設(shè)計的思想是把一些相似的功能(比如電機控制、閥控制)設(shè)計成函數(shù)函數(shù)塊,這樣就可以反復(fù)調(diào)用。其優(yōu)點是:使程序架構(gòu)更加清晰,避免重復(fù)編寫相似功能的
    的頭像 發(fā)表于 11-17 09:08 ?991次閱讀
    如何查看及更改<b class='flag-5'>函數(shù)</b>/<b class='flag-5'>函數(shù)</b>塊的<b class='flag-5'>調(diào)用</b>環(huán)境

    外部代碼放入個正確運行地程序里,外部代碼里引用了一些外部API函數(shù),系統(tǒng)報錯是什么原因?

    本帖最后由 只耳朵怪 于 2018-5-24 15:16 編輯 我在做個項目,需要用到ST公司的傳感器,并引入外部代碼。我將外部
    發(fā)表于 05-22 07:44

    CCS 編程接口 請問CCS有沒有給用戶一些可以調(diào)用外部接口?

    各位好,最近在調(diào)試DSP,C6678上的程序,經(jīng)常需要啟動調(diào)試器,從內(nèi)存中拷貝數(shù)據(jù)“Save Memory”,并執(zhí)行外部程序進行分析。請問CCS有沒有給用戶一些可以
    發(fā)表于 08-03 06:59

    怎樣去使用庫函數(shù)APIC代碼

    使用庫函數(shù)APIC代碼中嵌入?yún)R編代碼兩種方式使用同個系統(tǒng)
    發(fā)表于 12-20 07:30

    CodeViz--款分析C/C++源代碼函數(shù)調(diào)用關(guān)系的調(diào)用

    程序開發(fā)中,有時候需要閱讀別人的代碼,這時理解代碼的組織結(jié)構(gòu)就顯得非常重要。CodeViz是款分析CC++
    發(fā)表于 04-04 20:50 ?85次下載
    CodeViz--<b class='flag-5'>一</b>款分析<b class='flag-5'>C</b>/<b class='flag-5'>C</b>++源<b class='flag-5'>代碼</b>中<b class='flag-5'>函數(shù)</b><b class='flag-5'>調(diào)用</b>關(guān)系的<b class='flag-5'>調(diào)用</b>

    python代碼示例之基于Python的日歷api調(diào)用代碼實例

    本文檔的主要內(nèi)容詳細介紹的是python代碼示例之基于Python的日歷api調(diào)用代碼實例。
    發(fā)表于 09-06 14:25 ?42次下載
    python<b class='flag-5'>代碼</b>示例之基于Python的日歷<b class='flag-5'>api</b><b class='flag-5'>調(diào)用</b><b class='flag-5'>代碼</b>實例

    在LabVIEW中使用外部代碼的詳細資料說明

    本手冊介紹調(diào)用函數(shù)節(jié)點和代碼接口節(jié)點(CIN)。調(diào)用函數(shù)節(jié)點和CIN是用于從基于文本的編程語
    發(fā)表于 12-13 14:05 ?17次下載
    在LabVIEW中使用<b class='flag-5'>外部</b><b class='flag-5'>代碼</b>的詳細資料說明

    使用Python實現(xiàn)游戲APP充值API調(diào)用代碼實例

    本文檔的主要內(nèi)容詳細介紹的是使用Python實現(xiàn)游戲APP充值API調(diào)用代碼實例。
    發(fā)表于 01-15 11:34 ?38次下載

    如何使用Python實現(xiàn)購物比價API調(diào)用代碼實例

    本文檔的主要內(nèi)容詳細介紹的是如何使用Python實現(xiàn)購物比價API調(diào)用代碼實例免費下載。
    發(fā)表于 01-17 17:11 ?25次下載

    C代碼與javaScript函數(shù)的相互調(diào)用問題應(yīng)該如何解決

    本文檔的主要內(nèi)容詳細介紹的是C代碼與javaScript函數(shù)的相互調(diào)用問題應(yīng)該如何解決。
    發(fā)表于 03-05 11:47 ?17次下載

    FreeRTOS中的API函數(shù)功能分析及調(diào)用方法

    FreeRTOS中的API函數(shù)功能分析及調(diào)用方法說明。
    發(fā)表于 03-26 11:50 ?33次下載

    C語言使用函數(shù)調(diào)用在內(nèi)存中究竟發(fā)生了什么?

    C語言使用函數(shù)調(diào)用,我們再熟悉不過了,但是函數(shù)調(diào)用在內(nèi)存中究竟發(fā)生了什么真的清楚嗎?只有搞清楚內(nèi)存
    的頭像 發(fā)表于 01-13 14:09 ?1207次閱讀

    OpenAI API功能升級:ChatGPT支持描述函數(shù)調(diào)用

    由于函數(shù)調(diào)用發(fā)揮了至關(guān)重要的作用,人工智能模型可以智能地與外部工具和 API 連接。開發(fā)人員可以通過為這些模型指定函數(shù)來訪問大量的功能和服務(wù)
    的頭像 發(fā)表于 07-06 14:28 ?678次閱讀
    主站蜘蛛池模板: 榴莲黄版无限刷| 人成片在线观看亚洲无遮拦| 欧美特黄99久久毛片免费| 影音先锋亚洲AV少妇熟女| 韩国甜性涩爱| 野花日本大全免费高清完整版| 黄色三级图片| 在线观看日本污污ww网站| 狂野欧美性猛XXXX乱大交| 18国产精品白浆在线观看免费| 男女久久久国产一区二区三区 | 神马电影我不卡4k手机在线观看| 岛国大片在线播放免费| 天堂无码人妻精品AV一区| 国产精品亚洲国产三区| 亚洲AV无码影院在线播放| 久久爱狠狠综合网| 97视频免费上传播放| 日韩精品欧美在线视频在线| 国产精品野外AV久久久| 一边啪啪的一边呻吟声口述| 免费。色婬网站| 古风一女N男到处做高H| 亚洲国产精品久久无套麻豆 | 日日操夜夜操天天操| 国产精品白浆精子流水合集| 亚洲精品无码葡京AV天堂| 老头xxx| www.绿巨人| 無码一区中文字幕少妇熟女网站| 娇喘高潮教室h| av影音先锋天堂网| 午夜国产视频| 老子午夜伦不卡电影院| 第九色区av天堂| 亚洲一级毛片免费在线观看| 欧美97色伦影院在线观看| 国产色婷婷精品人妻蜜桃成熟| 在线自拍综合亚洲欧美| 日本久久网站| 久久成人免费观看全部免费|