演示在信号处理过程中不使用volatile关键字时编译器的优化效果?
Demonstrate compiler's optimization effects when volatile keyword is not used during signal handling?
在下面的代码中,我没有让变量quit
有volatile sig_atomic_t
。我将其保留为普通 int
.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#define UNUSED(x) (void) (x)
int quit;
void sigusr1_handler(int sig)
{
UNUSED(sig);
write(1, "handler\n", 8);
quit = 1;
}
int main()
{
struct sigaction sa;
sa.sa_handler = sigusr1_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
perror("sigaction");
return 1;
}
quit = 0;
while (!quit) {
printf("Working ...\n");
sleep(1);
}
printf("Exiting ...\n");
return 0;
}
由于 quit
变量未指定为 volatile
,我期望编译器的优化器会将代码中的 while
循环优化为:
while (1) {
printf("Working ...\n");
sleep(1);
}
但我没有看到这种情况发生。在一个终端上,我 运行 以下内容。
$ gcc -O3 foo.c && ./a.out
Working ...
Working ...
在另一个终端上,我将 SIGUSR1 发送到我的程序。
$ pkill -USR1 a.out
在第一个终端上,输出显示调用了程序的信号处理程序并且 while
-loop 退出。
Working ...
Working ...
handler
Exiting ...
$
由于 quit
不是 volatile
,我如何演示循环的优化?
很难强制编译器从 while 条件中优化 quit
的加载。我能够通过删除 while 循环的主体来做到这一点:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#define UNUSED(x) (void) (x)
int quit;
void sigusr1_handler(int sig)
{
UNUSED(sig);
write(1, "handler\n", 8);
quit = 1;
}
int main()
{
struct sigaction sa;
sa.sa_handler = sigusr1_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
perror("sigaction");
return 1;
}
quit = 0;
while (!quit) {
//printf("Working ...\n");
//sleep(1);
}
printf("Exiting ...\n");
return 0;
}
在 gcc 5.4 版上使用 -O3
编译 x86-64 时,这会导致无限循环,不检查 quit
变量。注意 .L5
:
处的跳转
.LC0:
.string "handler\n"
sigusr1_handler(int):
sub rsp, 8
mov edx, 8
mov esi, OFFSET FLAT:.LC0
mov edi, 1
call write
mov DWORD PTR quit[rip], 1
add rsp, 8
ret
.LC2:
.string "sigaction"
main:
sub rsp, 168
lea rdi, [rsp+8]
mov QWORD PTR [rsp], OFFSET FLAT:sigusr1_handler(int)
mov DWORD PTR [rsp+136], 0
call sigemptyset
xor edx, edx
mov rsi, rsp
mov edi, 10
call sigaction
cmp eax, -1
je .L7
mov DWORD PTR quit[rip], 0
.L5:
jmp .L5
.L7:
mov edi, OFFSET FLAT:.LC2
call perror
mov eax, 1
add rsp, 168
ret
quit:
.zero 4
将定义更改为 volatile int quit;
会导致正确的行为。请参阅 mov
、test
和 je
说明 .L6
:
.LC0:
.string "handler\n"
sigusr1_handler(int):
sub rsp, 8
mov edx, 8
mov esi, OFFSET FLAT:.LC0
mov edi, 1
call write
mov DWORD PTR quit[rip], 1
add rsp, 8
ret
.LC2:
.string "sigaction"
.LC3:
.string "Exiting ..."
main:
sub rsp, 168
lea rdi, [rsp+8]
mov QWORD PTR [rsp], OFFSET FLAT:sigusr1_handler(int)
mov DWORD PTR [rsp+136], 0
call sigemptyset
xor edx, edx
mov rsi, rsp
mov edi, 10
call sigaction
cmp eax, -1
je .L11
mov DWORD PTR quit[rip], 0
.L6:
mov eax, DWORD PTR quit[rip]
test eax, eax
je .L6
mov edi, OFFSET FLAT:.LC3
call puts
xor eax, eax
.L5:
add rsp, 168
ret
.L11:
mov edi, OFFSET FLAT:.LC2
call perror
mov eax, 1
jmp .L5
quit:
.zero 4
你可以在这里玩两个例子:without volatile
and with volatile
。
在下面的代码中,我没有让变量quit
有volatile sig_atomic_t
。我将其保留为普通 int
.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#define UNUSED(x) (void) (x)
int quit;
void sigusr1_handler(int sig)
{
UNUSED(sig);
write(1, "handler\n", 8);
quit = 1;
}
int main()
{
struct sigaction sa;
sa.sa_handler = sigusr1_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
perror("sigaction");
return 1;
}
quit = 0;
while (!quit) {
printf("Working ...\n");
sleep(1);
}
printf("Exiting ...\n");
return 0;
}
由于 quit
变量未指定为 volatile
,我期望编译器的优化器会将代码中的 while
循环优化为:
while (1) {
printf("Working ...\n");
sleep(1);
}
但我没有看到这种情况发生。在一个终端上,我 运行 以下内容。
$ gcc -O3 foo.c && ./a.out
Working ...
Working ...
在另一个终端上,我将 SIGUSR1 发送到我的程序。
$ pkill -USR1 a.out
在第一个终端上,输出显示调用了程序的信号处理程序并且 while
-loop 退出。
Working ...
Working ...
handler
Exiting ...
$
由于 quit
不是 volatile
,我如何演示循环的优化?
很难强制编译器从 while 条件中优化 quit
的加载。我能够通过删除 while 循环的主体来做到这一点:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#define UNUSED(x) (void) (x)
int quit;
void sigusr1_handler(int sig)
{
UNUSED(sig);
write(1, "handler\n", 8);
quit = 1;
}
int main()
{
struct sigaction sa;
sa.sa_handler = sigusr1_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
perror("sigaction");
return 1;
}
quit = 0;
while (!quit) {
//printf("Working ...\n");
//sleep(1);
}
printf("Exiting ...\n");
return 0;
}
在 gcc 5.4 版上使用 -O3
编译 x86-64 时,这会导致无限循环,不检查 quit
变量。注意 .L5
:
.LC0:
.string "handler\n"
sigusr1_handler(int):
sub rsp, 8
mov edx, 8
mov esi, OFFSET FLAT:.LC0
mov edi, 1
call write
mov DWORD PTR quit[rip], 1
add rsp, 8
ret
.LC2:
.string "sigaction"
main:
sub rsp, 168
lea rdi, [rsp+8]
mov QWORD PTR [rsp], OFFSET FLAT:sigusr1_handler(int)
mov DWORD PTR [rsp+136], 0
call sigemptyset
xor edx, edx
mov rsi, rsp
mov edi, 10
call sigaction
cmp eax, -1
je .L7
mov DWORD PTR quit[rip], 0
.L5:
jmp .L5
.L7:
mov edi, OFFSET FLAT:.LC2
call perror
mov eax, 1
add rsp, 168
ret
quit:
.zero 4
将定义更改为 volatile int quit;
会导致正确的行为。请参阅 mov
、test
和 je
说明 .L6
:
.LC0:
.string "handler\n"
sigusr1_handler(int):
sub rsp, 8
mov edx, 8
mov esi, OFFSET FLAT:.LC0
mov edi, 1
call write
mov DWORD PTR quit[rip], 1
add rsp, 8
ret
.LC2:
.string "sigaction"
.LC3:
.string "Exiting ..."
main:
sub rsp, 168
lea rdi, [rsp+8]
mov QWORD PTR [rsp], OFFSET FLAT:sigusr1_handler(int)
mov DWORD PTR [rsp+136], 0
call sigemptyset
xor edx, edx
mov rsi, rsp
mov edi, 10
call sigaction
cmp eax, -1
je .L11
mov DWORD PTR quit[rip], 0
.L6:
mov eax, DWORD PTR quit[rip]
test eax, eax
je .L6
mov edi, OFFSET FLAT:.LC3
call puts
xor eax, eax
.L5:
add rsp, 168
ret
.L11:
mov edi, OFFSET FLAT:.LC2
call perror
mov eax, 1
jmp .L5
quit:
.zero 4
你可以在这里玩两个例子:without volatile
and with volatile
。