为什么 printf 在 macOS 而不是 ubuntu 上中断信号处理程序?

Why does printf break signal handlers on macOS but not on ubuntu?

当我注意到一些奇怪的行为时,我正在执行一个简单的信号处理程序演示作为练习。以下代码在 ubuntu 14.04 LTS 上没有变化,但在 macOS Sierra 10.12.6 上没有变化。那是在 macOS 上程序永远挂起而没有输出。但是,它可以在两个平台上运行,在信号处理程序中注释掉 printf 或在 posix_memalign 调用之前的任何地方在主函数中添加额外的 printf。我不明白这种行为,这是怎么回事?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <signal.h>


//Has to be global for signal handler
char* mem_spot = NULL;
const char* copy_from = "hello";
const int mem_req = 5;

//signal handler prototype
void handle_seg(int fault);

int
main(int argc, char const *argv[])
{
    //On macOS this must be placed uncommented somewhere before
    //posix_memalign or the signal handler printing removed:

    //printf("Strange fixing code!");

    //setup signal handlers
    signal(SIGSEGV,handle_seg);
    signal(SIGBUS,handle_seg);
    //get memory aligned to the page and protect it
    posix_memalign((void**) &mem_spot, getpagesize(), mem_req);
    mprotect(mem_spot,mem_req,PROT_READ);
    //Copying into the memory slot (it's protected so should break!)
    strcpy(mem_spot,copy_from);
    //Output my results
    printf("Write of string '%s' to memory giving '%s' complete!\n",copy_from, mem_spot);
    return 0;
}

void handle_seg(int fault)
{
    if (fault == SIGSEGV){
        printf(" (Handled a segmentation fault!) ");
        mprotect(mem_spot,mem_req,PROT_READ | PROT_WRITE);
    }
    else if (fault == SIGBUS){
        printf(" (Handled a bus fault!) ");
        mprotect(mem_spot,mem_req,PROT_READ | PROT_WRITE);
    }
}

所以,给出一个实际的答案:您的程序以多种方式触发未定义的行为,这意味着它可以做任何事情。打印出来的东西,或者挂起,或者崩溃,都是 运行 它的同样有效的结果。

您的第一个问题:从信号处理程序调用非异步信号安全函数。可以从处理程序中安全调用的函数数量有限。您可以在 here 或 linux 上查看列表 man signal-safety

你的第二个问题:从一个由 SIGSEGV 触发的信号处理程序返回,而不是像 kill() 这样显式引发给定信号的函数触发的信号处理程序。引用上述文档:

The behavior of a process is undefined after it returns normally from a signal-catching function for a SIGBUS, SIGFPE, SIGILL, or SIGSEGV signal that was not generated by kill(), sigqueue(), or raise().

总而言之:您正在做的事情本质上是错误的,而不是可以预测、推理或理解的事情。别这样。