背景介绍
- 进程A
- 进程B
动态链接库
c.so -----------我们模块的so
d.so
e.so
进程A会加载c.so、d.so
进程B会加载c.so、d.so、e.so
启动d.so 和e.so的构造函数中均会创建线程私有数据
进程A每次重启时,会产生core文件,产生core的位置在c.so
进程B不会出core文件。
定位过程
发现进程A出core的地方是c.so中的私有数据,发现私有数据是乱的,怀疑是初始化时有问题。
查看代码发现函数初始化私有数据时调用pthread_getspecific(0)查看是否已经有配置,如果没有则创建私有数据
进程A调用pthread_getspecific(0)不为空,进程B调用pthread_getspecific(0)为空。
这就比较奇怪了,相同的代码,为什么结果不一样呢?想了好久,只有可能进程启动过程中已有的流程导致的,就试着把线程私有数据的各个函数(pthread_key_create、pthread_key_delete、pthread_setspecific、pthread_getspecific)均断住后启动进程。
(由于不知道是私有数据创建有问题,还是创建后又删除了,就用了这个笨办法,后来想如果先看看源码就好了,能帮助清理下思路)
启动过程中发现进程A和B启动过程果然不一样
首先加载的so不一样,进程B先加载e.so,然后是d.so;进程A先加载d.so。
更特别的是进程B第一次调用pthread_key_create创建线程私有数据的key后,没有调用pthread_setspecific设置线程私有数据。
而进程A第一次调用pthread_key_create创建线程私有数据的key后,接着调用pthread_setspecific设置线程私有数据。
猜测是进程A获取私有数据是正好是d.so中设置的私有数据,不为NULL,进程B中获取的是e.so中的私有数据,而e.so的私有数据没有设置,正好获取的是NULL。
由于都是release的.so,没办法看出来各个值是多少,只能通过查看cpu寄存器中key的入参和出参变化来验证猜想。发现第一个设置私有数据的key,key的入参和出参均是0;第二个设置私有数据时,key的入参是0,出参是1。
直接看源码,果然如此。
e.so创建了私有数据Key,没设置私有数据,创建的私有数据Key正好为0,我们模块获取私有数据的Key正好是0,获取私有数据为NULL,无巧不成书,就这么巧合的导致了进程A有问题,进程B没有问题。 |
源码
创建私有数据Key
key的分配是直接从0开始,一直向后找第一个未使用的值。
int pthread_key_create(pthread_key_t * key, destr_function destr)
{
int i;
__pthread_mutex_lock(&pthread_keys_mutex);