c语言 - -pthread, -lpthread 和最小的动态链接时间依赖关系



这个答案表明-pthread-lpthread更可取,因为预定义的宏。

从经验上讲,-pthread只给了我一个额外的宏:#define _REENTRANT 1而且它似乎也强制libpthread.so.0作为动态链接时间依赖关系。

当我使用-lpthread编译时,只有在我实际调用任何pthread函数时,才会添加该依赖项。

这对我来说是可取的,因为这样我就不必在我的构建脚本中以不同的方式对待多线程程序。

所以我的问题是,还有什么可以-pthread-lpthread的,是否可以在不强制使用动态链接时间依赖的情况下使用-pthread

示范:

$ echo 'int main(){ return 0; }' | c gcc -include pthread.h -x c - -lpthread && ldd a.out | grep pthread
$ echo 'int main(){  return pthread_self(); }' | c gcc -include pthread.h -x c - -lpthread && ldd a.out | grep pthread
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x0000003000c00000)
$ echo 'int main(){ return 0; }' | c gcc -include pthread.h -x c - -pthread && ldd a.out | grep pthread
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x0000003000c00000) 

你应该使用 GCC 的特殊选项-pthread而不是-lpthread的想法可能已经过时了十五年(关于 glibc,就是这样)。在现代 glibc 中,线程的切换完全是动态的,具体取决于 pthreads 库是否链接。glibc 标头中的任何内容都不会根据是否定义_REENTRANT更改其行为。

作为动态切换的示例,请考虑FILE *流。流上的某些操作正在锁定,例如putc。无论您是否编译单线程程序,它都会调用相同的putc函数;它不会由预处理器重新路由到"p线程感知"putc。发生的情况是,无所事事的存根函数用于完成锁定和解锁的动作。当线程库链接时,这些函数将被覆盖为实际函数。



我只是粗略地grep了glibc安装的包含文件树。在features.h中,_REENTRANT原因__USE_REENTRANT定义。反过来,似乎只有一件事取决于__USE_REENTRANT是否存在,但有一个并行条件也使它成为可能。也就是说,在<unistd.h>中有这个:

#if defined __USE_REENTRANT || defined __USE_POSIX199506
/* Return at most NAME_LEN characters of the login name of the user in NAME.
If it cannot be determined or some other error occurred, return the error
code.  Otherwise return 0.
This function is a possible cancellation point and therefore not
marked with __THROW.  */
extern int getlogin_r (char *__name, size_t __name_len) __nonnull ((1));
#endif

这看起来很可疑并且已经过时了;我在 glibc git 存储库的主分支中找不到它。

而且,哦,看,就在几天前(12 月 6 日),就这个主题进行了提交:

https://sourceware.org/git/?p=glibc.git;a=commit;h=c03073774f915fe7841c2b551fe304544143470f

Make _REENTRANT and _THREAD_SAFE aliases for _POSIX_C_SOURCE=199506L.
For many years, the only effect of these macros has been to make
unistd.h declare getlogin_r.  _POSIX_C_SOURCE >= 199506L also causes
this function to be declared.  However, people who don't carefully
read all the headers might be confused into thinking they need to
define _REENTRANT for any threaded code (as was indeed the case a long
time ago).

其中的变化包括:

--- a/posix/unistd.h
+++ b/posix/unistd.h
@@ -849,7 +849,7 @@ extern int tcsetpgrp (int __fd, __pid_t __pgrp_id) __THROW;
This function is a possible cancellation point and therefore not
marked with __THROW.  */
extern char *getlogin (void);
-#if defined __USE_REENTRANT || defined __USE_POSIX199506
+#ifdef __USE_POSIX199506
/* Return at most NAME_LEN characters of the login name of the user in NAME.
If it cannot be determined or some other error occurred, return the error
code.  Otherwise return 0.

看到了吗?:)

另一个答案解释说,当 C 库是 GNU C 库时,-pthread(在编译和链接时)在功能上等同于-lpthread(仅在链接时)。 但这并不是世界上唯一的 C 库。

我不知道当前一代符合POSIX标准的操作系统是否需要线程应用程序在链接时超过-lpthread,但是如果您在编译和链接时都使用-pthread,您至少会让那些试图保持旧铁运行的人的生活更轻松。

话虽如此,如果你不在不需要使用线程的程序上使用-pthread(或-lpthread),老铁也会更快乐。 "通用项目构建"不是一个好主意。

共享库在不需要的时候被拉入的问题最好通过-Wl,--as-needed来解决(使用与GNU兼容的链接器)。 为了向后兼容,默认情况下不启用此功能。

最新更新