Utilisation d'un segment de mémoire partagé

Introduction

Dans Linux, chaque processus se voit attribué un espace mémoire virtuel de 4Go.

Bien sur, c'est virtuel, seuls les segments mémoires réellement utilisés sont reliés (mappés) par le système à la mémoire réelle.

Le système gère totalement la mémoire réelle, nous n'y avons pas accès. Nous n'avons accès qu'à la mémoire virtuelle de chaque processus.

Puisque chaque processus est confiné dans un segment mémoire, le système met à disposition une ressource permettant de mettre en commun une zone mémoire : le segment de mémoire partagé.

Qt met à disposition la classe QSharedMemory.

AttentionRessource commune

En programmation, utiliser une ressource commune à plusieurs processus est toujours délicat car il faut contrôler l'accès à la ressource de manière protéger les données.

Comme vous le savez maintenant, un processus peut à tout moment être interrompu par l'ordonnanceur du système pour que le processeur exécute le code d'un autre processus.

Imaginez qu'une procédure d'écriture soit en cours sur la mémoire partagée, mais non terminée. Imaginez aussi que le processus suivant écrive dans ce segment de mémoire partagé. Les données pourraient être corrompues, invalides ou incohérentes.

C'est la raison pour laquelle il faut absolument protéger le segment de mémoire partagé dès lors que plusieurs processus lecteur/écrivain y accèdent.

La classe QSharedMemory

L'objet s'utilise de la manière suivante :

  • Instanciation de l'objet QSharedMemory.

  • Création (la première fois) du segment de mémoire partagé (create()).

  • Attachement du segment au processus en cours (attach()).

  • Utilisation protégée de la mémoire (pointeur).

  • Détachement du segment du processus en cours (detach()).

  • Destruction de l'objet.

En plus de gérer un segment de mémoire partagé, la classe encapsule un objet sémaphore de protection accessible par les méthodes lock() et unlock().

RappelLe sémaphore

Un sémaphore est une ressource mise à disposition par le système Linux pour permettre entre autre l'unicité d'accès à une ressource.

Chaque processus utilisant cette ressource doit utiliser le même sémaphore afin de s'assurer de l'unicité d'accès à la ressource.

En interne, un sémaphore est constitué d'un compteur et d'une file d'attente de processus.

Lorsqu'un processus "prends le jeton" (lock()) du sémaphore, le compteur décroît.

Lorsque le compteur atteint 0, les processus suivant qui "prendront le jeton" se verront placés en file d'attente jusqu'à ce que le compteur ait une valeur positive.

Lorsqu'un processus "libère le jeton"(unlock()), le compteur est incrémenté, les processus en attente sont réveillés.

ExempleCréation d'un segment de mémoire partagé

Il faut commencer par créer une structure de données contenant les informations qu'on souhaite partager entre les processus ou threads. Ci-dessous un exemple issu d'un projet.

Cette structure se déclare dans un fichier d'interface (.h).

1
// exemple
2
typedef struct {
3
    int noMes;                  // numéro de la mesure
4
    int adrCapteur;             // Adresse ou fichier d'accès au capteur en hexa sans particule
5
    int posL;                   // N° ligne affichage incrustation
6
    int posC;                   // N° colonne affichage incrustation
7
    char nomClasse[AFFMAX];     // nom de la classe de gestion du capteur
8
    char nomMes[AFFMAX];        // nom de la mesure
9
    char symbUnit[AFFMAX];      // Symbole de l'unité de la mesure
10
    char textUnit[AFFMAX];      // Texte de l'unité
11
    char valMes[AFFMAX];        // Valeur instantannée de la mesure
12
} T_Mes;

Il faut ensuite créer l'objet QSharedMemory et le segment de mémoire partagé :

1
#define KEY "./ditRasp"  
2
3
QSharedMemory *mShm;  // segment de mémoire partagé
4
T_Mes *mData;   // pointeur du segment de mémoire partagé
5
6
mShm = new QSharedMemory(KEY, this);
7
mShm->attach();   // tentative de s'attacher
8
if (!mShm->isAttached()) {   // si existe pas alors création
9
     res = mShm->create(9*sizeof(T_Mes));   // on réserve pour 9 capteurs max
10
     if (res == false)
11
          qDebug(mShm->errorString().toStdString().c_str());
12
} // if isattached
13
14
mShm->attach();   // tentative de s'attacher
15
mData = (T_Mes *)mShm->data(); // obtient le pointeur sur la mémoire
16
// utilisation de la mémoire partagée
17
mShm->detach();
18
  • La constante symbolique KEY est une chaîne de caractères représentant le chemin et nom d'un fichier existant. Le système se sert de ce fichier existant pour calculer une clef unique désignant le segment de mémoire partagé parmi d'autres.

  • La mémoire partagée n'est pas forcément créée à l'instanciation de l'objet. Si l'attachement au processus n'est pas possible (cas du segment non créé) il faut donc créer le segment par la méthode create().

  • Dans cet exemple, le segment est créé d'une dimension pouvant contenir 9 structures de données de type T_Mes.

  • La méthode data() permet de récupérer l'adresse de base (dans l'espace virtuel du processus) du segment de mémoire partagé. Avec ce pointeur, il est possible de lire/écrire dans la mémoire.

  • Ne pas oublier de détacher le segment une fois son utilisation terminée. La destruction du segment est automatique dès que le dernier processus détache le segment.