37 votes

Pourquoi les fonctions malloc() et printf() sont-elles considérées comme non rentables ?

Dans les systèmes UNIX, nous savons que malloc() est une fonction non récurrente (appel système). Pourquoi ?

De même, printf() On dit aussi qu'il n'y a pas de réentraînement ; pourquoi ?

Je connais la définition de la réentrance, mais je voulais savoir pourquoi elle s'applique à ces fonctions. Qu'est-ce qui les empêche d'être garanties réentrantes ?

56voto

Pavel Shved Points 34706

malloc y printf utilisent généralement des structures globales, et emploient une synchronisation interne basée sur les verrous. C'est pourquoi ils ne sont pas réentrants.

El malloc peut être soit sécurisée, soit non sécurisée. Les deux ne sont pas réentrantes :

  1. Malloc opère sur un tas global, et il est possible que deux invocations différentes de la commande malloc qui se produisent au même moment, renvoient le même bloc de mémoire. (Le deuxième appel malloc devrait avoir lieu avant qu'une adresse du bloc soit récupérée, mais le bloc n'est pas marqué comme indisponible). Ceci viole la postcondition de malloc Cette implémentation n'est donc pas ré-entrante.

  2. Pour éviter cet effet, une implémentation thread-safe de l'option malloc utiliserait une synchronisation basée sur les verrous. Cependant, si malloc est appelé depuis le gestionnaire de signaux, la situation suivante peut se produire :

    malloc();            //initial call
      lock(memory_lock); //acquire lock inside malloc implementation
    signal_handler();    //interrupt and process signal
    malloc();            //call malloc() inside signal handler
      lock(memory_lock); //try to acquire lock in malloc implementation
      // DEADLOCK!  We wait for release of memory_lock, but 
      // it won't be released because the original malloc call is interrupted

    Cette situation ne se produira pas lorsque malloc est simplement appelé à partir de différents threads. En effet, le concept de réentrance va au-delà de la sécurité des threads et exige également que les fonctions fonctionnent correctement même si l'une de ses invocations ne se termine jamais. . C'est en gros la raison pour laquelle toute fonction avec des verrous ne serait pas ré-entrante.

El printf a également opéré sur des données globales. Tout flux de sortie utilise généralement un tampon global attaché à la ressource vers laquelle les données sont envoyées (un tampon pour le terminal, ou pour un fichier). Le processus d'impression consiste généralement en une séquence de copie des données dans le tampon et de vidage du tampon par la suite. Ce tampon doit être protégé par des verrous de la même manière que malloc fait. Par conséquent, printf est également non-récidiviste.

12voto

JeremyP Points 46808

Comprenons ce que nous entendons par ré-entrant . Une fonction ré-entrante peut être invoquée avant qu'une invocation précédente ne soit terminée. Cela peut arriver si

  • une fonction est appelée dans un gestionnaire de signal (ou plus généralement sous Unix un gestionnaire d'interruption) pour un signal qui a été soulevé pendant l'exécution de la fonction.
  • une fonction est appelée de manière récursive

malloc n'est pas ré-entrant car il gère plusieurs structures de données globales qui suivent les blocs de mémoire libres.

printf n'est pas ré-entrant car il modifie une variable globale, c'est-à-dire le contenu du FILE* stout.

1voto

stdan28 Points 1

Très probablement parce que vous ne pouvez pas commencer à écrire la sortie alors qu'un autre appel à printf est encore en train de s'imprimer. Il en va de même pour l'allocation et la désallocation de la mémoire.

-2voto

ruslik Points 8442

C'est parce que les deux travaillent avec des ressources globales : les structures de mémoire du tas et la console.

EDIT : le tas n'est rien d'autre qu'une sorte de structure de liste liée. Chaque malloc o free le modifie, donc le fait d'avoir plusieurs threads en même temps avec un accès en écriture à celui-ci endommagera sa cohérence.

EDIT2 : autre détail : on pourrait les rendre réentrants par défaut en utilisant des mutex. Mais cette approche est coûteuse, et il n'y a aucune garantie qu'ils seront toujours utilisés dans l'environnement MT.

Il y a donc deux solutions : faire 2 fonctions de bibliothèque, une réentrante et une non réentrante ou laisser la partie mutex à l'utilisateur. Ils ont choisi la seconde.

Cela peut aussi être dû au fait que les versions originales de ces fonctions n'étaient pas réentrantes et qu'elles ont été déclarées comme telles pour des raisons de compatibilité.

-4voto

Puppy Points 90818

Si vous essayez d'appeler malloc à partir de deux threads séparés (à moins que vous n'ayez une version thread-safe, non garantie par la norme C), de mauvaises choses se produisent, car il n'y a qu'un seul tas pour deux threads. Idem pour printf - le comportement est indéfini. C'est ce qui les rend en réalité non rentables.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X