我正在使用这个脚本来测试陷阱:
#!/bin/bash
trap "echo segfault!" SIGSEGV
g++ forever.cpp
./a.out
并且forever.cpp
只运行一个递归函数:
void forever(){
forever();
}
int main(){
forever();
}
但是它提供Segmentation fault: 11
而不是打印segfault
。我不确定为什么。
我正在使用这个脚本来测试陷阱:
#!/bin/bash
trap "echo segfault!" SIGSEGV
g++ forever.cpp
./a.out
并且forever.cpp
只运行一个递归函数:
void forever(){
forever();
}
int main(){
forever();
}
但是它提供Segmentation fault: 11
而不是打印segfault
。我不确定为什么。
bash 陷阱将捕获 bash 本身中的段错误,而不是从 bash 产生的进程中。
在这种情况下,您正在生成一个进程,并且该进程会出现段错误。您需要在 C 程序 forever.cpp 中安装一个信号处理程序来捕获它。
该trap
语句捕获由 接收到的信号bash
,而不是它的子级。孩子收到段错误并将使用适当的退出代码退出。因此,您应该检查子进程的退出代码。从这里可以看出,退出码是128+信号号。SEGV
是 11(请参阅 参考资料man signal
),因此您将获得退出代码 139。因此只需$?
针对 139 进行测试,您就完成了。
你想要的是当 bash 从它分叉的进程中捕获 SIGCHLD 时进行捕获 - 你可以使用trap <expression> CHLD
它
trap 'if [[ $? -eq 139 ]]; then echo "SIGSEGV IN CHILD PROCESS, EXITING"; exit 139; fi' CHLD
或者,如果您想要一个通用的陷阱处理程序,您可以执行以下操作:
process_returned() {
local exit_code=$? # Save the exit code for the rest of the function
[[ $exit_code -eq 0 ]] && return
if [[ $? -eq 139 ]]; then
echo "SIGSEGV IN CHILD PROCESS, EXITING";
exit 139
fi
echo "Something other than SIGSEGV was returned ($exit_code)";
... trap handling logic ...
}
trap process_returned CHLD
请记住,每次process_returned
您调用的进程返回时都会触发- 处理程序函数中的第一个逻辑(如果您使用函数)可能应该是[[ $? -eq 0 ]] && return
您可能想要使用函数的原因是因为如果您对 感兴趣SIGSEGV
,您可能也对 感兴趣SIGBUS
,因为这些都可能由于进程中的内存损坏而SIGILL
发生SIGABRT
SIGSEGV
是由于尝试访问进程中未映射的虚拟内存地址或尝试在已映射但没有可执行权限的内存页面上执行而引起的
SIGBUS
往往由于硬件故障或在某些 RISC 架构 CPU 的情况下由于未对齐的读取或写入而发生。例如,这种情况发生在 SPARC、PA-RISC、MIPS 上,我敢肯定还有一两个——虽然不是 ARM、x86 或 x86_64——至少没有任何标准的存储/加载指令。现代 x86 / x86_64 芯片上的某些扩展可能存在对齐要求。
SIGILL
当 CPU 无法解码当前执行地址的指令时引发。最常见的原因是程序计数器在保存在堆栈中并返回到多字节 CPU 指令中的某个地址时损坏
SIGABRT
malloc()
当 glibc 检测到由于堆损坏导致的堆元数据不一致时,它会在内部引发。在所有这些中,SIGABRT
您不能总是假设与内存损坏或与内存相关的故障有任何关系,因此您可能不想抓住那个