Réapprentissage du système d'exploitation ---- 17 | Processus et threads: où est la surcharge des processus plus importante que les threads?

table des matières

 

Un, processus et fil

2. Allocation des ressources

Trois processus léger

Quatre, partage du temps et planification

Cinq, allouer des fragments de temps

Six, le statut des processus et des threads

Seven, conception de processus et de filetage

7.1 Représentation des processus et des threads

7.2 Plan d'isolement

7.3 Changement de processus (filetage)

7.4 Traitement multicœur

7.5 API pour la création de processus (threads)

Huit, le problème


Un, processus et fil

Le processus, comme son nom l'indique, est l'application en cours d'exécution, qui est la copie d'exécution du logiciel. Le fil est un processus léger.

Le processus est l'unité de base pour l'allocation des ressources. Pendant longtemps, les threads sont appelés Light Weighted Process (Light Weighted Process), qui est l'unité de base de l'exécution du programme.

À l'époque où les ordinateurs venaient de naître, les programmeurs prenaient une carte mémoire flash avec un programme écrit et l'inséraient dans la machine, puis l'énergie électrique poussait la puce à calculer. La puce lit une instruction de la carte mémoire flash à chaque fois, puis lit l'instruction suivante après l'exécution. Une fois toutes les instructions de la mémoire flash exécutées, l'ordinateur s'arrête.

 

Ce modèle à tâche unique s'appelait à l'époque Job . À cette époque, les ordinateurs étaient conçus pour pouvoir traiter plusieurs tâches.

Après l'apparition de l'interface graphique, les gens ont commencé à utiliser des ordinateurs pour le bureau, les achats, le chat, les jeux, etc. Par conséquent, le programme exécuté par une machine pouvait être interrompu à tout moment. Les gens ont donc pensé à concevoir des processus et des threads pour résoudre ce problème.

Chaque application, comme un jeu, est un processus après exécution. Cependant, le jeu nécessite un rendu graphique, des opérations réseau et utilisateur. Ces comportements ne peuvent pas se bloquer et doivent être exécutés en même temps, ils sont donc conçus comme des threads .

2. Allocation des ressources

Pour concevoir des processus et des threads, le système d'exploitation doit réfléchir à l'allocation des ressources. Les trois ressources les plus importantes sont:

  1. Ressources informatiques (CPU)
  2. Ressources de mémoire
  3. Ressource de fichier

Au début de la conception du système d'exploitation, il n'y avait pas de threads. Les trois types de ressources étaient alloués aux processus . Plusieurs processus étaient exécutés en alternance grâce à la technologie de partage de temps et les processus communiqués par la technologie des pipelines .

Mais ce faisant, les concepteurs constatent que les utilisateurs (programmeurs) doivent souvent ouvrir plusieurs processus pour une application, car les applications ont toujours beaucoup de choses à faire en parallèle.

Le parallélisme ne signifie pas une simultanéité absolue , mais la nécessité de faire en sorte que ces choses semblent se produire en même temps, comme le rendu graphique et la réponse aux entrées de l'utilisateur.

Les concepteurs ont donc pensé que dans le cadre du processus, une sorte d'unité d'exécution du programme était nécessaire et que seules les ressources du processeur étaient allouées, à savoir le thread .

Trois processus léger

Une fois le thread conçu, on l'appelle un processus léger car il ne lui est alloué que des ressources informatiques (CPU). La façon d'être alloué consiste à planifier les threads par le système d'exploitation. Une fois que le système d'exploitation a créé un processus, le programme d'entrée du processus est affecté à un thread principal pour l'exécution, il semble donc que le système d'exploitation planifie le processus, mais il s'agit en fait d'un thread dans le processus de planification.

