为什么 SIGSEGV 不会使进程崩溃?
Why doesn't SIGSEGV crash the process?
我正在尝试实现 breakpad 以获取我们的跨平台 Qt 应用程序的崩溃报告和堆栈跟踪。我想我实现了所有必要的代码,但我无法让应用程序在 Windows.
上可靠地崩溃
我使用 MinGW gcc 编译器和 Qt。
我在 UI 中创建了一个按钮。
void crash() {
printf(NULL);
int* x = 0;
*x = 1;
int a = 1/0;
}
/* .... */
connect(ui->btnCrash, SIGNAL(clicked()),this,SLOT(crash()));
单击按钮时,实际上没有任何反应。但是,当 运行 处于调试模式时,调试器 (gdb) 在第一次函数调用时检测到 SIGSEGV,然后放弃 运行 方法的其余部分。当我在代码的其他地方故意做非法的事情时,我注意到了同样的行为。这会导致 unexpected/undefined 行为。
现在此行为与 Linux 不同,后者在调用此 crash() 时,进程正确崩溃,并创建转储。
那么有什么区别呢?我怎样才能跨平台有相同的行为?
您的代码在
中有未定义的行为
*x = 1;
因为您不能取消引用空指针。实际上我不太确定除以零,但是一旦你下车 rails 无论如何所有的赌注都会取消。
如果你想发出 SIGSEGV
信号,那么就这样做,但不要使用可能导致你的代码执行任何操作的未定义行为。您不应该期望您的代码有任何输出,而是修复它 ;)。
这是一个最小控制台程序的源代码,它尝试
取消引用空指针
main.c
#include <stdio.h>
int shoot_my_foot() {
int* x = 0;
return *x;
}
int main()
{
int i = shoot_my_foot();
printf("%d\n",i);
return 0;
}
我会在 (Ubuntu 18.04) Linux:
上编译并 运行
$ gcc -Wall -Wextra -o prog main.c
$ ./prog
Segmentation fault (core dumped)
系统 return 代码是什么?
$ echo $?
139
当程序因致命信号而被杀死时,Linux returns 128 + 调用者的信号编号。所以
那是 128 + 11,即 128 + SIGSEGV
.
这就是在 Linux 上当程序试图取消引用空指针时发生的情况。
这就是 Linux 对行为不端的程序所做的事情 :它杀死了它并 return 教育了我们
128 + SIGSEGV
。这不是程序所做的:它不处理任何信号。
现在我将跳入 Windows 10 VM 并编译和 运行 相同的程序
微软 C 编译器:
>cl /Feprog /W4 main.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main.c
Microsoft (R) Incremental Linker Version 14.11.25547.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:prog.exe
main.obj
>prog
>
没有。所以程序崩溃了,并且:
>echo %errorlevel%
-1073741819
系统return代码为-1073741819
,即有符号整数值
0xc0000005
,著名的 Windows 错误代码,意思是 访问冲突 。
仍在 Windows,我现在将使用 GCC 编译和 运行 程序:
>gcc --version
gcc (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.2.0
>gcc -Wall -Wextra -o prog.exe main.c
>prog
>echo %errorlevel%
-1073741819
和之前一样,程序崩溃,系统代码0xc0000005
。
从顶部再来一次:
>gcc -Wall -Wextra -o prog.exe main.c
>prog
>echo %errorlevel%
-1073741819
没有变化。
这就是在 Windows 上当程序试图取消引用空指针时发生的情况。
这就是 Windows 对行为不端的程序所做的事情:它杀死了它并且 returns 我们
0xc0000005
.
对于行为不端的 C 程序,我们没有任何值得感谢的事实
Windows 无论我们用它编译 MinGW-W64 gcc
做同样的事情
或女士 cl
。 Windows 我们不能责怪它
与 Linux.
不同
确实,没有什么值得我们感谢的,因为同样的事情发生了
到行为不端的程序,用 GCC 编译,两次我们都只是 运行 它。因为丙
(或 C++)标准不承诺取消引用空指针会导致 SIGSEGV
被提升(或者除以 0 将导致 SIGFPE
,等等)。它只是承诺
此操作导致 未定义的行为 ,包括可能导致 SIGSEGV
当节目在星期二 gdb
下 运行 时,否则不会。
事实上,程序 确实 在我们的所有三个中引起 SIGSEGV
编译场景,我们可以通过为程序提供一个处理程序来观察
信号:
main_1.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <assert.h>
static void handler(int sig)
{
assert(sig == SIGSEGV);
fputs("Caught SIGSEGV\n", stderr);
exit(128 + SIGSEGV);
}
int shoot_my_foot(void) {
int* x = 0;
return *x;
}
int main(void)
{
int i;
signal(SIGSEGV, handler);
i = shoot_my_foot();
printf("%d\n",i);
return 0;
}
在 Linux:
$ gcc -Wall -Wextra -o prog main_1.c
$ ./prog
Caught SIGSEGV
$ echo $?
139
在 Windows 上,使用 MinGW-W64
gcc`:
>gcc -Wall -Wextra -o prog.exe main_1.c
>prog
Caught SIGSEGV
>echo %errorlevel%
139
Windows,MS cl
:
>cl /Feprog /W4 main_1.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main_1.c
Microsoft (R) Incremental Linker Version 14.11.25547.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:prog.exe
main_1.obj
>prog
Caught SIGSEGV
>echo %errorlevel%
139
这种一致的行为与我们观察到的不同
gdb
下的原程序:
>gcc -Wall -Wextra -g -o prog.exe main.c
>gdb -ex run prog.exe
GNU gdb (GDB) 8.0.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-w64-mingw32".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from prog.exe...done.
Starting program: C:\develop\so\scrap\prog.exe
[New Thread 6084.0x1e98]
[New Thread 6084.0x27b8]
Thread 1 received signal SIGSEGV, Segmentation fault.
0x0000000000401584 in shoot_my_foot () at main.c:5
5 return *x;
(gdb)
原因是 gdb
默认安装信号处理程序
所有致命信号,其 SIGSEGV
处理程序的行为是输出
比如:
Thread 1 received signal SIGSEGV, Segmentation fault.
0x0000000000401584 in shoot_my_foot () at main.c:5
5 return *x;
并转到 gdb
提示符,这与我们安装的 SIGSEGV
处理程序的行为不同
main_1.c
.
所以你有问题的答案:
How can I have the same behavior across platforms ?
在实践中已经尽善尽美了:-
您可以在程序中处理信号,并将信号处理程序限制为
在您的首选范围内,跨平台行为相同的代码
意思相同.
而且这个答案在实践中只是它得到的那样好,因为在 原则 中,
根据语言标准,您不能依赖导致 undefined 的操作
behaviour 发出任何特定信号,或产生任何特定甚至一致的结果。
如果实际上是您 objective 实现一致的跨平台 处理
致命信号 ,然后调用适当的函数来激发信号 sig
进行测试
目的由标准头文件 <signal.h>
提供(在 C++ 中,
<csignal>
):
int raise( int sig )
Sends signal sig
to the program.
我正在尝试实现 breakpad 以获取我们的跨平台 Qt 应用程序的崩溃报告和堆栈跟踪。我想我实现了所有必要的代码,但我无法让应用程序在 Windows.
上可靠地崩溃我使用 MinGW gcc 编译器和 Qt。
我在 UI 中创建了一个按钮。
void crash() {
printf(NULL);
int* x = 0;
*x = 1;
int a = 1/0;
}
/* .... */
connect(ui->btnCrash, SIGNAL(clicked()),this,SLOT(crash()));
单击按钮时,实际上没有任何反应。但是,当 运行 处于调试模式时,调试器 (gdb) 在第一次函数调用时检测到 SIGSEGV,然后放弃 运行 方法的其余部分。当我在代码的其他地方故意做非法的事情时,我注意到了同样的行为。这会导致 unexpected/undefined 行为。
现在此行为与 Linux 不同,后者在调用此 crash() 时,进程正确崩溃,并创建转储。
那么有什么区别呢?我怎样才能跨平台有相同的行为?
您的代码在
中有未定义的行为*x = 1;
因为您不能取消引用空指针。实际上我不太确定除以零,但是一旦你下车 rails 无论如何所有的赌注都会取消。
如果你想发出 SIGSEGV
信号,那么就这样做,但不要使用可能导致你的代码执行任何操作的未定义行为。您不应该期望您的代码有任何输出,而是修复它 ;)。
这是一个最小控制台程序的源代码,它尝试 取消引用空指针
main.c
#include <stdio.h>
int shoot_my_foot() {
int* x = 0;
return *x;
}
int main()
{
int i = shoot_my_foot();
printf("%d\n",i);
return 0;
}
我会在 (Ubuntu 18.04) Linux:
上编译并 运行$ gcc -Wall -Wextra -o prog main.c
$ ./prog
Segmentation fault (core dumped)
系统 return 代码是什么?
$ echo $?
139
当程序因致命信号而被杀死时,Linux returns 128 + 调用者的信号编号。所以
那是 128 + 11,即 128 + SIGSEGV
.
这就是在 Linux 上当程序试图取消引用空指针时发生的情况。
这就是 Linux 对行为不端的程序所做的事情 :它杀死了它并 return 教育了我们
128 + SIGSEGV
。这不是程序所做的:它不处理任何信号。
现在我将跳入 Windows 10 VM 并编译和 运行 相同的程序 微软 C 编译器:
>cl /Feprog /W4 main.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main.c
Microsoft (R) Incremental Linker Version 14.11.25547.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:prog.exe
main.obj
>prog
>
没有。所以程序崩溃了,并且:
>echo %errorlevel%
-1073741819
系统return代码为-1073741819
,即有符号整数值
0xc0000005
,著名的 Windows 错误代码,意思是 访问冲突 。
仍在 Windows,我现在将使用 GCC 编译和 运行 程序:
>gcc --version
gcc (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.2.0
>gcc -Wall -Wextra -o prog.exe main.c
>prog
>echo %errorlevel%
-1073741819
和之前一样,程序崩溃,系统代码0xc0000005
。
从顶部再来一次:
>gcc -Wall -Wextra -o prog.exe main.c
>prog
>echo %errorlevel%
-1073741819
没有变化。
这就是在 Windows 上当程序试图取消引用空指针时发生的情况。
这就是 Windows 对行为不端的程序所做的事情:它杀死了它并且 returns 我们
0xc0000005
.
对于行为不端的 C 程序,我们没有任何值得感谢的事实
Windows 无论我们用它编译 MinGW-W64 gcc
做同样的事情
或女士 cl
。 Windows 我们不能责怪它
与 Linux.
确实,没有什么值得我们感谢的,因为同样的事情发生了
到行为不端的程序,用 GCC 编译,两次我们都只是 运行 它。因为丙
(或 C++)标准不承诺取消引用空指针会导致 SIGSEGV
被提升(或者除以 0 将导致 SIGFPE
,等等)。它只是承诺
此操作导致 未定义的行为 ,包括可能导致 SIGSEGV
当节目在星期二 gdb
下 运行 时,否则不会。
事实上,程序 确实 在我们的所有三个中引起 SIGSEGV
编译场景,我们可以通过为程序提供一个处理程序来观察
信号:
main_1.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <assert.h>
static void handler(int sig)
{
assert(sig == SIGSEGV);
fputs("Caught SIGSEGV\n", stderr);
exit(128 + SIGSEGV);
}
int shoot_my_foot(void) {
int* x = 0;
return *x;
}
int main(void)
{
int i;
signal(SIGSEGV, handler);
i = shoot_my_foot();
printf("%d\n",i);
return 0;
}
在 Linux:
$ gcc -Wall -Wextra -o prog main_1.c
$ ./prog
Caught SIGSEGV
$ echo $?
139
在 Windows 上,使用 MinGW-W64
gcc`:
>gcc -Wall -Wextra -o prog.exe main_1.c
>prog
Caught SIGSEGV
>echo %errorlevel%
139
Windows,MS cl
:
>cl /Feprog /W4 main_1.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main_1.c
Microsoft (R) Incremental Linker Version 14.11.25547.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:prog.exe
main_1.obj
>prog
Caught SIGSEGV
>echo %errorlevel%
139
这种一致的行为与我们观察到的不同
gdb
下的原程序:
>gcc -Wall -Wextra -g -o prog.exe main.c
>gdb -ex run prog.exe
GNU gdb (GDB) 8.0.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-w64-mingw32".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from prog.exe...done.
Starting program: C:\develop\so\scrap\prog.exe
[New Thread 6084.0x1e98]
[New Thread 6084.0x27b8]
Thread 1 received signal SIGSEGV, Segmentation fault.
0x0000000000401584 in shoot_my_foot () at main.c:5
5 return *x;
(gdb)
原因是 gdb
默认安装信号处理程序
所有致命信号,其 SIGSEGV
处理程序的行为是输出
比如:
Thread 1 received signal SIGSEGV, Segmentation fault.
0x0000000000401584 in shoot_my_foot () at main.c:5
5 return *x;
并转到 gdb
提示符,这与我们安装的 SIGSEGV
处理程序的行为不同
main_1.c
.
所以你有问题的答案:
How can I have the same behavior across platforms ?
在实践中已经尽善尽美了:-
您可以在程序中处理信号,并将信号处理程序限制为 在您的首选范围内,跨平台行为相同的代码 意思相同.
而且这个答案在实践中只是它得到的那样好,因为在 原则 中,
根据语言标准,您不能依赖导致 undefined 的操作
behaviour 发出任何特定信号,或产生任何特定甚至一致的结果。
如果实际上是您 objective 实现一致的跨平台 处理
致命信号 ,然后调用适当的函数来激发信号 sig
进行测试
目的由标准头文件 <signal.h>
提供(在 C++ 中,
<csignal>
):
int raise( int sig )
Sends signal
sig
to the program.