Quelle est la différence entre les threads (et les processus) en mode kernel et ceux en mode utilisateur?

ma question:

1) Dans le système d’exploitation moderne de livre, il est indiqué que les threads et les processus peuvent être en mode kernel ou en mode utilisateur, mais ne dit pas clairement quelle est la différence entre eux.

2) Pourquoi le commutateur pour les threads et les processus en mode kernel coûte plus cher que le commutateur pour les threads et les processus en mode utilisateur?

3) maintenant, j’apprends Linux, je veux savoir comment créer des threads et des processus en mode kernel et en mode utilisateur, respectivement, dans le système LINUX?

4) Dans le système d’exploitation moderne de book, il est dit qu’il est possible que le processus soit en mode utilisateur, mais que les threads créés dans le processus en mode utilisateur puissent être en mode kernel. Comment cela serait-il possible?

Les threads en mode utilisateur sont planifiés en mode utilisateur par quelque chose dans le processus, et le processus lui-même est la seule chose gérée par le planificateur de kernel.

Cela signifie que votre processus reçoit une certaine quantité de grognement de la part du processeur et que vous devez le partager entre tous vos threads en mode utilisateur.

Cas simple, vous avez deux processus, l’un avec un seul thread et l’autre avec cent threads.

Avec une politique de planification du kernel simpliste, le thread dans le processus à un seul thread obtient 50% du processeur et chaque thread dans le processus à cent threads en reçoit 0,5%.

Avec les threads en mode kernel, le kernel lui-même gère vos threads et les planifie indépendamment. En utilisant le même ordonnanceur simpliste, chaque thread obtiendrait un toucher inférieur à 1% du grunt du processeur (101 threads pour partager le 100% du processeur).

En ce qui concerne les raisons pour lesquelles le changement de mode kernel est plus coûteux, cela tient probablement au fait que vous devez passer en mode kernel pour le faire. Les threads en mode utilisateur font tout leur travail en mode utilisateur (évidemment), il n’y a donc aucune implication du kernel dans une opération de commutation de thread.

Sous Linux, vous créez des threads (et des processus) avec l’appel de clone , similaire à fork mais avec un contrôle beaucoup plus précis sur les choses.

Votre dernier point est un peu obtus. Je ne peux pas en être sûr, mais il est probablement question du mode utilisateur et du mode kernel, en ce sens que l’un pourrait exécuter le code utilisateur et l’autre effectuer un appel système dans le kernel (ce qui nécessite de passer en mode kernel ou superviseur).

Ce n’est pas la même chose que la distinction lorsque vous parlez de la prise en charge des threads (prise en charge du mode thread par l’utilisateur ou par le kernel). Sans avoir une copie du livre à scope de main, je ne saurais dire définitivement, mais ce serait ma meilleure hypothèse.

Certains problèmes de terminologie sont plus dus à un accident historique qu’aucun autre problème.

“Fil” fait généralement référence à un fil de contrôle au sein d’un processus et peut (dans ce cas) signifier “une tâche avec sa propre stack, mais qui partage l’access à tout ce qui ne se trouve pas sur cette stack avec d’autres threads du même domaine de protection. “.

“Processus” a tendance à faire référence à un “domaine de protection” autonome qui peut (et a dans ce cas) la possibilité de contenir plusieurs threads. Étant donné deux processus P1 et P2 , le seul moyen pour que P1 affecte P2 (ou vice versa) consiste à utiliser un “canal de communication” défini particulier, tel qu’un fichier, un canal ou un socket; via des signaux “interprocessus” tels que des signaux Unix / Linux; etc.

Comme les threads n’ont pas ce type de barrière entre eux, un thread peut facilement interférer (corrompre les données utilisées par) un autre thread.

Tout cela est indépendant de l’utilisateur et du kernel , à une exception près: dans “le kernel”, notez qu’il existe une hypothèse implicite selon laquelle il n’y a qu’un seul kernel: vous avez access à tout moment à l’état de la machine et à tous les privilèges. faire n’importe quoi. Par conséquent, vous pouvez délibérément (ou dans certains cas accidentellement) ignorer ou désactiver la protection matérielle et manipuler des données “appartenant à” quelqu’un d’autre.

Cela couvre principalement plusieurs éléments potentiellement confus au premier sortingmestre. En ce qui concerne la question 2, la réponse à la question posée est “ce n’est pas”. En général, étant donné que les threads n’impliquent pas (autant) de protection, il est meilleur marché de passer d’un thread à un autre: vous n’avez pas à dire au matériel (de quelque manière que ce soit) qu’il ne devrait plus permettre différents types d’access, car les threads T1 et T2 ont “le même” access. En basculant entre les processus, cependant, comme avec P1 et P2 , vous “franchissez une barrière de protection”, ce qui entraîne certaines pénalités (la pénalité réelle varie considérablement en fonction du matériel et, dans une certaine mesure, des compétences des auteurs de systèmes d’exploitation).

Il est également intéressant de noter que le passage du mode utilisateur au mode kernel, et inversement, correspond également à un domaine de protection, ce qui a également un coût.

Sous Linux, les processus utilisateur disposent de plusieurs moyens pour créer le nombre de threads, y compris les “threads POSIX” (pthreads) et l’appel de clone (les détails relatifs au clone , extrêmement flexible, dépassent le cadre de cette réponse). . Si vous voulez écrire du code portable, vous devriez probablement vous en tenir à des pthreads.

Dans le kernel Linux, les threads sont réalisés de manière complètement différente, et vous aurez besoin de la documentation du kernel Linux.

Je ne peux pas répondre correctement à la question 4 car je n’ai pas le livre et je ne suis pas sûr de ce à quoi ils font référence ici. J’imagine qu’ils veulent dire que chaque fois qu’un processus-ou-thread utilisateur passe un “appel système” (demande de service auprès du système d’exploitation), cela franchit la barrière de protection utilisateur / kernel, et il appartient ensuite au kernel de vérifier que le code utilisateur dispose des privilèges appropriés pour cette opération, puis pour effectuer cette opération. La partie du kernel qui fait cela fonctionne avec des protections au niveau du kernel et doit donc être plus prudente.

Certains matériels (pour la plupart obsolètes de nos jours) ont (ou avaient) plus que deux niveaux de protection fournie par le matériel. Sur ces systèmes, ce sont les “processus utilisateur” qui ont le moins de privilèges directs, mais au-dessus de ceux-ci, on trouve le “mode exécutif”, le “mode système” et le mode (le plus privilégié) “kernel” ou “kernel”. Celles-ci visaient à réduire le coût de franchissement des différentes barrières de protection. Le code exécuté dans “executive” n’avait pas un access complet à tout ce qui se trouvait dans la machine. Par conséquent, il pouvait simplement supposer que l’adresse fournie par l’utilisateur était valide et essayer de l’utiliser. Si cette adresse était en fait invalide, l’exception passerait au niveau immédiatement supérieur. Avec seulement deux niveaux – “utilisateur”, sans privilège; et “kernel”, complètement privilégié – le code du kernel doit être écrit très soigneusement. Cependant, il est possible de fournir des “machines virtuelles” à faible coût ces jours-ci, ce qui rend quasiment obsolète la nécessité de disposer de plusieurs niveaux de protection matérielle. On écrit simplement un vrai kernel, puis on le laisse exécuter d’autres choses dans ce qu’ils “pensent” est le “mode kernel”. C’est ce que font VMware et les autres systèmes «hyperviseurs».