web-dev-qa-db-fra.com

Taille de pile par défaut pour pthreads

Si je comprends bien, la taille de pile par défaut pour un pthread sous Linux est 16K. J'obtiens des résultats étranges sur mon installation Ubuntu 64 bits.

$ ulimit -s
8192

Aussi:

pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &stacksize);
printf("Thread stack size = %d bytes \n", stacksize);

Prints
    Thread stack size = 8388608 bytes

Je suis sûr que la taille de la pile n'est pas "8388608". Qu'est-ce qui ne va pas?

24
Kamath
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

L'attribut stacksize doit définir la taille de pile minimale (en octets) allouée à la pile de threads créée.

Dans votre exemple, la taille de la pile est définie sur 8388608 octets, ce qui correspond à 8 Mo, comme renvoyé par la commande ulimit -s. Cela correspond donc.

De la description de pthread_create():

Sous Linux/x86-32 , la taille de pile par défaut pour un nouveau thread est 2 mégaoctets . Sous l'implémentation de thread NPTL, si la limite de ressources logicielles RLIMIT_STACK au moment où le programme a démarré a une valeur autre que "illimitée", alors il détermine la valeur par défaut taille de pile de nouveaux threads. En utilisant pthread_attr_setstacksize (3), l'attribut de taille de pile peut être explicitement défini dans l'argument attr utilisé pour créer un thread, afin d'obtenir une taille de pile autre que la valeur par défaut.

La taille de la pile de threads peut donc être définie via la fonction set ci-dessus ou la propriété système ulimit. Pour le 16k dont vous parlez, il n'est pas clair sur quelle plate-forme vous avez vu cela et/ou si une limite système a été définie pour cela.

Voir page pthread_create et ici pour quelques exemples intéressants à ce sujet.

22
fduff

En fait, votre virtuel taille de la pile est 8388608 octets (8 Mo). Bien sûr, il est naturel de conclure que cela ne peut pas être correct, car c'est une quantité ridiculement grande de mémoire pour chaque thread à consommer pour sa pile alors que 99% du temps, quelques Ko sont probablement tout ce dont ils ont besoin.

La bonne nouvelle est que votre thread utilise uniquement la quantité de mémoire physique dont il a réellement besoin. C'est l'un des pouvoirs magiques que votre système d'exploitation obtient de l'utilisation de l'unité de gestion de la mémoire matérielle (MMU) dans votre processeur. Voici ce qui se passe:

  1. Le système d'exploitation alloue 8 Mo de mémoire virtuelle à votre pile en configurant les tables de pages de la MMU pour votre thread. Cela nécessite très peu RAM pour contenir uniquement les entrées du tableau des pages.

  2. Lorsque votre thread s'exécute et essaie d'accéder à une adresse virtuelle sur la pile à laquelle aucune page physique n'est encore affectée, une exception matérielle appelée "erreur de page" est déclenchée par la MMU.

  3. Le cœur du processeur répond à l'exception de défaut de page en passant à un mode d'exécution privilégié (qui a sa propre pile) et en appelant la fonction de gestionnaire d'exception de défaut de page à l'intérieur du noyau.

  4. Le noyau alloue une page de physique RAM à cette page de mémoire virtuelle et retourne au thread de l'espace utilisateur.

Le thread de l'espace utilisateur ne voit rien de tout cela. De son point de vue, il utilise simplement la pile comme si la mémoire était toujours présente. Pendant ce temps, la pile s'agrandit automatiquement (ou non) pour répondre aux besoins du thread.

Le MMU est un élément clé du matériel des systèmes informatiques d'aujourd'hui. En particulier, il est responsable de beaucoup de "magie" dans le système, donc je recommande fortement d'en savoir plus sur ce que le = MMU le fait, et à propos de la mémoire virtuelle en général. De plus, si votre application est sensible aux performances et traite une quantité importante de données, vous devez comprendre comment fonctionne le TLB (le cache de table des pages de la MMU) et comment restructurer vos données ou vos algorithmes pour maximiser votre taux de réussite TLB.

47
jtchitty