Что означает инструкция "lock" в x86 ассемблере?

Я видел некоторые x86 сборки в исходниках Qt:

q_atomic_increment:
    movl 4(%esp), %ecx
    lock 
    incl (%ecx)
    mov $0,%eax
    setne %al
    ret

    .align 4,0x90
    .type q_atomic_increment,@function
    .size   q_atomic_increment,.-q_atomic_increment
  1. Погуглив, я понял, что инструкция lock заставит CPU заблокировать шину, но я не знаю, когда CPU освобождает шину?

  2. По поводу всего вышеприведенного кода, я не понимаю, как этот код реализует Add?

59
задан Ciro Santilli 新疆改造中心法轮功六四事件 28 June 2019 в 09:10
поделиться

1 ответ

Минимальные выполнимые потоки C++ + БЛОКИРУЮТ пример встроенного ассемблерного кода

main.cpp

#include <atomic>
#include <cassert>
#include <iostream>
#include <thread>
#include <vector>

std::atomic_ulong my_atomic_ulong(0);
unsigned long my_non_atomic_ulong = 0;
unsigned long my_arch_atomic_ulong = 0;
unsigned long my_arch_non_atomic_ulong = 0;
size_t niters;

void threadMain() {
    for (size_t i = 0; i < niters; ++i) {
        my_atomic_ulong++;
        my_non_atomic_ulong++;
        __asm__ __volatile__ (
            "incq %0;"
            : "+m" (my_arch_non_atomic_ulong)
            :
            :
        );
        __asm__ __volatile__ (
            "lock;"
            "incq %0;"
            : "+m" (my_arch_atomic_ulong)
            :
            :
        );
    }
}

int main(int argc, char **argv) {
    size_t nthreads;
    if (argc > 1) {
        nthreads = std::stoull(argv[1], NULL, 0);
    } else {
        nthreads = 2;
    }
    if (argc > 2) {
        niters = std::stoull(argv[2], NULL, 0);
    } else {
        niters = 10000;
    }
    std::vector<std::thread> threads(nthreads);
    for (size_t i = 0; i < nthreads; ++i)
        threads[i] = std::thread(threadMain);
    for (size_t i = 0; i < nthreads; ++i)
        threads[i].join();
    assert(my_atomic_ulong.load() == nthreads * niters);
    assert(my_atomic_ulong == my_atomic_ulong.load());
    std::cout << "my_non_atomic_ulong " << my_non_atomic_ulong << std::endl;
    assert(my_arch_atomic_ulong == nthreads * niters);
    std::cout << "my_arch_non_atomic_ulong " << my_arch_non_atomic_ulong << std::endl;
}

GitHub в восходящем направлении .

Компиляция и выполненный:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp -pthread
./main.out 2 10000

Возможный вывод:

my_non_atomic_ulong 15264
my_arch_non_atomic_ulong 15267

От этого мы видим, что префикс БЛОКИРОВКИ сделал дополнение атомарным: без него у нас есть условия состязания на многих из добавления, и общее количество в конце является меньше, чем синхронизируемые 20000.

См. также: , На что похож многоядерный ассемблер?

Протестированный в Ubuntu 19.04 amd64.

1
ответ дан 24 November 2019 в 18:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: