NOM
cpuset - Confiner des processus à des sous-ensembles de processeurs et
de noeuds mémoire
Le système de fichiers cpuset [ NdT : « cpuset » signifie mot à mot
« ensemble de CPU », mais comme il ne s’agit pas uniquement d’ensembles
de CPU, le terme cpuset sera utilisé par la suite ] est une interface
sous forme d’un pseudo système de fichiers pour le mécanisme « cpuset »
du noyau, qui permet de contrôler le placement de processus sur des
processeurs ou en mémoire. Il est habituellement monté dans
/dev/cpuset.
Sur les systèmes dont le noyau a été compilé avec la prise en charge
des cpusets, tous les processus sont attachés à un cpuset, et les
cpusets sont toujours présents. Si un système prend en charge les
cpusets, alors il aura une entrée nodev cpuset dans le fichier
/proc/filesystems. En montant le système de fichiers cpuset (consultez
la section EXEMPLE ci-dessous), l’administrateur peut configurer les
cpusets d’un système pour contrôler le placement des processus sur les
CPU et dans la mémoire. Par défaut, si la configuration des cpusets
d’un système n’est pas modifiée ou si le système de fichiers cpuset
n’est même pas monté, le mécanisme des cpusets, même s’il est présent,
n’a pas d’effets sur le comportement du système.
Un cpuset définit une liste de CPU et de noeuds mémoire.
Les CPU d’un système comprennent toutes les unités de traitement
logiques sur lesquelles peuvent s’exécuter des processus, comprenant,
s’il y en a, les différents coeurs d’un processeur et les Hyper-Threads
d’un coeur de processeur. Les noeuds mémoire comprennent tous les bancs
distinct de mémoire ; les petits systèmes et les systèmes SMP ont
typiquement un seul noeud mémoire qui contient toute la mémoire du
système, alors que les systèmes NUMA (« non-uniform memory access » :
accès non uniforme à la mémoire) ont plusieurs noeuds mémoire.
Les cpusets sont représentés par des répertoires dans un pseudo système
de fichiers hiérarchique dont le répertoire de plus haut niveau
(/dev/cpuset) représente le système complet (tous les CPU et noeuds
mémoire en ligne). Tout cpuset fils (le descendant) d’un autre cpuset
père contient un sous-ensemble des CPU et des noeuds mémoire du père.
Les répertoires et les fichiers qui représentent les cpusets ont les
permissions habituelles des systèmes de fichiers.
Chaque processus du système appartient à un unique cpuset. Un processus
est obligé de s’exécuter sur les CPU du cpuset auquel il appartient et
est obligé d’allouer de la mémoire uniquement sur les noeuds mémoire de
ce cpuset. Quand un processus crée un fils avec fork(2), le processus
fils est placé dans le même cpuset que le processus père. S’il a les
privilèges suffisants, le processus fils peut se déplacer d’un cpuset à
un autre et les CPU ou noeuds mémoire d’un cpuset existant peuvent être
changés.
Au début du démarrage du système, un seul cpuset est défini qui
comprend tous les CPU et tous les noeuds mémoire du système et tous les
processus se trouvent dans ce cpuset. Pendant le démarrage ou par la
suite lors de l’exécution normale du système, d’autres cpusets peuvent
être créés, en tant que sous-répertoire de ce cpuset de plus haut
niveau, sous le contrôle de l’administrateur système. Des processus
peuvent être placés dans ces autres cpusets.
Les cpusets sont intégrés dans le noyau avec le mécanisme d’affinité
d’ordonnancement de sched_setaffinity(2) et les mécanismes de placement
en mémoire de mbind(2) et set_mempolicy(2). Aucun de ces mécanismes ne
permettent à un processus d’utiliser un CPU ou un noeud mémoire qui
n’est pas autorisé par le cpuset du processus. Si une modification du
cpuset entre en conflit avec ces autres mécanismes, le placement dans
le cpuset est forcé, même si cela signifie qu’il faut outrepasser ces
autres mécanismes. Ceci est fait silencieusement par le noyau en
restreignant les CPU et noeuds mémoire demandés par ces autres
mécanismes à ceux qui sont autorisés par le cpuset du processus
appelant. Ces autres appels peuvent alors renvoyer une erreur si, par
exemple, ils sont amenés à demander un ensemble vide de CPU ou de
noeuds mémoire après que la demande soit restreinte au cpuset du
processus appelant.
Typiquement, un cpuset est utilisé pour gérer le confinement dans des
CPU ou des noeuds mémoire pour un ensemble de processus qui coopèrent
entre eux, comme un ordonnanceur de tâches, et ces autres mécanismes
permettent de gérer le placement de chacun des processus ou des régions
mémoire pour chacune de ces tâches.
FICHIERS
Chaque répertoire de /dev/cpuset représente un cpuset et contient un
ensemble fixé de pseudo fichiers qui décrivent l’état de ce cpuset.
Les nouveaux cpusets sont créés avec l’appel système mkdir(2) ou la
commande mkdir(1). Les propriétés d’un cpuset, comme ses drapeaux, les
CPU et noeuds mémoire autorisés et les processus attachés sont
récupérés ou modifiés en lisant ou écrivant dans le fichier approprié
du répertoire du cpuset. Ces fichiers sont décrits ci-dessous.
Les pseudo fichiers dans chaque répertoire d’un cpuset sont créés
automatiquement quand le cpuset est créé, suite à l’appel à mkdir(2).
Il n’est pas possible d’ajouter ou de supprimer directement ces pseudo
fichiers.
Le répertoire d’un cpuset qui ne contient pas de répertoire pour un
cpuset fils et n’a pas de processus lui étant attaché peut être
supprimé à l’aide de rmdir(2) ou rmdir(1). Il n’est pas nécessaire, ou
possible, de supprimer les pseudo fichiers du répertoire avant de le
supprimer.
Les pseudo fichiers des répertoires d’un cpuset sont de petits fichiers
texte qui peuvent être lus ou écrits en utilisant les outils
traditionnels comme cat(1) et echo(1) ou depuis un programme en
utilisant des fonctions d’une bibliothèque d’entrées sorties ou des
appels système, comme open(2), read(2), write(2) et close(2).
Les pseudo fichiers dans un répertoire d’un cpuset représentent l’état
interne du noyau et n’ont pas de représentation persistante sur le
disque. Les fichiers d’un cpuset sont listés et décrits ci-dessous.
tasks Liste des identifiants de processus (PID) des processus dans ce
cpuset. La liste contient une série de nombres décimaux au
format ASCII, chacun suivit d’une nouvelle ligne. Un processus
peut être ajouté à un cpuset (ce qui le supprime automatiquement
du cpuset qui le contenait auparavant) en écrivant son PID dans
le fichier tasks du cpuset (avec ou sans nouvelle ligne à la
fin).
Attention : un seul PID peut être écrit à la fois dans le
fichier tasks. Si une chaîne est écrite et qu’elle contient plus
d’un PID, seul le premier sera utilisé.
notify_on_release
Drapeau (0 ou 1). Lorsqu’il est activé (1), ce cpuset sera géré
de façon particulière une fois qu’il sera libéré, c’est-à-dire
après que tous les processus cessent de l’utiliser (c’est-à-dire
se terminent ou ont été déplacés dans un autre ensemble de CPU)
et que tous les répertoires des cpusets fils ont été supprimés.
Consultez la section Notification à la libération ci-dessous.
cpus Liste des numéros physiques des CPU sur lesquels les processus
de ce cpuset ont le droit de s’exécuter. Consultez la section
Format des listes ci-dessous pour une description du format de
cpus.
Les CPU autorisés pour un cpuset peuvent être changés en
écrivant une nouvelle liste dans la fichier cpus.
cpu_exclusive
Drapeau (0 ou 1). S’il est activé (1), le cpuset a un accès
exclusif à ses CPU (des cpusets frères ou cousins ne peuvent pas
partager de CPU). Par défaut, il est désactivé (0). Les cpusets
nouvellement créés ont aussi ce drapeau de désactivé (0).
Deux cpusets sont frres s’ils ont le même cpuset père dans la
hiérarchie /dev/cpuset. Deux cpusets sont cousins si aucun n’est
l’ancêtre de l’autre. Indépendamment du paramètre cpu_exclusive,
si un cpuset est l’ancêtre d’un autre et si ces deux cpusets ont
des listes de CPU (cpus) non vides, alors leurs listes de CPU
doivent se chevaucher parce que la liste cpus d’un cpuset est
toujours un sous ensemble de la liste cpus de son cpuset père.
mems Liste des noeuds mémoire sur lesquels les processus de ce cpuset
ont le droit d’allouer de la mémoire. Consultez la section
Format des listes ci-dessous pour une description du format de
mems.
mem_exclusive
Drapeau (0 ou 1). S’il est activé (1), le cpuset a un accès
exclusif à ses noeuds mémoire (pas de partage entre frères ou
cousins). S’il est activé, il s’agit également d’un cpuset
« Hardwall » (voir ci-dessous). Par défaut, il est désactivé
(0). Les cpusets nouvellement créés ont aussi ce drapeau de
désactivé (0).
Indépendamment du paramètre mem_exclusive, si un cpuset est
l’ancêtre d’un autre, alors leurs noeuds mémoires doivent se
chevaucher parce que l’ensemble des noeuds mémoire d’un cpuset
est toujours un sous ensemble des noeuds mémoire du cpuset père.
mem_hardwall (depuis Linux 2.6.25)
Drapeau (0 ou 1). S’il est activé, le cpuset est de type
Hardwall (voir ci-dessous). Contrairement à mem_exclusive, des
cpusets marqués avec mem_hardwall peuvent partager des noeuds
mémoire avec des cpusets frères ou cousins. Par défaut, il est
désactivé (0). Les cpusets nouvellement créés ont aussi ce
drapeau de désactivé (0).
memory_migrate (depuis Linux 2.6.16)
Drapeau (0 ou 1). S’il est activé (1), alors la migration
mémoire est activée. Par défaut, il est désactivé. Consultez la
section Migration mémoire ci-dessous.
memory_pressure (depuis Linux 2.6.16)
Une mesure de la pression mémoire causée par les processus d’un
cpuset. Consultez la section Pression mémoire ci-dessous. À
moins que memory_pressure_enabled soit activé, il vaut toujours
zéro. Ce fichier est en lecture seule. Consultez la section
AVERTISSEMENTS ci-dessous.
memory_pressure_enabled (depuis Linux 2.6.16)
Drapeau (0 ou 1). Ce fichier n’est présent que dans le cpuset
racine, qui est normalement /dev/cpuset. S’il est activé (1),
les calculs de memory_pressure sont activés pour tous les
cpusets du système. Par défaut, il est désactivé (0). Consultez
la section Pression mémoire ci-dessous.
memory_spread_page (depuis Linux 2.6.17)
Drapeau (0 ou 1). S’il est activé (1), les pages du cache de
pages du noyau (les tampons des systèmes de fichiers) sont
distribuées uniformément dans les cpusets. Par défaut, il est
désactivé (0) dans le cpuset racine et hérité du cpuset père
pour les cpusets nouvellement créés. Consultez la section
Répartition mémoire ci-dessous.
memory_spread_slab (depuis Linux 2.6.17)
Drapeau (0 ou 1). S’il est activé (1), les caches slab [ NdT :
tampons pré-alloués par le noyau ] pour les entrées-sorties de
fichiers (des structures pour des répertoires ou inoeuds) sont
répartis uniformément dans le cpuset. Par défaut, ce drapeau est
désactivé (0) dans le cpuset racine et les nouveaux cpusets
héritent du drapeau de leur père quand ils sont créés. Consultez
la section Répartition mémoire ci-dessous.
sched_load_balance (depuis Linux 2.6.24)
Drapeau (0 ou 1). S’il est activé (1, la valeur par défaut), le
noyau répartira automatiquement la charge des processus du
cpuset au travers les CPU autorisés pour le cpuset. S’il est
désactivé (0), le noyau ne répartira pas la charge des processus
du cpuset, moins qu’un autre cpuset qui partage des CPU avec
lui n’ait son drapeau sched_load_balance activé. Consultez la
section Répartition de la charge par l’ordonnanceur ci-dessous
pour plus de détails.
sched_relax_domain_level (depuis Linux 2.6.26)
Entier, compris entre -1 et une petite valeur positive.
sched_relax_domain_level contrôle la largeur de l’intervalle des
CPU pour lesquels le noyau effectue une répartition immédiate
des tâches exécutables. Si sched_load_balance est désactivé,
alors sched_relax_domain_level ne compte pas, puisqu’il n’y a
pas de répartition de la charge. Si sched_load_balance est
activé, alors plus sched_relax_domain_level est important, plus
l’intervalle des CPU sur lesquels le noyau essaie de répartir la
charge est important. Consultez la section Niveau du domaine de
détente de l’ordonnanceur ci-dessous pour plus de détails.
En plus des pseudo fichiers décrits ci-dessus, dans chaque répertoire
de /dev/cpuset, chaque processus a un pseudo fichier,
/proc/<pid>/cpuset, qui indique le chemin vers le répertoire du cpuset
du processus, relativement à la racine du système de fichiers cpuset.
Quatre lignes sont également ajoutées dans le fichier
/proc/<pid>/status, fournissant pour chaque processus les champs :
Cpus_allowed (sur quels CPU il peut être ordonnancé) et Mems_allowed
(sur quels noeuds mémoire de la mémoire peut être allouée), avec
l’Affichage sous forme de masque et l’Affichage sous forme de liste
(voir ci-dessous). Voici un exemple :
Cpus_allowed: ffffffff,ffffffff,ffffffff,ffffffff
Cpus_allowed_list: 0-127
Mems_allowed: ffffffff,ffffffff
Mems_allowed_list: 0-63
Les champs « allowed » ont été ajoutés dans Linux 2.6.24 ; les champs
« allowed_list » ont été ajoutés dans Linux 2.6.26.
CAPACITÉS ÉTENDUES
En plus de contrôler quels CPU (cpus) et noeuds mémoire (mems) un
processus à le droit d’utiliser, les cpusets fournissent les
fonctionnalités étendues suivantes.
Ensembles de CPU exclusifs
Si un cpuset est marqué avec cpu_exclusive ou mem_exclusive, aucun
autre cpuset, autre que des ancêtres ou descendants directs, peuvent
partager des CPU ou des noeuds mémoire avec ce cpuset.
Un cpuset dont mem_exclusive est activé restreint les allocations du
noyau pour les pages des tampons de cache et autres données internes du
noyau communément partagées par le noyau au travers différents
utilisateurs. Tous les cpusets, que mem_exclusive soit activé ou non,
restreignent l’allocation de mémoire depuis l’espace utilisateur. Ceci
permet de configurer un système de telle sorte que différentes tâches
puissent partager des données du noyau, tout en isolant toutes les
allocations en mode utilisateur des tâches dans leur propre cpuset.
Pour ceci, il faut créer un gros cpuset, avec mem_exclusive activé,
pour contenir toutes les tâches, et créer des cpuset fils sans
mem_exclusive pour chacune des tâches. Seule une petite partie de la
mémoire du noyau, comme les requêtes des gestionnaires d’interruptions,
est autorisée à être placée sur des noeuds mémoire en dehors d’un
cpuset, même si mem_exclusive est activé.
Hardwall
Un cpuset pour lequel mem_exclusive ou mem_hardwall est activé est un
cpuset hardwall. Un cpuset hardwall restreint les allocations mémoire
du noyau pour les pages, tampons et toutes autre données partagés
fréquemment par le noyau au travers différents utilisateurs. Tous les
cpusets, hardwall ou non, restreignent les allocations mémoire pour
l’espace utilisateur.
Ceci permet de configurer un système de telle sorte que différentes
tâches indépendantes puissent partager des données du noyau, comme des
pages des systèmes de fichiers, tout en isolant les allocations de
l’espace utilisateur de chaque tâche dans leur cpuset. Pour ceci, il
faut créer un gros cpuset hardwall qui contiendra toutes les tâches et
créer des cpusets fils (non hardwall) pour chacune des tâches.
Seule une petite quantité de mémoire noyau, comme les demandes des
gestionnaires d’interruption, peut être utilisée à l’extérieur d’un
cpuset hardwall.
Notification à la libération
Si le drapeau notify_on_release d’un cpuset est activé (1), alors quand
le dernier processus quitte le cpuset (il se termine ou s’attache à un
autre cpuset) et que le dernier cpuset fils de ce cpuset a été
supprimé, le noyau exécutera la commande /sbin/cpuset_release_agent en
lui fournissant le chemin (relatif au point de montage du système de
fichiers cpuset) du cpuset abandonné. Ceci permet de supprimer
automatiquement les cpusets abandonnés.
Le drapeau notify_on_release du cpuset racine est désactivé (0) par
défaut au moment du démarrage. La valeur par défaut pour les autres
cpusets lors de leur création est égale à la valeur de
notify_on_release de leur cpuset parent.
La commande /sbin/cpuset_release_agent est appelée, avec dans argv[1]
le nom (un chemin relatif à /dev/cpuset) du cpuset à supprimer.
Le contenu habituel de la commande /sbin/cpuset_release_agent est
simplement le script shell suivant :
#!/bin/sh
rmdir /dev/cpuset/$1
Comme pour les autres drapeaux ci-dessous, ce drapeau peut être modifié
en écrivant un 0 ou un 1 ASCII (avec ou sans fin de ligne) dans le
fichier pour respectivement désactiver ou activer le drapeau.
Pression mémoire
Le fichier memory_pressure d’un cpuset indique la moyenne instantanée
du taux auquel les processus du cpuset tentent de libérer de la mémoire
utilisée sur les noeuds du cpuset pour satisfaire les nouvelles
demandes de mémoire.
Ceci permet à un gestionnaire de tâches de superviser les tâches qui
s’exécutent dans des cpuset dédiés et détecter efficacement la pression
mémoire qu’une tâche produit.
Ceci est utile à la fois pour les systèmes très surveillés qui
exécutent diverses tâches qui leurs sont fournies et peuvent choisir de
terminer ou de changer la priorité des tâches qui essaient d’utiliser
plus de mémoire que les noeuds mémoire qui leurs ont été assignés leurs
permettent, et les systèmes pour du calcul scientifique avec des tâches
parallèles, fortement couplées, au temps d’exécution important, qui ne
pourraient plus fournir les performances demandées si elles se
mettaient à utiliser plus de mémoire qu’elles n’en ont droit.
Ce mécanisme fourni un moyen très économique pour détecter des signes
de pression mémoire sur un cpuset. L’action à effectuer lorsqu’un signe
de pression mémoire est détecté est laissé au libre arbitre du
gestionnaire des tâches ou autre code utilisateur.
À moins que le calcul de la pression mémoire soit activé par le pseudo
fichier /dev/cpuset/memory_pressure_enabled, cette pression mémoire
n’est calculée pour aucun cpuset et les lectures dans les fichiers
memory_pressure renvoient toujours zéro, c’est-à-dire la chaîne ASCII
« 0\n ». Consultez la section AVERTISSEMENTS ci-dessous.
Une moyenne instantanée par cpuset est utilisée pour les raisons
suivantes :
* Comme cette métrique est par cpuset plutôt que par processus ou par
région mémoire virtuelle, la charge du système due à la supervision
de cette métrique par un gestionnaire de tâches est fortement
réduite sur les gros systèmes, étant donné qu’il n’est pas
nécessaire de parcourir la liste des tâches à chaque fois.
* Comme cette métrique est une moyenne instantanée plutôt qu’un
compteur, un gestionnaire de tâches obtient la pression mémoire en
une seule lecture sans avoir à lire et se souvenir des résultats
pendant un certain temps.
* Comme cette métrique est par cpuset plutôt que par processus, le
gestionnaire de tâches peut obtenir l’information importante, la
pression mémoire dans un cpuset, en une seule lecture sans
nécessiter d’obtenir et de se souvenir des résultats pour tous les
processus d’un cpuset (la liste des processus peut changer
dynamiquement).
La pression mémoire d’un cpuset est calculée en utilisant un simple
filtre digital par cpuset dans le noyau. Pour chaque cpuset, ce filtre
suit le taux auquel les processus attachés à ce cpuset demandent au
noyau de réutiliser de la mémoire.
Ces demandes de réutilisation de mémoire se produisent quand un
processus doit satisfaire une demande de page mémoire en trouvant
d’abord une page à réutiliser, du fait de l’absence de page disponible
déjà prête. Les pages sales des systèmes de fichiers sont réutilisées
en les écrivant d’abord sur le disque. Les tampons des systèmes de
fichiers qui n’ont pas été modifiés sont réutilisés tout simplement en
les abandonnant, mais si cette page est nécessaire de nouveau, il
faudra la relire sur le disque.
Le fichier memory_pressure fournit un nombre entier qui représente le
taux des demandes récentes (la demi-vie est de 10 secondes) de
réutilisation de mémoire par les processus du cpuset, l’unité étant le
nombre de demandes par seconde fois 1000.
Répartition mémoire
Il y a deux fichiers, par cpuset, pour des drapeaux booléens qui
contrôlent où le noyau alloue les pages pour les tampons des systèmes
de fichiers et les structures de données liées internes au noyau. Ces
fichiers sont memory_spread_page et memory_spread_slab.
Si le drapeau booléen memory_spread_page est activé, alors le noyau
répartit les tampons des systèmes de fichiers (les caches des pages)
équitablement sur tous les noeuds autorisés pour le processus qui
demandela page, au lieu de placer ces pages de préférence sur le noeud
sur lequel s’exécute le processus.
Si le drapeau booléen memory_spread_slab d’un cpuset est activé, alors
le noyau répartira uniformément les caches slab liés aux systèmes de
fichiers, comme ceux pour des entrées d’inoeuds ou de répertoires, sur
tous les noeuds autorisés pour le processus qui demande de la mémoire,
plutôt que de préférer mettre ces pages sur le noeud sur lequel
s’exécute le processus.
La configuration de ces drapeaux n’affecte pas les pages du segment de
données (voir brk(2)) ou du segment de la pile d’un processus.
Par défaut, les deux types de répartition de la mémoire sont désactivé
et le noyau préfère allouer la mémoire sur le noeud local où s’exécute
le processus. Si ce noeud n’est pas autorisé par la politique NUMA du
processus ou par la configuration des cpusets ou s’il n’y a plus
suffisamment de pages mémoire disponibles sur ce noeud, alors le noyau
recherche le noeud le plus proche étant autorisé et ayant suffisamment
de pages disponibles.
Quand un nouveau cpuset est créé, il hérite de la configuration de
répartition mémoire de son père.
Activer la répartition mémoire a pour effet d’ignorer la politique
mémoire NUMA du processus pour les allocations de pages ou de caches
slab, qui sont alors éparpillées. Cependant, les changements dus à la
répartition mémoire demandée par un cpuset ne sont pas visibles pour
les appels système mbind(2) ou set_mempolicy(2). Ces deux appels
système liés à la politique mémoire NUMA semble se comporter comme si
aucune répartition mémoire n’était demandée par un cpuset, même si
c’est le cas. Si la répartition mémoire est par la suite désactivée
pour les cpuset, la dernière politique mémoire NUMA définie par ces
appels est automatiquement appliquée de nouveau.
memory_spread_page et memory_spread_slab sont tous les deux des
fichiers contenant des drapeaux booléens. Par défaut, ils contiennent
un « 0 », ce qui signifie que la fonctionnalité est désactivée pour ce
cpuset. Si un « 1 » est écrit dans le fichier, la fonctionnalité
correspondante est activée.
La répartition mémoire d’un cpuset se comporte de façon similaire à ce
qui est connu (dans d’autres contextes) comme le placement mémoire à
tour de rôle (« round-robin ») ou entrelacé (« interleave »).
La configuration d’une stratégie de répartition mémoire pour un cpuset
peut améliorer significativement les performances pour les tâches qui :
a) nécessitent de placer les données locales des threads dans des
noeuds mémoire proches des CPU qui exécutent les threads qui
accèdent le plus fréquemment à ces données ; mais aussi
b) nécessitent d’accéder à de gros ensembles de données de systèmes de
fichiers qui doivent être répartis sur différents noeuds du cpuset
de la tâche du fait de leurs tailles.
Sans cette politique, la répartition des allocations mémoire sur les
noeuds du cpuset de la tâche peut ne pas être équitable,
particulièrement pour les tâches qui n’auraient qu’un thread chargé de
l’initialisation ou de la lecture des données d’entrée.
Migration mémoire
Normalement, avec la configuration de memory_migrate par défaut
(désactivé), une fois qu’une page est allouée (une page physique de la
mémoire lui est donné), cette page reste sur le noeud où elle a été
allouée, tant qu’elle reste allouée, même si la politique de placement
mémoire du cpuset (mems) change par la suite.
Quand la migration mémoire est activée pour un cpuset, si la
configuration de mems est modifiée alors toute page mémoire utilisée
par un processus du cpuset qui se trouverait sur un noeud mémoire qui
n’est plus autorisé sera déplacée sur un noeud mémoire qui est
autorisé.
De plus, si un processus est déplacé dans un cpuset dont le drapeau
memory_migrate est activé, toutes les pages mémoire qu’il utilise et
qui se trouvent sur des noeuds mémoire qui étaient autorisés dans son
cpuset précédant mais ne le sont plus dans le nouveau cpuset seront
déplacées sur un noeud mémoire autorisé pour le nouveau cpuset.
L’emplacement relatif d’un page déplacée d’un cpuset est préservé si
possible lors de ces opérations de déplacement. Par exemple, si la page
se trouvait sur le deuxième noeud valable du précédent cpuset, alors la
page sera placée sur le deuxième noeud valable du nouveau cpuset, si
c’est possible.
Répartition de la charge par l’ordonnanceur
L’ordonnanceur du noyau répartit automatiquement la charge des
processus. Si un CPU est sous-utilisé, le noyau recherchera des
processus sur d’autres CPU plus chargés et déplacera ces processus sur
le CPU sous-utilisé à condition que les mécanismes comme les cpuset et
sched_setaffinity(2) le permettent.
Le coût de l’algorithme de répartition de la charge et son impact sur
les structures de données partagées du noyau, comme la liste des
processus, augmente plus que linéairement avec le nombre de CPU qui
interviennent pour la répartition de la charge. Par exemple le coût
pour la répartition de la charge dans un grand ensemble de CPU sera
supérieur à celui pour la répartition de la charge dans deux ensembles
ayant moitié moins de CPU. (La relation entre le nombre de CPU
intervenant dans la répartition de la charge et le coût de cette
répartition de charge dépend de l’implémentation de l’ordonnanceur de
processus du noyau, qui change dans le temps quand de meilleurs
algorithmes d’ordonnancement sont implémentés)
Le drapeau sched_load_balance d’un cpuset permet de supprimer cette
répartition automatique de la charge dans les cas où elle n’est pas
nécessaire et que sa suppression améliorerait les performances.
Par défaut, la répartition de la charge se fait sur tous les CPU, à
l’exception de ceux marqués comme étant isolés en utilisant au moment
du démarrage le paramètre du noyau « isolcpus= ». (Consultez la section
Niveau du domaine de détente de l’ordonnanceur ci-dessous pour changer
le comportement par défaut)
Cette répartition de la charge par défaut n’est pas bien adaptée aux
situations suivantes :
* Sur les gros systèmes, la répartition de la charge sur beaucoup de
CPU est très coûteuse. Si le système est géré avec des cpusets pour
placer les tâches indépendantes sur différents ensembles de CPU, une
répartition de la charge complète n’est pas nécessaire.
* Les systèmes avec une prise en charge temps réel sur certains CPU
doivent minimiser la surcharge du système sur ces CPU et donc éviter
la répartition de la charge des processus si elle n’est pas
nécessaire.
Quand le drapeau sched_load_balance d’un cpuset est activé (ce qui est
le cas par défaut), une répartition de la charge sur tous les CPU
autorisés par le cpuset est demandé, à condition que le processus
puisse être déplacé d’un CPU du cpuset à un autre CPU (c’est-à-dire
qu’il n’ait pas été attaché à des CPU avec, par exemple,
sched_setaffinity(2)).
Quand le drapeau sched_load_balance d’un cpuset est désactivé, alors
l’ordonnanceur évitera de déplacer des processus pour répartir la
charge des CPU du cpuset, sauf si un autre cpuset partage le même CPU
et a son drapeau sched_load_balance activé.
Ainsi, par exemple, si le cpuset racine a son drapeau
sched_load_balance activé, alors l’ordonnanceur répartira la charge sur
tous les CPU et la configuration du drapeau sched_load_balance des
autres cpusets n’a pas d’effet, puisqu’une répartition complète de la
charge est déjà demandée.
Dans les deux situations ci-dessus, le drapeau sched_load_balance
devrait donc être désactivé sur le cpuset racine et seuls les cpusets
fils plus petits devraient l’activer.
Lorsque vous faites ceci, vous ne devez généralement pas laisser un
processus non attaché à un CPU dans le cpuset racine qui pourrait
utiliser les CPU de façon non négligeable. De cette façon les processus
peuvent être artificiellement contraints à un sous ensemble des CPU en
fonction de la configuration de ce drapeau dans les cpusets
descendants. Même si ce processus pourrait utiliser des cycles CPU
inutilisés par certains CPU, l’ordonnanceur du noyau ne cherchera pas à
répartir la charge du processus sur le CPU sous utilisé.
Bien sûr, les processus attachés à un CPU particulier peuvent être
laissés dans un cpuset qui désactive sched_load_balance puisque ces
processus ne peuvent être déplacés de toute façon.
Niveau du domaine de détente de l’ordonnanceur
L’ordonnanceur du noyau effectue une répartition de la charge immédiate
lorsqu’un CPU devient disponible ou lorsqu’une autre tâche est prête.
Cette répartition de la charge permet de s’assurer que le plus de CPU
possibles sont utilisés efficacement en exécutant des tâches. Le noyau
effectue aussi une répartition de la charge de façon plus sporadique
sur la base de l’horloge logicielle décrite dans time(7). La
configuration de sched_relax_domain_level ne s’applique qu’à la
répartition de charge automatique. Indépendamment de la configuration
de sched_relax_domain_level, une répartition de charge sporadique est
effectuée à travers tous les CPU (sauf si cela a été désactivé avec
sched_load_balance). Dans tous les cas, bien sûr, les tâches ne seront
exécutées que sur les CPU autorisés par leur cpuset et par les appels
systèmes sched_setaffinity(2).
Sur les petits systèmes, avec peu de CPU, la répartition de charge
immédiate est utile pour améliorer l’interactivité du système et
minimiser les cycles CPU inutilisés. Mais sur les gros systèmes,
essayer de répartir la charge immédiatement sur un nombre important de
CPU peut être plus coûteux que ce que ça ne rapporte, en fonction des
performances des différentes tâches et du matériel.
La signification exacte des petites valeurs de sched_relax_domain_level
dépendra de l’implémentation de l’ordonnanceur du noyau et de
l’architecture non uniforme du matériel. Ces deux paramètres évolueront
dans le temps et dépendent de l’architecture du système et de la
version du noyau.
À ce jour, quand cette capacité a été introduite sous Linux 2.6.26, la
signification des valeurs positives de sched_relax_domain_level est la
suivante pour certaines des architectures les plus courantes :
(1) Effectuer immédiatement une répartition de la charge sur les
différents Hyper-Thread frères d’un même coeur.
(2) Effectuer immédiatement une répartition de la charge sur les
différents coeurs d’un processeur.
(3) Effectuer immédiatement une répartition de la charge sur les
différents CPU d’un même noeud ou d’une même lame.
(4) Effectuer immédiatement une répartition de la charge sur les
différents (détail d’implémentation) noeuds [pour les systèmes
NUMA].
(5) Effectuer immédiatement une répartition de la charge sur tous les
CPU d’un système [pour les systèmes NUMA].
La valeur zéro (0) pour sched_relax_domain_level signifie toujours
qu’il n’y a pas de répartition de charge immédiate, et donc la
répartition de la charge s’effectue périodiquement et non pas
immédiatement quand un CPU devient disponible ou qu’une tâche peut être
exécutée.
La valeur -1 pour sched_relax_domain_level signifie toujours qu’il faut
utiliser la valeur par défaut du système. La valeur par défaut du
système peut varier en fonction de l’architecture et du noyau. Cette
valeur par défaut du système peut être modifiée en fournissant au noyau
un paramètre « relax_domain_level= » lors du démarrage.
Si des cpusets partagent des CPU et ont des valeurs de
sched_relax_domain_level incompatibles, alors la valeur la plus élevée
s’applique à tous les CPU de ces cpusets. Dans ce cas, la valeur moins
un (-1) est la valeur la plus faible, remplacée par toute autre valeur
et la valeur zéro (0) est la valeur la plus faible suivante.
FORMATS
Les formats suivants sont utilisés pour représenter des ensembles de
CPU et de noeuds mémoire.
Affichage sous forme de masque
L’Affichage sous forme de masque est utilisé pour représenter les
masques de bits des CPU et noeuds mémoire dans le fichier
/proc/<pid>/status.
Ce format affiche chaque mot de 32 bits au format hexadécimal (en
utilisant les caractères ASCII « 0 » - « 9 » et « a » - « f ») ; le
début des mots est complété par des zéros si nécessaire. Pour les
masques de plus d’un mot, une virgule est utilisée pour séparer les
mots. Les mots sont affiché au format grand boutiste, avec le bit le
plus significatif en premier. Les chiffres hexadécimaux d’un mot
utilise aussi l’ordre grand boutiste.
Le nombre de mots de 32 bits affichés est le nombre minimal nécessaire
pour afficher tous les bits du masque, en fonction de la taille du
masque de bits.
Exemple d’Affichage sous forme de masque :
00000001 # seul le bit 0
40000000,00000000,00000000 # seul le bit 94
00000001,00000000,00000000 # seul le bit 64
000000ff,00000000 # seuls les bits 32-39
00000000,000E3862 # les bits 1,5,6,11-13,17-19
Un masque avec les bits 0, 1, 2, 4, 8, 16, 32 et 64 activés sera
affiché de cette façon :
00000001,00000001,00010117
Le premier « 1 » correspond au bit 64, le second au bit 32, le
troisième au bit 16, le quatrième au bit 8, le cinquième au bit 4 et le
« 7 » correspond aux bits 2, 1 et 0.
Affichage sous forme de liste
L’Affichage sous forme de liste pour les fichiers cpus et mems est une
liste de numéros ou intervalles de CPU ou de noeuds mémoire séparés par
des virgules, en décimal au format ASCII.
Exemple d’Affichage sous forme de liste :
0-4,9 # bits 0, 1, 2, 3, 4 et 9 activés
0-2,7,12-14 # bits 0, 1, 2, 7, 12, 13 et 14 activés
RÈGLES
Les règles suivantes s’appliquent à chaque cpuset :
* Ses CPU et noeuds mémoire doivent être des sous-ensembles de ceux de
leur parent (ou les mêmes ensembles).
* Il ne peut être marqué avec cpu_exclusive que si son parent l’est.
* Il ne peut être marqué avec mem_exclusive que si son parent l’est.
* S’il est marqué avec cpu_exclusive, ses CPU ne doivent pas être
partagés avec ses frères.
* S’il est marqué avec memory_exclusive, ses noeuds mémoire ne doivent
pas être partagés avec ses frères.
PERMISSIONS
Les permissions d’un cpuset sont déterminées par les permissions des
répertoires et pseudo fichiers du système de fichiers cpuset,
normalement monté dans /dev/cpuset.
Par exemple, un processus peut se placer dans un autre cpuset s’il peut
écrire dans le fichier tasks de ce cpuset. Ceci nécessite les
permission d’exécution des répertoires à traverser et la permission
d’écrire dans le fichier tasks.
Une contrainte supplémentaire s’applique aux demandes de déplacement
d’autres processus dans un cpuset. Un processus ne peut pas attacher un
autre processus à un cpuset à moins qu’il ait la permission d’envoyer
un signal à ce processus (voir kill(2)).
Un processus peut créer un cpuset fils s’il a accès et peut écrire dans
le répertoire du cpuset père. Il peut modifier les CPU et noeuds
mémoire d’un cpuset s’il a accès au répertoire de ce cpuset (les
permissions d’exécuter tous les répertoires parents) et s’il peut
écrire dans les fichiers correspondants cpus ou mems.
Il y a une petite différence entre la manière dont ces permissions sont
évaluées et la manière dont sont évaluées les permissions pour les
opérations sur des systèmes de fichiers normaux. Le noyau interprète
les chemins relatifs en fonction du répertoire de travail actuel d’un
processus. Même quand on opère sur un fichier d’un cpuset, les chemins
relatifs sont interprétés en fonction du répertoire de travail du
processus, et non pas relativement au cpuset actuel du processus. Les
seules façons pour que les chemins de cpusets soient interprétés
relativement au cpuset actuel du processus sont soit que le processus
utilise le répertoire du cpuset comme répertoire de travail (il a
d’abord effectué un cd ou chdir(2) dans le répertoire de son cpuset
dans /dev/cpuset, ce qui est plutôt inhabituel), soit que du code
utilisateur convertit le chemin relatif au cpuset en un chemin absolu.
En théorie, ceci signifie que le code utilisateur devrait indiquer les
cpusets en utilisant des chemins absolus, ce qui nécessite de connaître
le point de montage du système de fichier cpuset (d’habitude, mais sans
que ce soit nécessaire, /dev/cpuset). En pratique, à la connaissance de
l’auteur, tous les utilitaires en mode utilisateur supposent que si le
système de fichier cpuset est monté, alors il est monté dans
/dev/cpuset. De plus, une pratique assez courante utilisé pour du code
écrit soigneusement consiste à vérifier la présence du pseudo fichier
/dev/cpuset/tasks afin de vérifier que le pseudo système de fichiers
cpuset est bien monté.
AVERTISSEMENTS
Activation de memory_pressure
Par défaut, le fichier memory_pressure d’un cpuset vaut zéro (0). À
moins que cette fonctionnalité soit activée en écrivant « 1 » dans le
pseudo fichier /dev/cpuset/memory_pressure_enabled, le noyau ne calcule
pas les valeurs des fichiers memory_pressure des cpusets individuels.
Utilisation de la commande echo
Lorsque la commande echo est utilisée dans un interpréteur de commandes
pour changer les valeurs des fichiers d’un cpuset, soyez conscient que
la commande echo interne à certains interpréteurs de commandes
n’affiche pas de message d’erreur si l’appel système write(2) échoue.
Par exemple, si la commande :
echo 19 > mems
échoue parce que le noeud mémoire 19 n’est pas autorisé (par exemple le
système n’a pas de noeud mémoire numéro 19), alors la commande echo
peut n’afficher aucune erreur. If faut mieux utiliser la commande
externe /bin/echo pour changer la configuration d’un fichier d’un
cpuset puisque cette commande affichera les erreurs de write(2), comme
par exemple :
/bin/echo 19 > mems
/bin/echo : erreur d’écriture : Le résultat numérique est en dehors de l’intervalle
EXCEPTIONS
Placement mémoire
Les contraintes des cpusets ne s’appliquent pas à toutes les
allocations de mémoire système pour les raisons suivantes :
Si la fonctionnalité de connexion à chaud est utilisée pour supprimer
tous les CPU d’un cpuset, alors le noyau mettra à jour automatiquement
la liste de CPU autorisés (cpus_allowed) de tous les processus attachés
aux CPU du cpuset et autorisera tous les CPU. Le comportement est
similaire lorsque la fonctionnalité de connexion à chaud est utilisée
pour la mémoire. En général, le noyau préfère ne pas tenir compte du
placement sur les CPU ou les noeuds mémoire plutôt que d’abandonner un
processus dont tous les CPU ou noeuds mémoire autorisés sont
déconnectés. Le code utilisateur devrait reconfigurer les cpusets pour
ne mentionner que les CPU et les noeuds mémoire en ligne lorsque la
fonctionnalité de connexion à chaud est utilisée pour ajouter ou
retirer ces ressources.
Quelques demandes d’allocation mémoire critiques et internes au noyau,
marquées GFP_ATOMIC, doivent être satisfaites immédiatement. Le noyau
peut rater des demandes ou ne pas fonctionner correctement si certaines
de ces allocations échouent. Si une de ces demandes ne peut être
satisfaite par le cpuset du processus en cours, alors les contraintes
du cpuset sont relâchées et le noyau recherche de la mémoire là où il
peut en trouver. Il est préférable de ne pas respecter un cpuset plutôt
que de stresser le noyau.
Les allocations de mémoire demandées par des pilotes du noyau lors du
traitement d’une interruption ne se trouvent dans le contexte d’aucun
processus et ne sont donc pas contraintes par les cpusets.
Renommer des cpusets
Vous pouvez utiliser l’appel système rename(2) pour renommer des
cpusets. Seuls des renommages simples sont pris en charge ;
c’est-à-dire que changer le nom du répertoire d’un cpuset est autorisé,
mais déplacer le répertoire d’un cpuset dans un autre répertoire n’est
pas autorisé.
ERREURS
L’implémentation des cpusets du noyau Linux positionne errno pour
indiquer la raison de l’échec d’un appel système lié à un cpuset.
Les valeurs possible pour errno et leurs significations, lors d’un
échec d’un appel système lié à un cpuset sont listées ci-dessous :
E2BIG Tentative d’écriture (write(2)) dans un fichier spécial d’un
cpuset avec une longueur supérieure à la longueur autorisée par
le noyau pour ces écritures.
EACCES Tentative d’écriture (write(2)) d’un identifiant de processus
(PID) dans le fichier tasks d’un cpuset alors que l’appelant
n’est pas autorisé à déplacer le processus.
EACCES Tentative d’ajout, avec write(2), d’un CPU ou d’un noeud mémoire
dans un cpuset alors que ce CPU ou ce noeud mémoire ne se trouve
pas dans le cpuset parent.
EACCES Tentative d’activation, avec write(2), de cpu_exclusive ou de
mem_exclusive sur un cpuset dont le parent n’a pas ces
propriétés.
EACCES Tentative d’écriture (write(2)) dans un fichier memory_pressure.
EACCES Tentative de création d’un fichier dans le répertoire d’un
cpuset.
EBUSY Tentative de suppression, avec rmdir(2), d’un cpuset auquel sont
attachés des processus.
EBUSY Tentative de suppression, avec rmdir(2), d’un cpuset ayant des
ensembles de CPU fils.
EBUSY Tentative de suppression d’un CPU ou d’un noeud mémoire d’un
cpuset alors que le CPU ou le noeud mémoire se trouve également
dans un des fils du cpuset.
EEXIST Tentative de création, avec mkdir(2), d’un cpuset qui existe
déjà.
EEXIST Tentative de renommage (rename(2)) d’un cpuset avec un nom déjà
utilisé.
EFAULT Tentative de lecture (read(2)) ou d’écriture (write(2)) dans un
fichier d’un cpuset en utilisant un tampon en dehors de l’espace
mémoire accessible par le processus appelant.
EINVAL Tentative de modification d’un cpuset, en utilisant write(2), de
telle sorte que les attributs cpu_exclusive ou mem_exclusive ne
soient plus respectés pour ce cpuset ou ses frères.
EINVAL Tentative d’écriture (avec write(2)) d’une liste vide dans cpus
ou mems pour un cpuset auquel sont déjà attachés des processus
ou des cpuset fils.
EINVAL Tentative d’écriture (avec write(2)) dans cpus ou mems d’une
liste qui comprend un intervalle dont la borne supérieure est
inférieure à la borne inférieure.
EINVAL Tentative d’écriture (avec write(2)) dans cpus ou mems d’une
liste dont la chaîne comprend un caractère non valable.
EINVAL Tentative d’écriture (avec write(2)) dans le fichier cpus d’une
liste qui ne comprend aucun CPU en ligne.
EINVAL Tentative d’écriture (avec write(2)) dans le fichier mems d’une
liste qui ne comprend aucun noeud mémoire en ligne.
EINVAL Tentative d’écriture (avec write(2)) dans le fichier mems d’une
liste qui comprend un noeud qui ne contient pas de mémoire.
EIO Tentative d’écriture (avec write(2)) dans le fichier tasks d’un
cpuset d’une chaîne qui ne commence pas par un entier décimal au
format ASCII.
EIO Tentative de renommage (avec rename(2)) d’un cpuset dans un
autre répertoire.
ENAMETOOLONG
Tentative de lecture (avec read(2)) du fichier
/proc/<pid>/cpuset d’un cpuset, pour lequel le chemin est plus
long que la taille des pages du noyau.
ENAMETOOLONG
Tentative de création, avec mkdir(2), d’un cpuset dont le nom du
répertoire de base fait plus de 255 caractères.
ENAMETOOLONG
Tentative de création, avec mkdir(2), d’un cpuset dont le chemin
complet, préfixe du point de montage compris (typiquement
« /dev/cpuset/ »), fait plus de 4095 caractères.
ENODEV Le cpuset a été supprimé par un autre processus en même temps
qu’une tentative d’écriture (avec write(2)) sur un des pseudo
fichiers du répertoire du cpuset.
ENOENT Tentative de création, avec mkdir(2), d’un cpuset dans un cpuset
parent qui n’existe pas.
ENOENT Tentative d’accéder à (avec access(2)) ou d’ouvrir (avec
open(2)) un fichier inexistant du répertoire d’un cpuset.
ENOMEM Pas assez de mémoire disponible pour le noyau ; ceci peut se
produire pour différents appels système liés aux cpusets, mais
seulement si le système manque beaucoup de mémoire.
ENOSPC Tentative d’écriture (avec write(2)) de l’identifiant d’un
processus dans le fichier tasks d’un cpuset alors que les
fichier cpus ou mems sont vides.
ENOSPC Tentative d’écriture (avec write(2)) d’un fichier cpus ou mems
vide dans un cpuset auquel sont attachées des tâches.
ENOTDIR
Tentative de renommage (avec rename(2)) d’un cpuset qui n’existe
pas.
EPERM Tentative de suppression d’un fichier dans le répertoire d’un
cpuset.
ERANGE Une liste pour cpus ou mems a été fournie au noyau mais comprend
un nombre trop grand pour que le noyau l’ajoute à son champ de
bits.
ESRCH Tentative d’écriture (avec write(2)) de l’identifiant d’un
processus inexistant dans le fichier tasks d’un cpuset.
VERSIONS
Les cpusets sont apparus dans la version 2.6.12 du noyau Linux.
NOTES
Contrairement à ce que son nom indique, le paramètre pid est en fait un
identifiant de thread. Chaque thread d’un groupe de threads peut être
attaché un cpuset différent. La valeur renvoyée par un appel à
gettid(2) peut être fournie comme paramètre pid.
BOGUES
Les fichiers memory_pressure peuvent être ouverts en écriture en
demandant une création ou troncature, mais dans ce cas write(2)
échouera en positionnant errno à EACCES, et les options de création ou
de troncature de open(2) n’ont aucun effet.
EXEMPLE
Voici des exemples pour l’affichage et la modification d’options d’un
cpuset à l’aide d’un interpréteur de commandes.
Créer et s’attacher à un cpuset.
Voici les étapes pour créer un nouveau cpuset et lui attacher
l’interpréteur de commandes en cours :
1) mkdir /dev/cpuset (si ce n’est déjà fait)
2) mount -t cpuset none /dev/cpuset (si ce n’est déjà fait)
3) Créer un nouveau cpuset avec mkdir(1).
4) Assigner des CPU et noeuds mémoire au nouveau cpuset.
5) Attacher l’interpréteur de commandes au nouveau cpuset.
Par exemple, la séquence de commandes suivante définira un cpuset
appelé « Charlie », ne contenant que les CPU 2 et 3 et le noeud mémoire
1, et attachera l’interpréteur de commandes en cours à ce cpuset.
$ mkdir /dev/cpuset
$ mount -t cpuset cpuset /dev/cpuset
$ cd /dev/cpuset
$ mkdir Charlie
$ cd Charlie
$ /bin/echo 2-3 > cpus
$ /bin/echo 1 > mems
$ /bin/echo $$ > tasks
# Le shell en cours s’exécute désormais dans le cpuset Charlie
# La ligne suivante devrait afficher « /Charlie »
$ cat /proc/self/cpuset
Déplacer des tâches sur d’autres noeuds mémoire.
Pour déplacer les tâches attachées à un cpuset sur d’autres CPU et
d’autres noeuds mémoire du système et déplacer les pages mémoires
actuellement allouées par ces processus, effectuez les étapes
suivantes :
1) Supposons qu’il faille déplacer les tâches du cpuset alpha (les CPU
4-7 et noeuds mémoire 2-3) vers un autre cpuset beta (CPU 16-19 et
noeuds mémoire 8-9).
2) Créer d’abord le nouveau cpuset beta.
3) Autoriser les CPU 16-19 et les noeuds mémoire 8-9 pour beta.
4) Activer memory_migration dans beta.
5) Déplacer chaque tâche d’alpha vers beta.
La séquence de commandes suivante effectue cela.
$ cd /dev/cpuset
$ mkdir beta
$ cd beta
$ /bin/echo 16-19 > cpus
$ /bin/echo 8-9 > mems
$ /bin/echo 1 > memory_migrate
$ while read i; do /bin/echo $i; done < ../alpha/tasks > tasks
La séquence ci-dessus déplace tous les processus de alpha vers beta et
déplace toute mémoire utilisée par ces processus sur les noeuds mémoire
2-3 vers les noeuds mémoire 8-9.
Notez que la dernière étape de la séquence ci-dessus n’était pas :
$ cp ../alpha/tasks tasks
La boucle while, plutôt que l’utilisation de la commande cp(1), est
nécessaire par ce qu’un seul identifiant de processus à la fois peut
être écrit dans le fichier tasks.
La même chose (l’écriture d’un PID à la fois) peut se faire plus
efficacement qu’avec la boucle while, en moins de caractère et dans une
syntaxe qui fonctionne avec tous les interpréteurs de commandes mais
malheureusement de façon moins intelligible, en utilisant l’option -u
(sans tampon) de sed(1) :
$ sed -un p < ../alpha/tasks > tasks
VOIR AUSSI
taskset(1), get_mempolicy(2), getcpu(2), mbind(2),
sched_getaffinity(2), sched_setaffinity(2), sched_setscheduler(2),
set_mempolicy(2), CPU_SET(3), proc(5), numa(7), migratepages(8),
numactl(8)
Le fichier Documentation/cpusets.txt des sources du noyau.
COLOPHON
Cette page fait partie de la publication 3.23 du projet man-pages
Linux. Une description du projet et des instructions pour signaler des
anomalies peuvent être trouvées à l’adresse
http://www.kernel.org/doc/man-pages/.
TRADUCTION
Cette page de manuel a été traduite par Alain Portal <aportal AT
univ-montp2 DOT fr> en 2008, et mise à disposition sur
http://manpagesfr.free.fr/.
Veuillez signaler toute erreur de traduction en écrivant à
<debian-l10n-french@lists.debian.org> ou par un rapport de bogue sur le
paquet manpages-fr.
Vous pouvez toujours avoir accès à la version anglaise de ce document
en utilisant la commande « man -L C <section> <page_de_man> ».