当前位置: 首页 > 面试题库 >

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

颜宸
2023-03-14
问题内容

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


问题答案:

始终遵循 “自己清洁后清理 ”的规则(尽管现代技术会为您提供清洁方面的帮助)。

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

ctypes 使用 dlopen whel加载 .dll 。正如我注意到的那样,它 没有 调用相应的 dlclose, 这意味着
.dll (以及 在加载 .dll 时加载的所有从属文件 )将保留在内存中,直到进程终止(或直到​​明确卸载)为止。

来自[man7]:DLOPEN(3):

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

如果使用 dlopen() 再次加载相同的共享对象,则返回相同的对象句柄。动态连接器保持参考计数对象句柄,因此一个动态加载共享对象不释放直到
dlclose()调用在其上多次 dlopen()的 成功就可以了。任何初始化返回(见下文)仅被调用一次。

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

一种情况下,我能想到的是加载一个 .dll文件 ,它使用一个符号从另一个 .dll文件 。如果符号在另一个(也定义 3 次.DLL
,这是之前加载,则代码将表现与预期不同。

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

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.

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.


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.

[dll.c] (5) - [test]
test returned 0
dlclose returned 0
Segmentation fault (core dumped)



 类似资料:
  • 问题内容: 是否可以使用Go创建共享库(.so)? 更新 :为此创建了一个“ 问题 ”。 问题答案: 现在可以使用标志 您需要做的是首先运行以下命令: (以上代码使所有通用软件包都可共享!)然后 最后,在编译代码时,您需要运行: 上面这些就是什么,而不是静态链接所有内容而仅动态链接它们,您最终将获得更小的编译文件。为了让您了解我的带有静态链接的“ hello.go”文件为2.3MB,而使用动态链接

  • 问题内容: 我试图通过ctypes用Python中定义的回调替换共享库中的现有函数指针。 C中共享库的源代码: Python脚本的来源: 测试它的脚本: 我得到的输出: 因此,似乎在Python(plotxy)中定义的函数是type ,而在C中定义的函数指针是type 。尽管应用了替换in ,但在调用main函数时似乎没有任何效果。 除了阅读https://docs.python.org/3/li

  • 我将我的应用程序作为企业库移植到WebLogic,以便我的EJB和MDB可以调用其中的代码。 我为这两个选择了一个耳朵包装,比如Lib1。ear和Lib2。ear包含APP-INF/lib目录,两个目录中都有100个JAR。我还更新了清单。两者的MF包含规范版本和实现版本。两者都已成功部署并可供使用。 现在,我更新了包含EJB和MDB的ear,并在weblogic应用程序中添加了以下内容。xml;

  • 库用于将相似函数打包在一个单元中。然后这些单元就可为其他开发人员所共享,并因此有了模块化编程这种说法— 即,从模块中构建程序。Linux支持两种类型的库,每一种库都有各自的优缺点。静态库包含在编译时静态绑定到一个程序的函数。动态库则不同,它是在加载应用程序时被加载的,而且它与应用程序是在运行时绑定的。 使用共享库的方法有两种:您既可以在运行时动态链接库,也可以动态加载库并在程序控制之下使用它们。本

  • 我有一个CMACE项目,它的下一个结构是: 输出树为: 如果我在linux下编译程序,所有的工作都很完美,但是当我在Windows下做的时候,编译很好,但是app.exe不执行;我得到下一个错误: 我怀疑不要链接internal.cpp,因为当我将它的过程移到internal.h中时,就可以很好地工作。 有什么需要帮忙的吗? 以下是文件:root::cmakelists.txt app::cmak

  • 如果你想添加共享库支持到一个原来不包含共享库支持的 port 或是其它软件, 共享库的版本号应该遵循如下规则。通常来说,由此得出的数字与软件的发行版本无关。 建立共享库的三个原则是: 从1.0开始 如果改动与以前版本相兼容,增加副版本号(注意,ELF系统忽略副版本号)。 如果是个不兼容的改动,增加主版本号。 例如,添加函数和修正错误导致副版本号增加, 而删除函数、函数调用语法改变等,会迫使主版本号