ti spiego: fai finta che questo sia un piatto condiviso... |
Vediamo ora cosa contiene la libreria (libmmap.c)... vai col codice!
#include <stdio.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include "libmmap.h" // memMapOpen() - apre un memory mapped file Message *memMapOpen( const char *memname) { // apre un memory mapped file (il file "memname" è creato in /dev/shm) int fd; if ((fd = shm_open(memname, O_CREAT | O_RDWR, 0666)) < 0) { fprintf(stderr, "shm_open error: %s\n", strerror(errno)); return NULL; } // tronca un memory mapped file if (ftruncate(fd, sizeof(Message)) < 0) { fprintf(stderr, "ftruncate error: %s\n", strerror(errno)); return NULL; } // mappa un memory mapped file Message *ptr; if ((ptr = mmap(NULL, sizeof(Message), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) < 0) { fprintf(stderr, "mmap error: %s\n", strerror(errno)); return NULL; } return ptr; } // memMapClose() - chiude un memory mapped file int memMapClose( Message *ptr, const char *memname) { // un-mappa un memory mapped file if (munmap(ptr, sizeof(Message)) < 0) { fprintf(stderr, "munmap error: %s\n", strerror(errno)); return -1; } // cancella un memory mapped file if (shm_unlink(memname) < 0) { fprintf(stderr, "shm_unlink error: %s\n", strerror(errno)); return -1; } } // memMapFlush() - flush di un memory mapped file int memMapFlush( Message *ptr) { // sync su disco di un memory mapped file return msync(ptr, sizeof(Message), MS_SYNC); }Come potete vedere, poca roba ma densa. Come sempre il codice è auto-esplicativo, ampiamente commentato e con commenti che parlano da soli.
Le funzioni di open, close e flush usano le opportune system call Linux/POSIX per trattare il nostro Memory Mapped File. La funzione di flush è solo un wrapper per la chiamata msync() e, normalmente, non è necessario usarla: con questa libreria noi vogliamo trattare file mappati in memoria per condividere dati tra processi, per cui, non solo ci interessa poco che il file abbia una immagine reale sul disco, ma, per una semplice questione di prestazioni dovremmo evitare di scaricare realmente sul disco tutti i cambi effettuati in memoria, se no tanto varrebbe far comunicare i processi con dei file veri. Quindi a cosa serve il flush? Serve solo ad avere una eventuale versione reale e aggiornata del file condiviso, nel caso volessimo trattarlo anche con le classiche funzioni open(), close(), read(), ecc. Per questo nel file msgwriter.c descritto nel post precedente la chiamata memMapFlush() è commentata e descritta come opzionale.
Ed il classico parametro size che fine ha fatto? Beh, questa è una libreria specializzata per un tipo specifico che abbiamo definito (il tipo Message), quindi internamente alla libreria possiamo usare sizeof(Message) come e quando vogliamo. Il size, invece, serve per funzioni di librerie più generiche (che trattano tipi void*), dove il campo dati può contenere svariate cose e la funzione può disporre del size dei dati solo attraverso un apposito parametro.
Compito delle vacanze di Pasqua: fare una versione generica della libreria usando void *ptr al posto di Message *ptr in tutte le funzioni (ricordatevi di aggiungere anche il size!). Vi accorgerete che sono quattro modifiche in croce, una cosa semplicissima. Fidatevi del vostro C-Blogger favorito!
Ciao e al prossimo post!
Nessun commento:
Posta un commento