Question

Je peux comprendre comment on peut écrire un programme qui utilise plusieurs processus ou threads :fork() un nouveau processus et utilisez IPC, ou créez plusieurs threads et utilisez ce type de mécanismes de communication.

Je comprends également le changement de contexte.Autrement dit, avec un seul processeur, le système d'exploitation planifie le temps pour chaque processus (et il existe des tonnes d'algorithmes de planification) et nous parvenons ainsi à exécuter plusieurs processus simultanément.

Et maintenant que nous disposons de processeurs multicœurs (ou d’ordinateurs multiprocesseurs), nous pourrions avoir deux processus exécutés simultanément sur deux cœurs distincts.

Ma question concerne le dernier scénario :comment le noyau contrôle-t-il sur quel noyau un processus s'exécute ?Quels appels système (sous Linux, ou même Windows) planifient un processus sur un cœur spécifique ?

La raison pour laquelle je demande:Je travaille sur un projet pour l'école où nous devons explorer un sujet récent en informatique - et j'ai choisi les architectures multicœurs.Il semble y avoir beaucoup d'informations sur la façon de programmer dans ce type d'environnement (comment surveiller les blocages ou les conditions de concurrence), mais pas beaucoup sur le contrôle des cœurs individuels eux-mêmes.J'adorerais pouvoir écrire quelques programmes de démonstration et présenter des instructions d'assemblage ou du code C du type "Vous voyez, j'exécute une boucle infinie sur le 2ème cœur, regardez le pic d'utilisation du processeur pour ce noyau spécifique".

Des exemples de code ?Ou des tutoriels ?

modifier:Pour plus de précision, de nombreuses personnes ont dit que c'était le but du système d'exploitation et qu'il fallait laisser le système d'exploitation s'en occuper.Je suis complètement d'accord!Mais alors ce que je demande (ou j'essaie d'avoir une idée), c'est ce que fait réellement le système d'exploitation pour faire cela.Pas l'algorithme de planification, mais plus "une fois qu'un noyau est choisi, quelles instructions doivent être exécutées pour que ce noyau commence à récupérer les instructions ?"

Était-ce utile?

La solution

Comme d'autres l'ont déjà mentionné, l'affinité du processeur est spécifique au système d'exploitation . Si vous voulez faire cela en dehors des limites du système d'exploitation, vous vous amuserez beaucoup, et j'entends par là douleur.

Cela dit, d’autres ont mentionné SetProcessAffinityMask pour Win32. Personne n'a mentionné la méthode du noyau Linux pour définir l'affinité processeur, et je le ferai donc. Vous devez utiliser la fonction sched_setaffinity. Voici un didacticiel intéressant sur la procédure à suivre.

Autres conseils

Normalement, le système détermine le noyau d'une application. Cependant, vous pouvez définir la & Quot; affinité & Quot; pour une application à un noyau spécifique, indiquer au système d'exploitation de n'exécuter l'application que sur ce noyau. Normalement, ce n'est pas une bonne idée, mais il y a de rares cas où cela peut avoir un sens.

Pour ce faire dans Windows, utilisez le gestionnaire de tâches, cliquez avec le bouton droit de la souris sur le processus, puis choisissez & "Définir l'affinité &". Vous pouvez le faire par programme dans Windows en utilisant des fonctions telles que SetThreadAffinityMask, SetProcessAffinityMask ou SetThreadIdealProcessor.

ETA:

Si vous souhaitez savoir comment le système d'exploitation effectue la planification, consultez les liens suivants:

Article de Wikipedia sur le changement de contexte

article de Wikipedia sur la planification

Planification dans le noyau Linux

Avec la plupart des systèmes d’exploitation modernes, le système d’exploitation planifie l’exécution d’un thread par un noyau pour une courte période. Lorsque la tranche de temps expire ou que le thread effectue une opération d'E / S qui l'oblige à céder volontairement le noyau, le système d'exploitation planifie l'exécution d'un autre thread sur le noyau (s'il existe des threads prêts à être exécutés). Le fil qui est planifié dépend de l'algorithme de planification du système d'exploitation.

Les détails de la mise en œuvre expliquant exactement comment le changement de contexte se produit sont CPU & amp; Dépend du système d'exploitation. Il s'agira généralement d'un passage en mode noyau, le système d'exploitation enregistrant l'état du thread précédent, chargeant celui du nouveau thread, puis revenant en mode utilisateur et reprenant le thread nouvellement chargé. L'article de commutation de contexte que j'ai lié à ci-dessus contient un peu plus de détails à ce sujet.

Rien ne dit au cœur & "; commencez maintenant à exécuter ce processus &";

.

Le noyau ne voit pas le processus , il ne connaît que le code exécutable, les différents niveaux d'exécution et les limitations associées aux instructions pouvant être exécutées.

