Newsgroup: it.comp.lang.c++
Titolo: oggetti in memoria condivisa

Salve,
ho un problema nel gestire e mappare degli oggetti mappati in memoria con
shm_open e mmap (devo usare assolutamente lo standard posix).
Il fatto è che finchè uso classi( con all'interno solo dichiarazioni di
variabili) mappate in memoria riesco ad accedere a tali variabili sia dal
processo che le ha inserite in memoria condivisa sia da altri processi. Ma
nel momento che devo inserire in memoria oggetti allocati in memoria
dinamica (con new) e con all'interno svariati metodi  non posso usare far
usare tali metodi ad altri processi all'infuori di quello che a creato
l'oggetto e mi viene indicato un Segmentation fault.
Per farla breve il mio scopo è condividere (per forza in memoria condivisa
usando mmap e affini, e seguendo lo standar POSIX)  vari oggetti allocati in
memoria dinamica (non posso modificarne il codice o fare a meno dell'uso di
new!!!) e permettere la manipolazione con i metodi dei rispettivi oggetti.
Purtroppo i vincoli sono dati dalla mia tesi:(((
ecco le istruzioni imputate e so che il codice esistente va bene per
accedere a classi semplici (come se fossero delle struct) e alle relative
variabili, ma non per il caso in esame...ma non so come fare o dove vedere.
1° Programma:
......
fd=shm_open("/pippo",O_CREAT|O_EXCL|O_RDWR,S_IRWXO|S_IRWXG|S_IRWXU); /*apre
il segmento in lettura e scrittura*/
ftruncate(fd,sizeof(state_queue));  /*imposta la grandezza del
segmento;state_queue è una classe*/
shm_ptr=mmap(NULL,sizeof(state_queue)),PROT_WRITE|PROT_READ,MAP_SHARED,fd,0)
;
memset((void *) shm_ptr,0, sizeof(state_queue));
shm_ptr=new state_queue((unsigned long) NumStates);
.......
2° Programma
......
fd=shm_open("/pippo",O_EXCL|O_RDWR,0);
shm_ptr=mmap(NULL,sizeof(state_queue)),PROT_WRITE|PROT_READ,MAP_SHARED,fd,0)
;
shm_ptr->enqueue(s); /* enqueue è uno dei tanti metodi della classe
state_queue; Questa istruzione mi da SEGMENTATION FAULT */
grazie
Andrea

 CUT
Non l'ho sperimentato, vedi se può fare
al caso tuo:
http://cpptips.hyperformix.com/cpptips/sh_mem_pool
probabilmente ora l'header è <new>
-- 
SiMoStro

 rispettivi oggetti
Questo in generale non è possibile. Per vari motivi. Cito il più evidente: 
se l'oggetto (o un suo membro) contiene puntatori, questi saranno 
assolutamente inutilizzabili in un processo diverso da quello che li ha 
valorizzati. 
Gli unici oggetti che puoi ragionevolmente mettere in shared memory sono 
oggetti POD (plain old data, v. FAQ) privi di puntatori.
Max

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Max M. wrote:
| andrea wrote:
|
|
|>Per farla breve il mio scopo č condividere (per forza in memoria condivisa
|>usando mmap e affini, e seguendo lo standar POSIX)  vari oggetti allocati
|>in memoria dinamica (non posso modificarne il codice o fare a meno
|>dell'uso di new!!!) e permettere la manipolazione con i metodi dei
|>rispettivi oggetti
|
|
| Questo in generale non č possibile. Per vari motivi. Cito il piů evidente:
| se l'oggetto (o un suo membro) contiene puntatori, questi saranno
| assolutamente inutilizzabili in un processo diverso da quello che li ha
| valorizzati.
|
| Gli unici oggetti che puoi ragionevolmente mettere in shared memory sono
| oggetti POD (plain old data, v. FAQ) privi di puntatori.
Avete entrambi ragione.  Quello l'OP puo fare e' farsi un memory manager
che invece che allocare memoria nello heap alloca memoria in SHm.
C'e' anche un articolo interssante su un vecchio numero di C++ user journal
dove un tizio usava proprio una std::map in shared memory.
Gaetano Mendola
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (MingW32)
Comment: Using GnuPG with Thunderbird - 
http://enigmail.mozdev.org
iD8DBQFBV0fY7UpzwH2SGd4RAs+bAKC9evmq0PQ4MrAWq4owyiCCWwUuiACghSNv
e2Fz7wBcNzeDTVxP+7At3r4=
=uAHy
-----END PGP SIGNATURE-----

 valorizzati.
A meno che i puntatori non puntino a zone di memoria condivisa...
-- 
SiMoStro

 CUT
Questo l'ho scritto un po' al volo stamane - ammetto
che non mi era mai capitato un caso come il tuo - e
sulla mia FreeBSD box fila liscio liscio.
Non è "completo al massimo" ma dimostra che si può fare
tranquillamente.
Buon lavoro e buona tesi.
// Code *********************************
#include <iostream>
#include <new>
#include <string>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
class simple_print{
string local_s;
public:
 simple_print(){local_s="";}
 void init(const char* pc){ local_s=pc; }
 void print(){ cout<<"stringa: "<<local_s<<endl; }
};
int main(){
pid_t figlio;
char* sh_mem=(char*) mmap(0, sizeof(simple_print)*2, PROT_READ | PROT_WRITE
| PROT_EXEC, MAP_ANON | MAP_SHARED, -1, 0);
cout<<"Father init...\n";
simple_print* condiviso=new (sh_mem) simple_print;
condiviso->init("Ciao ciccio!\n");
cout<<"...but he wanna make a test first, so he calls shared object member
function:\n";
condiviso->print();
cout<<"Well, now father forks his son and starts wait...\n";
figlio=fork();
if(figlio==0){
 cout<<"Son calls shared object method:\n";
 condiviso->print();
 cout<<"and exits!\n";
 return 0;
}
waitpid(figlio, 0, 0);
cout<<"Father unmap memory... ";
munmap(sh_mem,sizeof(simple_print)*2);
cout<<"and exits.\nBye.\n";
return 0;
}
// Code *********************************
-- 
SiMoStro

 ha scritto nel messaggio
news:6uP5d.986$75.7...@news3.tin.it...


 migliorare la conoscenza del c++?
Il "mio sapere" è ben poca cosa, posso supplire con la "disponibilità",
e solo se "di tempo" :-) (quella economica, beh lasciamo perdere...)
Credo di poterti dire che questo genere di problemi esula un pochettino
dal C++ e richiede una buona (eufemisticamente parlando) conoscenza
delle syscall fornite dal kernel del propio sistema operativo.
E' ovvio che questo genere di cose con Windows te le puoi anche
sognare, o almeno non ti puoi sognare di farle con la stessa
semplicità, potenza ed efficienza di un sistema Unix.
Personalmente io ti consiglierei i libri dello Stevens più che un manuale
C++; per altro - almeno per me - *il* manuale C++ rimane quello di
Stroustrup.
Comunque:
"Advanced programming in the Unix environment"
e
"Unix network programming - vol 2: interprocess communications"
 seconda edizione
entrambi di Richard Stevens.
Se lavori in FreeBSD: "The design and implementation of the 4.4 BSD
Operating System", MCKusick, Bostic, Karels e Quarterman.
-- 
SiMoStro

 memoria condivisa su di esso.
Beh ma non č proprio quello che tu hai fatto nel tuo esempio?anche xchč č
ciņ che io dovrei implementare

 ciò che io dovrei implementare
Certo, l'oggetto è stato costruito in memoria condivisa ma
il puntatore all'oggetto non è in memoria condivisa, ogni
processo possiede il proprio puntatore.
Quello che volevo dire io - ma è una sottigliezza - è che
potresti allocare "anche" lo spazio per il puntatore in
memoria condivisa.
Questa differenza potrebbe essere non da poco se il numero di oggetti
da condividere cresce: in tal caso potresti prevedere una sorta
di "struttura" atta a contenere i punti di partenza degli oggetti
condivisi (i puntatori) e poi costruire tramite essi gli oggetti
veri e propri.
Insomma, una volta capito il modo, il resto è dato dalla personale
fantasia dell'implementatore...
;-)
-- 
SiMoStro

 
Nemmeno in questo caso. 
Siamo OT, ma verrei aggiungere una cosa. (Parlo di piattaforme *unix like.)
Un segmento di memoria condivisa viene mappato nello spazio di
indirizzamento del processo. L'indirizzo a cui verrà visto tale segmento
non sarà necessariamente lo stesso in tutti i processi che si agganciano
(shmget/shmat) alla stessa memoria condivisa, quindi qualsiasi riferimento
in forma di puntatore avrà validità soltanto nel contesto del processo che
ha ricavato e memorizzato quell'indirizzo. E' facile fare un esempio in cui
lo stesso segmento viene mappato ad indirizzi diversi. Di solito, in
memoria condivisa i riferimenti vengono gestiti come offset.
Max

 memoria condivisa i riferimenti vengono gestiti come offset.
