L'interface de programmation Linux montre la disposition d'un espace d'adressage virtuel d'un processus. Chaque région du diagramme est-elle un segment?
De Comprendre le noyau Linux ,
est-il exact que ce qui suit signifie que l'unité de segmentation dans MMU mappe les segments et les décalages au sein des segments dans l'adresse de mémoire virtuelle, et l'unité de pagination mappe ensuite l'adresse de mémoire virtuelle à l'adresse de mémoire physique?
L'unité de gestion de la mémoire (MMU) transforme une adresse logique en une adresse linéaire au moyen d'un circuit matériel appelé unité de segmentation; par la suite, un deuxième circuit matériel appelé unité de radiomessagerie transforme l'adresse linéaire en une adresse physique (voir la figure 2-1).
Alors pourquoi dit-on que Linux n'utilise pas la segmentation mais seulement la pagination?
La segmentation a été incluse dans les microprocesseurs 80x86 pour encourager les programmeurs à diviser leurs applications en entités logiquement liées, telles que des sous-programmes ou des zones de données globales et locales. Cependant, Linux utilise la segmentation de manière très limitée. En fait, la segmentation et la pagination sont quelque peu redondantes, car les deux peuvent être utilisées pour séparer les espaces d'adressage physiques des processus: la segmentation peut affecter un espace d'adressage linéaire différent à chaque processus, tandis que la pagination peut mapper le même espace d'adressage linéaire dans différents espaces d'adressage physiques . Linux préfère la pagination à la segmentation pour les raisons suivantes:
• La gestion de la mémoire est plus simple lorsque tous les processus utilisent les mêmes valeurs de registre de segment, c'est-à-dire lorsqu'ils partagent le même ensemble d'adresses linéaires.
• L'un des objectifs de conception de Linux est la portabilité vers un large éventail d'architectures; Les architectures RISC, en particulier, ont un support limité pour la segmentation.
La version 2.6 de Linux utilise la segmentation uniquement lorsqu'elle est requise par l'architecture 80x86.
Réponses:
L'architecture x86-64 n'utilise pas la segmentation en mode long (mode 64 bits).
Quatre des registres de segments: CS, SS, DS et ES sont forcés à 0 et la limite à 2 ^ 64.
https://en.wikipedia.org/wiki/X86_memory_segmentation#Later_developments
Il n'est plus possible pour l'OS de limiter les plages des "adresses linéaires" disponibles. Par conséquent, il ne peut pas utiliser la segmentation pour la protection de la mémoire; il doit reposer entièrement sur la pagination.
Ne vous inquiétez pas des détails des processeurs x86 qui ne s'appliqueraient qu'en cas d'exécution dans les modes 32 bits hérités. Linux pour les modes 32 bits n'est pas autant utilisé. Elle peut même être considérée "dans un état de négligence bénigne pendant plusieurs années". Voir Prise en charge x86 32 bits dans Fedora [LWN.net, 2017].
(Il arrive que Linux 32 bits n'utilise pas non plus la segmentation. Mais vous n'avez pas besoin de me faire confiance à ce sujet, vous pouvez simplement l'ignorer :-).
la source
mov eax, [fs:rdi + 16]
). Le noyau utilise GS (aprèsswapgs
) pour trouver la pile de noyau du processus actuel dans lesyscall
point d'entrée. Mais oui, la segmentation n'est pas utilisée dans le cadre du principal mécanisme de gestion de la mémoire / protection de la mémoire du système d'exploitation.Comme le x86 a des segments, il n'est pas possible de ne pas les utiliser. Mais les adresses de base
cs
(segment de code) etds
(segment de données) sont définies sur zéro, de sorte que la segmentation n'est pas vraiment utilisée. Une exception est les données locales de thread, l'un des registres de segments normalement inutilisés pointe vers les données locales de thread. Mais c'est principalement pour éviter de réserver l'un des registres à usage général pour cette tâche.Cela ne dit pas que Linux n'utilise pas de segmentation sur le x86, car cela ne serait pas possible. Vous avez déjà souligné une partie, Linux utilise la segmentation de manière très limitée . La deuxième partie est que Linux utilise la segmentation uniquement lorsque requis par l'architecture 80x86
Vous avez déjà cité les raisons, la pagination est plus facile et plus portable.
la source
Non.
Alors que le système de segmentation (en mode protégé 32 bits sur un x86) est conçu pour prendre en charge des segments de code, de données et de pile distincts, en pratique, tous les segments sont définis sur la même zone de mémoire. Autrement dit, ils commencent à 0 et se terminent à la fin de la mémoire (*) . Cela rend les adresses logiques et les adresses linéaires égales.
C'est ce qu'on appelle un modèle de mémoire "plat", et est un peu plus simple que le modèle où vous avez des segments distincts, puis des pointeurs en leur sein. En particulier, un modèle segmenté nécessite des pointeurs plus longs, car le sélecteur de segment doit être inclus en plus du pointeur de décalage. (Sélecteur de segment 16 bits + décalage 32 bits pour un total de pointeur de 48 bits; contre seulement un pointeur plat de 32 bits.)
Le mode long 64 bits ne prend pas vraiment en charge la segmentation autre que le modèle de mémoire plate.
Si vous deviez programmer en mode protégé 16 bits sur le 286, vous auriez plus besoin de segments, car l'espace d'adressage est de 24 bits mais les pointeurs ne sont que de 16 bits.
(* Notez que je ne me souviens pas comment Linux 32 bits gère la séparation noyau / espace utilisateur. La segmentation permettrait cela en définissant les limites des segments de l'espace utilisateur afin qu'ils n'incluent pas l'espace du noyau. La pagination le permet car elle fournit un niveau de protection par page.)
Le x86 a toujours les segments et vous ne pouvez pas les désactiver. Ils sont juste utilisés le moins possible. En mode protégé 32 bits, les segments doivent être configurés pour le modèle plat, et même en mode 64 bits, ils existent toujours en quelque sorte.
la source
wrfsbase
est illégal en mode protégé / compat, uniquement en mode long, donc sur un espace utilisateur de noyau 32 bits ne pouvait pas mettre FS base haut.Linux x86 / 32 n'utilise pas la segmentation dans le sens où il initialise tous les segments à la même adresse linéaire et limite. L'architecture x86 nécessite que le programme ait des segments: le code ne peut être exécuté qu'à partir du segment de code, la pile ne peut être localisée que dans le segment de pile, les données ne peuvent être manipulées que dans l'un des segments de données. Linux contourne ce mécanisme en définissant tous les segments de la même manière (avec des exceptions que votre livre ne mentionne pas de toute façon), afin que la même adresse logique soit valide dans n'importe quel segment. Cela équivaut en fait à ne pas avoir de segments du tout.
la source
Ce sont 2 utilisations presque totalement différentes du mot "segment"
Les usages ont une origine commune: si vous avez utilisé un modèle de mémoire segmentée ( en particulier sans mémoire paginée virtuel), vous pouvez avoir les données et les adresses du SRS être relatif à la base de segment DS, la pile par rapport à la base de SS et le code par rapport à la Adresse de base CS.
Ainsi, plusieurs programmes différents pourraient être chargés vers différentes adresses linéaires, ou même déplacés après le démarrage, sans modifier les décalages 16 ou 32 bits par rapport aux bases de segments.
Mais alors vous devez savoir à quel segment un pointeur est relatif, vous avez donc des "pointeurs éloignés" et ainsi de suite. (Les programmes x86 16 bits réels n'avaient souvent pas besoin d'accéder à leur code en tant que données, ils pouvaient donc utiliser un segment de code 64k quelque part, et peut-être un autre bloc 64k avec DS = SS, la pile passant de décalages élevés et les données à ou un minuscule modèle de code avec toutes les bases de segments égales).
Comment la segmentation x86 interagit avec la pagination
Le mappage d'adresses en mode 32/64 bits est:
les tables de pages (mises en cache par TLB) sont mappées linéairement à 32 (mode hérité), 36 (hérité PAE) ou 52-bit (x86-64) adresse physique. ( /programming/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the ).
Cette étape est facultative: la pagination doit être activée lors du démarrage en définissant un bit dans un registre de contrôle. Sans pagination, les adresses linéaires sont des adresses physiques.
Notez que la segmentation ne vous permet pas d'utiliser plus de 32 ou 64 bits d'espace d'adressage virtuel dans un seul processus (ou thread) , car l'espace d'adressage plat (linéaire) dans lequel tout est mappé n'a que le même nombre de bits que les décalages eux-mêmes. (Ce n'était pas le cas pour le x86 16 bits, où la segmentation était en fait utile pour utiliser plus de 64 Ko de mémoire avec principalement des registres et des décalages 16 bits.)
Le CPU met en cache les descripteurs de segment chargés à partir du GDT (ou LDT), y compris la base du segment. Lorsque vous déréférencer un pointeur, selon le registre dans lequel il se trouve, il est par défaut DS ou SS comme segment. La valeur de registre (pointeur) est traitée comme un décalage par rapport à la base du segment.
Étant donné que la base de segment est normalement nulle, les processeurs font un cas spécial. Ou d'un autre point de vue, si vous n'avez une base de segment non-zéro, les charges ont une latence supplémentaire parce que le « spécial » (normal) cas de contournement d' ajouter l'adresse de base ne s'applique pas.
Comment Linux configure les registres de segments x86:
La base et la limite de CS / DS / ES / SS sont toutes 0 / -1 en mode 32 et 64 bits. C'est ce qu'on appelle un modèle de mémoire plate car tous les pointeurs pointent vers le même espace d'adressage.
(Les architectes du processeur AMD ont neutralisé la segmentation en appliquant un modèle de mémoire plate pour le mode 64 bits parce que les systèmes d'exploitation traditionnels ne l'utilisaient pas de toute façon, à l'exception de la protection sans exécutable qui était fournie d'une bien meilleure manière en paginant avec le PAE ou x86- Format de table de 64 pages.)
TLS (Thread Local Storage): FS et GS ne sont pas fixes à base = 0 en mode long. (Ils étaient nouveaux avec 386, et n'étaient utilisés implicitement par aucune instruction, pas même les
rep
instructions -string qui utilisent ES). x86-64 Linux définit l'adresse de base FS pour chaque thread sur l'adresse du bloc TLS.Par exemple,
mov eax, [fs: 16]
charge une valeur 32 bits de 16 octets dans le bloc TLS pour ce thread.le descripteur de segment CS choisit le mode dans lequel se trouve la CPU (mode protégé 16/32/64 bits / mode long). Linux utilise une seule entrée GDT pour tous les processus de l'espace utilisateur 64 bits et une autre entrée GDT pour tous les processus de l'espace utilisateur 32 bits. (Pour que le CPU fonctionne correctement, DS / ES doit également être défini sur des entrées valides, tout comme SS). Il choisit également le niveau de privilège (noyau (anneau 0) par rapport à l'utilisateur (anneau 3)), donc même lorsqu'il revient à l'espace utilisateur 64 bits, le noyau doit toujours s'arranger pour que CS change, en utilisant
iret
ou à lasysret
place d'un normal instruction jump ou ret.Dans x86-64, le
syscall
point d'entrée utiliseswapgs
pour basculer GS de GS de l'espace utilisateur vers le noyau, qu'il utilise pour trouver la pile de noyau pour ce thread. (Un cas spécialisé de stockage local par thread). L'syscall
instruction ne modifie pas le pointeur de pile pour pointer vers la pile du noyau; il pointe toujours vers la pile utilisateur lorsque le noyau atteint le point d'entrée 1 .DS / ES / SS doivent également être définis sur des descripteurs de segment valides pour que le processeur fonctionne en mode protégé / mode long, même si la base / limite de ces descripteurs est ignorée en mode long.
Donc, fondamentalement, la segmentation x86 est utilisée pour TLS, et pour les tâches osdev x86 obligatoires que le matériel vous oblige à faire.
Note de bas de page 1: histoire amusante: il existe des archives de listes de diffusion de messages entre les développeurs du noyau et les architectes AMD datant de quelques années avant la sortie du silicium AMD64, ce qui a entraîné des modifications de la conception de
syscall
sorte qu'il était utilisable. Voir les liens dans cette réponse pour plus de détails.la source