mlock, mlock2, munlock, mlockall, munlockall - Verrouiller et
déverrouiller la mémoire
Bibliothèque C standard (
libc,
-lc)
#include <sys/mman.h>
int mlock(const void addr[.len], size_t len);
int mlock2(const void addr[.len], size_t len, unsigned int flags);
int munlock(const void addr[.len], size_t len);
int mlockall(int flags);
int munlockall(void);
mlock(),
mlock2() et
mlockall() verrouillent tout ou partie
de l'espace d'adressage du processus appelant dans la mémoire physique
pour empêcher cette mémoire d'être évincée
dans l'espace d'échange (swap).
munlock() et
munlockall() ont l'effet inverse, respectivement
déverrouillant une partie ou l'ensemble de l'espace d'adressage du
processus appelant, afin que les pages dans la zone indiquée puissent
à nouveau être évincées dans l'espace
d'échange si le gestionnaire de mémoire du noyau l'exige.
Le verrouillage et le déverrouillage de la mémoire s'effectuent
sur des unités de page entière.
mlock() verrouille les pages sur
len octets à partir de
l'adresse
addr. Toutes les pages qui contiennent une partie de la zone
mémoire indiquée ont la garantie de résider en
mémoire principale quand l'appel réussit ; elles ont la
garantie de rester en mémoire principale jusqu'à leur
déverrouillage.
mlock() verrouille aussi les pages de la plage indiquée sur
len octets à partir de l'adresse
addr. Néanmoins,
l'état des pages contenues dans cette plage après un appel
réussi dépendra de la valeur du paramètre
flags.
L'argument
flags peut être
0 ou la constante
suivante :
- MLOCK_ONFAULT
- Verrouiller les pages actuellement résidentes et
marquer toute la plage pour que le reste des pages non résidentes
se verrouillent quand elles se remplissent d'erreurs de pagination.
Si
flags vaut
0,
mlock2() se comporte exactement comme
mlock().
munlock() déverrouille la mémoire sur
len octets
à partir de l'adresse
addr. Après cet appel, toutes les
pages contenant une partie de la zone mémoire indiquée peuvent
de nouveau être évincées dans l'espace d'échange
par le noyau.
mlockall() verrouille toutes les pages projetées dans l'espace
d'adressage du processus appelant. Cela inclut les pages de code, de
données et de pile, ainsi que les bibliothèques
partagées, les données utilisateur dans le noyau, la
mémoire partagée, et les fichiers projetés en
mémoire. Toutes les pages projetées ont la garantie de
résider en mémoire principale quand l'appel
réussit ; elles ont la garantie de rester en mémoire
principale jusqu'à leur déverrouillage.
L'argument
flags est composé d'un
OU binaire avec les
options suivantes :
- MCL_CURRENT
- Verrouiller toutes les pages actuellement projetées
dans l'espace d'adressage du processus.
- MCL_FUTURE
- Verrouiller toutes les pages qui seront projetées
dans l'espace d'adressage du processus dans le futur. Il peut s'agir, par
exemple, de nouvelles pages nécessitées par la croissance du
tas et de la pile, ou de nouveaux fichiers projetés en
mémoire, ou des zones de mémoire partagée.
-
MCL_ONFAULT (depuis Linux 4.4)
- Utilisé avec MCL_CURRENT, MCL_FUTURE
ou les deux. Marquer toutes les projections actuelles (avec
MCL_CURRENT) ou futures (avec MCL_FUTURE) pour verrouiller
les pages quand elles contiennent des erreurs. Si on l'utilise avec
MCL_CURRENT, toutes les pages présentes sont
verrouillées mais mlockall() ne rencontrera pas d'erreur sur
des pages non présentes. Quand on l'utilise avec MCL_FUTURE,
toutes les projections futures seront marquées pour verrouiller les
pages quand elles rencontreront une erreur, mais elles ne seront pas
remplies par le verrou lors de la création de la projection.
MCL_ONFAULT doit être utilisé avec
MCL_CURRENT, MCL_FUTURE ou les deux.
Si
MCL_FUTURE a été utilisé, un appel système
ultérieur (p.ex.
mmap(2),
sbrk(2),
malloc(3))
risque d'échouer s'il cause un dépassement du nombre d'octets
verrouillés autorisé (voir ci‐dessous). Dans les
mêmes circonstances, la croissance de la pile risque également
d'échouer : le noyau interdira l'augmentation de la pile et
enverra le signal
SIGSEGV au processus.
munlockall() déverrouille toutes les pages projetées dans
l'espace d'adressage du processus appelant.
S'ils réussissent, ces appels système renvoient
0. En cas
d'erreur, ils renvoient
-1,
errno est positionné pour
indiquer l'erreur et les verrouillages de l'espace d'adressage du processus ne
sont pas modifiés.
- EAGAIN
- (mlock(), mlock2() et munlock()) Une
partie (ou l'ensemble) de l'espace d'adressage indiqué n'a pas pu
être verrouillée.
- EINVAL
- (mlock(), mlock2() et munlock()) La
somme de addr+len était inférieure à
addr (l'addition aurait pu conduire à un dépassement
par exemple).
- EINVAL
- (mlock2()) Des flags inconnus étaient
demandés.
- EINVAL
- (mlockall()) Des flags inconnus ont
été indiqués ou MCL_ONFAULT a
été indiqué sans MCL_FUTURE ou
MCL_CURRENT.
- EINVAL
- (Pas sous Linux) addr n'est pas un multiple de la
taille de la page.
- ENOMEM
- (mlock(), mlock2() et munlock()) Une
partie de la zone indiquée ne correspond pas à des pages
projetées dans l'espace d'adressage du processus.
- ENOMEM
- (mlock(), mlock2() et munlock()) Le
verrouillage ou le déverrouillage d'une région ferait
dépasser le nombre maximum de projections permises ayant des
attributs distincts (comme verrouillé contre
déverrouillé). Par exemple, le déverrouillage d'une
plage située au milieu d'une projection actuellement
verrouillée donnerait trois projections : deux
verrouillées de chaque côté et une
déverrouillée au milieu.
- ENOMEM
- (Linux 2.6.9 et plus récents) L'appelant
avait une limite souple RLIMIT_MEMLOCK non nulle, mais a
tenté de verrouiller plus de mémoire que la quantité
autorisée. Cette limite n'est pas imposée si le processus
est privilégié ( CAP_IPC_LOCK).
- ENOMEM
- (Linux 2.4 et précédents) Le processus
appelant a essayé de verrouiller plus de la moitié de la
mémoire vive.
- EPERM
- L'appelant n'est pas privilégié mais a besoin
de droits ( CAP_IPC_LOCK) pour réaliser les
opérations demandées.
- EPERM
- (munlockall()) (Linux 2.6.8 et
précédents) L'appelant n'est pas privilégié (
CAP_IPC_LOCK).
mlock2() est disponible depuis Linux 4.4 ; la prise en
charge de la glibc a été ajoutée depuis la
glibc 2.27.
mlock(),
munlock(),
mlockall() et
munlockall() : POSIX.1-2001, POSIX.1-2008, SVr4.
mlock2() est spécifique à Linux.
Sur les systèmes POSIX où
mlock() et
munlock() sont
disponibles, la constante symbolique
_POSIX_MEMLOCK_RANGE est
définie dans
<unistd.h> et le nombre d'octets par page
peut être déterminé grâce à la constante
PAGESIZE si définie dans
<limits.h> ou en appelant
sysconf(_SC_PAGESIZE).
Sur les systèmes POSIX sur lesquels
mlockall() et
munlockall() sont disponibles, la constante symbolique
_POSIX_MEMLOCK est définie dans
<unistd.h> comme
étant une valeur supérieure à
0. (Consultez aussi
sysconf(3).)
Il y a deux domaines principaux d'applications du verrouillage de pages :
les algorithmes en temps réel et le traitement de données
confidentielles. Les applications temps réel réclament un
comportement temporel déterministe, et la pagination est, avec
l'ordonnancement, une cause majeure de délais imprévus. Ces
algorithmes basculent habituellement sur un ordonnancement
temps‐réel avec
sched_setscheduler(2). Les logiciels de
cryptographie manipulent souvent quelques octets hautement confidentiels,
comme des mots de passe ou des clés privées. À cause de
la pagination, ces données secrètes risquent d'être
transférées sur un support physique où elles pourraient
être lues par un ennemi longtemps après que le logiciel se soit
terminé. Soyez toutefois conscient que le mode suspendu sur les
portables et certains ordinateurs de bureau sauvegardent une copie de la
mémoire sur le disque, quels que soient les verrouillages.
Les processus temps‐réel utilisant
mlockall() pour
éviter les délais dus à la pagination doivent
réserver assez de pages verrouillées pour la pile avant d'entrer
dans la section temporellement critique, afin qu'aucun défaut de page
ne survienne lors d'un appel de fonction. Cela peut être obtenu en
appelant une fonction qui alloue une variable automatique suffisamment grande
(comme un tableau) et écrit dans la mémoire occupée par
ce tableau afin de modifier ces pages de pile. Ainsi, suffisamment de pages
seront projetées pour la pile et pourront être
verrouillées. Les écritures bidon permettent de s'assurer que
même les pages copiées à l'écriture ne causeront
pas de défaut de page dans la section critique.
Les verrouillages de mémoire ne sont pas récupérés
par un enfant lors d'un
fork(2) et sont automatiquement
supprimés (déverrouillés) au cours d'un
execve(2)
ou lorsque le processus se termine. Les paramètres
MCL_FUTURE et
MCL_FUTURE | MCL_ONFAULT de
mlockall() ne sont pas
récupérés par un enfant créé par
fork(2) et sont effacés au cours d'un
execve(2).
Remarquez que
fork(2) préparera l'espace d'adressage pour une
opération copie-en-écriture. La conséquence est que tout
accès en écriture consécutif créera une erreur de
pagination qui, elle-même, peut causer des latences importantes dans un
processus en temps réel. Il est donc crucial de ne pas appeler
fork(2) après des opérations
mlockall() ou
mlock() ; même à partir d'un thread qui tourne en
priorité basse dans un processus dont un thread tourne en
priorité haute.
Le verrouillage de mémoire sur une zone est automatiquement enlevé
si la zone est invalidée par
munmap(2).
Il n'y a pas d'empilement des verrouillages mémoire, ce qui signifie
qu'une page verrouillée plusieurs fois par des appels
mlock(),
mlock2() ou
mlockall() sera libérée en un seul
appel à
munlock() pour la zone mémoire correspondante ou
par un appel à
munlockall(). Les pages qui sont projetées
à plusieurs endroits ou par plusieurs processus restent
verrouillées en mémoire vive tant qu'il y a au moins un
processus ou une zone qui les verrouille.
Si un appel à
mlockall(), qui utilise l'attribut
MCL_FUTURE, est suivi d'un autre appel qui n'indique pas cet attribut,
les changements effectués par l'appel
MCL_FUTURE seront perdus.
L'attribut
MLOCK_ONFAULT de
mlock2() et celui
MCL_ONFAULT
de
mlockall() permettent un verrouillage efficace de la mémoire
pour les applications qui ont à faire à de grandes projections
où seulement une (petite) partie des pages de la projection sont
modifiées. Dans ce cas, le verrouillage de toutes les pages d'une
projection risquerait une sanction lourde de verrouillage de mémoire.
Sous Linux,
mlock(),
mlock2() et
munlock() arrondissent
automatiquement
addr à la frontière de page la plus
proche. Toutefois, la spécification POSIX.1 de
mlock() et de
munlock() permet à l'implémentation d'imposer que
addr soit alignée sur une frontière de page. Les
applications portables devraient s'en assurer.
Le champ
VmLck du fichier
/proc/PID/status spécifique
à Linux indique combien de kilooctets de mémoire le processus
d'identifiant
PID a verrouillé en utilisant
mlock(),
mlock2(),
mlockall() et
MAP_LOCKED de
mmap(2).
Sous Linux 2.6.8 et précédents, un processus doit être
privilégié (
CAP_IPC_LOCK) pour verrouiller de la
mémoire et la limite souple
RLIMIT_MEMLOCK définit le
nombre maximal d'octets que le processus peut verrouiller en mémoire.
Depuis Linux 2.6.9, aucune limite n'est placée sur la quantité de
mémoire pouvant être verrouillée par un processus
privilégié, et la limite souple
RLIMIT_MEMLOCK
définit la quantité maximale de mémoire pouvant
être verrouillée par un processus non privilégié.
Dans Linux 4.8 et antérieurs, un bogue dans le calcul par le noyau de la
mémoire verrouillée pour les processus non
privilégiés (à savoir sans
CAP_IPC_LOCK) faisait
que si la région indiquée par
addr et
len incluait
un verrou existant, les octets déjà verrouillés dans la
région incluante étaient comptés deux fois lors de la
vérification de leur atteinte de limite. Un tel double comptage
calculerait mal une valeur de « mémoire
verrouillée totale » du processus qui a
dépassé la limite
RLIMIT_MEMLOCK, si bien que
mlock() et
mlock2() échoueraient sur des requêtes
qui auraient dû réussir. Ce bogue a été
corrigé dans Linux 4.9.
Dans Linux de la branche 2.4 des noyaux, jusqu'à
Linux 2.4.17 inclus, le paramètre
MCL_FUTURE de
mlockall() était hérité par l'enfant après
un
fork(2) en raison d'un bogue. Cela a été
corrigé dans Linux 2.4.18.
Depuis Linux 2.6.9, si un processus privilégié appelle
mlockall(MCL_FUTURE) et réduit ses privilèges plus tard
(perd la capacité
CAP_IPC_LOCK, par exemple en prenant un UID
effectif non nul), les allocations de mémoires suivantes (p.ex.
mmap(2),
brk(2)) échoueront si la limite
RLIMIT_MEMLOCK est dépassée.
mincore(2),
mmap(2),
setrlimit(2),
shmctl(2),
sysconf(3),
proc(5),
capabilities(7)
La traduction française de cette page de manuel a été
créée par Christophe Blaess
<
https://www.blaess.fr/christophe/>, Stéphan Rafin
<
[email protected]>, Thierry Vignaud
<
[email protected]>, François Micaux, Alain Portal
<
[email protected]>, Jean-Philippe Guérard
<
[email protected]>, Jean-Luc Coulon (f5ibh)
<
[email protected]>, Julien Cristau
<
[email protected]>, Thomas Huriaux <
[email protected]>,
Nicolas François <
[email protected]>, Florentin
Duneau <
[email protected]>, Simon Paillard
<
[email protected]>, Denis Barbier
<
[email protected]>, David Prévot <
[email protected]> et
Jean-Philippe MENGUAL <
[email protected]>
Cette traduction est une documentation libre ; veuillez vous reporter
à la
GNU
General Public License version 3 concernant les conditions de copie
et de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.
Si vous découvrez un bogue dans la traduction de cette page de manuel,
veuillez envoyer un message à
[email protected]