7

在下面的代码中,我没有使变量quithas 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代码中的 -loop 优化为:

    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

4

1 回答 1

4

强制编译器quit从 while 条件中优化 out 的负载可能很困难。我可以通过删除 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 上为 x86-64 编译时,-O3这会导致一个不检查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;会导致正确的行为。请参阅movtestje说明.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

你可以在这里玩这两个例子:没有volatilevolatile

于 2016-11-09T21:09:05.773 回答