2010-03-01 12 views
7

Je cherche un moyen d'incrémenter un short de manière atomique, puis de retourner cette valeur. J'ai besoin de le faire à la fois en mode noyau et en mode utilisateur, donc c'est en C, sous Linux, sur l'architecture Intel 32 bits. Malheureusement, en raison des exigences de vitesse, un verrou de mutex ne va pas être une bonne option.Comment faire un incrément atomique et récupérer en C?

Existe-t-il un autre moyen de le faire? À ce stade, il semble que la seule option disponible est d'intégrer un assemblage. Si c'est le cas, quelqu'un pourrait-il me diriger vers les instructions appropriées?

Répondre

7

GCC prend en charge des opérations atomiques:

gcc atomics

+0

En particulier, '__sync_add_and_fetch' ressemble à ce que l'OP est après. – caf

+0

J'ai trouvé ça avant, mais il y a deux problèmes. La première est que d'après ce que je comprends, ils ne fonctionneront pas avec le compilateur du noyau ... Je ne suis pas sûr de savoir pourquoi, alors peut-être qu'il y a un travail facile. En second lieu, même dans l'espace utilisateur, l'éditeur de liens me donne l'erreur suivante: undefined reference to '__sync_add_and_fetch_2' Si quelqu'un peut pointer des solutions à l'un de ces problèmes (ou les deux!) Je serais très reconnaissant. – Bryan

+0

Tout comme une mise à jour, __sync_add_and_fetch fonctionne dans l'espace utilisateur, si vous incluez l'indicateur -march = pentium lors de la compilation (http://stackoverflow.com/questions/130740/link-error-when-compiling-gcc-atomic-operation- en mode 32 bits), alors maintenant je travaille sur le fonctionnement du module du noyau. – Bryan

4

encastrés __atomic

Comme de GCC 4.8, __sync encastrés sont devenus obsolètes en faveur des __atomic encastrés: https://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/_005f_005fatomic-Builtins.html

Ils implémentent le modèle de mémoire C++ et std::atomic les utilisent en interne. L'exemple de thread POSIX suivant échoue de façon cohérente avec ++ sur x86-64 et fonctionne toujours avec _atomic_fetch_add.

#include <assert.h> 
#include <pthread.h> 
#include <stdlib.h> 

enum CONSTANTS { 
    NUM_THREADS = 1000, 
    NUM_ITERS = 1000 
}; 

int global = 0; 

void* main_thread(void *arg) { 
    int i; 
    for (i = 0; i < NUM_ITERS; ++i) { 
     __atomic_fetch_add(&global, 1, __ATOMIC_SEQ_CST); 
     /* This fails consistently. */ 
     /*global++*/; 
    } 
    return NULL; 
} 

int main() { 
    int i; 
    pthread_t threads[NUM_THREADS]; 
    for (i = 0; i < NUM_THREADS; ++i) 
     pthread_create(&threads[i], NULL, main_thread, NULL); 
    for (i = 0; i < NUM_THREADS; ++i) 
     pthread_join(threads[i], NULL); 
    assert(global == NUM_THREADS * NUM_ITERS); 
    return EXIT_SUCCESS; 
} 

C11 _Atomic

En 5.1, le code ci-dessus fonctionne avec:

_Atomic int global = 0; 
global++; 

Mais je pense threads.h est pas encore mis en œuvre pour créer réellement les fils sans POSIX.