Linux系統上java調用C++ so庫文件


 

PART1:

    java中使用jna替代jni調用c++/c生成的 dll/so庫文件需要做的事項

1、引入JNA依賴或者直接下載JNAjar包
 
                    <dependency>
                        <groupId>net.java.dev.jna</groupId>
                        <artifactId>jna</artifactId>
                        <version>5.2.0</version>
                    </dependency>
 
2、編寫Java 調用類
 
           
        package com.tree.go.util;
            import com.sun.jna.Library;
            import com.sun.jna.Native;
 
            //繼承Library,用於加載庫文件 --Class mapping
            public  interface CPPTest  extends Library { 
                // 加載libhello.so鏈接庫 
                  public static final String JNA_ImgProcess = "hello";
                  public static final CPPTest instance = (CPPTest)Native.loadLibrary(CPPTest.JNA_ImgProcess,CPPTest.class);
 
                // 此方法為鏈接庫中的方法  function mapping
                void test(); 
                int addTest(int a,int b);
             
            //調用,singleton
            public static void main(String[] args) {
                  CPPTest instance =CPPTest.instance;
                  instance.test(); 
                  int c =instance.addTest(10,20);   
            }
    }     

  

 
 
            
接下來的工作就是如何編寫可供調用的Cpp文件,以及編譯加載的問題了,查看part 2  

 
 

PART2:

    編寫C++/C文件,編譯

 
准備編寫C++代碼[T1.cpp],如下:
 
#include <iostream>  
 using namespace std;
 
 extern "C"{ //避免name mangling,編譯后名稱symbol破壞,導致無法找到函數,告訴編譯器下面的代碼塊使用c編譯器來編譯
       int addTest(int a, int b)
{
       cout << "a+b" << a + b << endl; 
       return a + b;
}
       void test()
       {
             cout << "hello word from C++ ! " << endl; 
}
}

  

 
 
1、如何將C++文件編譯為so文件?
 
    這里需要區分編譯c文件和c++文件使用的是不同的編譯器,具體編譯參數可以復用
 
        1.1、編譯c文件使用 gcc
    示例:
    使用命令:gcc -fPIC -shared -o libGoT.so  T1.c
 
 
        1.2、編譯c++文件使用的是 g++
                                g++ -fPIC -shared -o libhello.so  T1.cpp
 
        編譯完成后生成如下文件:
                    
 
2、如何加載編譯好的so文件?
 
         配置so文件加載位置:
打開  vim /etc/profile
添加如下配置:
/home/data/libso是自定義目錄,
export LD_LIBRARY_PATH=/home/data/libso
多個目錄用:隔開,如下
export LD_LIBRARY_PATH=/home/data/libso:/usr/lib
 
這樣就可在Java中調用使用C程序編寫好的代碼了

 
 

PART3:

    注意事項:

1、C++編譯后函數名稱破環問題
        名稱一致性問題,java中調用的和cpp文件中定義的名稱需要保持一致
        解決:使用   extern "C"放到一句代碼前,或者一段代碼前 extern "C"{ your code}
    
 
2、   編譯生成動態庫名的問題
            注意在編譯的時候一點要在庫民前面加上  lib+soname.so
            否則JNA如法加載到庫文件
            示例:我們需要一個hello庫需要這樣編譯,前面加上lib
 
                 g++ -fPIC -shared -o libhello.so  T1.cpp
                    
        

linux動態庫的命名規則

動態鏈接庫的名字形式為 libxxx.so,前綴是lib,后綴名為“.so”。
  針對於實際庫文件,每個共享庫都有個特殊的名字“soname”。在程序啟動后,程序通過這個名字來告訴動態加載器該載入哪個共享庫。
  在文件系統中,soname僅是一個鏈接到實際動態庫的鏈接。對於動態庫而言,每個庫實際上都有另一個名字給編譯器來用。它是一個指向實際庫鏡像文件的鏈接文件(lib+soname+.so)。
 
 
 

顯式調用C++動態庫注意點

對C++來說,情況稍微復雜。顯式加載一個C++動態庫的困難一部分是因為C++的name mangling;另一部分是因為沒有提供一個合適的API來裝載類,在C++中,您可能要用到庫中的一個類,而這需要創建該類的一個實例,這不容易做到。
name mangling可以通過extern "C"解決。C++有個特定的關鍵字用來聲明采用C binding的函數:extern "C" 。用 extern "C"聲明的函數將使用函數名作符號名,就像C函數一樣。因此,只有非成員函數才能被聲明為extern "C",並且不能被重載。盡管限制多多,extern "C"函數還是非常有用,因為它們可以象C函數一樣被dlopen動態加載。冠以extern "C"限定符后,並不意味着函數中無法使用C++代碼了,相反,它仍然是一個完全的C++函數,可以使用任何C++特性和各種類型的參數。
另外如何從C++動態庫中獲取類,附上幾篇相關文章,但我並不建議這么做:
l  《LoadLibrary調用DLL中的Class》: http://www.cppblog.com/codejie/archive/2009/09/24/97141.html
l  《C++ dlopen mini HOWTO》: http://blog.csdn.net/denny_233/article/details/7255673
“顯式”使用C++動態庫中的Class是非常繁瑣和危險的事情,因此能用“隱式”就不要用“顯式”,能靜態就不要用動態。

附件:Linux下庫相關命令

g++(gcc)編譯選項

  -shared :指定生成動態鏈接庫。
  -static :指定生成靜態鏈接庫。
  -fPIC :表示編譯為位置獨立的代碼,用於編譯共享庫。目標文件需要創建成位置無關碼,念上就是在可執行程序裝載它們的時候,它們可以放在可執行程序的內存里的任何地方。
  -L. :表示要連接的庫所在的目錄。
  -l:指定鏈接時需要的動態庫。編譯器查找動態連接庫時有隱含的命名規則,即在給出的名字前面加上lib,后面加上.a/.so來確定庫的名稱。
  -Wall :生成所有警告信息。
  -ggdb :此選項將盡可能的生成gdb 的可以使用的調試信息。
  -g :編譯器在編譯的時候產生調試信息。
  -c :只激活預處理、編譯和匯編,也就是把程序做成目標文件(.o文件) 。
  -Wl,options :把參數(options)傳遞給鏈接器ld 。如果options 中間有逗號,就將options分成多個選項,然后傳遞給鏈接程序。

nm命令

有時候可能需要查看一個庫中到底有哪些函數,nm命令可以打印出庫中的涉及到的所有符號。庫既可以是靜態的也可以是動態的。nm列出的符號有很多,常見的有三種:
  一種是在庫中被調用,但並沒有在庫中定義(表明需要其他庫支持),用U表示;
  一種是庫中定義的函數,用T表示,這是最常見的;
  一種是所謂的弱態”符號,它們雖然在庫中被定義,但是可能被其他庫中的同名符號覆蓋,用W表示。
$nm libhello.h

ldd命令

    ldd命令可以查看一個可執行程序依賴的共享庫,例如我們編寫的測試動態庫依賴下面這些庫:
        
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM