卸载共享库中的ctypes中的共享库 [英] Unload shared library inside ctypes loaded shared library

查看:83
本文介绍了卸载共享库中的ctypes中的共享库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在从我的python脚本中调用一个so文件。据我了解,我真的不需要释放使用ctypes在python中打开的共享库。但是,在我的so文件代码中,它dlopen另一个so文件,但不执行dlclose()。
在这种情况下,从python端使用安全吗?我不必释放在ctypes内部打开的共享库加载文件吗?

I'm calling a so file from my python script. As far as I understand, I don't really need to free shared library that is opened in python using ctypes. However, inside my so file code, it dlopens another so file and doesn't do dlclose(). In this case, is it safe to use from the python side? Don't I have to free the shared library that opened inside ctypes loade so file?

推荐答案

规则 始终保持清洁 (尽管现代技术会为您提供清洁方面的服务)。

The rule Clean up after yourself always applies (although modern technologies take care of the cleaning aspect for you).

[Python 3.5]:ctypes-Python的外部函数库包含许多有用的信息,并且应该是您的朋友。

[Python 3.5]: ctypes - A foreign function library for Python contains lots of useful info, and should be your friend.

ctypes 使用 dlopen 加载 .dll 。正如我注意到的那样,它调用相应的 dlclose ,这意味着 .dll (以及已加载的所有从属文件)

ctypes uses dlopen whel loading a .dll. As I noticed, it doesn't call the corresponding dlclose meaning that the .dll (and all of its dependents that were loaded when loading it) will remain in memory until the process terminates (or until explicitly unloaded).

From )时,它会保留在内存中,直到该进程终止(或直到​​明确卸载为止)。 org / linux / man-pages / man3 / dlopen.3.html rel = nofollow noreferrer> [man7]:DLOPEN(3)


如果文件名指定的对象依赖于其他共享对象,那么动态链接器也会使用相同的规则自动加载这些对象。 (如果这些对象又具有依赖性,则该过程可能会递归发生,依此类推。)

...

如果使用 dlopen重新加载了相同的共享对象(),将返回相同的对象句柄。动态链接器维护对象句柄的引用计数,因此直到对其调用 dlclose()的次数达到 dlopen()的次数之后,才会释放动态加载的共享对象。成功了。任何初始化返回(见下文)都只会被调用一次。

If the object specified by filename has dependencies on other shared objects, then these are also automatically loaded by the dynamic linker using the same rules. (This process may occur recursively, if those objects in turn have dependencies, and so on.)
...
If the same shared object is loaded again with dlopen(), the same object handle is returned. The dynamic linker maintains reference counts for object handles, so a dynamically loaded shared object is not deallocated until dlclose() has been called on it as many times as dlopen() has succeeded on it. Any initialization returns (see below) are called just once.

因此,我认为您不会有问题当然,一切都取决于上下文)。如您所见,多次加载一个库实际上并不会每次都加载,因此用尽内存的机会非常小(除非您正在加载大量不同的 .dll ,每个都有很多不同的依赖项。)

So, I don't think you'd have a problem (of course, everything depends on the context). As you noticed, loading a library multiple times doesn't actually load it every time, so the chance to run out of memory is pretty small (well unless you are loading a huge number of different .dlls, each with lots of different dependencies).

我可以想到的一种情况是加载使用另一个符号的 .dll em> .dll 。如果该符号也在之前加载的另一个( 3 rd .dll 中定义,则代码的行为将与预期的不同。

One case that I can think of is loading a .dll that uses a symbol from another .dll. If that symbol is also defined in another (3rd) .dll, which was loaded before, then the code would behave differently than expected.

无论如何,您可以手动卸载(或更好:减少其 refcount )一个 .dll (我是不确定如何适合推荐的方式最佳做法),如下例所示。

Anyway, you can manually unload (or better: decrease its refcount) a .dll (I'm not sure how this fits into the recommended ways or best practices ), like shown in the example below.

dll.c

#include <stdio.h>


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

code.py

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)


这篇关于卸载共享库中的ctypes中的共享库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