Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Spec couche 0 — Famille ipc

Spécification technique — Version 1.0

Vue d’ensemble de la famille

Le module air-sys-syscall::ipc expose les primitives kernel d’IPC modernes : eventfd, pipe, et les opérations zero-copy entre FDs (splice, tee, vmsplice). C’est une petite famille qui complète l’arsenal de communication inter-threads et inter-processus.

Périmètre de la famille.

Trois mécanismes distincts :

  1. eventfd : compteur kernel exposé comme FD, lisible quand non-nul. Notifications inter-threads ou inter-processus légères.

  2. pipe : pipe Unix classique, communication unidirectionnelle.

  3. Opérations zero-copy : splice, tee, vmsplice. Déplacement de données entre FDs sans passer par userspace.

Position par rapport aux autres familles.

  • Les sockets Unix (famille net) sont l’IPC principal d’Air, notamment pour AirCom.
  • La mémoire partagée via memfd (famille mem) est utilisée pour le data plane.
  • Cette famille ipc couvre les cas plus simples : notifications, pipes, optimisations zero-copy.

Caractéristiques transverses.

  1. CLOEXEC universel.
  2. Variantes modernes. eventfd2 et pipe2 préférés.
  3. Pas d’IPC System V. shmget, semget, msgget non wrappés. Listés dans UNSUPPORTED.md.
  4. Pas de POSIX message queues. mq_open etc. non wrappés.

Sous-section 1 : eventfd

eventfd2

#![allow(unused)]
fn main() {
pub fn eventfd2(
    initial: u64,
    flags: EventFdFlags,
) -> Result<EventFd, Errno>;

pub struct EventFd { /* possède OwnedFd interne */ }

impl EventFd {
    pub fn as_fd(&self) -> BorrowedFd<'_>;
    pub fn into_fd(self) -> OwnedFd;
    
    pub fn read(&self) -> Result<u64, Errno>;
    pub fn write(&self, value: u64) -> Result<(), Errno>;
}

bitflags! {
    pub struct EventFdFlags: i32 {
        const CLOEXEC = 0x80000;
        const NONBLOCK = 0x800;
        const SEMAPHORE = 1;
    }
}
}

Syscall sous-jacent. eventfd2 (x86_64 n°290, ARM64 n°19). Disponible depuis Linux 2.6.27.

Comportement.

Crée un FD qui encapsule un compteur 64 bits kernel. Deux modes selon le flag SEMAPHORE :

  • Mode normal : read() retourne la valeur courante et met à zéro. Bloque si le compteur est zéro.
  • Mode sémaphore : read() retourne 1 et décrémente. Bloque si zéro.

Cas d’usage typique : notification inter-threads.

#![allow(unused)]
fn main() {
let efd = eventfd2(0, EventFdFlags::empty())?;

// Thread A : attendre une notification
loop {
    let count = efd.read()?;
    process_events(count);
}

// Thread B : envoyer une notification
efd.write(1)?;
}

Intégration avec io_uring.

#![allow(unused)]
fn main() {
let efd = eventfd2(0, EventFdFlags::empty())?;
let mut buf = [0u8; 8];
let token = ring.submit_read(efd.as_fd(), buf.to_vec(), 0)?;
ring.submit()?;

// Depuis un autre thread :
efd.write(1)?;

// Le ring reçoit la complétion
let completion = ring.wait_completion()?;
}

C’est le pattern canonique pour faire interagir un thread reactor io_uring avec d’autres threads.

Performance. Création ~5-10 µs. Read/write ~500 ns à 1 µs.

Sous-section 2 : pipe

pipe2

#![allow(unused)]
fn main() {
pub fn pipe2(flags: PipeFlags) -> Result<(OwnedFd, OwnedFd), Errno>;

bitflags! {
    pub struct PipeFlags: i32 {
        const CLOEXEC = 0x80000;
        const DIRECT = 0x4000;
        const NONBLOCK = 0x800;
    }
}
}

Comportement.

