Pour vous, une application est une seule chose : une icône sur laquelle on clique, une fenêtre qu’on utilise, quelque chose qu’on installe et qu’on supprime. Pour l’ordinateur, ce sont trois choses distinctes, qui ne vont ensemble que de loin :
- des fichiers sur le disque : le programme, les bibliothèques qu’il utilise, ses images, ses traductions et ses icônes ;
- des processus en mémoire : une ou plusieurs instances de programmes en cours d’exécution, chacune avec son propre espace d’adressage ;
- des fichiers de données ailleurs : vos réglages, vos documents, des caches, l’état sauvegardé.
Supprimer l’icône de l’application efface une partie des premiers ; la quitter met fin aux seconds ; les troisièmes restent en général derrière. Ce chapitre examine chacun à son tour, sur le Mac sur lequel il a été écrit.
Une application sur le disque : le paquet
Sous macOS, une application est un paquet (bundle) : un dossier dont le nom finit par .app, que le Finder affiche comme une seule icône. La Calculette d’Apple, ouverte avec ls :
/System/Applications/Calculator.app/Contents
├── Info.plist ← the app's identity card
├── MacOS/Calculator ← the executable
├── PlugIns/CalculatorWidget.appex ← a widget: a second, separate program
├── Resources/ ← icons, compiled images, translations
│ ├── AppIcon.icns
│ ├── Assets.car
│ ├── Localizable.loctable
│ └── ar.lproj, ca.lproj, … fr.lproj, … (46 entries in all)
├── _CodeSignature/CodeResources ← hashes of every file, for the signature
└── version.plist
Le paquet entier fait 3,4 Mo, et c’est presque entièrement l’exécutable. Info.plist est un petit fichier structuré qui dit au système comment traiter l’application :
$ plutil -p Calculator.app/Contents/Info.plist
"CFBundleExecutable" => "Calculator"
"CFBundleIdentifier" => "com.apple.calculator"
"CFBundleShortVersionString" => "12.0"
"LSMinimumSystemVersion" => "26.6"
…
L’identifiant de paquet, com.apple.calculator, est le vrai nom de l’application pour le système. C’est à lui que sont rattachés les réglages, les autorisations et les conteneurs du bac à sable : renommez Calculator.app, ce sera toujours la même application.
L’exécutable est un fichier Mach-O, le format décrit dans le chapitre sur les fichiers exécutables, et c’est un binaire universel : deux programmes complets dans un seul fichier, pour Intel (x86_64) et pour les puces Apple (arm64e). codesign montre que le paquet est signé et que la signature couvre les deux :
$ codesign -dv /System/Applications/Calculator.app
Identifier=com.apple.calculator
Format=app bundle with Mach-O universal (x86_64 arm64e)
Pour le système d’exploitation, un fichier n’est qu’une suite d’octets, toute structure supplémentaire étant laissée aux applications, contrairement aux fichiers structurés en enregistrements des mainframes traditionnels. La suite d’octets l’a emporté partout hors des mainframes, et un paquet montre comment on ajoute de la structure par-dessus : c’est un dossier ordinaire rempli de fichiers ordinaires, et « ce dossier est une application » est une convention sur laquelle s’accordent le Finder et le reste du système.
Le code qu’une application n’embarque pas
L’essentiel de ce qu’exécute la Calculette n’est pas dans son paquet. otool -L liste 44 bibliothèques et frameworks dont elle dépend : AppKit pour les fenêtres et les menus, SwiftUI pour son interface, Foundation, CoreGraphics, l’environnement d’exécution de Swift et d’autres. Aucun n’est dans le paquet ; ils font partie du système d’exploitation, pré-liés ensemble dans le cache partagé de dyld, comme l’a montré le chapitre sur le lancement d’une application.
D’autres systèmes organisent les mêmes pièces autrement :
| Exécutable | Bibliothèques | Ressources | |
|---|---|---|---|
| macOS | dans le paquet .app | frameworks du système, ou embarqués dans le Frameworks/ du paquet | dans le Resources/ du paquet |
| Windows | C:\Program Files\<App>\<app>.exe | DLL du système, plus les DLL propres à l’application à côté du .exe | à côté du .exe, ou intégrées dedans comme ressources PE |
| Linux (paquets) | /usr/bin | /usr/lib, chaque bibliothèque dans son propre paquet | /usr/share/<app>, /usr/share/doc, /usr/share/man |
Les distributions Linux répartissent les fichiers d’une application par nature au lieu de les regrouper par application. Le paquet curl de Debian, listé avec dpkg -L curl, met son programme dans /usr/bin, sa page de manuel dans /usr/share/man/man1, sa documentation dans /usr/share/doc/curl et ses complétions de shell dans /usr/share/zsh ; le vrai code réseau est dans un paquet de bibliothèque séparé, libcurl, partagé avec tous les autres programmes qui s’en servent. Le gestionnaire de paquets sait quel fichier appartient à quel paquet. Des formats comme Flatpak et Snap, et les images de conteneurs, ramènent l’idée du paquet autonome sous Linux : un dossier par application.
Une application qui tourne : les processus
Un processus est un programme en cours d’exécution, avec son propre espace d’adressage, ses propres registres sauvegardés et ses propres fichiers ouverts, comme l’explique le chapitre sur les processus. La distinction compte parce que la correspondance entre applications et processus n’est pas un à un :
- Le même programme peut tourner dans plusieurs processus à la fois : deux fenêtres de terminal qui exécutent
zshsont deux processus qui exécutent le même fichier. - Une application, c’est souvent plusieurs processus.
pssur ce Mac montrait une instance de Chrome sous la forme de huit processus : le processus principal du navigateur, un processus GPU, un service réseau, un service de stockage et quatre processus de rendu, un par groupe de pages.
PID PPID role
7470 1 browser (main)
7488 7470 --type=gpu-process
7491 7470 --type=utility --utility-sub-type=network.mojom.NetworkService
7494 7470 --type=utility --utility-sub-type=storage.mojom.StorageService
84402 7470 --type=renderer
84403 7470 --type=renderer
84404 7470 --type=renderer
84405 7470 --type=renderer
Pourquoi découper une application en processus ? Parce que le processus est l’unité d’isolation du système d’exploitation. Le plantage d’un processus de rendu tue un onglet, pas le navigateur. Un processus de rendu qui traite des pages web non fiables peut être placé dans un bac à sable (sandbox) : privé d’accès aux fichiers, au réseau et à la plupart des appels système, si bien qu’un bogue exploité par une page malveillante ne rapporte presque rien. Le pilote GPU, qui plante plus souvent qu’on ne le voudrait, est tenu hors du processus principal. Le prix, c’est la mémoire (chaque processus a ses propres copies de certaines données) et la communication, qui doit passer par le noyau.
Les applications macOS font la même chose à plus petite échelle. Le widget de la Calculette est un programme distinct dans son propre processus, et beaucoup d’applications délèguent du travail à de petits services XPC rangés dans leur paquet. Sur l’ensemble du système, ps -A comptait 1 246 processus pendant l’écriture de ce chapitre : environ 950 appartenaient à l’utilisateur connecté, 188 à root, et environ 110 à des comptes système comme _windowserver, le propriétaire du processus du serveur de fenêtres. La plupart des utilisateurs n’imaginent pas qu’ils font tourner un millier de programmes.
Les fichiers vus par un processus
La plupart du temps, un processus en cours d’exécution ne voit pas les noms de fichiers. Il demande au noyau d’ouvrir un fichier par son nom ; le noyau vérifie les droits, trouve le fichier et renvoie un petit entier, un descripteur de fichier, que le processus utilise pour chaque read et write ultérieur. Trois sont déjà ouverts au démarrage de tout processus UNIX : 0 (l’entrée standard), 1 (la sortie standard) et 2 (la sortie d’erreur). La description classique des E/S sur fichiers (ouvrir, puis lire en indiquant le fichier, un tampon et un nombre d’octets, puis fermer) est exactement cette interface, inchangée.
Essayez : ouvrez quelques fichiers, fermez-en un, puis ouvrez-en un autre. Chaque open reçoit le plus petit numéro libre, si bien qu’un descripteur fermé est aussitôt réutilisé ; c’est pourquoi, dans un programme qui n’a encore rien ouvert, open renvoie 3.
À essayer : Cliquez sur un fichier à gauche pour l’ouvrir : il reçoit le plus petit numéro libre. Fermez-en un avec ×, puis ouvrez-en un autre et regardez quel numéro il reçoit.
- 0clavier
- 1écran
- 2écran (erreurs)
La table vit dans le noyau, pas dans le processus : le processus ne tient jamais que les numéros, ce qui permet au noyau de vérifier chaque read et chaque write. La façon dont le noyau transforme un nom en blocs sur un disque fait l’objet du chapitre sur les fichiers et les E/S.
Où vivent les réglages et les données
Le paquet d’une application est en lecture seule. Sous macOS, les applications d’Apple sont sur un volume système scellé qu’on ne peut pas modifier du tout. Tout ce que l’application écrit va donc ailleurs, à des emplacements fixés par convention :
| Réglages | Données de l’application | Caches | |
|---|---|---|---|
| macOS | ~/Library/Preferences/<bundle id>.plist | ~/Library/Application Support/<App>/ | ~/Library/Caches/<bundle id>/ |
| macOS, applications en bac à sable | les mêmes chemins dans ~/Library/Containers/<bundle id>/Data/ | ||
| Windows | le registre (HKEY_CURRENT_USER\Software\…) et %APPDATA% | %APPDATA% et %LOCALAPPDATA% | %LOCALAPPDATA% |
| Linux | ~/.config/<app>/ | ~/.local/share/<app>/ | ~/.cache/<app>/ |
La Calculette est en bac à sable, donc le système lui a donné un conteneur privé : tout un faux dossier personnel, ~/Library/Containers/com.apple.calculator/Data/, avec des liens vers les vrais Bureau, Téléchargements, etc., qu’elle ne peut utiliser qu’avec autorisation. Son fichier de réglages est dedans :
$ plutil -p ~/Library/Containers/com.apple.calculator/Data/Library/Preferences/com.apple.calculator.plist
{
"LastResultValue" => …
"NSWindow Frame main" => …
"RestoreInputValue" => …
"TrigonometricModeKey" => true
}
Voilà pourquoi la Calculette rouvre là où vous l’avez laissée, avec le dernier résultat et dans le même mode : elle a écrit la position de sa fenêtre et son état dans une liste de propriétés en quittant, et les relit au lancement. Sur ce Mac, il y a 712 conteneurs de ce genre, un par application, assistant et extension en bac à sable.
Cette séparation explique un agacement familier. Glisser une application dans la Corbeille supprime le paquet (les fichiers de la première catégorie) mais laisse derrière ses réglages, ses caches et ses conteneurs ; réinstallez-la, et vos anciens réglages reviennent. Les gestionnaires de paquets Linux ont la même séparation : désinstaller un paquet supprime ses fichiers sous /usr, tandis que votre ~/.config reste intact.
À retenir
- Une application, ce sont des fichiers (le programme, ses ressources et les bibliothèques qu’il utilise), un ou plusieurs processus (le programme en cours d’exécution), et des fichiers de données (réglages, documents, caches) rangés ailleurs.
- Sous macOS, une application est un dossier paquet :
Info.plist, l’exécutable (ici un binaire universel), des ressources et une signature de code. L’identifiant de paquet est son vrai nom. - L’essentiel du code d’une application vient des bibliothèques partagées du système : la Calculette en nomme 44. Les paquets Linux répartissent les fichiers par nature ; les paquets macOS les regroupent par application.
- Une application peut être plusieurs processus (une instance de Chrome en comptait 8) parce que le processus est l’unité d’isolation et de bac à sable. Ce Mac faisait tourner 1 246 processus.
- Les processus utilisent les fichiers à travers des descripteurs de fichiers : 0, 1 et 2 sont ouverts dès le départ, et
openrenvoie le suivant qui est libre. - Les réglages vivent dans des emplacements propres à chaque utilisateur (
~/Librarysous macOS, le registre et%APPDATA%sous Windows,~/.configsous Linux), ce qui explique que supprimer une application laisse en général ses données derrière elle.