在以下代码中,我没有将变量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
- 循环退出。
Working ...
Working ...
handler
Exiting ...
$
由于quit
不是volatile
,我怎样才能证明循环的优化?
答案 0 :(得分:4)
强制编译器从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
和with volatile
。