Avez-vous déjà entendu parler de la loi de Moore qui a régi l'industrie des semi-conducteurs pendant près de 60 ans ? Savez-vous que la règle de Moore n'a jamais été une loi, mais plutôt un constat ? Pour ceux qui ne connaissent pas cette loi, voici comment elle est exprimée.
La loi de Moore observe que le nombre de transistors dans les circuits intégrés denses double tous les deux ans.
Bien que la règle soit rédigée en termes d'électronique, du point de vue d'un ordinateur, elle indique que la puissance de calcul doublera tous les deux ans. Essentiellement, la puissance de calcul sur une seule puce augmente de façon exponentielle. N'est-ce pas fantastique ?
Ce qui est étonnant, c'est que depuis que cette observation a été faite, elle s'est avérée vraie et est maintenant considérée comme une règle.
Cependant, comme nous le savons tous, les belles choses ne sont pas toujours gratuites. Dans ce cas, le calcul plus puissant s'est fait au détriment d'un échauffement excessif. À leur arrivée en 2010, les transformateurs atteignaient des températures suffisamment élevées pour cuire les pâtes. (Consultez cette vidéo youtube pour en faire l'expérience vous-même).
De plus, à mesure que le nombre de transistors sur une puce augmentait, les limites physiques du matériau commençaient à être atteintes. Les forces subatomiques ont commencé à devenir actives à ce stade, rendant difficile le maintien de la trajectoire de la loi de Moore.
C'était comme si les bons moments étaient terminés où les ordinateurs devenaient de plus en plus rapides. Mais les ingénieurs avaient d'autres plans. Ils ont commencé à utiliser plusieurs cœurs puissants au lieu d'un seul cœur super puissant.
Bien qu'il ait pu résoudre le problème matériel, il a présenté un nouveau défi aux ingénieurs logiciels. Les ingénieurs devaient développer des logiciels tirant parti des nombreux cœurs dont disposaient désormais les ordinateurs. En conséquence, la programmation concurrente, souvent connue sous le nom de programmation parallèle, est née. Les threads sont à l'honneur dans ce domaine de la programmation. Mais avant de parler de threads, nous devons comprendre ce qu'est un processus.
Un processus Linux est défini comme une instance en cours d'exécution d'un programme.
Par conséquent, le système doit garder une trace d'un certain nombre d'éléments pendant l'exécution d'un programme, y compris la mémoire de la pile, le code source et les registres, entre autres. Sous Linux, l'agrégat de toutes ces choses est appelé un processus. Linux est construit sur une base de processus.
Pour vérifier les différents processus en cours d'exécution sur votre machine, vous pouvez essayer d'exécuter la commande suivante. Cela exécutera tous les processus avec leur ID de processus.
$ ps
Voici un exemple d'instantané.
Par défaut, la commande ci-dessus affiche uniquement les processus associés à l'utilisateur actuel. Pour lister tous les processus, nous pouvons utiliser la commande avec les options suivantes.
$ ps -aux
Voici un exemple d'instantané.
L'application de ces options supplémentaires à la commande ps
nous donne également des informations supplémentaires. Voici ce que signifiaient les différents drapeaux -
a
représente tous les utilisateursu
représente l'utilisateur actuelx
affiche le processus exécuté en dehors du terminal
Nous pouvons également tuer certains processus à l'aide de la commande kill
. Voici comment il est utilisé -
$ kill PID
Ici, PID
est l'ID de processus que nous pouvons obtenir à partir de la commande ps
. Chaque processus du système Linux aura un PID
unique qui est utilisé pour l'identifier. Vous pouvez même utiliser la commande pidof
pour trouver le PID
du processus.
$ pidof bash
Lorsque vous lancez un programme ou émettez une commande, un processus est formé. Lorsque vous exécutez une commande depuis le terminal, vous démarrez un nouveau processus. Parce qu'un terminal a été utilisé pour générer ce nouveau processus, nous disons que le processus terminal a lancé le nouveau processus de commande. En d'autres termes, le nouveau processus de commande est l'enfant du processus terminal.
Chaque processus de l'écosystème Linux a un processus parent qui l'a créé. Nous pouvons utiliser la commande suivante pour vérifier le processus parent d'un processus avec un PID
donné.
$ ps -o ppid= -p PID
Tous les processus sous Linux sont, directement ou indirectement, des enfants du processus avec le PID 1. Ce n'est pas une coïncidence. Le processus avec le PID 1 est le processus d'initialisation et est le tout premier processus lancé par le système au moment du démarrage. Tout processus ultérieur est créé en tant qu'enfant de ce processus.
Nous avons donc un arbre construit à partir de ces relations entre processus. C'est ce qu'on appelle l'arborescence des processus.
Les processus sont extrêmement utiles sous Linux, et nous ne pourrions pas vivre sans eux. Mais il y a un inconvénient pour eux, ou peut-être pas un inconvénient du tout, mais simplement la façon dont ils fonctionnent. Une procédure est lourde. Les données, la mémoire et les variables sont transférées chaque fois qu'un nouveau processus est lancé. Chaque processus qui exécute le même programme aura sa propre copie du code source. Par conséquent, générer un nombre élevé de processus n'est pas une bonne idée.
Cependant, comme les processus sont un mécanisme permettant de traiter plusieurs requêtes en même temps, nous sommes contraints par cette vérité désagréable. Nous ne pouvons servir qu'un petit nombre d'utilisateurs simultanés qui ont beaucoup en commun puisque nous ne pouvons lancer qu'un nombre restreint de processus dans notre système. Considérez un serveur Web qui doit servir de nombreux utilisateurs simultanés. Créer un nouveau processus pour chaque utilisateur est une opération coûteuse. En conséquence, nous voulons quelque chose de moins cher que la procédure. Les fils entrent en jeu ici.
Les threads ne sont que des processus légers. Un thread partage de la mémoire avec son processus parent et tous les threads qu'il a créés. Grâce à cette mémoire partagée, la création de nouveaux threads est moins coûteuse. Cela offre l'avantage supplémentaire d'une communication de fil plus rapide et d'un changement de contexte. En utilisant les threads, un processus peut effectuer plusieurs tâches simultanément.
Par rapport au nombre de processus, nous pouvons générer un nombre important de threads. Sur des machines à plusieurs cœurs, ces threads sont exécutés en parallèle. Contrairement à la génération de nombreux processus ou à l'exécution séquentielle de toutes les tâches, cela améliore les performances globales du programme.
Essayons de démarrer notre premier fil. Il est important de noter que nous ne pouvons pas démarrer de nouveaux threads avec bash. La commande bash ne peut être utilisée que pour créer des sous-processus. Donc, ce que nous allons faire, c'est écrire un code C qui lance deux threads. Ensuite, en utilisant bash, nous exécuterons ce code C en tant que sous-processus. Ensuite, deux threads seront créés par ce nouveau processus.
Commençons à mettre la main sur du code. Créez un nouveau fichier et nommez-le threads.c
. Allez-y et ouvrez-le dans l'un de vos IDE préférés.
La première étape consiste à importer les fichiers d'en-tête requis.
#include <pthread.h> #include <stdio.h>
Nous allons créer deux threads, chacun exécutant la même fonction mais avec des paramètres différents. Écrivons cette fonction.
void* print_multiple_messages(void* ptr) { char* message = (char*) ptr; for(int i=0; i<1000; ++i) { printf("%s \n", message); } }
Comme vous pouvez le voir, cette fonction ne fait rien de grand. Il prend un message comme paramètre d'entrée et l'imprime mille fois.
Écrivons maintenant la fonction principale.
int main() { // Continue writing from here }
Tout comme les processus, les threads ont également des identifiants qui sont utilisés pour les identifier de manière unique. Créez deux variables pour contenir ces identifiants.
pthread_t thread1, thread2;
Nous utiliserons des messages différents pour chaque fil. Créez deux chaînes (tableau de caractères) pour contenir différents messages.
char* message1 = "Thread 1"; char* message2 = "Thread 2";
L'étape suivante consiste à créer les deux threads. Nous utiliserons la méthode pthread_create
pour ce faire.
pthread_create(&thread1, NULL, print_multiple_messages, (void*) message1); pthread_create(&thread2, NULL, print_multiple_messages, (void*) message2);
Cela ouvrira deux nouveaux fils de discussion. Instruisons notre processus principal d'attendre que les deux threads aient terminé leur travail.
pthread_join(thread1, NULL); pthread_join(thread2, NULL);
Et c'est tout. Compilez le code et exécutez-le. Vous remarquerez que les messages de deux threads seront mélangés. Cela montre qu'ils sont exécutés en parallèle.
Félicitations, vous venez de créer votre tout premier fil.
Ainsi, dans cet article, nous avons parlé des threads et des processus. Ce sont quelques-unes des fonctionnalités les plus fascinantes de Linux, et leur maîtrise est cruciale. Il permet le développement de logiciels compatibles avec le matériel et l'utilisation efficace des ressources à notre disposition.
Ici, nous allons tirer une conclusion à cet article. Nous nous sommes efforcés d'entrer suffisamment dans les détails pour vous aider à démarrer, mais ce n'est pas ça. Alors continuez à en apprendre davantage. Si vous avez apprécié le contenu, vous voudrez peut-être commenter et/ou emote.
Profitez de l'apprentissage !