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 :

Erreur lors d'un passage d'une longue chaîne de caractère à un thread


Sujet :

C

  1. #1
    Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2023
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2023
    Messages : 2
    Points : 2
    Points
    2
    Par défaut Erreur lors d'un passage d'une longue chaîne de caractère à un thread
    Bonjour,

    Dans le script suivant, j'appelle initialement via une boucle for mon thread send_request_to_pub en lui passant en argument la variable sql_request. J'ai défini plus haut une structure d'argument car j'ai d'autres variables à lui faire passer.
    Lors de mes tests, j'ai observé que le script passait correctement par ma boucle for lorsque la variable sql_request passée en argument ne dépassait pas les 67 caractères. Dès cette limite passée, le script provoque un défaut de segmentation avant la fin du premier thread.
    J'ai essayé d'allouer dynamiquement de la mémoire à ma variable sans résultat.
    Merci par avance pour votre aide.

    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
     
    typedef struct {
    	char *sql_request;
    } thread_req_send_args;
     
    void *send_request_to_pub(void *arg) {
     
    	thread_req_send_args *args = (thread_req_send_args *)arg;
     
    	char *request = args->sql_request;
     
    	printf("---- req_thread : %s\n", request);
     
    	pthread_exit(NULL);
    }
     
    int main(int nbr, char* request[]) {
     
            char request_as_arg[400];
     
    	sprintf(request_as_arg, "%s", request[1]);
     
    	struct thread_req_send_args *args = malloc(sizeof(thread_req_send_args));
     
            args->sql_request = malloc(400);
     
            memcpy(args->sql_request, request_as_arg, sizeof(request_as_arg));
     
    	if (pthread_create(&thread_send_to_pub, NULL, send_request_to_pub, (void *)&args) != 0) {
    		system("echo $(date) ': L82 - Failed to create the sending request thread' >> /home/rpiaccess/replica_log.txt");
    		exit(1);
    	}
     
    	pthread_join(thread_send_to_pub, NULL);
     
    	free(args->sql_request);
    	free(args);
     
    	pthread_exit(NULL);
    }

  2. #2
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 684
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 684
    Points : 30 973
    Points
    30 973
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par Lucas30 Voir le message
    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
    void *send_request_to_pub(void *arg) {
    	thread_req_send_args *args = (thread_req_send_args *)arg;
    	...
    }
     
    int main(int nbr, char* request[]) {
     
    	...
    	struct thread_req_send_args *args = malloc(sizeof(thread_req_send_args));
    	...
    	if (pthread_create(&thread_send_to_pub, NULL, send_request_to_pub, (void *)&args) != 0) {
    		...
    	}
    	...tread_exit(NULL);
    }
    Si "args" est un "struct thread_req_send_args étoile", alors "&args" est un "struct thread_req_send_args étoile étoile" et même si tu le castes en void étoile, ça reste une adresse doublement déréférencée.
    De là, tu ne peux pas la considérer comme un "thread_req_send_args étoile" et même en la forçant à être vue comme un "thread_req_send_args étoile" ben ce n'en est quand-même pas une.
    Métaphore correspondante: si je file un vélo à un type en lui disant "ceci est une moto, appelle le moto" ben même s'il l'appelle "moto" ça reste un vélo.

    Tu as commis les erreurs les plus graves qu'on peut faire en C : ton premier code devait avoir des warnings mais au lieu de chercher le pourquoi et les corriger tu les as fait taire en castant tout ce qui n'allait pas pour faire croire au compilo que ce qui n'allait pas marcherait quand-même. Ok le compilo est tombé dans le panneau et a traduit gentiment des instructions foireuses en assembleur mais ça reste des instructions foireuses. Et ça donne au final un UB qui se traduit ici par "ça marche avec 67 mais pas avec 68".
    Un cast ce n'est pas un balai à ranger la poussière sous le tapis. C'est un outil permettant de rétablir les choses qui ne sont pas connues à l'avance (mais que le programmeur, lui, connait au moment où elles sont traitées et donc à ce moment là il peut caster pour rétablir la réalité). On ne l'utilise jamais pour effacer une erreur de compilation.

    Exemple de cast nécessaire
    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
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #include <stdio.h>
     
    void affiche(void* pt, char f) {
    	switch(f) {
    		case 'i':
    			printf("en int ça vaut %d\n", *(int*)pt);
    			break;
    		case 'f':
    			printf("en float ça vaut %f\n", *(float*)pt);
    			break;
    		case 's':
    			printf("en string ça vaut %s\n", (char*)pt);
    			break;
    	}
    }
     
    int main() {
    	int i=123;
    	float f=4.56;
    	char* s="Hello";
    	affiche(&i, 'i');
    	affiche(&f, 'f');
    	affiche(s, 's');
     
    	// affiche(&f, 'i');
    }
    La dernière ligne est mise en commentaire parce que là je demande à traiter l'adresse d'un float comme celle d'un int. Même avec les options de compilation les plus draconniennes, mon code compile nickel. Ensuite vient l'épreuve de son exécution...

    Ensuite je ne pige pas
    • pourquoi tu copies request[1] (qu'on nomme plutôt conventionnellement argv[1]) dans request_as_arg puis ensuite tu copies request_as_arg dans args->sql_request. C'est pour tester si la transitivité fonctionne aussi en C ?
    • pourquoi tu copies les 400 octets de request_as_arg dans args->sql_request alors que c'est visiblement une chaine que tu veux transmettre et que toutes les fonctions qui traiteront cette chaine s'arrêteront au '\0' qui la termine. Si la chaine fait 67 caractères ben tu en copies 333 pour rien.

    Et accessoirement on peut largement remplacer printf(request_as_arg, "%s", request[1]) en strcpy(request_as_arg, request[1]) (ou strncpy() qui te protège contre tout dépassement de taille).
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  3. #3
    Expert éminent sénior
    Avatar de Mat.M
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2006
    Messages
    8 360
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Novembre 2006
    Messages : 8 360
    Points : 20 378
    Points
    20 378
    Par défaut
    Et en déclarant request_as_arg en global donc en dehors du main ?
    Parce que là cette variable est locale au main() donc sa portée n'est que dans le main.
    A vérifier

  4. #4
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 684
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 684
    Points : 30 973
    Points
    30 973
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Mat.M Voir le message
    Parce que là cette variable est locale au main() donc sa portée n'est que dans le main...
    ...et dans toutes les fonctions appelées par le main auxquelles on leur passerait cette variable. Tant que le main() est en cours, la variable reste active.
    Et comme le main() est "la" fonction correspondant au programme (le programme commence et finit avec le main()), la portée de la variable est donc bel et bien dans tout le programme.

    Citation Envoyé par Mat.M Voir le message
    A vérifier
    Voilà.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  5. #5
    Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2023
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2023
    Messages : 2
    Points : 2
    Points
    2
    Par défaut
    Bonjour, et merci beaucoup pour vos réponses.
    Cependant, malgré ton retour Sve@r, je ne comprend pas vraiment comment améliorer mon code.
    Comment devrais-je déclarer mon args, et comment l'utiliser à l'appel de mon thread ?
    Concernant les potentiels warnings que je pouvais rencontrer, je ne les ai pas occultés volontairement. Je ne comprenais pas vraiment les solutions que j'essayais de mettre en place en castant mes différentes variables.
    Pour ce qui est de la copie de request[1] dans request_as_arg, c'est un moyen que j'ai trouvé pour stocker ma chaîne de caractère dans args->sql_request sans avoir d'erreur. Dans le cas contraire, une fois passée au thread, ma chaîne ressemble à cela : ▒G▒v▒B▒~.
    Enfin, je copie les 400 octets de request_as_arg dans args->sql_request car je ne connais pas à l'avance la taille de ma chaîne de caractère, et je n'ai pas encore trouvé de moyen d'allouer dynamiquement de la mémoire à celle-ci.
    Merci pour vos retours.

  6. #6
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 684
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 684
    Points : 30 973
    Points
    30 973
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Lucas30 Voir le message
    Cependant, malgré ton retour Sve@r, je ne comprend pas vraiment comment améliorer mon code.
    En supprimant les intermédiaires inutiles. Si tu copies request dans request_as_arg sans changement/travail alors l'une des deux variables est inutile.

    Citation Envoyé par Lucas30 Voir le message
    Concernant les potentiels warnings que je pouvais rencontrer, je ne les ai pas occultés volontairement.
    Oui enfin tu as des warnings, tu castes et tu n'as plus de warning et tu dis que ce n'est pas volontaire? Pourquoi tu castes alors???

    Citation Envoyé par Lucas30 Voir le message
    Je ne comprenais pas vraiment les solutions que j'essayais de mettre en place en castant mes différentes variables.
    Donc là tu dis que tu fais des trucs sans les comprendre ?
    Nom : images.jpg
Affichages : 125
Taille : 9,7 Ko

    Citation Envoyé par Lucas30 Voir le message
    Pour ce qui est de la copie de request[1] dans request_as_arg, c'est un moyen que j'ai trouvé pour stocker ma chaîne de caractère dans args->sql_request sans avoir d'erreur. Dans le cas contraire, une fois passée au thread, ma chaîne ressemble à cela : ▒G▒v▒B▒~.
    Dans le meilleur des cas, typique d'un affichage de string qui n'en est pas une (à laquelle il manque le '\0'). Dans le pire des cas, tu as passé un mauvais pointeur à ta fonction qui l'affiche (style mettre un & devant le nom de ta string).

    Citation Envoyé par Lucas30 Voir le message
    Enfin, je copie les 400 octets de request_as_arg dans args->sql_request car je ne connais pas à l'avance la taille de ma chaîne de caractère, et je n'ai pas encore trouvé de moyen d'allouer dynamiquement de la mémoire à celle-ci.
    Tu ne la connais pas à l'avance mais quand ta chaine est passée à ton programme, ton programme la connait et sait l'évaluer.
    Une chaine de caractères se termine par un '\0'. Toutes les fonctions de copie/lecture de chaine s'arrêtent donc d'elles-même à ce '\0'. Si ta chaine fait 10 caractères (donc si le caractère [10] contient un '\0'), alors les caractères [11] à [399] ne seront plus jamais utilisés par ton code et de fait ne servent plus à rien (enfin servent si plus tard tu réécris une nouvelle chaine). Donc seuls 10 (+1) caractères suffisent. Et justement les fonctions de copie de chaine savent s'arrêter au '\0' donc la boucle est bouclée. Tu prévois 400, tu t'assures que seuls 400 caractères au plus de la string entrante seront récupérés (pour éviter un débordement à l'entrée de ton code) et ensuite tu travailles à base de fonctions de traitement de strings qui, elles, s'arrêteront aux seuls caractères utiles de ta string en occultant d'office les caractères qui dépassent et qui sont inutiles. Peut-être que ce sera 400, mais peut-être (et même sûrement) que ce sera moins.

    J'ai l'impression qu'en plus des thread, tu as de sérieuses lacunes sur les bases du C (c'est quoi une string, un pointeur, etc...). Tu devrais aller lire ce topic qui détaille un peu plus les strings. De plus faire du malloc pour une taille connue... Enfin oui si la taille est super grosse (et de fait dépasse la pile) mais pour 400...

    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
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #define SZ_REQUEST				(400) 
     
    typedef struct {
    	char sql_request[SZ_REQUEST + 1];
    } thread_req_send_args;
     
    void* send_request_to_pub(thread_req_send* arg) {
    	printf("---- req_thread : [%s]\n", arg->sql_request);
     
    	pthread_exit(NULL);
    	// Ne manquerait pas un "return" à cette fonction censée retourner un pointeur???
    }
     
    int main(int nbr, char* argv[]) {
    	struct thread_req_send_args req;
     
    	strncpy(req.sql_request, argv[1], SZ_REQUEST);
    	req.sql_request[SZ_REQUEST]='\0';	// strncpy() ne met pas de '\0' si "n" a été atteint
     
    	if (pthread_create(&thread_send_to_pub, NULL, send_request_to_pub, &req) != 0) {
    		system("echo $(date) ': L82 - Failed to create the sending request thread' >> /home/rpiaccess/replica_log.txt");
    		exit(1);
    	}
     
    	pthread_join(thread_send_to_pub, NULL);
     
    	pthread_exit(NULL);
    }

    Reste ce "thread_send_to_pub" dont je n'ai pas retrouvé la définition dans ton code d'origine donc ne sachant pas s'il est bon ou pas.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  7. #7
    Expert éminent sénior
    Avatar de Mat.M
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2006
    Messages
    8 360
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Novembre 2006
    Messages : 8 360
    Points : 20 378
    Points
    20 378
    Par défaut
    Citation Envoyé par Lucas30 Voir le message
    je ne comprend pas vraiment comment améliorer mon code.
    quel est le principe d'un thread ? Un thread c'est un processus fils appartenant à un processus parent qui 'exécute en parallèle à ce processus parent.
    Donc les deux processus partagent le même espace mémoire, la même pile d'arguments...
    Là dans le premier code donné ce qui ne va pas c'est que vous créez un thread dans le main() donc le kernel lui va créer le processus.
    Par contre le main() lui va continuer et s'arrêter.
    Donc votre thread démarré ayant une adresse bien particulière pour que le kernel commute dessus à intervalles réguliers il va tourner dans le vide sans plus aucun processus parent ?
    Donc quelle est la solution alors ?

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 629
    Points : 10 554
    Points
    10 554
    Par défaut
    Citation Envoyé par Mat.M Voir le message
    quel est le principe d'un thread ? Un thread c'est un processus fils appartenant à un processus parent qui 'exécute en parallèle à ce processus parent.
    Pour être précis , thread n'est pas 1 processus mais 1 fil d'exécution.

    La différence entre 1 processus et 1 fil d'exécution : les fils d'exécution se partagent la mémoire. Chaque processus a sa propre mémoire.

Discussions similaires

  1. Réponses: 3
    Dernier message: 11/04/2021, 04h48
  2. Réponses: 3
    Dernier message: 07/12/2005, 14h28
  3. [vb.net] erreur lors de l'éxécution d'une classe
    Par os_rasta dans le forum VB.NET
    Réponses: 10
    Dernier message: 01/12/2005, 18h34
  4. Erreur lors de l'ajout d'une table
    Par FredMines dans le forum SQL Procédural
    Réponses: 1
    Dernier message: 27/07/2005, 13h13
  5. message d'erreur lors de la création d'une base
    Par franculo_caoulene dans le forum MS SQL Server
    Réponses: 4
    Dernier message: 16/04/2004, 15h47

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