2013-08-19 11 views
26

Desidero aggiungere il controllo di rete di una manciata di parametri utilizzati da un servizio (daemon) in esecuzione su un sistema embedded Linux. Non c'è bisogno di chiamate di procedura, ogni parametro può essere interrogato in modo molto naturale. La memoria condivisa sembra un buon modo per mantenere il codice di rete fuori dal demone e limitare l'accesso condiviso a un insieme di variabili attentamente controllato.È C++ 11 atomico <T> utilizzabile con mmap?

Poiché non desidero che le scritture parziali causino la visibilità dei valori mai scritti, stavo pensando di utilizzare std::atomic<bool> e std::atomic<int>. Tuttavia, sono preoccupato che std::atomic<T> possa essere implementato in un modo che funziona solo con i thread C++ 11 e non con più processi (potenzialmente, nemmeno con i thread del sistema operativo). In particolare, se l'implementazione utilizza qualsiasi struttura di dati memorizzata al di fuori del blocco di memoria condivisa, in uno scenario multi-processo ciò fallirebbe.

Vedo alcuni requisiti che suggeriscono di essere che std::atomic non terrà un oggetto di blocco incorporato o puntatore ai dati aggiuntivi:

Le specializzazioni integrali atomici e la specializzazione atomic<bool> avrà layout standard. Ognuno di essi ha un banale costruttore di default e un distruttore banale. Ognuno di essi supporterà la sintassi di inizializzazione aggregata.

Ci devono essere specializzazioni parziale del puntatore del modello di classe atomica. Queste specializzazioni devono avere layout standard, banali costruttori predefiniti e banali distruttori. Ognuno di essi supporterà la sintassi di inizializzazione aggregata.

Trivial costruzione e distruzione predefinite mi sembrano escludere i dati associati all'oggetto, memorizzati nell'oggetto, tramite una variabile membro puntatore o tramite un mapping esterno.

Tuttavia, non vedo nulla che escluda un'implementazione dall'utilizzo di una singola sezione mutex/critica globale (o anche una raccolta globale, purché gli elementi della raccolta non siano associati a singoli oggetti atomici - qualcosa sulla falsariga di è possibile utilizzare uno schema di associazione della cache per ridurre i falsi conflitti). Ovviamente, l'accesso da più processi non riuscirebbe su un'implementazione usando un mutex globale, perché gli utenti avrebbero mutex indipendenti e non si sincronizzerebbero effettivamente l'uno con l'altro.

È un'implementazione di atomic<T> autorizzata a fare cose che sono incompatibili con la memoria condivisa tra processi, o ci sono altre regole che lo rendono sicuro?


