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

Linux Discussion :

Segmentation fault, trop de thread fils


Sujet :

Linux

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    126
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 126
    Par défaut Segmentation fault, trop de thread fils
    Bonjour, je suis sur un projet pour les cours. On doit réaliser un système de lecteurs écrivains :

    Des requêtes (lecture ou écriture) arrivent, et elles doivent être insérées dans une file d'attente avant d'être traitées par le lancement d'un thread lecteur ou écrivain.

    Dans ma modélisation, je lance deux threads générateurs de requêtes (le premier génère des demandes de lectures, l'autre... des écritures). Les générateurs communiquent avec main() via un tableau que j'ai appelé BOITE (les générateurs remplissent la boite, le main la vide dans la file d'attente). La file d'attente est une liste chaînée.

    Bon, j'en ai fini pour le pitch, voici mon code.

    L'heure du crime

    Comme vous pouvez le voir, j'ai mis NB_QUERIES à 500, et parfois j'obtiens une segmentation fault :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    	E118 ecrit
    	L119 lit
    	L120 lit
    	E121 ecrit
    	E122 ecrit
    	E123 ecrit
    	E125 ecrit
    Segmentation fault
    Et voici ce que dit gdb :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    	L204 lit
    	L205 lit
    	E206 ecrit
    	E207 ecrit
    	L209 lit
     
    Program received signal EXC_BAD_ACCESS, Could not access memory.
    Reason: KERN_INVALID_ADDRESS at address: 0xb6b25070
    [Switching to process 478 thread 0xee03]
    0x90e35548 in pthread_getspecific ()
    Bref, je me doute que le problème est dû au nombre très important de threads, mais je ne vois pas trop comment le résoudre.

    Merci de votre aide.

  2. #2
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2008
    Messages
    1 515
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Octobre 2008
    Messages : 1 515
    Par défaut
    Citation Envoyé par Galdon Voir le message
    Bref, je me doute que le problème est dû au nombre très important de threads
    Non, il est dû à un bug dans ton code.

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    126
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 126
    Par défaut
    Dans ce cas, comment expliquer que ce bug n'intervient jamais pour un nombre de requêtes inférieur à 100 ?

    Et avec 500 requêtes, le bug ne se produit pas forcément à chaque exécution, parfois le programme termine sans problème.

  4. #4
    Membre Expert Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Par défaut
    Il y a des comportements indéterminés dans ton programme, notamment dans le dernier argument de pthread_create().

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    126
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 126
    Par défaut
    Qu'est ce qu'un comportement indéterminé et en quoi est-ce que le passage de i en paramètre est il indéterminé ?

    (avec pthread_create, le dernier argument est obligatoirement de type void).

  6. #6
    Membre éclairé

    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Février 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2005
    Messages : 464
    Par défaut
    Bonjour,
    J'ai compilé et exécuté ton code et le bog s'est produit.
    A vrai dire je n'ai pas compris tous les détails, comme l'application est à caractère 'abstrait'.
    Une chose cependant m'a frappée : il est inutile de poser des verrous sur des affectations de mots processeurs, ces opérations sont atomiques, par contre tu dois le faire sur l'ensemble des opérations modifiant des informations qui y sont liées. Je parle de la fonction générateur.
    Autre petit détail, tu débloques un verrou dans main() alors que tu ne l'a pas bloqué auparavant (je pense que le bog venait de la car l'arrêt du soft disait que tu accédais à un verrou dont tu n'est pas le propriétaire, je pense qu'a ce moment-l) un thread avait utilisé le verrou).
    Ces petites modifications faites j'ai pu exécuter plusieurs fois de suite ton programme sans accident.

    Voici ci-dessous le patch pour faire tes essais :
    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
     
    --- main.c.bak	2008-12-02 00:20:34.000000000 +0100
    +++ main.c	2008-12-02 01:32:04.000000000 +0100
    @@ -4,10 +4,26 @@
     #include <time.h>
     #include <sched.h>
     #include <errno.h>
    +#include <unistd.h>
    +#include <assert.h>
     
     /*
      gcc projet4.c -o p4 -lpthread -g3 -Wall
    -gdb p4
    +main.c: Dans la fonction «generateur» :
    +main.c:35: attention : transtypage d'un pointeur vers un entier de taille différente
    +main.c:53: attention : déclaration implicite de la fonction « «usleep» »
    +main.c: Dans la fonction «lecteur» :
    +main.c:82: attention : format «%d» expects type «int», but argument 2 has type «void *»
    +main.c:84: attention : control reaches end of non-void function
    +main.c: Dans la fonction «ecrivain» :
    +main.c:88: attention : format «%d» expects type «int», but argument 2 has type «void *»
    +main.c:91: attention : control reaches end of non-void function
    +main.c: Dans la fonction «ajouterFile» :
    +main.c:96: attention : déclaration implicite de la fonction « «filePleine» »
    +main.c: Dans la fonction «main» :
    +main.c:193: attention : transtypage vers un pointeur depuis un entier de taille différente
    +main.c:199: attention : transtypage vers un pointeur depuis un entier de taille différente
    +
     */
     
     //	Structure de file d'attente
    @@ -20,23 +36,26 @@
     
     //	Déclarations globales
     char priorite;
    -pthread_mutex_t mutex1;
     int BOITE[2];	//	Le tableau BOITE sert d'interface entre le(s) générateur(s) et main()
     
     //	Paramètres du générateur de requêtes
     pthread_mutex_t mutex_gen;
    -int NB_QUERIES = 500;
    +const int NB_QUERIES = 500;
     int NB_CREES = 0;
     int END = 0;
     int TAILLE_FILE = 5;
     int PERTES = 0;		//	Requêtes qui n'ont pu être traités pour cause de file d'attente pleine
     
    +int filePleine(llfile file_attente) ;
    +
     void *generateur(void *param){
     	int type = (int)param;		//	Forcage de type à l'ancienne, sinon : erreur compilation illisible
     	int crees = 0;				//	Au début, on n'a pas encore créé de requête
     	int pause = 0;
     	int ma_boite = 0;
     
    +	// j'ajoute ca car j'ai eu peur dans ton code principale : tu incremente i++ et tu le passes en argument dans cette fonction ?!
    +	assert((type < 2) && (type >=0)) ;
     	printf("generateur %d initialise\n",type);
     
     	//	Tant qu'on n'a pas atteint le nombre de requêtes à générer pour la simulation, on créé une nouvelle requête
    @@ -44,7 +63,6 @@
     
     		pthread_mutex_lock(&mutex_gen);
     		ma_boite = BOITE[type];
    -		pthread_mutex_unlock(&mutex_gen);
     
     		//	Si ma boite est vide, je vais générer une requête
     		if(ma_boite == 0){
    @@ -53,41 +71,33 @@
     			usleep(pause);
     			//printf("sortie pause : %d ms\n",pause/1000);
     
    -			pthread_mutex_lock(&mutex_gen);
     			BOITE[type] = 1;
    -			pthread_mutex_unlock(&mutex_gen);
     
     			//	Quand on a terminé une itération, on vient de générer une nouvelle requête, donc on met à jour le compteur
    -			pthread_mutex_lock(&mutex_gen);
     			NB_CREES++;
     			crees = NB_CREES;
    -			pthread_mutex_unlock(&mutex_gen);
     		}
     		//	Sinon, j'attends que le main me prévienne que la boite est vide (pour éviter de pédaler dans la semoule)
     		/*else{
     
     		}*/
    +		pthread_mutex_unlock(&mutex_gen);
     	}
    -	pthread_mutex_lock(&mutex_gen);
     	END++;
    -	pthread_mutex_unlock(&mutex_gen);
     
     	return 0;
     }
     
    -void *lecteur(void *tid){
    -	pthread_mutex_lock(&mutex1);
    -	pthread_mutex_unlock(&mutex1);
    -
    +void * lecteur(void *tid){
     	printf("\tL%d lit\n",tid);
     	usleep(10*1000);
    +	return NULL ;
     }
     
    -void *ecrivain(void *tid){
    -	pthread_mutex_lock(&mutex1);
    +void * ecrivain(void *tid){
     	printf("\tE%d ecrit\n",tid);
     	usleep(30*1000);
    -	pthread_mutex_unlock(&mutex1);
    +	return NULL ;
     }
     
     //	L'ajout d'une requête en file d'attente est une insertion en fin de liste
    @@ -176,10 +186,9 @@
     
     	llfile file_attente = NULL;	//	Création de la file d'attente
     
    -	pthread_mutex_init(&mutex1, NULL);
     	pthread_mutex_init(&mutex_gen, NULL);
     
    -	//	Création des deux générateurs de lecteurs et d'écrivains
    +	// Création des deux générateurs de lecteurs et d'écrivains
     	pthread_create(&gen_lecteurs, NULL, generateur, (void*)0);
     	pthread_create(&gen_ecrivains, NULL, generateur, (void*)1);
     
    @@ -227,7 +236,8 @@
     			BOITE[0] = 0;
     			BOITE[1] = 0;
     		}
    -		pthread_mutex_unlock(&mutex_gen);
    +		// Pkoi ceci ?
    +		// pthread_mutex_unlock(&mutex_gen);
     	}
     
     	pthread_join(gen_lecteurs,NULL);
    PS : pour l'assertion y'a pas de pb, j'avais mal lu.
    PS2: n'oublie pas de marquer resolu ton sujet quand ton pb sera réglé.

  7. #7
    Membre Expert Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Par défaut
    Citation Envoyé par Galdon Voir le message
    Qu'est ce qu'un comportement indéterminé et en quoi est-ce que le passage de i en paramètre est il indéterminé ?

    (avec pthread_create, le dernier argument est obligatoirement de type void).
    Non le dernier paramètre est de type (void*), hors toi tu castes ton i (type int) en (void*) ce que tu n'as pas le droit de faire.

  8. #8
    Membre Expert Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Par défaut
    Citation Envoyé par bizulk Voir le message
    Une chose cependant m'a frappée : il est inutile de poser des verrous sur des affectations de mots processeurs, ces opérations sont atomiques
    Non portable!!! c'est toi qui me frappe là!!

  9. #9
    Membre éclairé

    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Février 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2005
    Messages : 464
    Par défaut
    C'est un autre sujet, mais c'est au contraire très portable car j'ai bien parlé de mot processeur (c'est ce que désigne un int non ?).
    Après tu peux indiquer des limites à cela (je sais que l'implémentation de types peuvent changer d'une archi à l'autre), je suis preneur.
    Mais hônnetement on dérive du sujet là ....

    Sinon pour le cast en void*, je ne suis pas d'accord tu peux/dois le faire.
    Moi je compile le code suivant sans warning :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void test(void * var)
    {
            return ;
    }
     
    int main (void)
    {
            int i = 0;
            test(i) ;
            return 0 ;
    }
    sli@LinuxDev:~> gcc -O2 -Wall -ansi -pedantic main.c
    Après tu peux te demander quelle est la taille d'un void* sur ton archi pour vérifier qu'il n'y a pas 'troncage' de tes données.

    M. Stibon, je ne critique pas la pertinence de tes remarques, seulement je voudrais que tu les développes un peu plus.

  10. #10
    Membre Expert Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Par défaut
    Citation Envoyé par bizulk Voir le message
    M. Stibon, je ne critique pas la pertinence de tes remarques, seulement je voudrais que tu les développes un peu plus.
    OK,
    Pour l'atomicité des opérations, elle dépend du processeur, c'est pourquoi je conseille soit l'utilisation de mutex soit des builtins GCC pour le faire même si c'est moins portable.
    Pour ceux qui est du (void*), ça n'est pas portable, notamment si i vaut 0, on est dans un contexte pointeur, le compilateur doit "transformé" cette valeur en un pointeur NULL, cela peut être n'importe quelle valeur, dépendant de l'implémentation.

  11. #11
    Membre Expert Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Par défaut
    Citation Envoyé par bizulk Voir le message
    Moi je compile le code suivant sans warning :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void test(void * var)
    {
            return ;
    }
     
    int main (void)
    {
            int i = 0;
            test(i) ;
            return 0 ;
    }
    règle ton compilateur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    newfile.c:1: attention : no previous prototype fortest’
    newfile.c: In functiontest’:
    newfile.c:1: attention : unused parameter ‘var’
    newfile.c: In function ‘main’:
    newfile.c:9: attention : passing argument 1 of ‘test’ makes pointer from integer without a cast
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    "newfile.c", line 9: improper pointer/integer combination: arg #1

  12. #12
    Membre éclairé

    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Février 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2005
    Messages : 464
    Par défaut
    Autant pour moi je n'avais pas écris test(i) mais test((void*)i), je voulais voir les éventuels messages de compilation, ré-essaie maintenant ça doit passer
    J'utilise gcc Version 4.0.X

    D'après ce que tu dis, c'est l'utilisation de NULL qui n'est pas portable, et je n'ai pas compris pourquoi le préprocesseur voudrait traduire la valeur 0 en NULL.
    Pour ce qui est des affectations non-atomiques, je suis d'accord.

    Mais après tout le problème levé ici n'est pas la portabilité du code, mais de son implémentation qui causait des soucis à l'exécution.

  13. #13
    Membre Expert Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Par défaut
    Citation Envoyé par bizulk Voir le message
    Autant pour moi je n'avais pas écris test(i) mais test((void*)i), je voulais voir les éventuels messages de compilation, ré-essaie maintenant ça doit passer
    J'utilise gcc Version 4.0.X
    Concernant ce genre pratique, je te renvois à la signature de médinoc.

    Citation Envoyé par bizulk Voir le message
    D'après ce que tu dis, c'est l'utilisation de NULL qui n'est pas portable, et je n'ai pas compris pourquoi le préprocesseur voudrait traduire la valeur 0 en NULL.
    Je n'ai pas parlé du préprocesseur mais du compilateur qui doit dans un contexte pointeur traduire la valeur 0 en un pointeur NULL, c'est à dire en une adresse mémoire invalide. Et bien cette adresse dépend de l'implémentation. Donc imaginons que l'adresse invalide chez moi soit 0x32323232. Quand tu passes i = 0 et que tu le convertis en pointeur, il va être transformé par le compilateur en 0x32323232, et donc tu ne récupères pas pas la valeur 0 dans ton thread, mais la valeur 0x32323232, éventuellement tronqué si les types sont de tailles différentes.
    Voilà, j'espère que mon explication est assez claire.

  14. #14
    Membre éclairé

    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Février 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2005
    Messages : 464
    Par défaut
    Je me suis mal fait comprendre.
    C'est "NULL" qui possède une définition variable et que le compilateur
    va traduire en une @ invalide PROBABLEMENT APRES que le préprocesseur est remplacé cette macro : On trouve plusieurs définitions dans les headers qui sont des macros de préprocesseurs qui définissent NULL comme (void*)0 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    find /usr/include/ | xargs grep "define NULL"
    Maintenant la question est que fait le compilateur avec ceci : void * p = (void*)0 ? que vaut p ? CA VAUT ZERO ! Il n'y a pas de "traduction" de valeur mais simplement une conversion de type. En une phrase, le compilo ne se dit pas "tiens j'ai un zéro casté en void* donc je vais le traduire en une adresse invalide". Il place zéro dans p c'est tout, car zéro veut dire zéro et "pointeur null " peut-être autre chose".

  15. #15
    Membre Expert Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Par défaut
    Citation Envoyé par bizulk Voir le message
    Je me suis mal fait comprendre.
    C'est "NULL" qui possède une définition variable et que le compilateur
    va traduire en une @ invalide PROBABLEMENT APRES que le préprocesseur est remplacé cette macro :

    On trouve plusieurs définitions dans les headers qui sont des macros de préprocesseurs qui définissent NULL comme (void*)0 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    find /usr/include/ | xargs grep "define NULL"
    Maintenant la question est que fait le compilateur avec ceci : void * p = (void*)0 ? que vaut p ? CA VAUT ZERO ! Il n'y a pas de "traduction" de valeur mais simplement une conversion de type. En une phrase, le compilo ne se dit pas "tiens j'ai un zéro casté en void* donc je vais le traduire en une adresse invalide". Il place zéro dans p c'est tout, car zéro veut dire zéro et "pointeur null " peut-être autre chose".
    Tu as de grosses lacunes sur le langage qu'il te faut combler rapidement surtout si tu fais du développement industriel. Je te renvois à la norme :
    6.3.2.3 Pointers
    1 A pointer to void may be converted to or from a pointer to any incomplete or object
    type. A pointer to any incomplete or object type may be converted to a pointer to void
    and back again; the result shall compare equal to the original pointer.
    2 For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to
    the q-qualified version of the type; the values stored in the original and converted pointers
    shall compare equal.
    3 An integer constant expression with the value 0, or such an expression cast to type
    void *, is called a null pointer constant.55) If a null pointer constant is converted to a
    pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal
    to a pointer to any object or function.
    4 Conversion of a null pointer to another pointer type yields a null pointer of that type.
    Any two null pointers shall compare equal.
    5 An integer may be converted to any pointer type. Except as previously specified, the
    result is implementation-defined
    , might not be correctly aligned, might not point to an
    entity of the referenced type
    , and might be a trap representation.56)
    6 Any pointer type may be converted to an integer type. Except as previously specified, the
    result is implementation-defined
    . If the result cannot be represented in the integer type,
    the behavior is undefined. The result need not be in the range of values of any integer
    type
    .
    quelques ressources supplémentaires si tu as du mal à comprendre:
    http://web.torek.net/torek/c/numbers2.html

  16. #16
    Membre éclairé

    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Février 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2005
    Messages : 464
    Par défaut
    Tu as de grosses lacunes sur le langage qu'il te faut combler rapidement surtout si tu fais du développement industriel. Je te renvois à la norme :
    Merci, Cette conversation

    On devrait déplacer cette conversation car elle hors sujet. Moi, je ne me contente pas de critiquer la portabilité d'un code mais d'apporter la réponse à un comportement instable ou non conforme au résultat attendu, c'est en général le travail qui m'est demandé.

    Voilà comment je lis la norme :
    3) Un entier constant avec la valeur zéro casté en void* est un "pointeur null constant"
    Et d'après 5) c'est la seul définition que fait exception à la règle citée sur les conversion de type entier<->ptr.

    Maintenant je peux me tromper, mais il faudra le démontrer.

  17. #17
    Membre Expert Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Par défaut
    Ok je m'exprime peut être mal et ce qui est clair pour moi ne l'est peut être pas pour toi, je vais donc laisser un gourou s'exprimer à ma place pour t'expliquer qu'un pointeur NULL n'a pas forcément un bit pattern à 0 :
    http://groups.google.fr/group/comp.l...268a9e1b2b40db
    Si là tu ne comprends toujours pas, je ne peux plus rien pour toi.

  18. #18
    Membre éclairé

    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Février 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2005
    Messages : 464
    Par défaut
    Salut !

    La confusion vient du fait que tu utilises l'expression "pointeur NULL", qui elle n'est pas définit par la norme mais prend souvent la forme d'une macro.

    La norme etait claire pour moi, la discussion aussi. Je pense que l'on essaie de dire la même chose toi, moi, ou lui, mais que l'on ne parle pas des mêmes assertions :
    "To put it another way, I believe that choosing a value other than
    all-bits-zero for the null pointer does not imply that
    integer-to-pointer conversion has to do anything other than a bitwise
    copy *except* in the case of a null pointer constant (C99)".

    "void * p = (void*)0;" => affectation d'un pointeur null constant avec pour résultat un pointeur nul (du moins a partir de C99).
    int i = 0;
    "void * p = (void*)i;" => dépendant de l'implémentation (peut ne pas être pointeur nul, même si cela l'est dans plupart des cas, d'après le thread.)

    Respect.

    NB : Quelques liens en plus :
    http://www.open-std.org/jtc1/sc22/wg...2004/n1601.pdf
    http://dmst.aueb.gr/dds/res/cstyle/port.htm
    http://h-deb.clg.qc.ca/Sujets/Divers...CPP--NULL.html

  19. #19
    Membre éclairé

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Août 2007
    Messages
    509
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux

    Informations forums :
    Inscription : Août 2007
    Messages : 509
    Par défaut
    Citation Envoyé par nicolas.sitbon Voir le message
    Non le dernier paramètre est de type (void*), hors toi tu castes ton i (type int) en (void*) ce que tu n'as pas le droit de faire.
    Normalement, dans la boucle, avec pthread_create il est très conseillé de ne passer directement l'adresse de i qui peut etre modifié par la thread principale avant/pendant la création de la nouvelle thread.
    Donc, l'idéal serait de faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    int *ptr;
     
    for(i=0; i < NB_THREAD; i++){
     ptr = (int *) malloc(sizeof(i)); 
     pthread_create(&L[nb_lecteurs], NULL, lecteur, (void*)ptr);
     
     
    //blablabla
     
    }
    Concernant la portabilité, on pourrait programmer suivant la norme POSIX, non? On s'en fout un peu de la façon dont NULL et compagnie est implémentée du moment que le système respecte cette norme. (corrigez moi si je dis une connerie)

  20. #20
    Membre Expert Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Par défaut
    Citation Envoyé par bizulk Voir le message
    Maintenant la question est que fait le compilateur avec ceci : void * p = (void*)0 ? que vaut p ? CA VAUT ZERO ! Il n'y a pas de "traduction" de valeur mais simplement une conversion de type. En une phrase, le compilo ne se dit pas "tiens j'ai un zéro casté en void* donc je vais le traduire en une adresse invalide". Il place zéro dans p c'est tout, car zéro veut dire zéro et "pointeur null " peut-être autre chose".
    Citation Envoyé par bizulk Voir le message
    La norme etait claire pour moi, la discussion aussi. Je pense que l'on essaie de dire la même chose toi, moi, ou lui, mais que l'on ne parle pas des mêmes assertions :
    "To put it another way, I believe that choosing a value other than
    all-bits-zero for the null pointer does not imply that
    integer-to-pointer conversion has to do anything other than a bitwise
    copy *except* in the case of a null pointer constant (C99)".

    "void * p = (void*)0;" => affectation d'un pointeur null constant avec pour résultat un pointeur nul (du moins a partir de C99).
    int i = 0;
    "void * p = (void*)i;" => dépendant de l'implémentation (peut ne pas être pointeur nul, même si cela l'est dans plupart des cas, d'après le thread.)
    Tu es contradictoire sur ces 2 posts, tu dis que tu as compris la norme et tu dis dans le premier post qu'il y a forcément la valeur zéro dans ton pointeur auquel on a affecté un pointeur NULL. NON, il n'y a pas forcément la valeur 0 dans ce pointeur, le résultat dépend de l'implementation et n'est pas portable, c'est bien ce que j'ai mis en gras dans la norme donc non on ne peut pas passé i directement comme valeur dans un paramètre qui attend un pointeur.

Discussions similaires

  1. Segmentation fault with thread
    Par Kroui dans le forum Interfaces Graphiques
    Réponses: 6
    Dernier message: 05/08/2011, 13h14
  2. Segmentation fault avec threads
    Par aljekeny dans le forum Linux
    Réponses: 3
    Dernier message: 12/01/2009, 19h37
  3. Réponses: 3
    Dernier message: 21/01/2008, 14h38
  4. Réponses: 13
    Dernier message: 13/07/2004, 15h41
  5. Comment contrer la "segmentation fault" ?
    Par guillaume_pfr dans le forum C
    Réponses: 15
    Dernier message: 08/08/2003, 13h43

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