为什么 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().
总而言之:您正在做的事情本质上是错误的,而不是可以预测、推理或理解的事情。别这样。
当我注意到一些奇怪的行为时,我正在执行一个简单的信号处理程序演示作为练习。以下代码在 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().
总而言之:您正在做的事情本质上是错误的,而不是可以预测、推理或理解的事情。别这样。