Ho appena notato che la costruzione di default banale lascia l'oggetto in uno stato di non-ready, è richiesta una chiamata a atomic_init. E lo standard menziona l'inizializzazione delle serrature. Se questi sono memorizzati all'interno dell'oggetto (e l'allocazione della memoria dinamica sembra impossibile, poiché il distruttore rimane banale), saranno condivisi tra i processi. Ma sono ancora preoccupato per la possibilità di un mutex globale.

In ogni caso, garantire una singola chiamata a atomic_init per ciascuna variabile in un'area condivisa sembra difficile ... quindi suppongo che dovrò allontanarmi dai tipi atomici di C++ 11.

+0

Come un addendum, [persone sono state raccomandando l'uso di operazioni atomiche con memoria condivisa] (http://stackoverflow.com/questions/4668592/ipc-via-mmaped-file-should-atomics-and- o-volatile-be-used), sebbene non sia chiaro se intendessero includere o escludere 'std :: atomic' o se altre API sono garantite per funzionare. –

+0

Mi aspetto che un sistema ragionevole non utilizzi strutture dati esterne per le variabili 'atomiche'; in primo luogo sconfiggerebbe il punto atomico ... – Mehrdad

+0

@Mehrdad: Non vedo come prendere una serratura globale possa vanificare lo scopo più che prendere una serratura locale, e lo Standard parla in modo specifico di implementazioni che fanno quest'ultima. –

risposta

17

Sono due mesi di ritardo, ma sto avendo lo stesso identico problema in questo momento e credo di aver trovato una sorta di una risposta. La versione breve è che dovrebbe funzionare, ma non sono sicuro che ne dipenderei.

Ecco cosa ho trovato:

  • Lo standard C++ 11 definisce un nuovo modello di memoria, ma non ha alcuna nozione di sistema operativo a livello di "processo", quindi tutto multiprocessing relativo è non standard .

  • Tuttavia, la sezione 29.4 "struttura-Lock" dello standard (o almeno il progetto che ho, N3337) termina con questa nota:

    [Nota: Le operazioni che sono lock-libero dovrebbe anche senza indirizzo. Cioè, le operazioni atomiche sulla stessa posizione di memoria tramite due diversi indirizzi comunicheranno atomicamente. L'implementazione non dovrebbe dipendere da uno stato per processo. Questa restrizione consente la comunicazione da parte della memoria mappata in un processo più di una volta e dalla memoria condivisa tra due processi. - end note]

    Questo suona molto promettente. :)

  • che la nota sembra provenire da N2427, che è ancora più esplicito:

    per facilitare la comunicazione tra processi tramite memoria condivisa, è nostra intenzione che le operazioni senza blocchi anche essere indirizzo- gratuito. Cioè, le operazioni atomiche nella stessa posizione di memoria tramite due diversi indirizzi comunicheranno atomicamente. L'implementazione non dipende da nessuno stato per processo. Mentre una tale definizione va oltre lo scopo dello standard, una chiara dichiarazione del nostro intento consentirà un'espressione portatile di classe di un programma già esistente.

    Quindi sembra che sì, tutte le operazioni di blocco dovrebbero funzionare in questo scenario esatto.

  • Ora, le operazioni su std::atomic<type> sono atomiche ma possono essere o meno bloccate per particolari type, a seconda delle funzionalità della piattaforma. E possiamo controllare qualsiasi variabile x chiamando x.is_lock_free().

  • Allora, perché ho scritto che non avrei dipendere da questo? Non riesco a trovare alcun tipo di documentazione per gcc, llvm o chiunque altro che sia esplicito a riguardo.

1

Fino al C++ 11, lo standard non specificava come più thread condividessero la memoria, quindi abbiamo scritto programmi con più thread che si basavano sul comportamento specifico dell'implementazione. Lo standard continua a non specificare in che modo i processi con memoria condivisa o, se preferisci, i thread che condividono solo parzialmente la memoria, interagiscono. Qualsiasi cosa tu finisca, ti affiderai a garanzie specifiche per l'implementazione.

Detto questo, penso che un'implementazione che supporta la memoria condivisa dal processo proverà a rendere i suoi meccanismi di sincronizzazione dei thread come gli atomici utilizzabili nella memoria condivisa dal processo per la sincronizzazione dei processi. Per lo meno, penso che sarebbe difficile escogitare un'implementazione senza blocco di una specializzazione std :: atomic che non funziona correttamente cross-process.

+0

Sono d'accordo, ma lo Standard esplicitamente non richiede che 'std :: atomic' sia bloccato. –

+0

@BenVoigt Vero - ma lo considererei un QoI scadente al punto di essere un bug se un'implementazione in C++ non supportava atomici a 64 bit privi di lock su X64, ad esempio. Esiste un sacco di aree grigie nelle specifiche standard per comportamento non definito, ma in molti casi il comportamento è realisticamente limitato dalle nostre aspettative su ciò che è consentito per un'implementazione di qualità ragionevole. Se rinuncio a un puntatore NULL, i demoni non voleranno via dal mio naso - Avrò un SIGSEGV perché la mia implementazione non è un pezzo di spazzatura. – Casey

+0

Non tutte le implementazioni con memoria condivisa supporteranno 'std :: atomic ' nella memoria condivisa - ma non è probabile che ne usiate una che non lo fa. Hai già preso la decisione di affidarti a comportamenti non standard utilizzando in primo luogo la memoria condivisa dal processo, inoltre richiedere che 'std :: atomic ' funzioni in quella memoria condivisa non restringerà realisticamente la tua potenziale portabilità di molto se non del tutto – Casey

Problemi correlati