On me demande de décrire les étapes impliquées dans un changement de contexte (1) entre deux processus différents et (2) entre deux threads différents dans le même processus.
Est-ce trop général ou qu'apporteriez-vous pour expliquer le processus plus clairement?
Il est beaucoup plus facile d'expliquer ceux-ci dans l'ordre inverse, car un commutateur de processus implique toujours un commutateur de thread.
Un changement de contexte de thread typique sur un processeur monocœur se produit comme ceci:
Tous les changements de contexte sont déclenchés par une "interruption". Il peut s'agir d'une interruption matérielle réelle qui exécute un pilote (par exemple, à partir d'une carte réseau, d'un clavier, de la gestion de la mémoire ou du matériel du minuteur) ou d'un appel logiciel (appel système) qui exécute une séquence d'appels de type interruption matérielle pour entrer dans l'OS. Dans le cas d'une interruption du pilote, le système d'exploitation fournit un point d'entrée que le pilote peut appeler au lieu d'effectuer le retour d'interruption direct `` normal '' et permet ainsi à un pilote de quitter via le planificateur du système d'exploitation s'il a besoin du système d'exploitation pour définir un thread prêt, (par exemple, il a signalé un sémaphore).
Les systèmes non triviaux devront initier un changement de niveau de protection du matériel pour entrer dans un état du noyau afin que le code/les données du noyau, etc. soient accessibles.
L'état de base du thread interrompu doit être enregistré. Sur un système embarqué simple, cela pourrait simplement pousser tous les registres sur la pile de threads et enregistrer le pointeur de pile dans son bloc de contrôle des threads (TCB).
De nombreux systèmes passent à une pile dédiée au système d'exploitation à ce stade afin que la majeure partie des exigences de la pile interne au système d'exploitation ne soient pas infligées à la pile de chaque thread.
Il peut être nécessaire de marquer la position de la pile de threads où le changement d'état d'interruption s'est produit pour permettre les interruptions imbriquées.
L'appel du pilote/système s'exécute et peut modifier l'ensemble de threads prêts en ajoutant/supprimant des TCB des files d'attente internes pour les différentes priorités de thread, par exemple. le pilote de carte réseau peut avoir défini un événement ou signalé un sémaphore qu'un autre thread attendait, de sorte que le thread sera ajouté à l'ensemble prêt, ou un thread en cours d'exécution peut avoir appelé sleep () et ainsi choisi de se retirer de l'ensemble prêt .
L'algorithme du planificateur du système d'exploitation est exécuté pour décider quel thread exécuter ensuite, généralement le thread prêt de priorité la plus élevée qui se trouve à l'avant de la file d'attente pour cette priorité. Si le thread suivant doit appartenir à un processus différent du thread précédemment exécuté, des éléments supplémentaires sont nécessaires ici (voir plus loin).
Le pointeur de pile enregistré à partir du TCB pour ce thread est récupéré et chargé dans le pointeur de pile matériel.
L'état de base du thread sélectionné est restauré. Sur mon système simple, les registres seraient extraits de la pile du thread sélectionné. Les systèmes plus complexes devront gérer un retour à la protection au niveau de l'utilisateur.
Un retour d'interruption est effectué, transférant ainsi l'exécution au thread sélectionné.
Dans le cas d'un processeur multicœur, les choses sont plus complexes. Le planificateur peut décider qu'un thread qui s'exécute actuellement sur un autre noyau peut devoir être arrêté et remplacé par un thread qui vient d'être prêt. Il peut le faire en utilisant son pilote interprocesseur pour interrompre matériellement le noyau exécutant le thread qui doit être arrêté. La complexité de cette opération, en plus de toutes les autres choses, est une bonne raison pour éviter d'écrire des noyaux de système d'exploitation :)
Un changement de contexte de processus typique se produit comme ceci:
Les changements de contexte de processus sont initiés par un changement de contexte de thread, donc tout ce qui précède, 1-9, devra se produire.
À l'étape 5 ci-dessus, le planificateur décide d'exécuter un thread appartenant à un processus différent de celui qui possédait le thread précédemment exécuté.
Le matériel de gestion de la mémoire doit être chargé avec l'espace d'adressage pour le nouveau processus, c'est-à-dire quels que soient les sélecteurs/segments/drapeaux/tout ce qui permet au thread/s du nouveau processus d'accéder à sa mémoire.
Le contexte de tout matériel FPU doit être enregistré/restauré à partir du PCB.
Il peut y avoir un autre matériel dédié au processus qui doit être enregistré/restauré.
Sur tout système réel, les mécanismes dépendent de l'architecture et ce qui précède est un guide approximatif et incomplet des implications de chaque changement de contexte. Il y a d'autres frais généraux générés par un commutateur de processus qui ne font pas strictement partie du commutateur - il peut y avoir des vidages de cache supplémentaires et des défauts de page après un commutateur de processus car une partie de sa mémoire peut avoir été paginée en faveur des pages appartenant au processus propriétaire du thread qui s'exécutait auparavant.
J'espère que je pourrai fournir une image plus détaillée/claire.
Tout d'abord, le système d'exploitation planifie les threads, pas les processus, car les threads sont les seules unités exécutables du système. Le commutateur de processus est juste un commutateur de threads où les threads appartiennent à différents processus, et donc la procédure est fondamentalement la même.
Le planificateur est appelé. Il existe trois scénarios de base dans lesquels cela peut se produire:
Dans tous les cas, pour pouvoir effectuer un changement de contexte, le contrôle doit être passé au noyau. Dans le cas de commutations involontaires, ceci est effectué par une interruption. Dans le cas des changements de contexte volontaires (et semi-volontaires), le contrôle est transmis au noyau via un appel système.
Dans les deux cas, l'entrée du noyau est assistée par CPU. Le processeur effectue une vérification des autorisations, enregistre le pointeur d'instruction (afin que l'exécution puisse être poursuivie à partir de la bonne instruction plus tard), passe du mode utilisateur utilisateur au mode noyau, active la pile du noyau (spécifique au thread actuel) et passe à un paramètre prédéfini et point bien connu dans le code du noyau.
La première action effectuée par le noyau est la sauvegarde du contenu des registres CPU, qu'il doit utiliser à ses propres fins. Habituellement, le noyau utilise uniquement des registres CPU à usage général et les enregistre en les poussant sur la pile.
Le noyau gère ensuite une requête principale si nécessaire. Il peut gérer une interruption, préparer une demande de lecture de fichier, recharger une minuterie, etc.
À un certain moment pendant la gestion des demandes, le noyau effectue une action qui affecte l'état du thread actuel (a décidé qu'il n'y a actuellement rien à faire dans ce thread car il attend quelque chose) ou celui d'un autre thread (ou threads) (un thread est devenu prêt à s'exécuter car un événement qu'il attendait s'est produit - un mutex a été libéré, par exemple).
Le noyau appelle le planificateur. L'ordonnanceur doit prendre deux décisions.
Une fois les deux décisions prises, le planificateur effectue le changement de contexte en utilisant le TCB du thread en cours ainsi que celui du thread qui doit être exécuté ensuite.
Un changement de contexte lui-même comprend trois étapes principales.
À ce stade, le noyau vérifie si les threads planifiés et non planifiés appartiennent au même processus. Sinon (commutateur "processus" plutôt que "thread"), le noyau réinitialise l'espace d'adressage actuel en pointant le MMU (unité de gestion de la mémoire) vers la table des pages du processus planifié. Le TLB (Translation Lookaside Buffer), qui est un cache contenant les récentes traductions d'adresses virtuelles en adresses physiques, est également vidé pour éviter une traduction d'adresse erronée. Notez que c'est la seule étape de l'ensemble des actions de changement de contexte qui se soucie des processus!
Le noyau prépare le stockage local de threads pour le thread planifié. Par exemple, il mappe les pages de mémoire respectives aux adresses spécifiées. Comme autre exemple, sur la plate-forme IA-32, une approche courante consiste à charger un nouveau segment qui pointe vers les données TLS du thread entrant.
Le noyau charge l'adresse de pile de noyau du thread actuel dans le CPU. Après cela, chaque invocation du noyau utilisera cette pile de noyau au lieu de la pile de noyau du thread non planifié.
Une autre étape qui peut être effectuée par le noyau est la reprogrammation du temporisateur système. Lorsque le minuteur se déclenche, le contrôle est retourné au noyau. La période de temps entre le changement de contexte et le déclenchement de la minuterie est appelée un quantum de temps et indique la durée d'exécution du thread actuel à ce moment. Ceci est connu sous le nom de planification préventive.
Les noyaux collectent généralement des statistiques lors des changements de contexte pour améliorer la planification ainsi que pour montrer aux administrateurs système et aux utilisateurs ce qui se passe dans le système. Ces statistiques peuvent inclure des informations telles que le temps processeur consommé par le thread, le nombre de fois qu'il a été planifié, le nombre de fois que son quantum a expiré, la fréquence à laquelle les changements de contexte se produisent dans le système, etc.
Le changement de contexte peut être considéré comme prêt à ce stade, et le noyau continue les actions système précédemment interrompues. Par exemple, si le thread a tenté d'acquérir un mutex lors d'un appel système et que le mutex est désormais libre, le noyau peut terminer l'opération interrompue.
À un moment donné, le thread termine ses activités système et souhaite revenir en mode utilisateur pour exécuter du code non système. Le noyau apparaît à partir du contenu de la pile du noyau des registres à usage général qui a été précédemment enregistré lors de l'entrée du noyau et oblige le CPU à exécuter une instruction spéciale pour revenir en mode utilisateur.
Le CPU capture les valeurs du pointeur d'instruction et du pointeur de pile, qui étaient précédemment enregistrés, le mode noyau a été entré et les restaure. À ce stade, la pile du mode utilisateur du thread est également activée et le mode noyau est quitté (cela interdit l'utilisation d'instructions système spéciales).
Enfin, le processeur continue son exécution à partir du point où se trouvait le thread lorsqu'il n'a pas été planifié. Si cela s'est produit pendant un clal système, le thread procédera à partir du point où l'appel système a été appelé, en capturant et en gérant son résultat. En cas de préemption par interruption, le thread continuera son exécution comme si de rien n'était.
Quelques notes de synthèse:
Le noyau planifie et exécute uniquement les threads, pas les processus - les changements de contexte ont lieu entre les threads.
La procédure de basculement vers le contexte d'un thread d'un autre processus est essentiellement la même dans un changement de contexte entre des threads appartenant au même processus. Une seule étape supplémentaire est requise: changer les tables de pages (et vider le TLB).
Le contexte du thread est stocké soit dans la pile du noyau, soit dans le TCB (pas PCB!).
La commutation de contexte est une opération coûteuse - elle a un coût direct significatif en termes de performances, et le coût indirect provoqué par la pollution du cache (et le vidage TLB si le changement s'est produit entre les processus) est encore plus élevé.
(Source: changement de contexte )
1.Enregistrez le contexte du processus en cours d'exécution sur la CPU. Mettez à jour le bloc de contrôle de processus et d'autres champs importants.
2. Déplacez le bloc de contrôle de processus du processus ci-dessus dans la file d'attente appropriée telle que la file d'attente prête, la file d'attente d'E/S, etc.
3.Sélectionnez un nouveau processus d'exécution.
4.Mettez à jour le bloc de contrôle de processus du processus sélectionné. Cela inclut la mise à jour de l'état du processus en cours d'exécution.
5.Mettez à jour les structures de données de gestion de la mémoire selon les besoins.
6. Restaurez le contexte du processus qui s'exécutait précédemment lorsqu'il est à nouveau chargé sur le processeur. Cela se fait en chargeant les valeurs précédentes du bloc de contrôle de processus et des registres.