IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

stratégie de gestion d'échec d'allocation mémoire


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 65
    Par défaut stratégie de gestion d'échec d'allocation mémoire
    Comment gérer professionnellement un échec de malloc/realloc/calloc ?

    Ma première tentation en tant que développeur de script serait de sortir violemment par un exit 1. Est-ce une chose à faire dans le cadre d'une application de moyenne importance ?

    Faut-il implémenter une routine chargée d'écrire ce qui peut l'être dans les fichiers ouverts par exemple ?
    Auquel cas, peut-être utiliser un gestionnaire de signaux ? Ce qui parait quand même assez complêxe à priori.

    Dans un sous-programme, se contenter de rendre la main en signalant l'erreur ?
    Mais est-ce utile de songer à poursuivre alors que le système ne peut allouer de mémoire ?!

    1/ Comment gére-t-on correctement jusqu'au-bout ce problème au delà des exemples simples quel l'on trouve dans les tutoriels et autres bouquins ?
    - dans des cas non-critiques
    - dans des applis critiques, je pense aux systèmes embarqués par exemple ?

    2/ Comment simuler à des fins de test un ratage de malloc ?

  2. #2
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 65
    Par défaut
    J'ai un début de solution à ma question 1 en lisant ceci: http://www.developpez.net/forums/d78...ignal-sigsegv/

    Effectivement, un gestionnaire de signaux peut être une idée.

    Quant à ma question 2, je n'ai pas encore trouvé.

    En fait, pour dire les choses plus simplement. Ce qui me trouble, c'est pourquoi se contenter de faire un return (comme j'ai pu le lire en général dans les exemples) dans le cas d'un problème d'allocation mémoire au niveau d'un sous-programme, ce qui revient à reporter la gestion du problème au niveau de la fonction appelante, quand on pourrait envisager un exit directement ?

    Est-ce juste pour une question de pureté du codage ?

    Je ne suis plus sûr non plus de bien comprendre le comportement du système vis à vis du programme en tel cas. N'est-il pas tué de toute façon sur le signal SIGSEV comme j'ai cru le lire dans le lien plus haut ? Auquel cas, pourquoi même gérer le problème d'un malloc qui retourne NULL ?!

  3. #3
    Membre Expert Avatar de plxpy
    Homme Profil pro
    Ingénieur géographe
    Inscrit en
    Janvier 2009
    Messages
    792
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur géographe
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2009
    Messages : 792
    Par défaut
    Pour répondre uniquement à ta deuxième question (le sujet général que tu abordes est très vaste et, même si j'ai quelques idées/conseils, en 5 minutes, je ne sais pas faire)

    Demande simplement l'allocation d'une taille très importante, trop importante, soit d'un coup, soit en cumulé (exemple ci-dessous)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #include <stdio.h>
    #include <stdlib.h>
     
    #define MEGA_200        209715200
     
    int main(void)
    {
            int i;
     
            for (i=1;;i++)
            {
                    if (malloc(MEGA_200))
                    {
                            printf("%d x 200Mo : ok\n",i);
                            continue;
                    }
                    printf("%d x 200Mo: ECHEC\n",i);
                    break;
            }
     
            printf("apres echec malloc, ca continue\n");
    }
    Ce bout de code te permet aussi de voir que, si tu traites correctement le retour du malloc, ton programme continue à tourner.

    Ensuite, faire un exit directement après un échec d'allocation ou retourner un code d'erreur, ça rejoint le sujet général de ton post. Si tu souhaites traiter "proprement" cet échec, la fonction où se produit l'échec d'allocation n'a pas forcément connaissance du contexte (pas du tout ou trop peu) et c'est à un niveau "plus haut", où le contexte est connu, de "faire ce qu'il faut" pour terminer ou continuer proprement.

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 65
    Par défaut
    Merci pour tes éclaircissements qui me suffisent.
    Et j'avais cru qu'un signal SIGSEGV était envoyé mais non.

  5. #5
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Par défaut
    Citation Envoyé par vitoubien Voir le message
    Comment gérer professionnellement un échec de malloc/realloc/calloc ?

    Ma première tentation en tant que développeur de script serait de sortir violemment par un exit 1. Est-ce une chose à faire dans le cadre d'une application de moyenne importance ?

    Faut-il implémenter une routine chargée d'écrire ce qui peut l'être dans les fichiers ouverts par exemple ?
    Auquel cas, peut-être utiliser un gestionnaire de signaux ? Ce qui parait quand même assez complêxe à priori.

    Dans un sous-programme, se contenter de rendre la main en signalant l'erreur ?
    Mais est-ce utile de songer à poursuivre alors que le système ne peut allouer de mémoire ?!

    1/ Comment gére-t-on correctement jusqu'au-bout ce problème au delà des exemples simples quel l'on trouve dans les tutoriels et autres bouquins ?
    - dans des cas non-critiques
    - dans des applis critiques, je pense aux systèmes embarqués par exemple ?
    Il n'y a pas de réponse générique à cette question. Cela va dépendre énormément du contexte.

    Citation Envoyé par vitoubien Voir le message
    En fait, pour dire les choses plus simplement. Ce qui me trouble, c'est pourquoi se contenter de faire un return (comme j'ai pu le lire en général dans les exemples) dans le cas d'un problème d'allocation mémoire au niveau d'un sous-programme, ce qui revient à reporter la gestion du problème au niveau de la fonction appelante, quand on pourrait envisager un exit directement ?

    Est-ce juste pour une question de pureté du codage ?
    Pour plusieurs raisons, les premières me venant à l'esprit étant:
    • exit n'est pas toujours une solution adéquate,
    • le "sous-programme" n'a pas nécessairement connaissance de tous les éléments permettant de prendre une décision,
    • si on souhaite écrire du code réutilisable, il est souvent préférable de ne pas donner la responsabilité du traitement d'erreur de ce type au couche les plus basses mais de le propager aux couches plus hautes.

  6. #6
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Par défaut
    Citation Envoyé par vitoubien Voir le message
    Comment gérer professionnellement un échec de malloc/realloc/calloc ?
    Impossible de répondre sans connaître le contexte du programme : on ne fait pas du tout la même chose dans une IHM et dans un programme bas niveau...

    Citation Envoyé par vitoubien Voir le message
    Ma première tentation en tant que développeur de script serait de sortir violemment par un exit 1. Est-ce une chose à faire dans le cadre d'une application de moyenne importance ?
    Sortir brutalement en cas d'échec de l'allocation n'est en général "autorisé" (=pas considéré comme une technique de porc) que dans des programmes de très bas niveau, qui fonctionnent de façon manichéenne (100% OK ou 100% KO, pas de "millieu").

    Citation Envoyé par vitoubien Voir le message
    Faut-il implémenter une routine chargée d'écrire ce qui peut l'être dans les fichiers ouverts par exemple ?
    Là, cela dépend du moment où échoue l'allocation... En initialisation de programme, tu peux être un peu "brutal" et simplement tout arrêter.
    En cours de fonctionnement, il est en général apprécié de tenter de continuer au maximum, ou au minimum de sortir proprement (flushs et fermeture des handles, suppression des données temporaires, etc.).

    Citation Envoyé par vitoubien Voir le message
    Dans un sous-programme, se contenter de rendre la main en signalant l'erreur ?
    Oui, c'est en général ce que l'on doit faire. Par contre, encore faut-il traiter l'erreur au niveau de l'appelant...

    Citation Envoyé par vitoubien Voir le message
    Mais est-ce utile de songer à poursuivre alors que le système ne peut allouer de mémoire ?!
    Pourquoi est-ce que ça ne le serait pas ?

    Exemple concret : je fais un éditeur de fichiers. Il marche, tout va bien. Je décide d'ouvrir un "monstre" : cela échoue par manque de mémoire. Dois-je planter mon application pour ça, sachant que j'ai peut-être d'autres fichiers non-sauvés déjà ouverts, et que l'utilisateur n'a pas envie de perdre ?

    Citation Envoyé par vitoubien Voir le message
    1/ Comment gére-t-on correctement jusqu'au-bout ce problème au delà des exemples simples quel l'on trouve dans les tutoriels et autres bouquins ?
    - dans des cas non-critiques
    On prévient en général l'utilisateur que la fonction a échoué, qu'il doit tenter plus tard ou se débrouiller.

    Citation Envoyé par vitoubien Voir le message
    - dans des applis critiques, je pense aux systèmes embarqués par exemple ?
    Il y a "critique" et "critique"... Tu as deux cas principaux :
    • Obligation de résultat : C'est simple, la fonction ne doit PAS échouer. Point. Au pire, on accepte un fonctionnement dégradé, mais le programme DOIT continuer de tourner quoi qu'il arrive.
      Si l'allocation dynamique ne marche pas, on passe sur un fichier de swap, et/ou on gère le swap soi-même, et/ou on connait à l'avance la taille maximale possible des données dynamiques.
      Tu trouves ça en général dans des systèmes à tolérance de panne.
    • Définition par contraintes : Le système doit être dimensionné pour permettre au programme de fonctionner. Si une allocation échoue, l'intégralité du système s'arrête car il n'est plus dans les conditions nominales.
      C'est en général le cas de la plupart des applications embarquées "classiques", surtout celles en temps réel qui n'ont pas le temps de jouer à allouer la mémoire : on alloue en général tout ce qu'il faut à l'initialisation, et si ça plante, on ne démarre rien du tout.


    Citation Envoyé par vitoubien Voir le message
    2/ Comment simuler à des fins de test un ratage de malloc ?
    Via un "faux" malloc qui, en fonction du nombre d'itérations effectuées et/ou de la taille demandée, va échouer ou renvoyer un résultat correct.

    Par exemple, tu peux décider de faire foirer les allocations du 30ème au 40ème appel, et/ou de faire échouer toutes les demandes réclamant entre 1.000 et 10.000 octets (et d'honorer toutes les autres).
    Bien sûr, cela demande de définir une macro d'allocation afin de ne pas avoir à retoucher l'intégralité du code.

    Tu peux aussi avoir des choses nettement plus complexes, comme par exemple (si le préprocesseur le permet) utiliser le nom de la fonction appelante, et/ou le nom de fichier / numéro de ligne de l'appel initial, afin d'avoir des tests plus fins et plus ciblés.

    Exemple :
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void* debug_malloc ( char* File, int Line, size_t Size ) {
      printf("Allocation in file %s, line %d : %d bytes.\n",File,Line,Size);
     
      if ( /* Tests éventuels sur File/Line */)
        return NULL ;
      else
        return malloc(Size) ;
    }
     
    #ifdef DEBUG
      #define ALLOCATE(Size) debug_malloc(__FILE__,__LINE__,Size)
    #else
      #define ALLOCATE malloc
    #endif

    Tu peux aussi faire une macro d'allocation au cas par cas, pour laquelle tu définis toi-même si elle va échouer ou pas :
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #ifdef DEBUG
      #define ALLOCATE(Fail,Size) ((Fail)?(NULL):(malloc(Size)))
    #else
      #define ALLOCATE(Fail,Size) malloc(Size)
    #end
    Ainsi, en mode "Release", toutes les allocations sont parfaitement normales.
    En mode Debug, tu peux définir pour chaque allocation si elle va échouer ou pas.
    On peut faire encore plus "fin" au besoin, en rajoutant un compteur d'itérations et une fonction d'allocation spéciale pour le mode Debug. En cherchant un peu, on doit même pouvoir faire échouer la compilation en Release s'il reste un paramètre "Fail" non-nul, histoire de laisser le code source propre...
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 65
    Par défaut
    @Mac LAK,

    C'est un vrai mini-mémoire que tu m'as fait sur ce sujet que je n'avais encore jamais vu traité jusqu'ici. Précieux et à conserver.
    Même si mon niveau de grand-débutant en C ne me permet pas encore de tout comprendre dans le détail du codage à la dernière partie sur les tests, que c'est presque gênant.
    Mais je perçois beaucoup mieux maintenant les bonnes questions à se poser et comment y répondre quant au reste.
    L'impression rassurante tout de suite de moins naviguer au doigt mouillé en terra incognita la prochaine fois que je ferai un test de retour de malloc.
    Merci beaucoup !

  8. #8
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Par défaut
    De rien, et faut pas te sentir gêné : j'ai "un peu" d'avance sur toi sur le sujet, c'est normal que j'ai des "trucs" qui ne te paraissent pas évidents au premier abord !!

    Pour le reste, essaie les macros que j'ai donné, quitte à virer la partie "test" de "debug_malloc", afin de voir un peu mieux ce que ça fait. Bien entendu, pour éviter d'avoir à tripoter le code sans arrêt, je te conseille d'utiliser des macros définies à 0 ou 1 indiquant quels sont les mallocs à faire échouer/réussir, et tu passes ces macros comme paramètre "Fail".

    Exemple :
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #define MF_TEMP_STRING 1    // Flag pour les chaînes temporaires.
     
    ...
     
    char* string_function ( ...... ) {
       char temp_string[MAXPATH] ;  // Buffer statique interne.
       char* result ;                 // Retour de la fonction, alloué "au plus juste".
       ....
       // Fais une copie du buffer interne et la renvoie.
       result = ALLOCATE(MF_TEMP_STRING,strlen(temp_string));
       if (result)    // Copie si tout s'est bien passé.
          strcpy(result, temp_string);
       return result ;
    }
    Ainsi, tu peux regrouper dans un seul entête ("debug_defs.h" par exemple) toutes les définitions type "MF_TEMP_STRING", en nombre plus ou moins grand en fonction de la finesse de ton test, et qui sont donc modifiables et vérifiables d'un seul coup d'œil. Et surtout, changer le jeu de test ne t'obligera pas à modifier le code !

    Tu peux même faire encore plus vicieux, en définissant une macro de cette manière :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #define MF_IMPORTANT_FUNCTION (!(rand() % 5))
     
    ...
     
       value = ALLOCATE(MF_IMPORTANT_FUNCTION,sizeof(....));
    Sous réserve d'une initialisation correcte du générateur pseudo-aléatoire (voir cette discussion), tu vas donc avoir l'effet suivant intéressant : en moyenne, l'allocation échouera une fois sur 5 à l'exécution...
    Toujours sympathique pour stresser un peu ton programme lors des tests, et ne pas être "trop" prévisible...
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Août 2009
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 65
    Par défaut
    Ma gêne vient surtout du fait que je doute d'avoir jamais à être aussi vicieux.
    Trop d'avance dans mon retard.

  10. #10
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Par défaut
    Citation Envoyé par vitoubien Voir le message
    Ma gêne vient surtout du fait que je doute d'avoir jamais à être aussi vicieux.
    Trop d'avance dans mon retard.
    Cela vient avec l'expérience, et surtout en fonction du domaine d'activité, tout simplement...
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Gestion des allocations mémoires
    Par Khan34 dans le forum C++
    Réponses: 10
    Dernier message: 25/04/2012, 15h04
  2. Conteneur map, set, échec allocation mémoire
    Par _niko_ dans le forum SL & STL
    Réponses: 4
    Dernier message: 14/10/2011, 14h12
  3. Allocation mémoire
    Par DestyNov@ dans le forum C++
    Réponses: 9
    Dernier message: 23/08/2005, 08h09
  4. [Pointeur] Allocation mémoire
    Par Rayek dans le forum Langage
    Réponses: 22
    Dernier message: 20/05/2005, 10h26
  5. Allocation mémoire dynamique
    Par ITISAR dans le forum VB 6 et antérieur
    Réponses: 6
    Dernier message: 21/01/2005, 09h59

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo