记一次:OSError: dlopen: cannot load any more object with static TLS 解决过程

问题描述

使用python3.6 运行项目的时候报以下错误
在这里插入图片描述
问题:OSError: dlopen: cannot load any more object with static TLS
操作系统错误:dlopen:无法再使用静态TLS加载任何对象

解决过程

看到这个错误,一头雾水,于是开始逐步查原因:
(1)根据错误回溯信息,查看源码
在这里插入图片描述
vim /soft/python3.6/lib/python3.6/site-packages/lightgbm/basic.py + 29
在这里插入图片描述
此处是使用python的标准库 ctypes来加载共享库,增加打印 lib_path[0],重新运行:
在这里插入图片描述
查看共享库是否存在:是存在的
/soft/python3.6/lib/python3.6/site-packages/lightgbm/lib_lightgbm.so

ll /soft/python3.6/lib/python3.6/site-packages/lightgbm

在这里插入图片描述
使用python交互式来加载这个库:
在这里插入图片描述
加载正常
(2)一顿操作之后,陷入了困境:单独加载动态共享库能成功,但是在程序中加载就会失败
(3)此时回归最初的问题
问题:OSError: dlopen: cannot load any more object with static TLS
操作系统错误:dlopen:无法再使用静态TLS加载任何对象

现在我们需要去了解以下东西:
<1>、static TLS 是啥?
TLS(thread local storage)线程本地存储,在一个进程中,所有的线程是共享同一个地址空间的。这样的话 如果有一个变量是全局或者是静态的,那么多有线程访问的是内存中的同一份,意味着某一个线程对其进行了修改,也会影响其他所有线程。当然有时候这不是我们所希望看到的,这时候可以使用基于堆栈的自动变量、函数参数来访问数据。不过有些时候(比如可能是特定设计的dll,so 等动态库),我们就是需要依赖全局变量或者静态变量,那有没有办法保证在多线程程序中能访问而不互相影响呢?答案是有的。操作系统帮我们提供了这个功能——TLS线程本地存储。TLS的作用是能将数据和执行的特定的线程联系起来。
实现TLS有两中方法:静态TLS 和 动态 TLS

现在我们知道了,首先是TLS是操作系统的提供的东西,而我们的错误 恰好就是 OSError

<2>、dlopen
dlopen()是一个计算机函数,功能是以指定模式打开指定的动态链接库文件。
所以ctypes 加载动态库时,肯定使用的是系统函数dlopen
<3>、回归错误:any more : 无法再使用静态TLS加载任何对象
这个再使用,意味着前面有些库时加载成功的
(4)怀疑:操作系统是不是对动态库的加载有限制,导致不能再加载了,这样也能解释 我们单独加载库的时候是正常的
最后确定了:Linux对可以加载到进程中的带有TLS(线程本地存储,以支持c++的线程存储类)的共享库的数量有一个静态限制。这个限制很小,比如14或32,这取决于操作系统版本(重要的是glibc的版本)。
(5)使用操作系统版本相同的干净机器运行程序:是正常的,排除了操作系统版本的原因
(6)查看glibc的版本,根据动态库加载路径来查看
在这里插入图片描述
版本是2.8 而干净系统上的版本是 2.17
在这里插入图片描述
这时候想起来了:之前安装其他软件时更新过这台机器的libc库。

总结

问题原因:程序引导太多使用了 静态TLS 的动态库,而linux 系统对于进程可加载的TLS 库,有限制
解决方法:方法1、查出哪些库使用了 静态TLS ,并减少这些库的加载,使用其他方式去加载
方法2、增加系统的限制数,可以多加载一些库,不同的操作系统版本和glibc 的版本的限制数都会不同

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值