[C/Unix] Problème de copie à l'écriture avec fork()
13 réponses
JKB
Bonjour à tous,
Je butte sur un problème qui doit être idiot. J'ai un programme
écrit en C sous Unix qui a une fuite de mémoire assez conséquente
lors d'un fork (ça peut faire quelques Mo).
Considérons une processus père qui effectue une boucle. Dans cette
boucle, il fait un fork() et continue ses traitements dans lesquels
il y a des allocations de gros blocs de mémoire.
Le fils est fichu de voir ces allocations alors qu'elles ont lieu
_après_ le fork().
Grossièrement, l'algorithme est le suivant :
while(1)
{
ios = fork();
if (ios = 0)
{
// plein de trucs tordus
exit(0);
}
else
{
// sleep();
mem = malloc();
// plein de trucs abscons
free(mem);
}
}
et je récupère le pointeur mem dans le processus fils ! Je ne vois
qu'une seule explication, le fork() en question est une copie à
l'écriture pour le fils et se fait entre le malloc() et le free().
Ou alors, le free() est reporté à plus tard et s'exécute en tâche de
fond lors de l'appel à fork(). Il faut noter que je ne
récupère ce pointeur qu'après quelques itérations. Je suis incapable
de le recevoir lors de la première, mais je ne sais pas si c'est
significatif.
Le père n'a _aucune_ fuite de mémoire (testé dans tous les sens avec
valgrind et des outils fait maison). La fuite n'apparaît que dans
les fils.
Si je rajoute un sleep() pour bloquer le père durant quelques
dixièmes de secondes, la fuite de mémoire disparaît dans les fils.
Il y a un thread parallèle au père mais il ne fait strictement
aucune allocation et j'ai testé avec hellgrind qui ne m'a trouvé
aucune race condition.
Est-ce que l'un d'entre vous aurait une idée pour forcer la copie
dès le fork() et éliminer ce problème ? Je ne suis pas sûr que ça
vienne de là, mais je préfère ôter ce doute avant de chercher
ailleurs.
Cordialement,
JKB
--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
=> http://grincheux.de-charybde-en-scylla.fr
Le 17-06-2010, ? propos de [C/Unix] Problème de copie à l'écriture avec fork(),
Bon, je me réponds à moi-même puisque je viens de trouver une solution qui semble fonctionner. Ce n'est pas franchement optimal et j'aurais tendance à appeler ça du goret codage.
J'arrête le fonctionnement du père juste après le fork() jusqu'à celui-ci recoive un signal du fils disant qu'il a démarré et qu'il a déjà écrit quelque chose en mémoire pour forcer le COW.
Ce que je n'arrive pas à vraiment saisir, c'est le fonctionnement u COW. Je pensais bêtement que ça fonctionnait dans les deux sens, le fils faisant une copie de l'image mémoire du père dès que le père ou le fils écrit quelque chose dans cette image. Visiblement, ce n'est valable que lorsque le fils écrit quelque chose...
Pour ceux que ça intéresse, le code source est ici : <http://www.rpl2.fr/cgi-bin/cvsweb/rpl/src/instructions_d5.c?annotate=1.28;sortbyÚte> aux alentours de la ligne 1161.
Cordialement,
JKB
-- Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre masse corporelle, mais disperse à lui seul 25% de l'énergie que nous consommons tous les jours. => http://grincheux.de-charybde-en-scylla.fr
Le 17-06-2010, ? propos de
[C/Unix] Problème de copie à l'écriture avec fork(),
Bon, je me réponds à moi-même puisque je viens de trouver une
solution qui semble fonctionner. Ce n'est pas franchement optimal et
j'aurais tendance à appeler ça du goret codage.
J'arrête le fonctionnement du père juste après le fork() jusqu'à
celui-ci recoive un signal du fils disant qu'il a démarré et qu'il a
déjà écrit quelque chose en mémoire pour forcer le COW.
Ce que je n'arrive pas à vraiment saisir, c'est le fonctionnement u
COW. Je pensais bêtement que ça fonctionnait dans les deux sens, le
fils faisant une copie de l'image mémoire du père dès que le père ou
le fils écrit quelque chose dans cette image. Visiblement, ce n'est
valable que lorsque le fils écrit quelque chose...
Pour ceux que ça intéresse, le code source est ici :
<http://www.rpl2.fr/cgi-bin/cvsweb/rpl/src/instructions_d5.c?annotate=1.28;sortbyÚte>
aux alentours de la ligne 1161.
Cordialement,
JKB
--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
=> http://grincheux.de-charybde-en-scylla.fr
Le 17-06-2010, ? propos de [C/Unix] Problème de copie à l'écriture avec fork(),
Bon, je me réponds à moi-même puisque je viens de trouver une solution qui semble fonctionner. Ce n'est pas franchement optimal et j'aurais tendance à appeler ça du goret codage.
J'arrête le fonctionnement du père juste après le fork() jusqu'à celui-ci recoive un signal du fils disant qu'il a démarré et qu'il a déjà écrit quelque chose en mémoire pour forcer le COW.
Ce que je n'arrive pas à vraiment saisir, c'est le fonctionnement u COW. Je pensais bêtement que ça fonctionnait dans les deux sens, le fils faisant une copie de l'image mémoire du père dès que le père ou le fils écrit quelque chose dans cette image. Visiblement, ce n'est valable que lorsque le fils écrit quelque chose...
Pour ceux que ça intéresse, le code source est ici : <http://www.rpl2.fr/cgi-bin/cvsweb/rpl/src/instructions_d5.c?annotate=1.28;sortbyÚte> aux alentours de la ligne 1161.
Cordialement,
JKB
-- Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre masse corporelle, mais disperse à lui seul 25% de l'énergie que nous consommons tous les jours. => http://grincheux.de-charybde-en-scylla.fr
Antoine Leca
JKB écrivit :
Ce que je n'arrive pas à vraiment saisir, c'est le fonctionnement u COW. Je pensais bêtement que ça fonctionnait dans les deux sens, le fils faisant une copie de l'image mémoire du père dès que le père ou le fils écrit quelque chose dans cette image.
Je le voyais un peu différemment : Lors du fork(), toutes les pages de la région .data sont marquées lecture seule + COW avec deux possesseurs (plus exactement, un possesseur de plus que précédemment) ; puis, lorsque un processus écrit, cela génère une faute de page, qui détecte le drapeau COW, qui dissocie pour le processus écrivant la page en question et lui fournit une copie inscriptible. Je ne vois pourquoi il devrait faire une différence entre savoir si c'est le fils ou le père (surtout que s'il y a des petits-enfants, cela va vite devenir incompréhensible).
La vraie question, c'est qu'advient-il pour l'autre processus (celui qui n'a _pas_ demandé l'écriture donc la copie) : d'abord, il faut voir que la question ne se pose que s'il y a au départ exactement deux posses- seurs de la page marquée COW (s'il y en a plus, la version lecture seule reste en lecture seule COW). Une solution est de passer alors la page originale à l'état inscriptible, et donc annuler l'état COW : cela économise une possible faute de page, mais peut coûter en terme de cohérence de TLB, surtout si on est en multi-processeur. Une autre solution est de mettre la tête dans le sable, et si plus tard il y a effectivement une seconde faute de page, on détectera alors qu'il y a plus qu'un seul possesseur de la page, donc on ne fera pas de copie, on marque la page lisible et c'est reparti.
Cela étant, cela ne m'explique pas ce qui se peut se passer avec ta bécane. Je soupçonne un pépin avec l'allocateur mémoire qui multiplexe mal à travers les processus, mais je ne vois pas comment ce cas, qui n'est pas si complexe que cela, pourrait avoir échappé aux tests...
Antoine
JKB écrivit :
Ce que je n'arrive pas à vraiment saisir, c'est le fonctionnement u
COW. Je pensais bêtement que ça fonctionnait dans les deux sens, le
fils faisant une copie de l'image mémoire du père dès que le père ou
le fils écrit quelque chose dans cette image.
Je le voyais un peu différemment : Lors du fork(), toutes les pages de
la région .data sont marquées lecture seule + COW avec deux possesseurs
(plus exactement, un possesseur de plus que précédemment) ; puis,
lorsque un processus écrit, cela génère une faute de page, qui détecte
le drapeau COW, qui dissocie pour le processus écrivant la page en
question et lui fournit une copie inscriptible. Je ne vois pourquoi il
devrait faire une différence entre savoir si c'est le fils ou le père
(surtout que s'il y a des petits-enfants, cela va vite devenir
incompréhensible).
La vraie question, c'est qu'advient-il pour l'autre processus (celui qui
n'a _pas_ demandé l'écriture donc la copie) : d'abord, il faut voir que
la question ne se pose que s'il y a au départ exactement deux posses-
seurs de la page marquée COW (s'il y en a plus, la version lecture seule
reste en lecture seule COW).
Une solution est de passer alors la page originale à l'état
inscriptible, et donc annuler l'état COW : cela économise une possible
faute de page, mais peut coûter en terme de cohérence de TLB, surtout si
on est en multi-processeur.
Une autre solution est de mettre la tête dans le sable, et si plus tard
il y a effectivement une seconde faute de page, on détectera alors qu'il
y a plus qu'un seul possesseur de la page, donc on ne fera pas de copie,
on marque la page lisible et c'est reparti.
Cela étant, cela ne m'explique pas ce qui se peut se passer avec ta
bécane. Je soupçonne un pépin avec l'allocateur mémoire qui multiplexe
mal à travers les processus, mais je ne vois pas comment ce cas, qui
n'est pas si complexe que cela, pourrait avoir échappé aux tests...
Ce que je n'arrive pas à vraiment saisir, c'est le fonctionnement u COW. Je pensais bêtement que ça fonctionnait dans les deux sens, le fils faisant une copie de l'image mémoire du père dès que le père ou le fils écrit quelque chose dans cette image.
Je le voyais un peu différemment : Lors du fork(), toutes les pages de la région .data sont marquées lecture seule + COW avec deux possesseurs (plus exactement, un possesseur de plus que précédemment) ; puis, lorsque un processus écrit, cela génère une faute de page, qui détecte le drapeau COW, qui dissocie pour le processus écrivant la page en question et lui fournit une copie inscriptible. Je ne vois pourquoi il devrait faire une différence entre savoir si c'est le fils ou le père (surtout que s'il y a des petits-enfants, cela va vite devenir incompréhensible).
La vraie question, c'est qu'advient-il pour l'autre processus (celui qui n'a _pas_ demandé l'écriture donc la copie) : d'abord, il faut voir que la question ne se pose que s'il y a au départ exactement deux posses- seurs de la page marquée COW (s'il y en a plus, la version lecture seule reste en lecture seule COW). Une solution est de passer alors la page originale à l'état inscriptible, et donc annuler l'état COW : cela économise une possible faute de page, mais peut coûter en terme de cohérence de TLB, surtout si on est en multi-processeur. Une autre solution est de mettre la tête dans le sable, et si plus tard il y a effectivement une seconde faute de page, on détectera alors qu'il y a plus qu'un seul possesseur de la page, donc on ne fera pas de copie, on marque la page lisible et c'est reparti.
Cela étant, cela ne m'explique pas ce qui se peut se passer avec ta bécane. Je soupçonne un pépin avec l'allocateur mémoire qui multiplexe mal à travers les processus, mais je ne vois pas comment ce cas, qui n'est pas si complexe que cela, pourrait avoir échappé aux tests...
Antoine
JKB
Le 18-06-2010, ? propos de Re: [C/Unix] Problème de copie à l'écriture avec fork(), Antoine Leca ?crivait dans fr.comp.lang.c :
JKB écrivit :
Ce que je n'arrive pas à vraiment saisir, c'est le fonctionnement u COW. Je pensais bêtement que ça fonctionnait dans les deux sens, le fils faisant une copie de l'image mémoire du père dès que le père ou le fils écrit quelque chose dans cette image.
Je le voyais un peu différemment : Lors du fork(), toutes les pages de la région .data sont marquées lecture seule + COW avec deux possesseurs (plus exactement, un possesseur de plus que précédemment) ; puis, lorsque un processus écrit, cela génère une faute de page, qui détecte le drapeau COW, qui dissocie pour le processus écrivant la page en question et lui fournit une copie inscriptible. Je ne vois pourquoi il devrait faire une différence entre savoir si c'est le fils ou le père (surtout que s'il y a des petits-enfants, cela va vite devenir incompréhensible).
La vraie question, c'est qu'advient-il pour l'autre processus (celui qui n'a _pas_ demandé l'écriture donc la copie) : d'abord, il faut voir que la question ne se pose que s'il y a au départ exactement deux posses- seurs de la page marquée COW (s'il y en a plus, la version lecture seule reste en lecture seule COW). Une solution est de passer alors la page originale à l'état inscriptible, et donc annuler l'état COW : cela économise une possible faute de page, mais peut coûter en terme de cohérence de TLB, surtout si on est en multi-processeur. Une autre solution est de mettre la tête dans le sable, et si plus tard il y a effectivement une seconde faute de page, on détectera alors qu'il y a plus qu'un seul possesseur de la page, donc on ne fera pas de copie, on marque la page lisible et c'est reparti.
Cela étant, cela ne m'explique pas ce qui se peut se passer avec ta bécane. Je soupçonne un pépin avec l'allocateur mémoire qui multiplexe mal à travers les processus, mais je ne vois pas comment ce cas, qui n'est pas si complexe que cela, pourrait avoir échappé aux tests...
Tu ne peux pas imaginer les trucs sur lesquels je suis déjà tombé en terme d'allocation de mémoire (surtout sous Solaris et Linux, deux spécialistes de la chose).
JKB
-- Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre masse corporelle, mais disperse à lui seul 25% de l'énergie que nous consommons tous les jours. => http://grincheux.de-charybde-en-scylla.fr
Le 18-06-2010, ? propos de
Re: [C/Unix] Problème de copie à l'écriture avec fork(),
Antoine Leca ?crivait dans fr.comp.lang.c :
JKB écrivit :
Ce que je n'arrive pas à vraiment saisir, c'est le fonctionnement u
COW. Je pensais bêtement que ça fonctionnait dans les deux sens, le
fils faisant une copie de l'image mémoire du père dès que le père ou
le fils écrit quelque chose dans cette image.
Je le voyais un peu différemment : Lors du fork(), toutes les pages de
la région .data sont marquées lecture seule + COW avec deux possesseurs
(plus exactement, un possesseur de plus que précédemment) ; puis,
lorsque un processus écrit, cela génère une faute de page, qui détecte
le drapeau COW, qui dissocie pour le processus écrivant la page en
question et lui fournit une copie inscriptible. Je ne vois pourquoi il
devrait faire une différence entre savoir si c'est le fils ou le père
(surtout que s'il y a des petits-enfants, cela va vite devenir
incompréhensible).
La vraie question, c'est qu'advient-il pour l'autre processus (celui qui
n'a _pas_ demandé l'écriture donc la copie) : d'abord, il faut voir que
la question ne se pose que s'il y a au départ exactement deux posses-
seurs de la page marquée COW (s'il y en a plus, la version lecture seule
reste en lecture seule COW).
Une solution est de passer alors la page originale à l'état
inscriptible, et donc annuler l'état COW : cela économise une possible
faute de page, mais peut coûter en terme de cohérence de TLB, surtout si
on est en multi-processeur.
Une autre solution est de mettre la tête dans le sable, et si plus tard
il y a effectivement une seconde faute de page, on détectera alors qu'il
y a plus qu'un seul possesseur de la page, donc on ne fera pas de copie,
on marque la page lisible et c'est reparti.
Cela étant, cela ne m'explique pas ce qui se peut se passer avec ta
bécane. Je soupçonne un pépin avec l'allocateur mémoire qui multiplexe
mal à travers les processus, mais je ne vois pas comment ce cas, qui
n'est pas si complexe que cela, pourrait avoir échappé aux tests...
Tu ne peux pas imaginer les trucs sur lesquels je suis déjà tombé en
terme d'allocation de mémoire (surtout sous Solaris et Linux, deux
spécialistes de la chose).
JKB
--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
=> http://grincheux.de-charybde-en-scylla.fr
Le 18-06-2010, ? propos de Re: [C/Unix] Problème de copie à l'écriture avec fork(), Antoine Leca ?crivait dans fr.comp.lang.c :
JKB écrivit :
Ce que je n'arrive pas à vraiment saisir, c'est le fonctionnement u COW. Je pensais bêtement que ça fonctionnait dans les deux sens, le fils faisant une copie de l'image mémoire du père dès que le père ou le fils écrit quelque chose dans cette image.
Je le voyais un peu différemment : Lors du fork(), toutes les pages de la région .data sont marquées lecture seule + COW avec deux possesseurs (plus exactement, un possesseur de plus que précédemment) ; puis, lorsque un processus écrit, cela génère une faute de page, qui détecte le drapeau COW, qui dissocie pour le processus écrivant la page en question et lui fournit une copie inscriptible. Je ne vois pourquoi il devrait faire une différence entre savoir si c'est le fils ou le père (surtout que s'il y a des petits-enfants, cela va vite devenir incompréhensible).
La vraie question, c'est qu'advient-il pour l'autre processus (celui qui n'a _pas_ demandé l'écriture donc la copie) : d'abord, il faut voir que la question ne se pose que s'il y a au départ exactement deux posses- seurs de la page marquée COW (s'il y en a plus, la version lecture seule reste en lecture seule COW). Une solution est de passer alors la page originale à l'état inscriptible, et donc annuler l'état COW : cela économise une possible faute de page, mais peut coûter en terme de cohérence de TLB, surtout si on est en multi-processeur. Une autre solution est de mettre la tête dans le sable, et si plus tard il y a effectivement une seconde faute de page, on détectera alors qu'il y a plus qu'un seul possesseur de la page, donc on ne fera pas de copie, on marque la page lisible et c'est reparti.
Cela étant, cela ne m'explique pas ce qui se peut se passer avec ta bécane. Je soupçonne un pépin avec l'allocateur mémoire qui multiplexe mal à travers les processus, mais je ne vois pas comment ce cas, qui n'est pas si complexe que cela, pourrait avoir échappé aux tests...
Tu ne peux pas imaginer les trucs sur lesquels je suis déjà tombé en terme d'allocation de mémoire (surtout sous Solaris et Linux, deux spécialistes de la chose).
JKB
-- Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre masse corporelle, mais disperse à lui seul 25% de l'énergie que nous consommons tous les jours. => http://grincheux.de-charybde-en-scylla.fr