设为首页 加入收藏

TOP

Linux程序链接时-lpthread对程序正确性的影响(一)
2015-02-02 14:16:18 来源: 作者: 【 】 浏览:25
Tags:Linux 程序 链接 -lpthread 正确性 影响

理论上来说,多线程程序在链接时应该加上-lpthread或者-pthread。实际上很多时候忘记加这个也能链接过去,最近我线上的一个重要服务经常卡死,CPU使用率很高。用pstack看,经常是停留在这样的地方:

# 0x0000003a21e0e054 in __lll_lock_wait () from /lib64/libpthread.so.0
#1 0x0000003a21e0bca1 in pthread_cond_signal@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#2 0x00007f04f8e0696d in __db_pthread_mutex_unlock () from /usr/lib64/libdb-4.7.so
#3 0x00007f04f8e0655d in __db_tas_mutex_unlock () from /usr/lib64/libdb-4.7.so
#4 0x00007f04f8ea6b8e in __db_cursor_int () from /usr/lib64/libdb-4.7.so
#5 0x00007f04f8ebd9af in __db_cursor () from /usr/lib64/libdb-4.7.so
#6 0x00007f04f8ebe2c0 in __db_get () from /usr/lib64/libdb-4.7.so
#7 0x00007f04f8ebe63b in __db_get_pp () from /usr/lib64/libdb-4.7.so

大部分CPU都被__db_tas_mutex_unlock和__db_tas_mutex_lock这两个函数占去了。按理说unlock一个mutex不该占用太多cpu才对。(后来我发现这是bdb的mutex的实现太畸形太挫了)

我在网上发现有个工程师遇到了和我类似的问题
http://www.jimmo.org/threads-blocked-in-pthread_cond_signal-on-linux/?他说如果忘记链接到pthread库,可能导致条件变量所依赖的mutex没有被正确初始化,而导致程序死锁等。理论上来说是这样的,但是实际上我没有办法重现作者的实验。


我发现libdb-4.7.so中pthread的符号和我预期的不一样
$ readelf -a /usr/lib64/libdb-4.7.so |grep pthread_cond_signal
000000370f88? 000f00000007 R_X86_64_JUMP_SLO 0000000000000000 pthread_cond_signal + 0
? ? 15: 0000000000000000? ? 0 FUNC? ? GLOBAL DEFAULT? UND pthread_cond_signal@GLIBC_2.3.2 (3)

我自己如果编译一个小程序,例如
#include

int func(){
? pthread_cond_signal(NULL);
? return 0;
}

$ gcc -o libt.so test.c -shared -fPIC
$ readelf -a libt.so? |grep pthread
000000201018? 000300000007 R_X86_64_JUMP_SLO 0000000000000000 pthread_cond_signal + 0
? ? 3: 0000000000000000? ? 0 FUNC? ? GLOBAL DEFAULT? UND pthread_cond_signal@GLIBC_2.3.2 (2)
? ? 45: 0000000000000000? ? 0 FUNC? ? GLOBAL DEFAULT? UND pthread_cond_signal@@GLIB
它的符号表中应该有两条记录。不知道为什么bdb中只有一条。

后来查了下文档终于搞明白,带@的是versioned symbol。weak symbol是给静态库用的,动态库没法用weak symbol。

glibc中的pthread的mutex等的实现是空的,这是为了提高单线程程序的执行效率。当某个程序真的需要使用多线程的时候,得让libpthread.so把正确的symbols填充进去。静态库可以通过weak symbol做到这一点,而动态库可以直接覆盖,也可以用versioned symbol。

$ nm /lib64/libc.so.6 | grep pthread_mutex
00000000000f8110 T pthread_mutex_destroy
00000000000f8140 T pthread_mutex_init
00000000000f8170 T pthread_mutex_lock
00000000000f81a0 T pthread_mutex_unlock
?
注意,是T,不是W。 (cond的输出更有所不同。稍后叙述)

当编译一个不带-pthread的程序的时候,
$ ldd t
? ? linux-vdso.so.1 =>? (0x00007fff5f4e2000)
? ? libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007fbef59f9000)
? ? libm.so.6 => /lib64/libm.so.6 (0x00007fbef5775000)
? ? libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fbef555e000)
? ? libc.so.6 => /lib64/libc.so.6 (0x00007fbef51ca000)
? ? /lib64/ld-linux-x86-64.so.2 (0x00007fbef5d2f000)
?
当编译一个带-pthread的程序之后
$ ldd t
? ? linux-vdso.so.1 =>? (0x00007fff805fe000)
? ? libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007f72c5a26000)
? ? libm.so.6 => /lib64/libm.so.6 (0x00007f72c57a2000)
? ? libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f72c558b000)
? ? libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f72c536e000)
? ? libc.so.6 => /lib64/libc.so.6 (0x00007f72c4fda000)
? ? /lib64/ld-linux-x86-64.so.2 (0x00007f72c5d5c000)
?
libpthread.so.0一定是出现在libc.so.6之上。它也提供了同样的符号
$ nm /lib64/libpthread.so.0 | grep pthread_mutex_init
0000000000008d70 T __pthread_mutex_init
0000000000008d70 t __pthread_mutex_init_internal
0000000000008d70 T pthread_mutex_init

默认情况下,链接器是按顺序优先选择第一个找到的。所以它会使用libpthread.so.0中的符号替换libc.so.6

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇浅析C#中的泛型 下一篇Calabash+Gearman实现多手机同步..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: