web-dev-qa-db-fra.com

Pourquoi malloc () et printf () sont-ils dits non réentrants?

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

De même, on dit que printf() est non réentrant; Pourquoi?

Je connais la définition de la rentrée, mais je voulais savoir pourquoi cela s'applique à ces fonctions. Qu'est-ce qui empêche leur garantie d'être réentrant?

37
ultimate cause

malloc et printf utilisent généralement des structures globales et utilisent une synchronisation basée sur le verrouillage en interne. C'est pourquoi ils ne sont pas réentrants.

La fonction malloc peut être thread-safe ou thread-unsafe. Les deux ne sont pas réentrants:

  1. Malloc fonctionne sur un segment global et il est possible que deux invocations différentes de malloc qui se produisent en même temps renvoient le même bloc de mémoire. (Le 2ème appel malloc devrait avoir lieu avant qu'une adresse du morceau ne soit extraite, mais le morceau n'est pas marqué comme indisponible). Ceci viole la condition postérieure de malloc, donc cette implémentation ne serait pas ré-entrante.

  2. Pour éviter cet effet, une implémentation thread-safe de malloc utiliserait une synchronisation basée sur un verrou. Toutefois, 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 sera simplement appelé à partir de différents threads. En effet, le concept de réentrance va au-delà de la sécurité des threads et nécessite également que les fonctions fonctionnent correctement même si l'une de ses invocations ne se termine jamais C'est essentiellement la raison pour laquelle toute fonction avec des verrous ne serait pas ré-entrante.

La fonction printf a également été utilisée pour les données globales. Tout flux de sortie utilise généralement un tampon global attaché aux ressources. Des données sont envoyées (tampon de terminal ou fichier). Le processus d'impression consiste généralement à copier les données dans la mémoire tampon, puis à vider la mémoire tampon. Ce tampon doit être protégé par des verrous de la même manière que malloc. Par conséquent, printf est également non réentrant.

53
P Shved

Comprenons ce que nous entendons par ré-entrant . Une fonction ré-entrante peut être invoquée avant la fin d'une invocation précédente. Cela pourrait arriver si 

  • une fonction est appelée dans un gestionnaire de signaux (ou plus généralement qu'Unix, un gestionnaire d'interruptions) pour un signal généré lors de l'exécution de la fonction
  • une fonction s'appelle récursivement

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 fichier FILE * stout.

11
JeremyP

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 de mémoire et la désallocation.

1
stdan28