Indubbiamente quello che dici ha pregressi teorici non ignorabili
:-D
voglio dire, il problema della condivisione dei segmenti e degli
indirizzi virtuali "probabilmente" (o magari solo possibilmente)
diversi è uno dei primi aspetti di fronte al quale si viene di solito
posti nel corso di sistemi operativi.
Nessuno vieta al kernel di mappare stessi segmenti (di memoria fisica)
ad indirizzi virtuali diversi in processi diversi...
...ed è anche vero che, dato il topic, qui siamo fortemente OT,
tuttavia non credo che la discussione sia così di "bassa lega" da
non doversi effettuare assolutamente :-)
Insomma, dicevo, il fatto che il segmento condiviso cominci ad indirizzi
virtuali diversi in effetti "può" accadere, ma non è detto che "debba"
accadere.
Oppure - suona meglio - può accadere ma si può evitare che accada.
In molti sistemi Unix (ammetto di non usare Linux da un po', negli
ultimi anni mi sono molto affezionato a FreeBSD) quando si mappa
un segmento come shared spesso si ottiene che tale segmento cominci
allo stesso indirizzo virtuale in chi lo eredita - se c'è un "figlio"
che lo eredita, ma se si tratta di far aprire ad un processo già
esistente lo stesso segmento ad un certo indirizzo e quel processo in
quell'indirizzo ha già allocato roba, allora è impossibile condividere,
oltre che a semplici oggetti, anche spazio di indirizzamento: in tal caso
è stravero il discorso che hai fatto tu, non si può che accedere ad
oggetti offsett-ed dallo starting point (differente da processo a processo)
shared.
Vorrei tuttavia farti un esempio "non forzato": infilando un puntatore
nella classe di dell'esempio che ho postato sopra ed effettuando la new
nella memoria condivisa si ottiene quanto segue:
// ***************************
Father init...
...but he wanna make a test first, so he calls shared object member
function:
pointer to string is: 0x281a7008
stringa "embedded": Ciao ciccio!
stringa "new" generated: Ciao, sono una stringa shared.
Father sees shared starting at: 0x281a7000
Well, now father forks his son and starts wait...
Son calls shared object method:
pointer to string is: 0x281a7008
stringa "embedded": Ciao ciccio!
stringa "new" generated: Ciao, sono una stringa shared.
Son sees shared starting at: 0x281a7000
// ***************************
Il fatto che la shared memory cominci al solito indirizzo, in questo
particolare esempio, dipende *chiaramente* dall'ereditarietà padre/figlio
e, probabilmente, dal fatto che i due processi sono molto semplici: non
si può, però, solo "sperare" che le cose funzionino
La mmap() ha un primo parametro che è uno "hint" al kernel di dove
piazzare il segmento condiviso: certo è che se si spera di aprire
un segmento condiviso a ridosso, che so, di BSS e heap si va poco
lontano, ma - in dipendenza dei comportamenti del sistema operativo - se
si riesce ad individuare le classiche aree (virtuali) "spesso free",
non è impossibile realizzare full shared memory.
A tal proposito esistono anche delle librerie che permettono di
aprire (o meglio, tentano di aprire) shared memory object mantenendo
la consistenza dei puntatori; addirittura esistono patch per i kernel
che permettono di aggiungere tale capability "di default" per le
regioni mappate shared (attivabile, naturalmente, con dei flags).
Insomma, l'idea di base credo sia semplice: la consistenza dei puntatori
"all'interno della regione shared" può essere garantita facendo cominciare
la regione agli stessi indirizzi virtuali nei diversi processi.
Ovvio che un puntatore che indirizza al di fuori di tale regione
genererà un SEG_FAULT.
Ora, Andrea su queste cose ci deve fare la tesi: se gli si dice tutto
poi non se la guadagna! :-)
-- 
SiMoStro

