我希望程序在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()的意义何在?
我是新来的LinuxAPI,所以任何帮助都是值得感谢的。

最佳答案

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假设它是(对于线程取消之类的事情),并且它通常工作得好像是异步信号安全的。

关于c - 在malloc期间接收信号,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46863569/

10-11 07:08