2016-02-01 16 views
11

Sto facendo qualche ricerca sui thread verdi C++, principalmente boost::coroutine2 e simili funzioni POSIX come makecontext()/swapcontext() e pianificando di implementare una libreria di thread verde C++ sopra a boost::coroutine2. Entrambi richiedono il codice utente per allocare uno stack per ogni nuova funzione/coroutine.Allocazione dello stack per i fili verdi C++

La mia piattaforma di destinazione è x64/Linux. Voglio che la mia libreria di thread verde sia adatta per l'uso generale, quindi le pile dovrebbero espandersi come richiesto (un limite superiore ragionevole va bene, ad esempio 10 MB), sarebbe bello se le pile potessero ridursi quando troppa memoria non è utilizzata (non richiesta). Non ho trovato un algoritmo appropriato per allocare gli stack.

Dopo un po 'googling, ho capito alcune opzioni me stesso:

  1. uso frazionato pila attuato dal compilatore (gcc -fsplit-stack), ma stack di Spalato ha sovraccarico delle prestazioni. Go si è già allontanato dallo split stack per motivi di prestazioni.
  2. allocare un grande blocco di memoria con mmap() sperare che il kernel sia abbastanza intelligente da lasciare la memoria fisica non allocata e allocarla solo quando si accede agli stack. In questo caso, siamo in balia del kernel.
  3. riserva un ampio spazio di memoria con mmap(PROT_NONE) e imposta un gestore di segnale SIGSEGV. Nel gestore di segnale, quando lo SIGSEGV è causato dall'accesso allo stack (la memoria utilizzata è all'interno dello spazio di memoria grande riservato), allocare la memoria necessaria con mmap(PROT_READ | PROT_WRITE). Ecco il problema per questo approccio: mmap() non è asincrono sicuro, non può essere chiamato all'interno di un gestore di segnale. Può ancora essere implementato, , molto ingannevole: creare un altro thread durante l'avvio del programma per l'allocazione della memoria e utilizzare pipe() + read()/write() per inviare le informazioni di allocazione della memoria dal gestore di segnali alla discussione.

qualche altra domanda su Opzione 3:

  1. non sono sicuro il sovraccarico delle prestazioni di questo approccio, quanto bene/male il kernel/CPU esegue quando lo spazio di memoria è estremamente frammentato a causa migliaia di chiamate mmap()?
  2. Questo approccio è corretto se si accede alla memoria non allocata nello spazio del kernel? per esempio. quando viene chiamato read()?

Esistono altre (migliori) opzioni per l'assegnazione dello stack per i fili verdi? In che modo gli stack di thread verdi sono allocati in altre implementazioni, ad es. Vai/Java?

+1

Mentre 'mmap' non è sicuro async secondo POSIX, è in realtà asincrono sicuro in Linux e praticamente ogni variante UNIX ragionevole, utilizzabile là fuori. –

+0

@ChrisDodd Posso chiedere perché 'mmap' può essere utile per i thread verdi? Non sono un esperto ma volevo sapere. – VermillionAzure

+0

@ChrisDodd Non ho trovato nessuna pagina man/link su questo, potresti per favore darmi un link? – user416983

risposta

0

Perché mmap? Quando si assegna a new (o malloc) la memoria è intatta e sicuramente non mappata.

const int STACK_SIZE = 10 * 1024*1024; 
char*p = new char[STACK_SIZE*numThreads]; 

p ora ha memoria sufficiente per i thread che si desidera. Quando avete bisogno di memoria, avviare l'accesso a p + STACK_SIZE * i

+2

Questo non è sicuramente garantito per essere non mappato, o inizializzato su qualsiasi valore in particolare. Usando la GNU libc malloc, quella grande allocazione alla fine chiamerà comunque mmap(). – Matt

1

Il modo in cui glibc assegna stack per i normali programmi C è quello di mmap una regione con il flag seguente mmap progettato solo per questo scopo:

MAP_GROWSDOWN 
      Used for stacks. Indicates to the kernel virtual memory system 
      that the mapping should extend downward in memory. 

Per compatibilità, probabilmente dovresti usare anche MAP_STACK. Quindi non devi scrivere tu stesso il gestore SIGSEGV e lo stack cresce automaticamente.I limiti possono essere impostati come descritto qui What does "ulimit -s unlimited" do?

Se si desidera una dimensione di stack limitata, che è normalmente ciò che le persone fanno per i gestori di segnale se vogliono chiamare sigaltstack(2), emettere semplicemente una normale chiamata mmap.

Il kernel Linux esegue sempre il mapping di pagine fisiche su pagine virtuali, rilevando l'errore di pagina al primo accesso a una pagina (forse non in kernel in tempo reale ma sicuramente in tutte le altre configurazioni). È possibile utilizzare l'interfaccia /proc/<pid>/pagemap (o questo strumento che ho scritto https://github.com/dwks/pagemap) per verificarlo se si è interessati.

Problemi correlati