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?
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:
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.
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.
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
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.
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.