Lorsque l'ordinateur démarre, pour des raisons de simplicité, un seul processeur / processeur est actif et exécute n'importe quel code. Ensuite, si le système d’exploitation est capable de fonctionner avec plusieurs processeurs, il active les autres cœurs avec une instruction spécifique au système, d’autres probablement récupérés exactement au même endroit que les autres cœurs et exécutés à partir de là.

Le planificateur examine donc les structures internes du système d'exploitation (file d'attente de tâches / processus / threads), en choisit une et le marque comme étant en cours d'exécution. Ensuite, les autres instances de planificateur exécutées sur d'autres cœurs ne la toucheront pas tant que la tâche ne sera plus en attente (et non marquée comme épinglée à un cœur spécifique). Une fois que la tâche est marquée comme étant en cours d'exécution, le planificateur exécute le basculement en mode utilisateur avec la reprise de la tâche au moment où elle avait été suspendue.

Techniquement, rien n'empêche les cœurs d'exécuter exactement le même code exactement au même moment (comme le font de nombreuses fonctions déverrouillées), mais à moins d'écrire du code dans l'attente, il pisserait sur lui-même.

Le scénario va plus loin avec des modèles de mémoire plus exotiques (ci-dessus suppose que & "; espace &" habituel de la mémoire de travail linéaire) où les cœurs ne voient pas nécessairement tous la même mémoire et il peut être nécessaire d'extraire du code Les embrayages d’autres noyaux, mais ils sont beaucoup plus faciles à gérer en gardant simplement les tâches épinglées au noyau (l’architecture AFAIK Sony PS3 avec SPU est comme ça).

Le projet OpenMPI a un bibliothèque pour définir l'affinité des processeurs sur Linux de manière portable.

Il y a quelque temps, je l'ai utilisé dans un projet et cela a bien fonctionné.

Avertissement: je me souviens vaguement qu'il y avait quelques problèmes pour savoir comment le système d'exploitation numérote les cœurs. Je l'ai utilisé dans un système à 2 processeurs Xeon avec 4 cœurs chacun.

Un coup d'œil sur cat /proc/cpuinfo pourrait vous aider. Sur la boîte que j'ai utilisée, c'est assez bizarre. La production est réduite à la fin.