Crée un pipe unidirectionnel. Retourne (read_end, write_end).

Les pipes ont une taille de buffer kernel (typiquement 64 KB, ajustable via fcntl).

PipeFlags::DIRECT : mode “packet” où chaque write produit un paquet distinct côté lecture.

Cas d’usage typique.

  • Communication parent-enfant après fork.
  • Notification simple où eventfd serait sur-dimensionné.
  • Implémentation de pipelines.

Sous-section 3 : Opérations zero-copy

splice

#![allow(unused)]
fn main() {
pub fn splice(
    fd_in: BorrowedFd<'_>,
    off_in: Option<&mut u64>,
    fd_out: BorrowedFd<'_>,
    off_out: Option<&mut u64>,
    length: usize,
    flags: SpliceFlags,
) -> Result<usize, Errno>;

bitflags! {
    pub struct SpliceFlags: u32 {
        const MOVE = 1;
        const NONBLOCK = 2;
        const MORE = 4;
        const GIFT = 8;
    }
}
}

Préconditions.

Au moins un des deux FDs doit être un pipe.

Comportement.

Transfère jusqu’à len octets de fd_in vers fd_out sans passer par l’espace utilisateur.

Cas d’usage typiques.

  1. Servir un fichier sur un socket : transfert depuis FD fichier vers FD socket sans copie.
  2. Pipeline entre processus.
  3. Logging haute performance.

Performance. Gain principal : élimination des copies kernel → user → kernel. Débit 2-3 fois supérieur à read/write pour les gros transferts.

tee

#![allow(unused)]
fn main() {
pub fn tee(
    fd_in: BorrowedFd<'_>,
    fd_out: BorrowedFd<'_>,
    length: usize,
    flags: SpliceFlags,
) -> Result<usize, Errno>;
}

Les deux FDs doivent être des pipes. Duplique le contenu d’un pipe dans un autre sans consommer du pipe source.

vmsplice

#![allow(unused)]
fn main() {
pub fn vmsplice(
    fd: BorrowedFd<'_>,
    iov: &[IoSlice<'_>],
    flags: SpliceFlags,
) -> Result<usize, Errno>;
}

fd doit être un pipe. Transfère des données depuis des buffers userspace vers un pipe.

Le gifting (mode optimisé) est dangereux : les pages données ne doivent plus être modifiées. Air documente fortement les contraintes.

Récapitulatif de la famille ipc

Fonctions exposées :

CatégorieFonctions principales
eventfdeventfd2, EventFd::read, EventFd::write
pipepipe2
zero-copysplice, tee, vmsplice

Total : ~6 fonctions publiques principales.

Syscalls non-wrappés (listés dans UNSUPPORTED.md) :

  • eventfd (sans le 2) : remplacé par eventfd2.
  • pipe (sans le 2) : remplacé par pipe2.
  • shmget, shmat, shmdt, shmctl : System V shared memory, deprecated.
  • semget, semop, semctl : System V semaphores, deprecated.
  • msgget, msgsnd, msgrcv, msgctl : System V message queues, deprecated.
  • mq_open, mq_send, mq_receive, mq_close, mq_unlink, mq_notify, mq_setattr : POSIX message queues, marginal.

Types ajoutés à air-sys-types

  • EventFd, EventFdFlags
  • PipeFlags
  • SpliceFlags

Soit ~4 types supplémentaires.

Décisions de fond émergées dans la famille ipc

1. Pas de System V IPC.

Décision claire : on n’expose pas les mécanismes legacy qui ont des problèmes de sémantique et de sécurité connus.

2. Pas de POSIX message queues.

Usage marginal, supplanté par sockets et memfd.

3. EventFd::read retourne u64, pas Vec<u8>.

Air expose directement le compteur comme u64. Conversion interne au wrapper.

4. vmsplice gifting documenté mais peu testé.

Mode puissant mais dangereux. Cas avancé en phase 0.


Licence du document : MPL 2.0 Statut : Spécification technique du module air-sys-syscall::ipc (couche 0).