virli/tutorial/4/ipcns.md

11 KiB

Le namespace IPC

Introduction

L'espace de noms IPC, introduit dans Linux 2.6.19, isole les objets de communication inter-processus (IPC) System V et les files de messages POSIX.

Les objets isolés incluent :

  • Les files de messages System V (msgget, msgsnd, msgrcv)
  • Les sémaphores System V (semget, semop)
  • Les segments de mémoire partagée System V (shmget, shmat)
  • Les files de messages POSIX (mq_open, mq_send, mq_receive)

Une fois dans un namespace IPC différent, un processus ne peut communiquer qu'avec les processus du même namespace via ces mécanismes IPC.

Pourquoi isoler les IPC ?

Sans isolation IPC, des processus dans différents conteneurs pourraient :

  • Communiquer entre eux via des files de messages partagées
  • Accéder à la mémoire partagée d'autres conteneurs
  • Interférer avec les sémaphores d'autres applications

L'isolation IPC garantit que chaque conteneur dispose de son propre ensemble d'objets IPC, renforçant ainsi la sécurité et l'isolation.

Utilisation pratique

Observer les objets IPC du système\

La commande ipcs permet de lister tous les objets IPC du système :

```bash 42sh$ ipcs

------ Message Queues -------- key msqid owner perms used-bytes messages

------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000000 32768 alice 600 524288 2 dest

------ Semaphore Arrays -------- key semid owner perms nsems 0x12345678 0 bob 666 1

</div>

Cette commande nous montre tous les objets IPC visibles dans notre *namespace*
actuel.


#### Créer une file de messages System V\

Créons d'abord une file de messages dans notre *namespace* initial :

<div lang="en-US">
```c
// ipc_create.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>

int main(void)
{
    key_t key;
    int msgid;

    // Créer une clé unique
    key = ftok("/tmp", 'A');
    if (key == -1) {
        perror("ftok");
        exit(EXIT_FAILURE);
    }

    // Créer une file de messages
    msgid = msgget(key, 0666 | IPC_CREAT);
    if (msgid == -1) {
        perror("msgget");
        exit(EXIT_FAILURE);
    }

    printf("File de messages créée avec l'ID : %d\n", msgid);
    printf("Clé : 0x%08x\n", key);

    return 0;
}

::::: {.code}

Compilation et exécution :

```bash 42sh$ gcc -o ipc_create ipc_create.c 42sh$ touch /tmp/ipc_key_file 42sh$ ./ipc_create File de messages créée avec l'ID : 0 Clé : 0x41000001

42sh$ ipcs -q ------ Message Queues -------- key msqid owner perms used-bytes messages 0x41000001 0 alice 666 0 0

</div>

:::::


#### S'isoler dans un nouveau *namespace* IPC\

Maintenant, créons un nouveau *namespace* IPC :

<div lang="en-US">
```bash
42sh$ unshare --ipc /bin/bash
  inipcns$ ipcs -q
  ------ Message Queues --------
  key        msqid      owner      perms      used-bytes   messages

Notre nouveau namespace ne contient aucun objet IPC ! La file de messages créée précédemment n'est pas visible ici.

Si nous créons une nouvelle file de messages dans ce namespace :

```bash inipcns$ ./ipc_create File de messages créée avec l'ID : 0 Clé : 0x41000001

inipcns$ ipcs -q ------ Message Queues -------- key msqid owner perms used-bytes messages 0x41000001 0 alice 666 0 0

</div>

Cette nouvelle file a le même ID (0) et la même clé que celle créée dans le
*namespace* initial, mais il s'agit d'un objet complètement distinct.


#### Communication entre processus dans le même *namespace*\

Créons un exemple de communication par file de messages :

<div lang="en-US">
```c
// ipc_send.c
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>

struct message {
    long mtype;
    char mtext[100];
};

int main(void)
{
    key_t key;
    int msgid;
    struct message msg;

    key = ftok("/tmp", 'A');
    msgid = msgget(key, 0666 | IPC_CREAT);

    msg.mtype = 1;
    snprintf(msg.mtext, sizeof(msg.mtext), "Hello from PID %d", getpid());

    if (msgsnd(msgid, &msg, sizeof(msg.mtext), 0) == -1) {
        perror("msgsnd");
        exit(EXIT_FAILURE);
    }

    printf("Message envoyé : %s\n", msg.mtext);

    return 0;
}
```c // ipc_receive.c #include #include <sys/ipc.h> #include <sys/msg.h> #include

struct message { long mtype; char mtext[100]; };

int main(void) { key_t key; int msgid; struct message msg;

key = ftok("/tmp", 'A');
msgid = msgget(key, 0666 | IPC_CREAT);

if (msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0) == -1) {
    perror("msgrcv");
    exit(EXIT_FAILURE);
}

printf("Message reçu : %s\n", msg.mtext);

return 0;

}

</div>

::::: {.code}

Compilation :

<div lang="en-US">
```bash
42sh$ gcc -o ipc_send ipc_send.c
42sh$ gcc -o ipc_receive ipc_receive.c

Test dans le même namespace :

```bash # Terminal 1 42sh$ ./ipc_receive # Attend un message...

Terminal 2

42sh$ ./ipc_send Message envoyé : Hello from PID 12345

Terminal 1 reçoit :

Message reçu : Hello from PID 12345

</div>

:::::


#### Isolation entre *namespaces*\

Maintenant, testons avec deux *namespaces* IPC différents :

<div lang="en-US">
```bash
# Terminal 1 - namespace IPC A
42sh$ unshare --ipc /bin/bash
  ipcns-A$ ./ipc_receive
  # Attend un message...