Ce genre de thread qui est directement planifié par le système d'exploitation, nous devenons également un thread au niveau du noyau. De plus, dans certains langages de programmation ou applications, les utilisateurs (programmeurs) eux-mêmes implémentent également des threads. Cela équivaut à la planification du thread principal par le système d'exploitation et le programme du thread principal utilise des algorithmes pour implémenter des sous-threads. Cette situation est appelée threads de niveau utilisateur. L'API PThread de Linux est un thread au niveau de l'utilisateur et l'API KThread est un thread au niveau du noyau.

Quatre, partage du temps et planification

Parce que généralement le nombre de cœurs de processeur dans une machine est petit (de quelques à des dizaines) et que le nombre de processus et de threads est grand (de dizaines à centaines ou même plus), vous pouvez le comparer à moins de moteurs et plus de machines, donc les processus sont Le système d'exploitation ne peut être mis en file d'attente pour exécution qu'un par un. Chaque processus recevra une tranche de temps allouée par le système d'exploitation lors de son exécution. Si ce temps est dépassé, ce sera le prochain processus (thread) à exécuter. Encore une fois, les systèmes d'exploitation modernes planifient directement les threads, pas les processus.

Cinq, allouer des fragments de temps

Comme le montre la figure ci-dessous, le processus 1 nécessite 2 tranches de temps, le processus 2 n'a qu'une seule tranche de temps et le processus 3 nécessite 3 tranches de temps. Par conséquent, lorsque le processus 1 est exécuté à mi-chemin, il sera d'abord suspendu, puis le processus 2 commencera à s'exécuter; le processus 2 peut être exécuté en même temps, puis le processus 3 commencera à s'exécuter, mais le processus 3 ne pourra pas être exécuté à un moment donné. Après l'exécution d'un segment de temps, le processus 1 commence à s'exécuter, il se répète comme ceci. C'est la technologie de partage de temps.

L'image suivante est plus intuitive: le processus P1 exécute d'abord un segment de temps, puis le processus P2 commence à exécuter un segment de temps, puis le processus P3, puis le processus P4 ...

Notez que les deux images ci-dessus sont illustrées en unités de processus. Si vous passez aux threads, le système d'exploitation les gère toujours de cette manière.

Six, le statut des processus et des threads

Un processus (thread) passe par les trois états suivants:

Une fois que le processus (thread) est créé, il commence à être mis en file d'attente, à ce moment-là, il sera à l'état "Prêt" (Prêt);

Quand c'est au tour du processus (thread) de s'exécuter, il deviendra "Running";

Lorsqu'un processus (thread) manque de tranches de temps allouées par le système d'exploitation, il revient à l'état "Prêt".

J'ai toujours utilisé des processus (threads) ici parce que l'ancien système d'exploitation planifie les processus sans threads; les systèmes d'exploitation modernes planifient les threads.

Parfois, un processus (thread) attend que le disque lise les données ou attend que l'imprimante réponde. À ce stade, le processus lui-même entre dans l'état "Bloquer".

Étant donné que la réponse de l'ordinateur ne peut pas être donnée immédiatement à ce moment, il doit attendre que le traitement du disque et de l'imprimante soit terminé, et avertir le processeur par le biais d'interruptions, puis le processeur exécute un programme de contrôle d'interruption court pour transférer le contrôle au fonctionnement. système, et le système d'exploitation transfère ensuite l'original. Le processus bloqué (thread) est placé dans l'état «Prêt» et remis en file d'attente.

