Quando si utilizza la memoria condivisa, ciascun processo può eseguire la divisione della regione condivisa in un'area diversa del rispettivo spazio indirizzo. Ciò significa che quando si memorizzano i puntatori all'interno della regione condivisa, è necessario store them as offsets dell'avvio dell'area condivisa. Sfortunatamente, questo complica l'uso di istruzioni atomiche (ad esempio se stai cercando di scrivere un lock free algorithm). Ad esempio, supponiamo di avere un gruppo di nodi con riferimenti di riferimento nella memoria condivisa, creati da un singolo autore. Periodicamente, lo scrittore aggiorna periodicamente un puntatore "p" per puntare a un nodo valido con conteggio di riferimento positivo. I lettori vogliono scrivere atomicamente su "p" perché punta all'inizio di un nodo (una struttura) il cui primo elemento è un conteggio di riferimento. Poiché p punta sempre a un nodo valido, l'incremento del conteggio ref è sicuro e rende sicuro il dereferenziamento "p" e l'accesso ad altri membri. Tuttavia, tutto funziona solo quando tutto è nello stesso spazio degli indirizzi. Se i nodi e il puntatore 'p' sono memorizzati nella memoria condivisa, quindi i clienti soffrono una condizione di competizione:È possibile memorizzare puntatori nella memoria condivisa senza utilizzare offset?
- x = leggere p
- y = x + offset
- Incremento refcount ay
Durante il passaggio 2, p potrebbe cambiare e x potrebbe non puntare più a un nodo valido. L'unica soluzione a cui riesco a pensare è in qualche modo forzare tutti i processi a concordare su dove mappare la memoria condivisa, in modo che i riferimenti reali anziché gli offset possano essere memorizzati nella regione mmap'd. C'è un modo per farlo? Vedo MAP_FIXED nella documentazione di mmap, ma non so come potrei scegliere un indirizzo che sarebbe sicuro.
Modifica: utilizzando l'assembly inline e il prefisso 'lock' su x86, forse è possibile creare un "incremento ptr X con offset Y per valore Z"? Opzioni equivalenti su altre architetture? Non ho scritto molto assemblaggio, non so se esistono le istruzioni necessarie.
Se cmpxchg esegue già una lettura atomica e una scrittura atomica, è necessario il "blocco"? O questo garantisce che edi + edx sia fatto atomicamente? Ho sempre usato davvero l'assemblaggio MIPS. –
Blocco garantisce l'accesso atomico al bus di memoria, quindi è necessaria l'istruzione di blocco. Probabilmente puoi utilizzare anche API InterlockedCompareExchange (controlla MSDN per spiegazioni). Innanzitutto caricare il puntatore a 32 bit della memoria come OldValue e poi incrementarlo per ottenere NewValue, quindi provare a eseguire InterlockedCompareExchange. InterlockedCompareExchange (Destination + Offset, NewValue, OldValue) restituirà il valore confrontato se non lo stesso di OldValue rispetto ad altri thread lo si scambia, quindi non è stato effettuato alcun scambio ed è necessario ripetere la procedura. –