2011-06-24 46 views
11

我正在研究一个非常多线程的项目,并且想知道是否有办法让编译器标记使用对C库的非重入调用(例如strtok_r的strtok intsead)?如果没有,是否有一个不可重入呼叫的列表,所以我可以定期通过我的代码库进行grep?有没有办法标记使用非重入C库调用?

一个相关的问题是,如果有一种方法来标记3D党库使用的非重入调用。

我假设重入意味着线程安全的,但不一定是周围的其他方式。在线程项目中使用非重入调用是否有很好的理由?

+0

问题 - Linux中的所有C运行时调用都保持调用之间的状态(例如malloc,rand,strtok等...)本质上是非线程安全的吗?还是有一个编译器/链接器指令来指定与这些调用的线程安全版本的链接?我真的想知道是否真的有线程安全问题需要解决。 – selbie

+0

@selbie:不,它有所不同。例如,[malloc](http://www.bozemanpass.com/info/linux/malloc/Linux_Heap_Contention.html)通常是线程安全的。无论如何,rand()的线程安全性是一个哲学问题。一个完美的'rand'实现(一个oracle)本质上是线程安全的。 – MSalters

回答

5

对于源,你可能坚持每天源文件包含行:

#include <beware.h> 
的C头后

,然后beware.h头文件包含:

#define strtok unsafe_function_call_detected_strtok 
#define getenv unsafe_function_call_detected_getenv 

或一些其它合适的一套不太可能成为真正的功能的名字。这将导致编译和/或链接器错误。

对于图书馆来说,这有点困难。您可以使用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 

你可以看到在输出未解决的符号与U标记(和gcc已经很鬼祟地决定用puts,而不是printf,因为我给它一个常量字符串没有格式化命令) 。

+0

好主意在头文件和nm脚本。在某处是否存在非重入C库函数列表? – Ravi

+0

如果你使用GCC,你可以使用'#pragma GCC poison strtok'作为'#define'的更好选择。 –

+0

作为nm风格的变体,您可以像这样定义一个文件: _checkfns.c_:'void not_thread_safe(); void puts(){not_thread_safe(); }'那么它会隐藏这些函数的系统版本,并且如果你尝试使用puts,你会得到如下错误:'Undefined symbols:“_not_thread_safe”,引用自:checkfns.o中的_puts。 (你需要在不使用它的情况下启用死代码剥离,虽然......'-Xlinker -dead_strip') –

1

解决你问题的第二部分:

非重入调用可以在给他们带来了性能上的优势的方式来实现。在这种情况下,如果您知道您只从一个线程(或在一个关键部分内)发出这些呼叫,并且这是您的瓶颈,那么选择非重入呼叫是有道理的。但我只会做,如果有性能测量表明这是至关重要的......并仔细记录它..

0

对于二进制文件,你可以使用LD_PRELOAD截取任何你喜欢的C库函数你想(中止,记录一个错误,但继续等)

在开发过程中的任何行动,您还可以使用的valgrind做同样的。

对于一些示例代码和参考,请参阅答案how could I intercept linux sys calls?

2

有呼叫的列表 是不可重入,所以我可以通过我的代码库用grep 定期?

我查看了GNU libc函数列表,并用_r找出了那些。这是清单。

asctime,隐窝,的ctime,drand48,ECVT,加密,erand48,fcvt,fgetgrent,fgetpwent,GETDATE,getgrent,getgrgid,getgrnam,gethostbyaddr,gethostbyname2,的gethostbyname,getmntent,getnetgrent,的getpwent,getpwnam,getpwuid,getutent, getutid,getutline,gmtime,hcreate,hdestroy,hsearch,initstate,jrand48,lcong48,lgamma,lgammaf,lgammal,localtime,lrand48,mrand48,nrand48,ptsname,qecvt,qfcvt,rand,random,readdir64,readdir,seed48,setkey, setstate,srand48,srandom,strerror,strtok,tmpnam,ttyname

+0

有关'readdir_r'的注意事项:请查看联机帮助页。 GNU libc建议使用常规的'readdir'并弃用'readdir_r' – Dacav

0

Cppcheck将标志使用非重入标准库函数。启用可移植性警告以启用此检查。

请参考non_reentrant_functions_list in checknonreentrantfunctions.h了解Cppcheck将标记的功能列表。该消息的

例Cppcheck将发出:

非重入函数 '的strtok' 调用。对于线程安全应用程序,建议使用可重入替换函数'strtok_r'。 (可移植性:nonreentrantFunctionsstrtok)

+0

检查的函数列表显示不完整。 strerror_r()缺失。还有其他不可重入的函数(没有_r后缀),比如getenv(),system()和其他一些函数。这里列出:[http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_01]。但即使这样还不完整。考虑一下setlocale()或其他修改流程范围属性的函数。你不能从线程库中调用那些不会造成破坏的东西。 –

相关问题