python - 具有 Ctypes 的独立 CDLL 库实例
问题描述
我正在尝试使用 ctypes 并两次加载相同的编译 Fortran 库,这样我就有了它的两个独立实例,这样库包含的任何模块变量都不会存储在相同的内存位置。所描述的一般解决方案(例如,此处:https ://mail.python.org/pipermail/python-list/2010-May/575368.html )是提供库的完整路径,而不仅仅是其名称。但是,我无法让它像这样工作。这是一个演示问题的最小工作示例:
测试.f90:
module test
use iso_c_binding, only: c_int
implicit none
integer :: n
contains
integer(c_int) function get() bind(c, name='get')
get = n
end function get
subroutine set(new_n) bind(c, name='set')
integer(c_int), intent(in) :: new_n
n = new_n
end subroutine set
end module test
测试.py:
import os
from ctypes import cdll, c_int, byref
if __name__ == '__main__':
lib1 = cdll.LoadLibrary(os.path.abspath('test.so'))
lib2 = cdll.LoadLibrary(os.path.abspath('test.so'))
lib1.set(byref(c_int(0)))
lib2.set(byref(c_int(1)))
print(lib1.get())
Fortran 库是使用以下命令编译的:
gfortran -shared -fPIC -o test.so test.f90
当我运行时,python test.py
我得到1
输出,而我想得到0
. 有谁知道如何使这项工作?
解决方案
ctypes([Python 3]:ctypes - Python 的外部函数库)使用dlopen加载库(在Nix上) 。根据[man7]: DLOPEN(3):
如果使用dlopen()再次加载相同的共享对象,则返回相同的对象句柄。动态链接器维护对象句柄的引用计数,因此在对动态加载的共享对象调用dlclose()的次数与dlopen()对它成功的次数相同之前,不会释放动态加载的共享对象。
我准备了一个小例子。
dll.c:
#if defined(_WIN32)
# define DLL_EXPORT __declspec(dllexport)
#else
# define DLL_EXPORT
#endif
static int val = -1;
DLL_EXPORT int get() {
return val;
}
DLL_EXPORT void set(int i) {
val = i;
}
代码.py:
#!/usr/bin/env python3
import sys
import os
import shutil
import ctypes
DLL0_NAME = "./dll0.so"
DLL1_NAME = "./dll1.so"
DIR0_NAME = "dir0"
def get_dll_funcs(dll):
get_func = dll.get
get_func.argtypes = None
get_func.restype = ctypes.c_int
set_func = dll.set
set_func.argtypes = [ctypes.c_int]
set_func.restype = None
return get_func, set_func
def main():
os.makedirs(DIR0_NAME, exist_ok=True)
shutil.copy(DLL0_NAME, DIR0_NAME)
shutil.copy(DLL0_NAME, DLL1_NAME)
dll_names = [DLL0_NAME, os.path.abspath(DLL0_NAME), os.path.join(DIR0_NAME, DLL0_NAME), DLL1_NAME]
dlls = [ctypes.CDLL(item) for item in dll_names]
for idx, dll in enumerate(dlls):
print("Item {:d} ({:s}) was loaded at {:08X}".format(idx, dll_names[idx], dll._handle))
set_func = get_dll_funcs(dll)[1]
set_func(idx * 10)
for idx, dll in enumerate(dlls):
get_func = get_dll_funcs(dll)[0]
print("Item {:d} get() returned {: d}".format(idx, get_func()))
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
输出:
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q054243176]> ls code.py dll.c [cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q054243176]> gcc -o dll0.so -shared dll.c [cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q054243176]> ls code.py dll0.so dll.c [cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q054243176]> ./code.py Python 3.5.2 (default, Nov 12 2018, 13:43:14) [GCC 5.4.0 20160609] on linux Item 0 (./dll0.so) was loaded at 02437A80 Item 1 (/home/cfati/Work/Dev/StackOverflow/q054243176/dll0.so) was loaded at 02437A80 Item 2 (dir0/./dll0.so) was loaded at 02438690 Item 3 (./dll1.so) was loaded at 02438EF0 Item 0 get() returned 10 Item 1 get() returned 10 Item 2 get() returned 20 Item 3 get() returned 30
从输出中可以看出(还要注意_handle属性),尝试多次加载相同的.dll(通过其路径)(在Win上的行为相同):
- 如果位于同一路径中(即使指定不同),实际上不会再次加载它,它只是增加其引用计数
- 如果其名称或位置不同,则重新加载
简而言之,要回答您的问题:只需以不同的名称复制它并加载它。
推荐阅读
- python - Python setuptools LINK:致命错误 LNK1181:无法打开输入文件“C:\Program.obj”
- android - GetCurrentLocation 在调试中工作,但在 Android 的发布 apk 中不起作用
- python - 如何将csv文件复制到字典中?
- javascript - 在 React JS 中使用动态数字键循环
- ignite - 禁用缓存的持久性
- assembly - 为什么写入可写 .data 部分时出现段错误?使用 Ubuntu、x86、nasm、gdb、readelf
- python - 根据分隔符拆分列值(熊猫)
- python - 如何对包含嵌套列表的列表进行排序,该列表还包含字典。目的是按字典 VALUE 对主列表进行排序
- excel - 为什么具有相同日期的单元格具有不同的日期序列号?
- python - 如何避免多次加载模块?