De toute évidence, les cœurs numérotés de manière uniforme se trouvent sur le premier cpu et les cœurs numérotés de manière étrange sur le deuxième cpu. Cependant, si je me souviens bien, il y avait un problème avec les caches. Sur ces processeurs Intel Xeon, deux cœurs de chaque processeur partagent leurs caches L2 (je ne me souviens pas si le processeur dispose d'un cache L3). Je pense que les processeurs virtuels 0 et 2 partageaient un cache L2, 1 et 3 partageaient un, 4 et 6 partageaient un, et 5 et 7 partageaient un.

À cause de cette bizarrerie (il y a 1,5 ans, je ne trouvais aucune documentation sur la numérotation des processus sous Linux), je ferais bien de faire ce genre de réglage de bas niveau. Cependant, il y a clairement des utilisations. Si votre code fonctionne sur quelques types de machines, il peut être intéressant de faire ce type de réglage. Une autre application serait dans une langue spécifique à un domaine, telle que StreamIt où le compilateur pourrait le faire. sale travail et calculer un horaire intelligent.

processor       : 0
physical id     : 0
siblings        : 4
core id         : 0
cpu cores       : 4

processor       : 1
physical id     : 1
siblings        : 4
core id         : 0
cpu cores       : 4

processor       : 2
physical id     : 0
siblings        : 4
core id         : 1
cpu cores       : 4

processor       : 3
physical id     : 1
siblings        : 4
core id         : 1
cpu cores       : 4

processor       : 4
physical id     : 0
siblings        : 4
core id         : 2
cpu cores       : 4

processor       : 5
physical id     : 1
siblings        : 4
core id         : 2
cpu cores       : 4

processor       : 6
physical id     : 0
siblings        : 4
core id         : 3
cpu cores       : 4

processor       : 7
physical id     : 1
siblings        : 4
core id         : 3
cpu cores       : 4

Pour connaître le nombre de processeurs au lieu d'utiliser / proc / cpuinfo, exécutez simplement:

nproc

Pour exécuter un processus sur un groupe de processeurs spécifiques:

taskset --cpu-list 1,2 my_command 

dira que ma commande ne peut s'exécuter que sur les processeurs 1 ou 2.

Pour exécuter un programme sur 4 processeurs effectuant 4 tâches différentes, utilisez le paramétrage. L'argument au programme lui dit de faire quelque chose de différent:

for i in `seq 0 1 3`;
do 
  taskset --cpu-list $i my_command $i;
done

Un bon exemple en est le traitement de 8 millions d’opérations dans un tableau, de sorte que 0 à (2 mil-1) se rende au processeur 1, 2 mil à (4 mil-1) au processeur 2, etc.

.

Vous pouvez examiner la charge de chaque processus en installant htop à l'aide d'apt-get / yum et en s'exécutant sur la ligne de commande:

 htop

Comme d'autres l'ont mentionné, il est contrôlé par le système d'exploitation. Selon le système d'exploitation, il peut vous fournir ou non des appels système vous permettant d'affecter le noyau sur lequel un processus donné s'exécute. Cependant, vous devez généralement laisser le système d'exploitation adopter le comportement par défaut. Si vous avez un système à 4 cœurs avec 37 processus en cours d'exécution et que 34 de ces processus sont en veille, les 3 processus actifs restants seront planifiés sur des cœurs distincts.

Vous ne verrez probablement qu’une amélioration de la vitesse lorsque vous jouez avec des affinités principales dans des applications multithread très spécialisées. Par exemple, supposons que vous ayez un système avec 2 processeurs double cœur. Supposons que vous ayez une application avec 3 threads et que deux d’entre eux fonctionnent fortement sur le même ensemble de données, tandis que le troisième utilise un ensemble de données différent. Dans ce cas, vous auriez tout intérêt à disposer des deux threads qui interagissent sur le même processeur et du troisième sur l'autre processeur, car ils peuvent alors partager un cache. Le système d'exploitation n'a aucune idée de la mémoire à laquelle chaque thread doit accéder. Il est donc possible qu'il n'alloue pas les threads aux cœurs de manière appropriée.

Si vous êtes intéressé par comment le système d'exploitation, consultez la page planification . Vous trouverez des informations détaillées sur le multitraitement sur x86 dans le logiciels pour architectures Intel 64 et IA-32. Manuels du développeur . Les chapitres 7 et 8 du volume 3A contiennent des informations pertinentes, mais n'oubliez pas que ces manuels sont extrêmement techniques.

Le système d'exploitation sait comment faire cela, vous n'avez pas à le faire. Vous pourriez rencontrer toutes sortes de problèmes si vous spécifiiez le noyau sur lequel exécuter, dont certains pourraient réellement ralentir le processus. Laissez l’OS comprendre, il vous suffit de démarrer le nouveau thread.

Par exemple, si vous demandiez à un processus de démarrer sur le noyau x, alors que celui-ci était déjà surchargé, vous en seriez moins bien que si vous veniez de laisser le système d'exploitation le gérer.

Je ne connais pas les instructions de montage.Mais la fonction API Windows est SetProcessAffinityMask.Tu peux voir un exemple de quelque chose que j'ai bricolé il y a quelque temps pour exécuter Picasa sur un seul cœur

Linux sched_setaffinity Exemple d'exécution minimal C

Dans cet exemple, nous obtenons l'affinité, la modifions et vérifions si elle prend effet avec sched_getcpu() . .

#define _GNU_SOURCE
#include <assert.h>
#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void print_affinity() {
    cpu_set_t mask;
    long nproc, i;

    if (sched_getaffinity(0, sizeof(cpu_set_t), &mask) == -1) {
        perror("sched_getaffinity");
        assert(false);
    } else {
        nproc = sysconf(_SC_NPROCESSORS_ONLN);
        printf("sched_getaffinity = ");
        for (i = 0; i < nproc; i++) {
            printf("%d ", CPU_ISSET(i, &mask));
        }
        printf("\n");
    }
}

int main(void) {
    cpu_set_t mask;

    print_affinity();
    printf("sched_getcpu = %d\n", sched_getcpu());
    CPU_ZERO(&mask);
    CPU_SET(0, &mask);
    if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == -1) {
        perror("sched_setaffinity");
        assert(false);
    }
    print_affinity();
    /* TODO is it guaranteed to have taken effect already? Always worked on my tests. */
    printf("sched_getcpu = %d\n", sched_getcpu());
    return EXIT_SUCCESS;
}

Compiler et exécuter avec:

gcc -std=c99 main.c
./a.out

Exemple de sortie:

sched_getaffinity = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 
sched_getcpu = 9
sched_getaffinity = 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
sched_getcpu = 0

Ce qui signifie que:

  • initialement, tous mes 16 cœurs étaient activés et le processus s'exécutait de manière aléatoire sur le noyau 9 (le 10ème)
  • après que nous ayons défini l'affinité avec le premier noyau uniquement, le processus a nécessairement été déplacé vers le noyau 0 (le premier)

Il est également amusant d’exécuter ce programme avec taskset:

taskset -c 1,3 ./a.out

Ce qui donne une sortie de forme:

sched_getaffinity = 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 
sched_getcpu = 2
sched_getaffinity = 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
sched_getcpu = 0

et nous voyons donc que cela limitait l'affinité dès le début.

Cela fonctionne car l'affinité est héritée par des processus enfants, ce qui <=> est en train de forger: Comment éviter l'héritage de l'affinité CPU par les processus fils-dérivés?

Testé sous Ubuntu 16.04,

Métal nu x86

Si vous êtes aussi inconditionnel: Qu'est-ce que le langage d'assemblage multicœur ressembler?

Comment Linux le met en œuvre

Comment fonctionne sched_setaffinity ()?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top