# Terminal 2 - namespace IPC B (différent)
42sh$ unshare --ipc /bin/bash
  ipcns-B$ ./ipc_send
  Message envoyé : Hello from PID 23456

# Terminal 1 ne reçoit RIEN car les namespaces sont différents

Les deux processus utilisent la même clé IPC, mais comme ils sont dans des namespaces différents, ils ne peuvent pas communiquer.

Exemple avec la mémoire partagée

Voici un exemple utilisant la mémoire partagée System V :

```c // shm_demo.c #include #include #include <sys/ipc.h> #include <sys/shm.h> #include #include

int main(void) { key_t key; int shmid; char *data;

// Créer une clé
key = ftok("/tmp", 'S');

// Créer un segment de mémoire partagée de 1024 octets
shmid = shmget(key, 1024, 0644 | IPC_CREAT);
if (shmid == -1) {
    perror("shmget");
    exit(EXIT_FAILURE);
}

// Attacher le segment
data = shmat(shmid, NULL, 0);
if (data == (char *)(-1)) {
    perror("shmat");
    exit(EXIT_FAILURE);
}

printf("Segment de mémoire partagée attaché\n");
printf("Écriture dans la mémoire partagée...\n");

sprintf(data, "Hello from PID %d in namespace", getpid());

printf("Contenu : %s\n", data);
printf("Appuyez sur Entrée pour détacher...\n");
getchar();

// Détacher
shmdt(data);

printf("Voulez-vous supprimer le segment ? (o/n) : ");
char choice = getchar();
if (choice == 'o' || choice == 'O') {
    shmctl(shmid, IPC_RMID, NULL);
    printf("Segment supprimé\n");
}

return 0;

}

</div>

::::: {.code}

Compilation et test :

<div lang="en-US">
```bash
42sh$ gcc -o shm_demo shm_demo.c

# Dans le namespace initial
42sh$ ./shm_demo &
Segment de mémoire partagée attaché
Écriture dans la mémoire partagée...
Contenu : Hello from PID 12345 in namespace

42sh$ ipcs -m
------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x53000001 0          alice      644        1024       1

# Dans un nouveau namespace IPC
42sh$ unshare --ipc /bin/bash
  inipcns$ ipcs -m
  ------ Shared Memory Segments --------
  key        shmid      owner      perms      bytes      nattch     status

  # Aucun segment visible !

:::::

Files de messages POSIX

Le namespace IPC isole également les files de messages POSIX (qui sont différentes des files System V) :

```c // mqueue_demo.c #include #include #include #include #include

int main(void) { mqd_t mq; struct mq_attr attr; char buffer[1024];

// Configurer les attributs de la file
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = 1024;
attr.mq_curmsgs = 0;

// Créer ou ouvrir une file de messages POSIX
mq = mq_open("/myqueue", O_CREAT | O_RDWR, 0644, &attr);
if (mq == (mqd_t)-1) {
    perror("mq_open");
    exit(EXIT_FAILURE);
}

printf("File de messages POSIX créée : /myqueue\n");

// Envoyer un message
sprintf(buffer, "Hello from PID %d", getpid());
if (mq_send(mq, buffer, strlen(buffer) + 1, 0) == -1) {
    perror("mq_send");
    exit(EXIT_FAILURE);
}

printf("Message envoyé : %s\n", buffer);

// Recevoir le message
if (mq_receive(mq, buffer, 1024, NULL) == -1) {
    perror("mq_receive");
    exit(EXIT_FAILURE);
}

printf("Message reçu : %s\n", buffer);

mq_close(mq);
mq_unlink("/myqueue");

return 0;

}

</div>

::::: {.code}

Pour compiler (nécessite la bibliothèque `librt`) :

<div lang="en-US">
```bash
42sh$ gcc -o mqueue_demo mqueue_demo.c -lrt
42sh$ ./mqueue_demo
File de messages POSIX créée : /myqueue
Message envoyé : Hello from PID 12345
Message reçu : Hello from PID 12345

Les files de messages POSIX peuvent être listées dans /dev/mqueue/ :

```bash 42sh$ ls -l /dev/mqueue/ total 0 -rw-r--r-- 1 alice alice 80 Nov 15 10:30 myqueue ```

Dans un nouveau namespace IPC, ce répertoire sera vide.

:::::

Combinaison avec d'autres namespaces

En pratique, le namespace IPC est souvent combiné avec d'autres namespaces pour une isolation complète :

```bash 42sh# unshare --ipc --pid --mount --fork --mount-proc /bin/bash incontainer# # Conteneur isolé avec IPC, PID et mount namespaces ```

Nettoyage des objets IPC

Les objets IPC persistent généralement après la terminaison des processus qui les ont créés. Pour les nettoyer :

```bash # Supprimer une file de messages 42sh$ ipcrm -q

Supprimer un segment de mémoire partagée

42sh$ ipcrm -m

Supprimer un sémaphore

42sh$ ipcrm -s

Ou supprimer par clé

42sh$ ipcrm -Q 0x41000001

</div>

Lorsqu'un *namespace* IPC est détruit (tous les processus terminés), tous ses
objets IPC sont automatiquement nettoyés.


### Aller plus loin  {-}

Pour plus d'informations :

- `ipc_namespaces(7)` - page de manuel
- `ipcs(1)` - lister les objets IPC
- `ipcrm(1)` - supprimer les objets IPC
- `svipc(7)` - aperçu de System V IPC
- `mq_overview(7)` - aperçu des files de messages POSIX