Operațiunea atomică
Operație atomică ( greacă άτομος - indivizibilă) - o operație care fie este efectuată în întregime, fie nu este efectuată deloc; o operație care nu poate fi efectuată parțial și parțial neefectuat.
Acest articol descrie cele mai simple operații atomice (citire, scriere etc.), deși termenul se poate referi la operațiuni de nivel superior, cum ar fi, de exemplu, o serie de interogări către SGBD în cadrul unei singure tranzacții .
Operațiile atomice sunt utilizate în computerele cu mai multe procesoare și în sistemele de operare multitasking pentru a oferi acces pentru mai multe procese și/sau mai multe fire ale aceluiași proces la resursele partajate între ele. O operație atomică este efectuată de un singur fir .
Clasificare
Atomicitatea operațiilor poate fi asigurată de hardware (hardware) și software (codul de program). În primul caz, sunt utilizate instrucțiuni speciale ale mașinii , a căror atomicitate este garantată de hardware. În al doilea caz, sunt utilizate instrumente software speciale de sincronizare , cu ajutorul cărora resursa partajată este blocată ; dupa blocare se executa operatia ce trebuie facuta atomic. O blocare este o operațiune atomică care fie acordă o resursă unui fir de execuție , fie îi spune firului de execuție că resursa este deja utilizată de un alt fir de execuție sau proces (ocupat).
Instrucțiuni de asamblare și atomicitate
Instrucțiuni de mașină, a căror execuție poate fi întotdeauna considerată atomică:
- instrucțiuni de mașină pentru citirea datelor din memorie la o adresă aliniată și scrierea lor într-un registru general;
- instrucțiuni de mașină pentru citirea datelor dintr-un registru de uz general și scrierea acestora în memorie la o adresă aliniată;
- instrucțiuni ale mașinii special concepute pentru a funcționa atomic, denumite în mod obișnuit instrucțiuni atomice.
Instrucțiuni de mașină care nu sunt atomice:
- instrucțiuni de mașină pentru citirea/scrierea datelor la o adresă nealiniată (prin executarea uneia dintre aceste instrucțiuni, procesorul este forțat să acceseze două celule de memorie. În momentul în care procesorul accesează o celulă, cealaltă celulă poate fi schimbată de un alt procesor);
- toate instrucțiunile mașinii de forma „ citire-modificare-scriere ” (execuția unei astfel de instrucțiuni se reduce la citirea datelor din memorie, schimbarea datelor în ALU și scrierea datelor în memorie. După citirea datelor din memorie, conținutul memoriei se poate modifica);
- string machine instrucțiuni pentru procesoare x86 ;
- instrucțiuni pentru mașină push și pop pentru procesoare x86;
- instrucțiuni de mașină care funcționează cu registre de control speciale (astfel de instrucțiuni pot fi executate în mai multe cicluri de procesor și generează zeci sau sute de accesări la memorie, ele fiind utilizate numai în software-ul de sistem ).
Instrucțiuni atomice pentru procesoare x86
Instrucțiuni atomice pentru procesoare cu arhitectură x86 :
- CMPXCHG, CMPXCHG8B, CMPXCHG16B este principala instrucțiune atomică a procesoarelor x86 care realizează compararea și schimbul . Când este utilizat cu prefixul LOCK [1] [2] , compară atomic valoarea unei variabile cu valoarea specificată și, în funcție de rezultatul comparației, fie setează valoarea specificată la variabilă, fie nu face nimic. Este baza pentru implementarea tuturor algoritmilor neblocatori , adesea folosiți în implementarea spinlock -urilor , RWLock -urilor și aproape tuturor elementelor de sincronizare la nivel înalt, cum ar fi semaforele, mutexurile, evenimentele etc.;
- XCHG este o operațiune de schimb de date între un registru și o celulă de memorie, sau între două registre. Atomicitatea acestei operații contează atunci când operandul instrucțiunii este o celulă de memorie. Pe procesoarele x86 , se execută atomic chiar și fără a utiliza prefixul LOCK [3] (din acest motiv, utilizarea acestei instrucțiuni pur și simplu pentru a schimba valorile unui registru și a unei locații de memorie ar trebui evitată, acest lucru va provoca întârzieri inutile și foarte semnificative în executarea codului). Adesea folosit în implementarea spinlock -urilor .
În plus, multe instrucțiuni de mașină de citire-modificare-scriere sunt executate atomic atunci când sunt prefixate cu LOCK [4] ( opcode 0xF0), cum ar fi următoarele:
- comenzile de adunare și scădere ADD, ADC, SUB și SBB dacă operandul destinație este adresa unei celule de memorie;
- comenzile de creștere și decrementare INC și DEC;
- comenzi logice ȘI, SAU și XOR;
- instrucțiuni cu un singur operand NEG și NOT;
- operațiuni pe biți BTS, BTR și BTC;
- operația de adăugare și schimb XADD.
Prefixul LOCK blochează accesul la memorie pe durata instrucțiunii. O blocare se poate extinde pe o zonă de memorie mai largă decât lungimea operandului, cum ar fi lungimea unei linii de cache .
Instrucțiuni atomice în procesoarele RISC
O caracteristică a arhitecturilor procesoarelor RISC este absența instrucțiunilor de citire-modificare-scriere . Procesoarele DEC Alpha , PowerPC , MIPS și ARM (ARMv6 și mai vechi) RISC acceptă accesul exclusiv la memorie fără blocare. Operațiile atomice sunt implementate folosind o pereche de instrucțiuni exclusive de citire-scriere LL și SC, după cum urmează:
- încărcarea cu un marcaj (LL - load linked);
- modificarea datelor;
- încercare de scriere (SC - stocare condiționată).
Prima instrucțiune (LL) încarcă datele din locația de memorie într-un registru și marchează locația ca locație pentru acces exclusiv. În continuare, se efectuează modificările necesare de date în registru. Scrierea datelor din registru în memorie (SC) se efectuează numai dacă valoarea celulei de memorie nu s-a schimbat. Dacă valoarea s-a modificat, cele trei operații (LL, modificarea datelor și SC) trebuie repetate.
Instrucțiuni atomice și compilatoare
Compilatorii limbilor de nivel înalt , de regulă, nu folosesc instrucțiuni atomice atunci când generează cod, deoarece, în primul rând, operațiunile atomice necesită mult mai multe resurse decât cele obișnuite și, în al doilea rând, compilatorul nu are informații despre când ar trebui accesul la date. să fie efectuată atomic (deoarece nici modificatorul volatil pentru o variabilă în C/C++ nu înseamnă o nevoie reală de a folosi operații atomice). Dacă este necesar, programatorul poate folosi instrucțiuni atomice în unul dintre următoarele moduri:
- inserați instrucțiuni atomice în cod folosind asamblatorul furnizat de compilator , de exemplu, GCC Inline Assembly al compilatorului gcc ;
- utilizați funcții furnizate de compilator care apelează instrucțiuni atomice, cum ar fi funcțiile familiilor __builtin_ sau __sync_ ale compilatorului gcc ;
- utilizați funcții furnizate de biblioteci care apelează instrucțiuni atomice, de exemplu, funcții ale bibliotecii Glib ;
- utilizați limbaje de programare care acceptă atomicitate, cum ar fi limbaje standard C11 și C++14 care acceptă tipurile _Atomic și atomic și funcțiile familiei atomic_ [5] .
Vezi și
Note
- ↑ CMPXCHG - Comparați și schimbați Arhivat 2 noiembrie 2012 la Wayback Machine .
- ↑ CMPXCHG8B - Comparați și schimbați 8 octeți Arhivat la 30 noiembrie 2012 la Wayback Machine .
- ↑ http://faydoc.tripod.com/cpu/xchg.htm Arhivat 20 noiembrie 2012 la Wayback Machine „Dacă se face referire la un operand de memorie, protocolul de blocare al procesorului este implementat automat pe durata operațiunii de schimb, indiferent de prezența sau absența prefixului LOCK sau a valorii IOPL.”
- ↑ Operații atomice. Istoria problemei . Consultat la 12 noiembrie 2012. Arhivat din original pe 17 noiembrie 2012. (nedefinit)
- ↑ Biblioteca de operațiuni atomice - cppreference.com . Consultat la 12 noiembrie 2012. Arhivat din original la 13 august 2015. (nedefinit)
Link -uri