in risposta ad un msg di prima, è vero la tesi me la devo guadagnare :) e
difatti non riesco ancora ad uscire fuori da questo problema.
Innanzitutto non capisco xchè in mmap c'è uno sizeof(simple_print)*2; il *2
mi sfugge.
In secondo luogo se io in un processo mappo in shared memory un oggetto come
hai fatto tu (SiMoStro) ma facendo anche di  uso di shm_open,e in un altro
processo, che non sia figlio del primo, rimappo la regione di memoria e la
assegno ad un puntatore presente in questo processo (che chiamo queue), e
poi cerco di compiere un'azione del tipo "queue->enqueue(e)" mi continua a
dare segmentation fault.
Mi piace capire le cose da solo e studiarmele, ma in questo caso in
letteratura sto trovando solo esempi di utilizzo di mmap specifici per il c
e quindi senza uso di oggetti. E difatti non ho avuto problemi a mappare in
memoria classi con soli attributi ed usarle in ambo i processi

 dare segmentation fault.
Perché in questo caso ha ragione Max M.: se la regione condivisa ha
start point diverso nei due processi i puntatori non possono funzionare,
la regione condivisa deve essere mappata ad indirizzi virtuali
identici in entrambi i processi.
Un tentativo che puoi fare, dopo aver creato la memoria condivisa
nel primo processo, è aprire lo stesso oggetto shared nel secondo
processo e mapparlo in memoria passando come primo argomento alla
mmap l'indirizzo restituito dalla chiamata alla mmap nel primo processo;
se la mmap restituisce un indirizzo diverso allora vuol dire che quella
porzione di memoria non riesce a "posizionarla" dove l'hai richiesto tu
ed i puntatori (lì dentro) non puoi usarli.
Un altro aspetto da tenere in considerazione è l'uso che della
memoria dinamica fanno le classi che istanzi, soprattutto se
non puoi modificarle: devi valutare se un oggetto, in fase
di creazione, non effettui delle new che allochino al di fuori
della memoria shared: in tal caso la dislocazione sulla new eseguita
dal processo non viene applicata e ricadi nel problema precedente...
Francamente non ho mai provato a fare cose del genere, la memoria
condivisa l'ho sempre dovuta usare per soli dati: ti consiglio di fare
un po' di prove e se ottieni il comportamento voluto cercare di rendere
automatica la cosa con un opportuna classe.
Facce sapè.
-- 
SiMoStro