View Full Version : [C] Memory mapping di shared object
Ciao a tutti,
vorrei capire come effettuare un memory mapping di uno shared memory object, anche se non mi è molto chiaro come fare. La stragrande maggioranza degli esempio che si trova in rete utilizza mmap con dei files. Ad esempio, vorrei provare a fare una memory map di un array:
int*array=(int*) mmap(NULL, 100*sizeof(int), PROT_READ| PROT_WRITE, MAP_SHARED, -1, 0);
ma mi restituisce MAP_FAILED :(
Non mi è chiaro il mappaggio di shared objects con mmap, qualcuno mi può illuminare? Grazie.
se vuoi usare la mmap per la memoria condivisa devi usare il flag MAP_ANONYMOUS
se vuoi usare la mmap per la memoria condivisa devi usare il flag MAP_ANONYMOUS
Ok, grazie per la dritta. Ma anche con quel flag non fa il mapping :(
Ok, grazie per la dritta. Ma anche con quel flag non fa il mapping :(
Che errore ti da ?
Che errore ti da ?
Sono riuscito a fare il mapping così:
int*array=(int*) mmap(NULL, 100*sizeof(int), PROT_READ| PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
Il mio scopo però è fare un memory mapping di una matrice bidimensionale:
int**array=(int**) mmap(NULL, dim_x*sizeof(int*), PROT_READ| PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
for(..i..)
array[i]=(int*) mmap(NULL, dim_y*sizeof(int), PROT_READ| PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
Però se cerco di allocare oltre un limite che non conosco (ma già con 40 mb succede), mi dice " Cannot allocate memory" nel mapping dentro il for. Però, stranamente, un mapping di controesempio delle stesse dimensioni su un singolo array funziona. Ha senso effettuare un mapping di una matrice in questa maniera? Preciso che la macchina in questione ha circa 50 Gb di RAM.
Sono riuscito a fare il mapping così:
int*array=(int*) mmap(NULL, 100*sizeof(int), PROT_READ| PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
Il mio scopo però è fare un memory mapping di una matrice bidimensionale:
int**array=(int**) mmap(NULL, dim_x*sizeof(int*), PROT_READ| PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
for(..i..)
array[i]=(int*) mmap(NULL, dim_y*sizeof(int), PROT_READ| PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
Però se cerco di allocare oltre un limite che non conosco (ma già con 40 mb succede), mi dice " Cannot allocate memory" nel mapping dentro il for. Però, stranamente, un mapping di controesempio delle stesse dimensioni su un singolo array funziona. Ha senso effettuare un mapping di una matrice in questa maniera? Preciso che la macchina in questione ha circa 50 Gb di RAM.
Potrebbe essere un limite sul numero di aree di memoria allocate.
In ogni caso non la vedo una buona scelta, per due motivi:
In primis di performance, una matrice con doppi puntatori e' generalmente piu' lenta di una implementata con un singolo array.
La seconda e' che, a meno che tu poi non condivida la memoria con dei figli generati con una fork, hai dei puntatori che in generale non hanno senso in altri processi..
Per ovviare al problema del numero di chiamate, potresti semplicemente allocare una singola area condivisa.
Poi puoi usare una struttura piatta per la matrice oppure ,se vuoi mantenere quella attuale, prenderne dei segmenti da assegnare al vettore elle righe piuttosto che alle singole colonne.
Potrebbe essere un limite sul numero di aree di memoria allocate.
Potresti chiarirmi questo punto?
In ogni caso non la vedo una buona scelta, per due motivi:
In primis di performance, una matrice con doppi puntatori e' generalmente piu' lenta di una implementata con un singolo array.
Si, questo lo so.
La seconda e' che, a meno che tu poi non condivida la memoria con dei figli generati con una fork, hai dei puntatori che in generale non hanno senso in altri processi..
Il discorso è un pò più complesso, in quanto sto allocando con la mmap per utilizzare la memoria in modalità intervealed tramite la libnuma, una libreria di gestione della memorai modalità NUMA, la quale prevede in questo caso che la memoria sia allocata con mmap o shmat. Tale memoria sarà condivisa poi da più threads.
Poi puoi usare una struttura piatta per la matrice oppure ,se vuoi mantenere quella attuale, prenderne dei segmenti da assegnare al vettore elle righe piuttosto che alle singole colonne.
Mm, puoi farmi un esempio? Grazie.
Potresti chiarirmi questo punto?
Ah pensavo potesse esserci un limite sul numero di memorie mappate, pero' guardando sembra non sia cosi'. Potrebbe cmq essere qualche altro limite impostato per processo. Ad esempio:
RLIMIT_MEMLOCK
The maximum number of bytes of memory that may be locked into RAM. In effect this limit is rounded down to the nearest multiple of the system page size. This limit affects mlock(2) and mlockall(2) and the mmap(2) MAP_LOCKED operation. Since Linux 2.6.9 it also affects the shmctl(2) SHM_LOCK operation, where it sets a maximum on the total bytes in shared memory segments (see shmget(2)) that may be locked by the real user ID of the calling process. The shmctl(2) SHM_LOCK locks are accounted for separately from the per-process memory locks established by mlock(2), mlockall(2), and mmap(2) MAP_LOCKED; a process can lock bytes up to this limit in each of these two categories. In Linux kernels before 2.6.9, this limit controlled the amount of memory that could be locked by a privileged process. Since Linux 2.6.9, no limits are placed on the amount of memory that a privileged process may lock, and this limit instead governs the amount of memory that an unprivileged process may lock.
Potrebbe essere questo.
Il discorso è un pò più complesso, in quanto sto allocando con la mmap per utilizzare la memoria in modalità intervealed tramite la libnuma, una libreria di gestione della memorai modalità NUMA, la quale prevede in questo caso che la memoria sia allocata con mmap o shmat. Tale memoria sarà condivisa poi da più threads.
Ok, quindi si tratta tutti di thread di uno stesso processo ? In tal caso la mia osservazione non fa testo.
Mm, puoi farmi un esempio? Grazie.
Qualcosa tipo
int nRows;
int nCols;
int size
...
size = nRows * sizeof(int*) + nRows*nCols*sizeof(int);
void* data=(int*) mmap(NULL, size, PROT_READ| PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
int** array = (int**)data;
for ( int i=0; i< nRows; ++i )
{
array[i] = (int*)(array+nRows) + i*nCols;
}
(potrebbe esserci qualche errore eh, controlla)
ovvero alloco un'unica zona di memoria e poi la suddivido cosi':
+-------+--------+--------+-----+---
| array | riga 1 | riga 2 | ... |
+-------+--------+--------+-----+---
Ah pensavo potesse esserci un limite sul numero di memorie mappate, pero' guardando sembra non sia cosi'. Potrebbe cmq essere qualche altro limite impostato per processo. Ad esempio:
Potrebbe essere questo.
Mm, si, avevo visto questo limite. Ma controllando con ulimit -l ( che dovrebbe indicare proprio tale limite) dice che è settato "unlimited", quindi pare non dipenda da questo.
Ok, quindi si tratta tutti di thread di uno stesso processo ? In tal caso la mia osservazione non fa testo.
Si esatto.
Qualcosa tipo
int nRows;
int nCols;
int size
...
size = nRows * sizeof(int*) + nRows*nCols*sizeof(int);
void* data=(int*) mmap(NULL, size, PROT_READ| PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
int** array = (int**)data;
for ( int i=0; i< nRows; ++i )
{
array[i] = (int*)(array+nRows) + i*nCols;
}
(potrebbe esserci qualche errore eh, controlla)
ovvero alloco un'unica zona di memoria e poi la suddivido cosi':
+-------+--------+--------+-----+---
| array | riga 1 | riga 2 | ... |
+-------+--------+--------+-----+---
Ok, buona idea. Quindi alloco un singolo array ma accedo all'elemento i-j-e-simo con array[i][j] se non ho capito male, giusto?
Io però ancora non ho ben chiaro su cosa cambi nell'allocare memoria di uno shared object tra la malloc e la mmap. Potresti chiarirmi questo punto? Grazie.
Ok, buona idea. Quindi alloco un singolo array ma accedo all'elemento i-j-e-simo con array[i][j] se non ho capito male, giusto?
Si' esatto, quello che cambia e' solo che fai un'unica allocazione iniziale
Io però ancora non ho ben chiaro su cosa cambi nell'allocare memoria di uno shared object tra la malloc e la mmap. Potresti chiarirmi questo punto? Grazie.
La memoria allocata con una malloc e' visibile solo all'interno del processo che l'ha allocata.
La memoria che ricevi con una mmap e' indirizzabile anche da altri processi.
Se vi deve accedere un processo esterno devi mapparla mediante file (altrimenti non hai modo di dire "voglio vedere _quella_ zona di memoria".
La mappatura anonima va bene quando prima mappi la memoria e poi fai le fork dei vari processi. La differenza rispetto ad una malloc in questo caso e' che dopo una fork la memoria ottenuta precedentemente da una malloc viene copiata ed e' distinta mentre quella ottenuta da una mmap e' effettivamente comune a tutti i processi.
Nel tuo caso hai dei thread che condividono tutto la stessa memoria per cui in sostanza dovrebbe cambiare poco. L'unica nota che mi viene in mente e' che se hai sotto una architettura numa, avere dei thread di uno stesso processo che girano su nodi differenti potrebbe non essere ideale: una parte della memoria viene condivisa tra i nodi e nel caso di scritture ottieni una sincronizzazione non voluta tra i nodi.
Si' esatto, quello che cambia e' solo che fai un'unica allocazione iniziale
La memoria allocata con una malloc e' visibile solo all'interno del processo che l'ha allocata.
La memoria che ricevi con una mmap e' indirizzabile anche da altri processi.
Se vi deve accedere un processo esterno devi mapparla mediante file (altrimenti non hai modo di dire "voglio vedere _quella_ zona di memoria".
La mappatura anonima va bene quando prima mappi la memoria e poi fai le fork dei vari processi. La differenza rispetto ad una malloc in questo caso e' che dopo una fork la memoria ottenuta precedentemente da una malloc viene copiata ed e' distinta mentre quella ottenuta da una mmap e' effettivamente comune a tutti i processi.
Perfetto! Adesso ho capito :)
Nel tuo caso hai dei thread che condividono tutto la stessa memoria per cui in sostanza dovrebbe cambiare poco. L'unica nota che mi viene in mente e' che se hai sotto una architettura numa, avere dei thread di uno stesso processo che girano su nodi differenti potrebbe non essere ideale: una parte della memoria viene condivisa tra i nodi e nel caso di scritture ottieni una sincronizzazione non voluta tra i nodi.
Cosa intendi per nodi in questo caso?
Cosa intendi per nodi in questo caso?
Un insieme di processori della macchina "vicini" dal punto di vista della memoria. Un sistema NUMA non è come il classico SMP dove hai N processori che condividono tutta la memoria. Viene piuttosto suddiviso in un certo numero di nodi i quali hanno una loro memoria locale. Un nodo puo' accedere anhe al resto della memoria, ma con tempi di accesso piu' elevati
Sulle architetture x86 mi risulta che questo modus operandi sia presente solo sugli opteron e alcuni xeon bi/quadri processore, e comunque selezionabile da BIOS.
su che sistema stai lavorando ?
Un insieme di processori della macchina "vicini" dal punto di vista della memoria. Un sistema NUMA non è come il classico SMP dove hai N processori che condividono tutta la memoria. Viene piuttosto suddiviso in un certo numero di nodi i quali hanno una loro memoria locale. Un nodo puo' accedere anhe al resto della memoria, ma con tempi di accesso piu' elevati
Sulle architetture x86 mi risulta che questo modus operandi sia presente solo sugli opteron e alcuni xeon bi/quadri processore, e comunque selezionabile da BIOS.
su che sistema stai lavorando ?
Ok, la tua definizione di nodo coincide con la mia :) Sto lavorando su Linux. Facendo come suggerisci tu, la chiamata:
numa_interleave_memory fallisce se l'interleaving non parte dal primo elemento di array. Cioè, questo funziona:
numa_interleave_memory(&array[0], 1, &numa_all_nodes);
Questo no:
numa_interleave_memory(&array[1], 1, &numa_all_nodes);
Al di là della funzione numa_interleave_memory, che non so se conosci, in un array creato con mmap, c'è qualche differenza su come sono mappati in memoria array[0] e array[1]? Sembrerebbe di si..
Ok, la tua definizione di nodo coincide con la mia :) Sto lavorando su Linux. Facendo come suggerisci tu, la chiamata:
numa_interleave_memory fallisce se l'interleaving non parte dal primo elemento di array. Cioè, questo funziona:
numa_interleave_memory(&array[0], 1, &numa_all_nodes);
Questo no:
numa_interleave_memory(&array[1], 1, &numa_all_nodes);
Al di là della funzione numa_interleave_memory, che non so se conosci, in un array creato con mmap, c'è qualche differenza su come sono mappati in memoria array[0] e array[1]? Sembrerebbe di si..
Dal manuale:
numa_interleave_memory() interleaves size bytes of memory page by page from start on nodes specified in nodemask. The size argument will be rounded
up to a multiple of the system page size.
A naso direi che la memoria deve partire da un page boundary.
mmap ritorna memoria sempre mappata (in linux) all'inizio di una page, per cui array[0] e' all'inizio di una pagina, array[1] no.
Dal manuale:
A naso direi che la memoria deve partire da un page boundary.
mmap ritorna memoria sempre mappata (in linux) all'inizio di una page, per cui array[0] e' all'inizio di una pagina, array[1] no.
Si vero, grazie mille :D
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.