我希望程序在malloc()
期间收到信号后将堆栈内容写入文件。为此,我尝试使用backtrace()
和backtrace_symbols_fd()
函数,但后来发现它们不是异步信号安全的。我编写了下面的代码只是为了测试,似乎程序在大多数运行中挂起。
#include <execinfo.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
typedef int bool;
#define true 1
#define false 0
static void signal_handler_child(int sig)
{
char error_msg_buffer[4096];
int fd = open("./backtrace_log.txt", O_RDWR | O_TRUNC | O_CREAT, 0777);
strcpy(error_msg_buffer, "entered signal_handler_child()");
write(fd, error_msg_buffer, strlen(error_msg_buffer));
void* buffer[1024];
const int size = backtrace(buffer, 1024);
if(size <= 0)
{
strcpy(error_msg_buffer, "unable to dump call stack trace: backtrace() returned bad size");
write(fd, error_msg_buffer, strlen(error_msg_buffer));
return ;
}
backtrace_symbols_fd(buffer, size, fd);
close(fd);
_exit(EXIT_SUCCESS);
}
int main(int argc, char *argv[])
{
pid_t pid = fork();
if(pid == 0)
{
signal(SIGSEGV, signal_handler_child);
while(true)
{
void *pointer = malloc(1000000);
free(pointer);
}
}
else if(pid == -1)
{
printf("fork() error\n");
}
else
{
sleep(3);
if(kill(pid, SIGSEGV) == -1)
printf("kill() error\n");
wait(NULL);
}
}
那么如何在这种情况下安全地将堆栈内容写入文件? backtrace()
一般可以使用下面的malloc()
吗?
手册页也说
backtrace_symbols_fd()不调用malloc(3),因此可以使用 在后一种功能失败的情况下。
但如果backtrace_symbols_fd()
实际受backtrace()
影响,那么函数malloc()
的重点是什么?
我是linux api的新手,所以感谢任何帮助。
答案 0 :(得分:2)
backtrace
调用malloc
的主要原因是它需要使用libgcc_s
加载dlopen
。您可以先调用backtrace
来初始化自己,从而获得额外的可靠性。对backtrace
的后续调用不应触发对malloc
的调用,如以下示例所示。
#define _GNU_SOURCE
#include <dlfcn.h>
#include <execinfo.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void *
malloc (size_t size)
{
const char *message = "malloc called\n";
write (STDOUT_FILENO, message, strlen (message));
void *next = dlsym (RTLD_NEXT, "malloc");
return ((__typeof__ (malloc) *) next) (size);
}
int
main (void)
{
/* This calls malloc. */
puts ("First call to backtrace.");
void *buffer[10];
backtrace (buffer, 10);
/* This does not. */
puts ("Second call to backtrace.");
backtrace (buffer, 10);
}
由于其他原因,libgcc unwinder仍然不是异步信号安全的,但是glibc认为它(对于线程取消之类的东西),它通常就像是异步信号安全一样。
答案 1 :(得分:1)
backtrace()
和backtrace_symbols()
是asyc信号不安全的,backtrace_symbols_fd()
是异步信号安全的。您可以在GNU documentation中阅读详细信息。
当信号出现时,但是如果backtrace()实际上受到malloc的影响,函数backtrace_symbols_fd()的重点是什么?
回溯机制不仅仅 用于获取回溯 - 即使在正常情况下也可以使用它。除了手册页之外,backtrace()
不会影响malloc()
。它说约backtrace_symbols()
个函数。引用man page:
backtrace_symbols_fd()采用与backtrace_symbols()相同的缓冲区和大小参数,但不是将字符串数组返回给调用者,而是将每行一个字符串写入文件描述符fd。 backtrace_symbols_fd()不调用malloc(3),因此可以在后一个函数可能失败的情况下使用。
但是,无论如何,GNU文档将backtrace
标记为asyn-csignal-unsafe。因此,即使它不受malloc()的影响,从信号处理程序调用它也是不安全的。但是当你在SIGSEGV处理程序中时,你可能已经处于可怕的情况(你正在处理的SIGSEGV可能是由某些未定义的行为引起的)。总之,从信号处理程序中使用backtrace()
没有安全的方法。调用backtrace()
只是不安全(没有强有力的保证),但在大多数情况下可能效果很好。