在ctypes加载的共享库中卸载共享库

问题描述 投票:0回答:1

我从我的python脚本中调用了一个so文件。据我所知,我真的不需要使用ctypes释放在python中打开的共享库。但是,在我的so文件代码中,它会停止另一个so文件并且不执行dlclose()。在这种情况下,从python端使用是否安全?我不是必须释放在ctypes中打开的共享库所以提交文件吗?

python shared-libraries ctypes
1个回答
0
投票

规则清理后总是适用(尽管现代技术为您处理清洁方面)。

[Python 3.5]: ctypes - A foreign function library for Python包含许多有用的信息,应该是你的朋友。

ctypes使用dlopen whel加载.dll。正如我注意到的,它没有调用相应的dlclose,这意味着.dll(及其加载时加载的所有依赖项)将保留在内存中,直到进程终止(或直到​​显式卸载)。

来自[man7]: DLOPEN(3)

如果filename指定的对象依赖于其他共享对象,则动态链接器也会使用相同的规则自动加载这些对象。 (如果这些对象依次具有依赖性,则此过程可以递归地发生,等等。) ... 如果使用dlopen()再次加载相同的共享对象,则返回相同的对象句柄。动态链接器维护对象句柄的引用计数,因此动态加载的共享对象在释放dlclose()之前不会被释放,因为dlopen()已经成功执行了多次。任何初始化返回(见下文)都只调用一次。

所以,我不认为你有问题(当然,一切都取决于上下文)。正如您所注意到的,多次加载库实际上并不是每次都加载它,因此内存耗尽的可能性非常小(除非您加载大量不同的.dll,每个都有很多不同的依赖项) 。

我能想到的一个案例是加载一个使用另一个.dll符号的.dll。如果该符号也在之前加载的另一个(3rd).dll中定义,那么代码的行为将与预期的不同。

无论如何,您可以手动卸载(或更好地:减少其引用计数)a .dll(我不确定这是如何符合推荐的方法或最佳实践),如下例所示。

dll.c:

#include <stdio.h>


int test() {
    printf("[%s] (%d) - [%s]\n", __FILE__, __LINE__, __FUNCTION__);
    return 0;
}

code.朋友:

import sys
from ctypes import CDLL, \
    c_int, c_void_p


DLL = "./dll.so"

dlclose_func = CDLL(None).dlclose  # This WON'T work on Win
dlclose_func.argtypes = [c_void_p]


def _load_dll(dll_name):
    dll_dll = CDLL(dll_name)
    print("{:}".format(dll_dll))
    return dll_dll


def _load_test_func(dll):
    test_func = dll.test
    test_func.restype = c_int
    return test_func


def main():
    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_dll = _load_dll(DLL)
    dll_handle = dll_dll._handle
    del dll_dll
    print("{:} returned {:d}".format(dlclose_func.__name__, dlclose_func(dll_handle)))  # Even if the ctypes dll object was destroyed, the dll wasn't unloaded
    print("{:} returned {:d}".format(dlclose_func.__name__, dlclose_func(dll_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_dll = _load_dll(DLL)
    dll1_dll = _load_dll(DLL)
    print("{:} returned {:d}".format(dlclose_func.__name__, dlclose_func(dll0_dll._handle)))
    print("{:} returned {:d}".format(dlclose_func.__name__, dlclose_func(dll1_dll._handle)))
    print("{:} returned {:d}".format(dlclose_func.__name__, dlclose_func(dll1_dll._handle)))

    print("\nLoad a dll via `ctypes`, and load one of its funcs. Try calling it before and after unloading the dll.")
    dll_dll = _load_dll(DLL)
    test_func = _load_test_func(dll_dll)
    print("{:} returned {:d}".format(test_func.__name__, test_func()))
    print("{:} returned {:d}".format(dlclose_func.__name__, dlclose_func(dll_dll._handle)))
    print("{:} returned {:d}".format(test_func.__name__, test_func()))  # Comment this line as it would segfault !!!



if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

输出:

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q052179325]> ls
code.py  dll.c
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q052179325]> gcc -fPIC -shared -o dll.so dll.c
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q052179325]> python3 ./code.py
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] 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 './dll.so', handle 1d7aa20 at 0x7fa38715f240>
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 './dll.so', handle 1de2c80 at 0x7fa38715f240>
<CDLL './dll.so', handle 1de2c80 at 0x7fa38715f278>
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 './dll.so', handle 1de39c0 at 0x7fa38715f8d0>
[dll.c] (5) - [test]
test returned 0
dlclose returned 0
Segmentation fault (core dumped)
© www.soinside.com 2019 - 2024. All rights reserved.