最近項目需要提升所有 Python 算法的執行時間,並給 Java 框架調用,根據 Python一鍵轉Jar包,Java調用Python新姿勢!的思路可以用 Cython 將 Python 代碼轉換為 C 代碼再編譯為動態連接庫 (so / dll),提升 Python 代碼執行速度。同時提供 Java Native 接口以供 Java 框架調用。
問題
但在根據剛剛所提博文進行復現時,出現了一個老大難問題,一直都沒什么頭緒
Exception in thread "main" java.lang.UnsatisfiedLinkError: /path/libTest.cpython-36m-x86_64-linux-gnu.so: /path/libTest.cpython-36m-x86_64-linux-gnu.so: undefined symbol: PyInit_Test
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1941)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1824)
at java.lang.Runtime.load0(Runtime.java:809)
at java.lang.System.load(System.java:1086)
at Test.<clinit>(Test.java:6)
at Demo.main(Demo.java:3)
直到看到這篇博客 解決 Linux 上 C/C++ 的 undefined symbol 或 undefined reference,可以通過 nm -D LIBRARY_FILE
來查看動態代碼段的相關符號 (symbol),輸出信息如下
0000000000004a10 T Java_Test_uninitModule
0000000000004a20 T Java_Test_upperFunction
......
U PyImport_ImportModule
U PyInit_Test
00000000000042e7 T PyInit_libTest
......
其中,T
表示全局/局部符號,U
表示為未定義的符號。
原因
也就是說,這個 PyInit_Test
函數未定義,但 PyInit_libTest
卻定義了,明明在 main.c
中沒有定義這個lib函數,唯一出現的地方就是在編譯文件 setup.py
中
extensions = [Extension("libTest", sourcefiles,
include_dirs=['/usr/lib/jvm/java-8-oracle/include/',
'/usr/lib/jvm/java-8-oracle/include/linux/',
'/python/path/include/python3.6m/'],
library_dirs=['/python/path/lib/'],
libraries=['python3.6m'])]
第一個參數是 name
,表示的是這個 extension 的全稱,而之后的調用就會依托這個名字,同時在 main.c
中 PyInit_
之后接的是該模塊的名稱。也就是說,當這兩個名稱相同的時候,Cython 才能成功編譯這個模塊。
解決方案
因此將參數 name
從 "libTest"
替換為 "Test"
,問題解決。
重新用 nm -D Test.so
來查看,可以看到 PyInit_Test
這個函數已定義,收工~
00000000000042b7 T PyInit_Test