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 :
-
eventfd: compteur kernel exposé comme FD, lisible quand non-nul. Notifications inter-threads ou inter-processus légères. -
pipe: pipe Unix classique, communication unidirectionnelle. -
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(famillemem) est utilisée pour le data plane. - Cette famille
ipccouvre les cas plus simples : notifications, pipes, optimisations zero-copy.
Caractéristiques transverses.
- CLOEXEC universel.
- Variantes modernes.
eventfd2etpipe2préférés. - Pas d’IPC System V.
shmget,semget,msggetnon wrappés. Listés dansUNSUPPORTED.md. - Pas de POSIX message queues.
mq_openetc. 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.
- Servir un fichier sur un socket : transfert depuis FD fichier vers FD socket sans copie.
- Pipeline entre processus.
- 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égorie | Fonctions principales |
|---|---|
| eventfd | eventfd2, EventFd::read, EventFd::write |
| pipe | pipe2 |
| zero-copy | splice, 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,EventFdFlagsPipeFlagsSpliceFlags
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).