#!/usr/bin/env python
import ctypes as cts
import sys
DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")
def _load_dll(name):
dll = cts.CDLL(name)
print(dll)
return dll
def _load_test_func(dll):
testf = dll.test
testf.restype = cts.c_int
return testf
def main(*argv):
dlclose = cts.CDLL(None).dlclose # This WON'T work on Win
dlclose.argtypes = (cts.c_void_p,)
print("Loading a dll via `ctypes`, then delete the object. The dll is not unloaded. Call `dlclose` to unload. A 2nd call will fail.")
dll = _load_dll(DLL_NAME)
handle = dll._handle
del dll
print("{:} returned {:d}".format(dlclose.__name__, dlclose(handle))) # Even if the ctypes dll object was destroyed, the dll wasn't unloaded
print("{:} returned {:d}".format(dlclose.__name__, dlclose(handle))) # A new dlclose call will fail
print("\nUse `ctypes` to load the dll twice. The dll is not actually loaded only the 1st time (both have the same handle), but its ref count is increased. `dlclose` must be also called twice.")
dll0 = _load_dll(DLL_NAME)
dll1 = _load_dll(DLL_NAME)
print("{:} returned {:d}".format(dlclose.__name__, dlclose(dll0._handle)))
print("{:} returned {:d}".format(dlclose.__name__, dlclose(dll1._handle)))
print("{:} returned {:d}".format(dlclose.__name__, dlclose(dll1._handle)))
print("\nLoad a dll via `ctypes`, and load one of its funcs. Try calling it before and after unloading the dll.")
dll = _load_dll(DLL_NAME)
test = _load_test_func(dll)
print("{:} returned {:d}".format(test.__name__, test()))
print("{:} returned {:d}".format(dlclose.__name__, dlclose(dll._handle)))
print("{:} returned {:d}".format(test.__name__, test())) # Comment this line as it would segfault !!!
if __name__ == "__main__":
print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32, sys.platform))
rc = main(*sys.argv[1:])
print("\nDone.\n")
sys.exit(rc)
输出:
(qaic-env) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q052179325]> ~/sopr.sh
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
[064bit prompt]> ls
code00.py dll00.c
[064bit prompt]>
[064bit prompt]> gcc -fPIC -shared -o dll00.so dll00.c
[064bit prompt]>
[064bit prompt]> python ./code00.py
Python 3.8.10 (default, Nov 14 2022, 12:59:47) [GCC 9.4.0] 064bit on linux
Loading a dll via `ctypes`, then delete the object. The dll is not unloaded. Call `dlclose` to unload. A 2nd call will fail.
<CDLL './dll00.so', handle 137b280 at 0x7f75c7863c10>
dlclose returned 0
dlclose returned -1
Use `ctypes` to load the dll twice. The dll is not actually loaded only the 1st time (both have the same handle), but its ref count is increased. `dlclose` must be also called twice.
<CDLL './dll00.so', handle 137b630 at 0x7f75c7863c10>
<CDLL './dll00.so', handle 137b630 at 0x7f75c777e340>
dlclose returned 0
dlclose returned 0
dlclose returned -1
Load a dll via `ctypes`, and load one of its funcs. Try calling it before and after unloading the dll.
<CDLL './dll00.so', handle 13edea0 at 0x7f75c777e220>
[dll00.c] (6) - [test]
test returned 0
dlclose returned 0
Segmentation fault
2条答案
按热度按时间mf98qq941#
规则***清理后,你自己***总是适用(虽然现代技术照顾清洁方面为您)。
【Python文档】:ctypes -Python的一个外来函数库包含了很多有用的信息,应该是你的朋友。
从[Man7]: DLOPEN(3)开始:
如果由 filename 指定的对象依赖于其他共享对象,则动态链接器也会使用相同的规则自动加载这些共享对象(如果这些对象反过来又具有依赖性,则此过程可能递归发生,依此类推)。
...
如果用**dlopen()再次加载同一个共享对象,则返回同一个对象句柄。动态链接器维护对象句柄的引用计数,因此动态加载的共享对象不会被释放,直到dlclose()被调用的次数与dlopen()**成功调用的次数相同。任何初始化返回(见下文)只被调用一次。
所以,我不认为你会有问题(当然,一切都取决于上下文).正如你所注意到的,多次加载一个库并不是每次都加载它,所以耗尽内存的可能性很小(除非你加载大量不同的 .dll,每个都有很多不同的依赖项).
我能想到的一种情况是加载使用另一个 .dll 中的符号的 .dll。如果该符号也在之前加载的另一个(3rd).dll 中定义,则代码的行为将与预期不同。
不管怎样,你可以手动卸载(或者更好:减少它的 RefCount)a .dll(我不确定这是否符合 * 推荐方式 * 或 * 最佳实践 *),如下例所示。
输出:
相关(或多或少):
h7appiyu2#
卸载依赖项的示例
在Linux Fedora 32、Python 3.7.6(anaconda)、ctypes 1.1.0、g++ 10.2.1上进行了测试。依赖项为
OpenCv
(版本4.2)。更多详细信息请访问:How can I unload a DLL using ctypes in Python?代码.cpp
使用
g++ code.cpp -shared -fPIC -Wall -std=c++17 -I/usr/include/opencv4 -lopencv_core -o so_opencv.so
编译Python代码