Accès à l’OS

Dans les chapitres sur la programmation système, nous avons étudié des primitives ayant vocation à être portables d’un système d’exploitation à l’autre.

En échange, le prix à payer est que nous ne pouvions pas tirer parti des spécificités de chaque système d’exploitation. Par exemple, nous ne pouvions pas interroger les inoeuds et permissions de fichiers sous Linux, ni avoir accès aux identifiants bruts de fichiers ouverts, connexions réseau, … afin de pouvoir appeler des fonctions spécifiques à chaque OS sur nos objets Rust.

Pour avoir accès à ces fonctionnalités et concepts non portables, il suffit d’importer dans le scope les traits du module std::os. Ceux-ci ajoutent aux types standard portable des fonctionnalités non-portables spécifiques à un système d’exploitation donné.

Par exemple, dans un exemple du chapitre précédent sur #[cfg()], nous avons utilisé un de ces traits pour obtenir le numéro de descripteur d’un fichier ouvert :

#![allow(unused)]
fn main() {
// Ouverture d'un fichier en écriture (cf chapitre système)
use std::fs::File;
let fichier = File::create("/tmp/test.txt")
                   .expect("Echec de création du fichier");

// Instructions spécifiques aux systèmes Unix (Linux, macOS...)
#[cfg(unix)]
{
    use std::os::unix::io::AsRawFd;
    let fd = fichier.as_raw_fd();
    println!("On utilise le descripteur de fichier numéro {fd}");
}
}

Notez l’utilisation de #[cfg(unix)] : le trait AsRawFd n’est disponible que quand on compile pour un système Unix. Tenter d’utiliser quoi que ce soit du module std::os::unix sous un autre OS tel que Windows causera une erreur de compilation : si on n’est pas sur le bons système, le code associé de la bibliothèque standard n’est tout simplement pas compilé.

Les systèmes d’exploitation qui exposent un grand nombre de ces “traits d’extension” fournissent également un module prelude qui permet d’importer facilement l’ensemble des traits d’un coup. Par exemple, le code ci-dessus pourrait aussi s’écrire de la façon suivante :

#![allow(unused)]
fn main() {
// Ouverture d'un fichier en écriture (cf chapitre système)
use std::fs::File;
let fichier = File::create("/tmp/test.txt")
                   .expect("Echec de création du fichier");

// Instructions spécifiques aux systèmes Unix (Linux, macOS...)
#[cfg(unix)]
{
    use std::os::unix::prelude::*;  // Inclut notamment AsRawFd
    let fd = fichier.as_raw_fd();
    println!("On utilise le descripteur de fichier numéro {fd}");
}
}

Les traits de std::os ne visent cependant qu’à compléter les types de la bibliothèque standard par des fonctionnalités non portables. Ils n’ajoutent pas de nouvelles notions complètement spécifiques à chaque système d’exploitation. Donc si on veut utiliser des fonctionnalités complètement spécifiques à un système d’exploitation, telles que io_uring sous Linux ou Direct3D sous Windows, il faudra utiliser des bibliothèques spécifiques à chaque système d’exploitation pour y avoir accès.

Deux bons points d’entrée sont libc pour les systèmes Unix et windows pour Windows.