这个非常简单的c程序在使用valgrind运行时会给我一个分段错误。 它开始正常时运行良好。 将USR1信号发送到过程时会崩溃。
问题似乎是printf处理浮点值格式化的方式,因为如果使用字符串(%s)或int(%d)格式参数,它可以正常工作。
P.S。我知道你不应该在信号处理程序中调用任何printf系列函数,但为什么它只会在valgrind中崩溃。
#include <stdio.h>
#include <signal.h>
void sig_usr1(int sig) {
char buf[128];
snprintf(buf, sizeof(buf), "%f", 1.0);
}
int main(int argc, char **argv) {
(void) signal(SIGUSR1, sig_usr1);
while(1);
}
答案 0 :(得分:4)
正如cnicutar所述,valgrind可能对任何与时间相关的因素产生影响,信号处理程序肯定会有资格。
我认为snprintf
在信号处理程序中使用是安全的,所以它可能只是偶然地在非valgrind情况下工作然后valgrind进来,改变时间,你得到火焰在没有valigrind的情况下你冒着死亡的风险。
我在这里找到了信号处理程序中安全的函数列表(根据POSIX.1-2003):
是的,linux.die.net
手册页有点过时但是这里的列表(感谢RedX找到这个):
除了OpenBSD的上下文之外,
没有提及snprintf
:
... OpenBSD中的异步安全,但“可能不在其他系统上”,包括snprintf(),...
所以这意味着snprintf
通常在信号处理程序中不安全。
并且,感谢Nemo,我们有一个可安全用于信号处理程序的权威功能列表:
http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03
从该链接开始,向下搜索_Exit
,您将看到该列表;然后你会看到snprintf
不在列表中。
另外,我记得在信号处理程序中使用write()
,因为fprintf
对于信号处理程序来说不安全,但那是很久以前的事了。
我没有相关标准的副本,所以我不能用任何真正权威的东西支持这个,但我想我还是会提到它。
答案 1 :(得分:4)
来自手册:http://www.network-theory.co.uk/docs/valgrind/valgrind_27.html和http://www.network-theory.co.uk/docs/valgrind/valgrind_24.html
Valgrind的信号模拟并不像它可能那样强大。提供了基本的POSIX兼容的sigaction和sigprocmask功能,但是可以想象,如果你用信号做奇怪的事情,事情可能会出错。解决方法:不要。在任何情况下,执行非POSIX信号技巧的程序本身都是不可移植的,因此应尽可能避免使用。
因此,信号处理程序中的snprintf不是POSIX允许的信号技巧,而valgrind有权阻止你的程序。
为什么snprintf不是信号安全的?
glibc手册说:http://www.gnu.org/software/hello/manual/libc/Nonreentrancy.html
如果函数使用并修改了您提供的对象,则它可能是不可重入的;如果两个调用使用相同的对象,则会发生干扰。
当您使用流进行I / O时会出现这种情况。假设信号处理程序使用fprintf打印消息。假设在传递信号时,程序正在使用相同的流进行fprintf调用。信号处理程序的消息和程序的数据都可能被破坏,因为两个调用都在相同的数据结构上运行 - 流本身。
但是,如果您知道处理程序使用的流在信号到达时可能无法被程序使用,那么您就是安全的。如果程序使用其他流,则没有问题。
你可以说s * printf *不是在流上,而是在字符串上。但在内部,glibc的snprintf确实在特殊流上工作:
ftp://sources.redhat.com/pub/glibc/snapshots/glibc-latest.tar.bz2/glibc-20090518/libio/vsnprintf.c
int
_IO_vsnprintf (string, maxlen, format, args)
{
_IO_strnfile sf; // <<-- FILE*-like descriptor
glibc中的%f
输出代码里面还有一个malloc调用:
/* Allocate buffer for output. We need two more because while rounding
it is possible that we need two more characters in front of all the
other output. If the amount of memory we have to allocate is too
large use `malloc' instead of `alloca'. */
size_t wbuffer_to_alloc = (2 + (size_t) chars_needed) * sizeof (wchar_t);
buffer_malloced = ! __libc_use_alloca (chars_needed * 2 * sizeof (wchar_t));
if (__builtin_expect (buffer_malloced, 0))
{
wbuffer = (wchar_t *) malloc (wbuffer_to_alloc);
if (wbuffer == NULL)
/* Signal an error to the caller. */
return -1;
}
else
wbuffer = (wchar_t *) alloca (wbuffer_to_alloc);
答案 2 :(得分:2)
Valgrind略微改变了你的计划中的时间。
在FAQ处获得战利品。
我的程序正常崩溃,但不在Valgrind下,或副 反之亦然即可。发生了什么事?
当一个程序在Valgrind下运行时,它的环境是轻微的 不同于它本地运行时。 大部分时间这没有任何区别,但它可以, 特别是如果你的程序有问题。
答案 3 :(得分:1)
这是一个valgrind错误。它会根据ABI的要求,使用不是16字节对齐的堆栈调用信号处理程序。在x86_64上,浮点参数在XMM寄存器中传递,这些寄存器只能存储在16字节对齐的地址中。您可以通过编译32位(gcc -m32
)来解决此问题。