C++ 有没有一种方法可以标记不可重入的C库调用的使用?

C++ 有没有一种方法可以标记不可重入的C库调用的使用?,c++,c,linux,multithreading,reentrancy,C++,C,Linux,Multithreading,Reentrancy,我正在从事一个多线程的项目,我想知道是否有一种方法可以让编译器标记对C库的不可重入调用的使用(例如strtok\u r的strtok intsead)?如果没有,是否有一个不可重入的调用列表,以便我可以定期grep我的代码库 一个相关的问题是,是否有方法标记3d party library对不可重入调用的使用 我假设重入意味着线程安全,但不一定相反。在线程化项目中使用不可重入调用是否有充分的理由?对于源代码,您可以坚持每个源文件都包含以下行: #include <beware.h>

我正在从事一个多线程的项目,我想知道是否有一种方法可以让编译器标记对C库的不可重入调用的使用(例如strtok\u r的strtok intsead)?如果没有,是否有一个不可重入的调用列表,以便我可以定期grep我的代码库

一个相关的问题是,是否有方法标记3d party library对不可重入调用的使用


我假设重入意味着线程安全,但不一定相反。在线程化项目中使用不可重入调用是否有充分的理由?

对于源代码,您可以坚持每个源文件都包含以下行:

#include <beware.h>
或者其他一些不太可能是实函数的合适名称集。这将导致编译和/或链接器错误

对于图书馆来说,这有点困难。您可以研究使用
nm
提取每个对象文件中所有未解析的名称,并确保没有调用任何不安全的名称

编译器不会这样做,但它很容易合并到构建脚本中。见以下发言稿:

$ cat qq.c
    #include <stdio.h>

    int main (int argc, char *argv[]) {
        printf ("Hello, world.\n");
        return 0;
    }

$ gcc -c -o qq.o qq.c

$ nm qq.o
00000000 b .bss
00000000 d .data
00000000 r .rdata
00000000 t .text
         U ___main
00000000 T _main
         U _puts
$cat qq.c
#包括
int main(int argc,char*argv[]){
printf(“你好,世界。\n”);
返回0;
}
$gcc-c-o qq.o qq.c
$nm qq.o
00000000B.bss
00000000 d.数据
00000000 r.rdata
00000000吨文本
U主管道
00000000吨干管
U_put

您可以使用
U
标记在该输出中看到未解析的符号(并且
gcc
非常偷偷地决定使用
put
而不是
printf
,因为我给了它一个常量字符串,没有格式化命令)。

解决问题的第二部分:


非重入调用的实现方式可以使它们具有性能优势。在这种情况下,如果您知道您只从一个线程(或在一个关键部分内)进行这些调用,并且它们是您的瓶颈,那么选择不可重入调用是有意义的。但我只会在有绩效评估表明这样做很关键的情况下才这么做。。。并仔细地记录下来

对于二进制文件,您可以使用
LD_PRELOAD
截取您喜欢的任何C库函数,并执行您想要的任何操作(中止、记录错误但继续,等等)

在开发过程中,您也可以使用valgrind执行相同的操作

有关一些示例代码和参考,请参阅的答案

有没有一个电话列表 是不可重入的,所以我可以grep 定期通过我的代码库

我浏览了GNULIBC函数列表,选择了带有_r的函数。这是名单

astime、crypt、ctime、drand48、ecvt、encrypt、erand48、fcvt、fgetgrent、fgetpwent、getdate、getgrent、getgrgid、getgrnam、gethostbyaddr、gethostbyname2、gethostbyname、getmnent、getnetgrent、getpwent、getpwnam、getpwuid、getutent、getutid、getutline、gmtime、hcreate、hdestroy、hsearch、initstate、jrand48、lcong48、lgama、lgaf、lgama、lgamaf、lgamal、,localtime、lrand48、mrand48、nrand48、ptsname、qecvt、qfcvt、rand、random、readdir64、readdir、seed48、setkey、setstate、srand48、srandom、strerror、strtok、tmpnam、ttyname将标记使用不可重入的标准库函数。启用可移植性警告以启用此检查

有关Cppcheck will标志的功能列表,请参阅

Cppcheck将发出的消息示例:

调用了不可重入函数“strtok”。对于线程安全应用程序,建议使用可重入替换函数“strtok_r”。(可移植性:非EntrentFunctionsStrtok)


关于头文件和nm脚本的好主意。是否有不可重入的C库函数列表?如果您使用的是GCC,可以使用
#pragma GCC-poison strtok
作为
#define
的更好替代。作为nm样式的变体,您可以定义如下文件:checkfns.C:
void not#thread_safe();void puts(){not_thread_safe()}
然后它将隐藏函数的系统版本,您将得到一个错误,如:
未定义的符号:“_not_thread_safe”,引用自:_putsin checkfns.o
,如果您尝试使用puts。(您需要为不使用它的情况启用死代码剥离…
-Xlinker-dead\u strip
为我)如果您使用
#define
,请使用类似
unsafe\u function\u call\u detected
的名称,这样编译器的错误信息中就包含了一些东西,告诉你什么是真正的错误。@Karl:ooga-booga-wtf还不够清楚吗?:-)不,说真的,这是一个很好的建议,我将更新答案。问题-Linux中所有在调用之间保持状态的C运行时调用(例如malloc、rand、strtok等)是否本质上都是非线程安全的?或者是否有编译器/链接器指令来指定这些调用的线程安全版本的链接?我真的在想,是否真的有一个线程安全问题需要解决。@selbie:不,它是不同的。例如,它通常是线程安全的。无论如何,
rand()
的线程安全性是一个哲学问题。完美的
rand
实现(oracle)本质上是线程安全的。检查的函数列表似乎不完整。strerror\u()已丢失。还有其他不可重入的函数(不带_r后缀),如getenv()、system()和其他所有函数。此处列出:[。但即使这样也不完整。请考虑setlocale()或任何其他修改进程范围属性的函数。您不能从线程库调用这些函数,否则可能会造成严重破坏。请注意
readdir\u r
:查看手册页。GNU libc建议使用常规
readdir
并弃用
readdir\r
$ cat qq.c
    #include <stdio.h>

    int main (int argc, char *argv[]) {
        printf ("Hello, world.\n");
        return 0;
    }

$ gcc -c -o qq.o qq.c

$ nm qq.o
00000000 b .bss
00000000 d .data
00000000 r .rdata
00000000 t .text
         U ___main
00000000 T _main
         U _puts