在信号处理期间未使用volatile关键字时,演示编译器的优化效果?

时间:2016-11-07 02:20:29

标签: c signals volatile sig-atomic-t

在以下代码中,我没有将变量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,我怎样才能证明循环的优化?

1 个答案:

答案 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上的testje.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 volatilewith volatile