我正在研究一个非常多线程的项目,并且想知道是否有办法让编译器标记使用对C库的非重入调用(例如strtok_r的strtok intsead)?如果没有,是否有一个不可重入呼叫的列表,所以我可以定期通过我的代码库进行grep?有没有办法标记使用非重入C库调用?
一个相关的问题是,如果有一种方法来标记3D党库使用的非重入调用。
我假设重入意味着线程安全的,但不一定是周围的其他方式。在线程项目中使用非重入调用是否有很好的理由?
我正在研究一个非常多线程的项目,并且想知道是否有办法让编译器标记使用对C库的非重入调用(例如strtok_r的strtok intsead)?如果没有,是否有一个不可重入呼叫的列表,所以我可以定期通过我的代码库进行grep?有没有办法标记使用非重入C库调用?
一个相关的问题是,如果有一种方法来标记3D党库使用的非重入调用。
我假设重入意味着线程安全的,但不一定是周围的其他方式。在线程项目中使用非重入调用是否有很好的理由?
对于源,你可能坚持每天源文件包含行:
#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
,因为我给它一个常量字符串没有格式化命令) 。
好主意在头文件和nm脚本。在某处是否存在非重入C库函数列表? – Ravi
如果你使用GCC,你可以使用'#pragma GCC poison strtok'作为'#define'的更好选择。 –
作为nm风格的变体,您可以像这样定义一个文件: _checkfns.c_:'void not_thread_safe(); void puts(){not_thread_safe(); }'那么它会隐藏这些函数的系统版本,并且如果你尝试使用puts,你会得到如下错误:'Undefined symbols:“_not_thread_safe”,引用自:checkfns.o中的_puts。 (你需要在不使用它的情况下启用死代码剥离,虽然......'-Xlinker -dead_strip') –
解决你问题的第二部分:
非重入调用可以在给他们带来了性能上的优势的方式来实现。在这种情况下,如果您知道您只从一个线程(或在一个关键部分内)发出这些呼叫,并且这是您的瓶颈,那么选择非重入呼叫是有道理的。但我只会做,如果有性能测量表明这是至关重要的......并仔细记录它..
对于二进制文件,你可以使用LD_PRELOAD
截取任何你喜欢的C库函数你想(中止,记录一个错误,但继续等)
在开发过程中的任何行动,您还可以使用的valgrind做同样的。
对于一些示例代码和参考,请参阅答案how could I intercept linux sys calls?
有呼叫的列表 是不可重入,所以我可以通过我的代码库用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
有关'readdir_r'的注意事项:请查看联机帮助页。 GNU libc建议使用常规的'readdir'并弃用'readdir_r' – Dacav
Cppcheck将标志使用非重入标准库函数。启用可移植性警告以启用此检查。
请参考non_reentrant_functions_list
in checknonreentrantfunctions.h了解Cppcheck将标记的功能列表。该消息的
例Cppcheck将发出:
非重入函数 '的strtok' 调用。对于线程安全应用程序,建议使用可重入替换函数'strtok_r'。 (可移植性:nonreentrantFunctionsstrtok)
检查的函数列表显示不完整。 strerror_r()缺失。还有其他不可重入的函数(没有_r后缀),比如getenv(),system()和其他一些函数。这里列出:[http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_01]。但即使这样还不完整。考虑一下setlocale()或其他修改流程范围属性的函数。你不能从线程库中调用那些不会造成破坏的东西。 –
问题 - Linux中的所有C运行时调用都保持调用之间的状态(例如malloc,rand,strtok等...)本质上是非线程安全的吗?还是有一个编译器/链接器指令来指定与这些调用的线程安全版本的链接?我真的想知道是否真的有线程安全问题需要解决。 – selbie
@selbie:不,它有所不同。例如,[malloc](http://www.bozemanpass.com/info/linux/malloc/Linux_Heap_Contention.html)通常是线程安全的。无论如何,rand()的线程安全性是一个哲学问题。一个完美的'rand'实现(一个oracle)本质上是线程安全的。 – MSalters