De plus, une fois qu'un processus (thread) entre dans l'état de blocage, le processus (thread) n'a rien à faire pour le moment, mais il ne peut pas être remis en file d'attente (car il doit attendre l'interruption), donc le processus (thread) a besoin pour ajouter un état "bloquant" (Bloquer)

Notez que comme un processus (thread) qui est "Prêt" est toujours en file d'attente, le programme du processus (thread) ne peut pas être exécuté, c'est-à-dire qu'il ne déclenchera pas l'opération de lecture des données du disque. À ce stade, "Prêt" (Prêt) L'état ne peut pas être changé en état bloqué, il n'y a donc pas de flèche de prêt à bloqué dans la figure ci-dessous.

Et si le processus (thread) dans l'état "Block" reçoit les données lues sur le disque, il doit être remis en file d'attente, il ne peut donc pas retourner directement à l'état "Running", donc il n'y a pas de flèche de l'état de blocage à l'état de fonctionnement.

Seven, conception de processus et de filetage

Ensuite, nous pensons à plusieurs contraintes de conception de base:

Comment les processus et les threads sont-ils représentés en mémoire? Quels champs sont obligatoires?

Les processus représentent des applications et doivent être isolés les uns des autres. Comment concevoir ce schéma d'isolation?

Le système d'exploitation planifie les threads et bascule constamment entre les threads Comment réaliser cette situation?

Besoin de prendre en charge un environnement de cœur multi-CPU, comment concevoir pour cette situation?

7.1 Représentation des processus et des threads

Il peut être conçu comme ceci: Deux tables sont conçues en mémoire, l'une est la table de processus et l'autre est la table de threads.

La table de processus enregistre l'emplacement de stockage du processus dans la mémoire, quel est son PID, quel est son état actuel, combien de mémoire est allouée, à quel utilisateur il appartient, etc. Cela a la table de processus. Sans ce tableau, le processus sera perdu et le système d'exploitation ne sait pas quel processus il possède. Cette table peut être considérée directement dans le noyau.

Pour la subdivision, la table de processus a besoin de ces types d'informations.

  • Informations descriptives: Cette partie est le numéro d'identification unique qui décrit le processus, c'est-à-dire le PID, y compris le nom du processus, l'utilisateur auquel il appartient, etc.
  • Informations sur les ressources: cette partie est utilisée pour enregistrer les ressources appartenant au processus, telles que la façon dont le processus et la mémoire virtuelle sont mappés, les fichiers détenus, les périphériques d'E / S utilisés, etc. Bien sûr, les périphériques d'E / S sont également des fichiers.
  • Disposition de la mémoire: le système d'exploitation précise également comment le processus utilise la mémoire. Comme le montre la figure ci-dessous, il décrit comment un processus est grossièrement divisé en plusieurs zones et à quoi sert chaque zone. Nous appelons chaque zone un segment.

Le système d'exploitation a également besoin d'une table pour gérer les threads, qui est la table des threads. Thread a également besoin d'un ID, qui peut être appelé ThreadID. Ensuite, le thread doit enregistrer son propre statut d'exécution (bloqué, en cours d'exécution, prêt), sa priorité, son compteur de programme et les valeurs de tous les registres, etc. Les threads doivent enregistrer les valeurs des compteurs et des registres de programme car plusieurs threads doivent partager le même processeur et les threads basculent souvent dans les deux sens, de sorte que les valeurs des registres et des pointeurs PC doivent être stockées en mémoire.

Il existe une relation de mappage entre les threads au niveau de l'utilisateur et les threads au niveau du noyau, vous pouvez donc envisager de maintenir une table des threads au niveau du noyau dans le noyau, y compris les champs mentionnés ci-dessus.

Si vous considérez cette relation de mappage, telle que le mappage plusieurs-à-plusieurs de nm, les informations de thread peuvent toujours être stockées dans le processus et le thread de niveau noyau est utilisé chaque fois qu'il est exécuté. Cela équivaut à un pool de threads dans le noyau, en attente de l'utilisation de l'espace utilisateur. Chaque fois qu'un thread de niveau utilisateur passe le compteur du programme, etc., après la fin de l'exécution, le thread du noyau n'est pas détruit et attend la tâche suivante. Il existe en fait de nombreuses implémentations flexibles ici. D'une manière générale, la création d'un processus est coûteuse et coûteuse, la création d'un thread a une petite surcharge et un faible coût.

7.2 Plan d'isolement

Un grand nombre de processus sont en cours d'exécution dans le système d'exploitation. Pour éviter qu'ils n'interfèrent les uns avec les autres, vous pouvez envisager d'allouer des zones de mémoire complètement isolées les unes des autres. Même si le programme interne du processus lit la même adresse, l'adresse physique réelle ne sera pas la même. C'est comme si le bâtiment n ° 10 808 dans le quartier A et le bâtiment n ° 10 808 dans le quartier B ne sont pas un ensemble de maisons. Cette méthode est appelée espace d'adressage.

Par conséquent, dans des circonstances normales, le processus A ne peut pas accéder à la mémoire du processus B, à moins que le processus A ne trouve une vulnérabilité dans un système d'exploitation et ne manipule de manière malveillante la mémoire du processus B.

Pour plusieurs threads dans un processus, vous pouvez envisager de partager les ressources de mémoire allouées par le processus, de sorte que les threads n'ont besoin que de ressources d'exécution allouées.

7.3 Changement de processus (filetage)

Les processus (threads) sont constamment commutés dans le système d'exploitation, et uniquement le changement de thread dans les systèmes d'exploitation modernes. Chaque fois que vous changez, vous devez sauvegarder la mémoire de la valeur actuelle du registre.Notez que le pointeur PC est aussi une sorte de registre. Lorsque l'exécution reprend, il est nécessaire de lire tous les registres de la mémoire, de restaurer l'état précédent, puis de l'exécuter.

Le contenu mentionné ci-dessus peut être résumé comme suit: 5 étapes:

  1. Lorsque le système d'exploitation constate qu'un processus (thread) doit être commuté, il est très dangereux de contrôler directement le saut du pointeur du PC, le système d'exploitation doit donc envoyer un signal "d'interruption" au CPU pour arrêter le processus en cours d'exécution (thread ).
  2. Lorsque la CPU reçoit un signal d'interruption, le processus d'exécution (thread) s'arrête immédiatement. Notez que comme le processus (thread) est arrêté immédiatement, il n'a pas le temps d'enregistrer son propre état, le système d'exploitation suivant doit donc terminer cette opération.
  3. Une fois que le système d'exploitation a pris en charge l'interruption, alors que les données du registre n'ont pas été détruites, une petite section du programme de très bas niveau (généralement écrit en assemblage) doit être exécutée immédiatement pour aider le registre à enregistrer l'état du processus précédent (thread ).
  4. Une fois que le système d'exploitation a enregistré l'état du processus, il exécute le planificateur et détermine le processus suivant (thread) à exécuter.
  5. Enfin, le système d'exploitation exécute le processus suivant (thread).

Bien entendu, une fois qu'un processus (thread) est sélectionné pour exécution, il continuera à terminer la tâche lorsqu'il a été interrompu. Cela nécessite que le système d'exploitation exécute une petite section du programme sous-jacent pour aider le processus (thread) à restaurer l'état. .

Un algorithme possible est à travers la structure de données de la pile . Une fois le processus (thread) interrompu, le système d'exploitation est chargé de pousser les données clés (telles que les registres) sur la pile. Lors de la restauration de l'exécution, le système d'exploitation est chargé de sortir de la pile et de restaurer la valeur du registre.

7.4 Traitement multicœur

Dans un système multicœur, les principes de conception que nous avons mentionnés ci-dessus sont toujours valables, mais avec plus de puissance, des processus (threads) qui peuvent être exécutés en parallèle. Normalement, si le CPU a plusieurs cœurs, il peut exécuter plusieurs processus (threads) en parallèle. Ici, nous soulignons un concept, nous disons généralement la concurrence, l'anglais est simultané, ce qui signifie que plusieurs tâches semblent être exécutées en même temps dans un laps de temps (le multi-core n'est pas nécessaire); alors que parallèlement, l'anglais est parallèle, doit être exécuté absolument en même temps (nécessite Multi-core).

Par exemple, un processeur à 4 cœurs semble avoir 4 pipelines et peut exécuter 4 tâches en parallèle. L'exécution de plusieurs threads dans un processus entraînera des conditions de concurrence, que nous vous présenterons dans la section «19 conférences» des verrous et sémaphores. Étant donné que le système d'exploitation offre la possibilité d'enregistrer et de restaurer l'état du processus, le processus (thread) peut également être basculé entre plusieurs cœurs.

7.5 API pour la création de processus (threads)

Le moyen le plus direct pour un utilisateur de créer un processus est d'exécuter un programme à partir de la ligne de commande ou de double-cliquer pour ouvrir une application. Mais pour les programmeurs, il y a clairement un besoin d'une meilleure conception.

Du point de vue d'un concepteur, vous pouvez penser comme ceci: Premièrement, il devrait y avoir une API pour ouvrir une application, par exemple, une application peut être ouverte via une fonction; d'autre part, si le programmeur veut effectuer une opération coûteuse processus d'initialisation, le programme en cours L'état de est copié plusieurs fois et devient un processus d'exécution unique, puis le système d'exploitation fournit l'instruction fork.

C'est-à-dire que chaque fork créera un processus cloné, ce processus cloné, tous les états sont les mêmes que le processus d'origine, mais aura son propre espace d'adressage. Si vous souhaitez créer deux processus de clonage, vous devez bifurquer deux fois.

Vous pouvez demander: Et si je veux simplement démarrer un nouveau programme?

J'ai dit plus haut: le système d'exploitation fournit une API pour démarrer de nouveaux programmes.

Vous pouvez également demander: si je veux simplement utiliser un nouveau processus pour exécuter un petit programme, par exemple, chaque fois que le serveur reçoit une demande du client, je souhaite utiliser un processus pour gérer la demande.

Si tel est le cas, je vous suggère de ne pas démarrer le processus séparément, mais d'utiliser des threads. Parce que le coût de création du processus est trop élevé, il n'est pas recommandé de faire de telles choses: créer des entrées, allouer de la mémoire, en particulier pour former un segment dans la mémoire, divisé en différentes zones. Donc, généralement, nous avons tendance à créer plus de threads.

Différents langages de programmation fournissent des API pour créer des threads. Par exemple, Java a la classe Thread; go a go-routine (notez qu'il ne s'agit pas d'une coroutine, mais d'un thread).

Huit, le problème

Où la surcharge du processus est-elle plus grande que celle du thread?

La création d'un processus sous Linux créera naturellement un thread, qui est le thread principal. La création d'un processus nécessite de diviser un espace mémoire complet pour le processus, et il existe de nombreuses opérations d'initialisation, telles que la segmentation de la mémoire (pile, zone du corps, etc.). La création d'un thread est beaucoup plus simple. Il vous suffit de déterminer la valeur du pointeur et du registre du PC et d'affecter une pile au thread pour exécuter le programme. Plusieurs threads dans le même processus peuvent réutiliser la pile. Par conséquent, la création d'un processus est plus lente que la création d'un thread et la surcharge de mémoire du processus est plus importante.

fourchette()

fourchette()

fourchette()

print ("Hello World \ n")

Une fois ce programme exécuté, combien de fois la sortie Hello World sera-t-elle imprimée?

La réponse peut être obtenue en analysant attentivement les procédures suivantes. 


#include <unistd.h>
#include <stdio.h> 
int main () 
{ 
	pid_t fpid; //fpid表示fork函数返回的值
	int count=0;
	fpid=fork(); 
	if (fpid < 0) 
		printf("error in fork!"); 
	else if (fpid == 0) {
		printf("i am the child process, my process id is %d/n",getpid()); 
		printf("我是爹的儿子/n");
		count++;
	}
	else {
		printf("i am the parent process, my process id is %d/n",getpid()); 
		printf("我是孩子他爹/n");
		count++;
	}
	printf("统计结果是: %d/n",count);
	return 0;
}

 Avant l'instruction fpid = fork (), un seul processus exécute ce code, mais après cette instruction, il devient deux processus en cours d'exécution. Les deux processus sont presque identiques et l'instruction suivante à exécuter Les deux sont if (fpid <0) ......
    Pourquoi les fpids des deux processus sont-ils différents? Ceci est lié aux caractéristiques de la fonction fork. Une des choses merveilleuses à propos de l'appel de fork est qu'il n'est appelé qu'une seule fois, mais il peut renvoyer deux fois. Il peut avoir trois valeurs de retour différentes:
    1) Dans le processus parent, fork renvoie l'ID de processus du processus enfant nouvellement créé;
    2 ) Dans le processus enfant , fork renvoie l'ID de processus du processus enfant nouvellement créé Pendant le processus, fork renvoie 0;
    3) Si une erreur se produit, fork renvoie une valeur négative;

    Une fois la fonction fork exécutée, si le nouveau processus est créé avec succès, deux processus apparaîtront, l'un est le processus enfant et l'autre le processus parent. Dans le processus enfant, la fonction fork renvoie 0 et dans le processus parent, fork renvoie l'ID de processus du processus enfant nouvellement créé. Nous pouvons utiliser la valeur retournée par fork pour déterminer si le processus actuel est un processus enfant ou un processus parent.

    Citant un internaute pour expliquer pourquoi la valeur de fpid est différente dans le processus parent-enfant. "En fait, cela équivaut à une liste chaînée. Le processus forme une liste chaînée. Le fpid (p signifie point) du processus parent pointe vers l'ID de processus du processus enfant. Parce que le processus enfant n'a pas de processus enfant , son fpid est 0.
    Il peut y avoir deux raisons aux erreurs de fork:
    1) Le nombre actuel de processus a atteint la limite supérieure spécifiée par le système et la valeur de errno est définie sur EAGAIN.2
    ) La mémoire système est insuffisante, et la valeur de errno est définie sur ENOMEM. Une
    fois le nouveau processus créé avec succès, deux apparaissent dans le système Fondamentalement, le même processus, il n'y a pas d'ordre fixe pour l'exécution de ces deux processus. Le processus qui est exécuté en premier dépend du système stratégie de planification de processus.
    Chaque processus a un identificateur de processus unique (différent) (ID de processus). Il peut être obtenu via la fonction getpid (), et il existe une variable qui enregistre le pid du processus parent et la valeur de la variable peut être obtenu via la fonction getppid (). Après l'
    exécution du fork, deux processus apparaissent,

 Certains disent que le contenu des deux processus est exactement le même et que les résultats imprimés sont différents. C'est à cause des conditions de jugement. La liste ci-dessus ne contient que le code et les instructions du processus, ainsi que les variables.
    Après avoir exécuté fork, la variable du processus 1 est count = 0, fpid! = 0 (processus parent). Les variables du processus 2 sont count = 0 et fpid = 0 (processus fils). Les variables de ces deux processus sont indépendantes et existent à des adresses différentes et ne sont pas partagées. Faites attention à cela. On peut dire que nous utilisons fpid pour identifier et manipuler les processus parent-enfant.
    D'autres peuvent se demander pourquoi le code n'est pas copié depuis #include. C'est parce que fork copie la situation actuelle du processus. Lorsque le fork est exécuté, le processus a été exécuté. Int count = 0; fork ne copie que le suivant à être Code exécuté à un nouveau processus.


#include <unistd.h>
#include <stdio.h>
int main(void)
{
   int i=0;
   printf("i son/pa ppid pid  fpid/n");
   //ppid指当前进程的父进程pid
   //pid指当前进程的pid,
   //fpid指fork返回给当前进程的值
   for(i=0;i<2;i++){
       pid_t fpid=fork();
       if(fpid==0)
    	   printf("%d child  %4d %4d %4d/n",i,getppid(),getpid(),fpid);
       else
    	   printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);
   }
   return 0;
}

Référence: processus de compréhension des fourches https://blog.csdn.net/jason314/article/details/5640969

Je suppose que tu aimes

Origine blog.csdn.net/MyySophia/article/details/114106985
conseillé
Classement