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);
    }

しかし、私はこれが起こっているのを見ません。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次の場所にある jump-to-self に注意してください.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;と、正しい動作が発生します。movtest、およびの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

ここでは、なしvolatileとありvolatile両方の例で遊ぶことができます。

于 2016-11-09T21:09:05.773 に答える