Проблемы с примером неатомарного доступа на сайте GNU - PullRequest
0 голосов
/ 17 октября 2018

На веб-сайте GNU имеется простой пример, который должен продемонстрировать проблемы, возникающие при неатомарном доступе.В примере содержится небольшая ошибка, они забыли #include <unistd.h>:

#include <signal.h>
#include <stdio.h>
#include <unistd.h>

struct two_words { int a, b; } memory;

static struct two_words zeros = { 0, 0 }, ones = { 1, 1 };

void handler(int signum)
{
   printf ("%d,%d\n", memory.a, memory.b);
   alarm (1);
}

int main (void)
{
   signal (SIGALRM, handler);
   memory = zeros;
   alarm (1);
   while (1)
     {
    memory = zeros;
    memory = ones;
     }
}

Идея состоит в том, что присваивание memory = zeros; или memory = ones; занимает несколько циклов и, таким образом, обработчик прерываний сможет печатать«0 1» или «1 0» в некоторый момент времени.

Однако, что интересно для архитектуры x86-64, код сборки, создаваемый компилятором gcc, выглядит следующим образом.Похоже, что назначение выполняется в течение одного цикла командой movq:

    .file   "interrupt_handler.c"
    .text
    .comm   memory,8,8
    .local  zeros
    .comm   zeros,8,8
    .data
    .align 8
    .type   ones, @object
    .size   ones, 8
ones:
    .long   1
    .long   1
    .section    .rodata
.LC0:
    .string "%d,%d\n"
    .text
    .globl  handler
    .type   handler, @function
handler:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    %edi, -4(%rbp)
    movl    4+memory(%rip), %edx
    movl    memory(%rip), %eax
    movl    %eax, %esi
    leaq    .LC0(%rip), %rdi
    movl    $0, %eax
    call    printf@PLT
    movl    $1, %edi
    call    alarm@PLT
    nop
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   handler, .-handler
    .globl  main
    .type   main, @function
main:
.LFB1:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    leaq    handler(%rip), %rsi
    movl    $14, %edi
    call    signal@PLT
    movq    zeros(%rip), %rax
    movq    %rax, memory(%rip)
    movl    $1, %edi
    call    alarm@PLT
.L3:
    movq    zeros(%rip), %rax
    movq    %rax, memory(%rip)
    movq    ones(%rip), %rax
    movq    %rax, memory(%rip)
    jmp .L3
    .cfi_endproc
.LFE1:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 7.3.0-16ubuntu3) 7.3.0"
    .section    .note.GNU-stack,"",@progbits

Может кто-нибудь объяснить, как возможно, что два разных назначения выполняются в одном цикле?Потому что я думаю, что присвоение двух разных целочисленных значений должно происходить двум разным частям памяти, но здесь почему-то кажется, что они записаны в одном месте.

Этот пример меняется, когда вместо intЯ бы использовал двойной.Цикл while в сборке становится:

.L3:
    movq    zeros(%rip), %rax
    movq    8+zeros(%rip), %rdx
    movq    %rax, memory(%rip)
    movq    %rdx, 8+memory(%rip)
    movq    ones(%rip), %rax
    movq    8+ones(%rip), %rdx
    movq    %rax, memory(%rip)
    movq    %rdx, 8+memory(%rip)
    jmp .L3
...