2016-12-16 1 views
2

J'ai fait un programme très simple appelant à forking et appelant un autre problème. Pendant qu'il fait ce que je veux, un bogue se produit et cout se produit double fois le temps de for loop. Voici le code: main.cppSpawning enfants et exec - comportement indéfini

`#include <iostream> 
#include <cstdlib> 

#include <stdio.h> 
#include <string.h> 
#include <time.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <sys/wait.h> 
#include <sys/ipc.h> 

using namespace std; 

char *Strduplicate(const char *source) { 
    char *dest = (char *) malloc(strlen(source) + 1); // +1 = '\0' 
    if (dest == NULL) 
     return NULL; 
    strcpy(dest, source); 
    return dest; 
} 

string Get_cwd(string word) { 
    char cwd[256]; 
    getcwd(cwd, sizeof(cwd)); 
    strcat(cwd,"/"); 
    strcat(cwd,word.c_str()); 
    string returnVal(cwd); 
    return returnVal; 
} 

void Call_exec(const char *name,int value) { 
    char *exec_array[3]; 
    exec_array[0] = Strduplicate(name); 
    exec_array[1] = (char *)malloc(2); 
    exec_array[1] = (char *)"-m"; 
    asprintf(&(exec_array[2]), "%d", value); 
    for (int i = 0 ; i < 3; i++) 
     cout << exec_array[i] << " "; 
    cout << endl; 
    execv(exec_array[0],exec_array); 
} 

int main(int argc ,char **argv) { 
    srand(time(NULL)); 
    /* Getting arguments */ 
    //........ 
    /* Spawning children */ 
    for (int i = 0 ; i < 3 ; i++) { 
     int value = rand()%100 + 1; 
     pid_t waiterpid = fork(); 
     if (waiterpid < 0) 
      cout << "ERROR FORK" << endl; 
     else if (!waiterpid) { 
      string program_name = Get_cwd("child"); 
      Call_exec(program_name.c_str(),value); 
     } 
    } 
    return EXIT_SUCCESS; 
} 
` 

et l'autre processus est Child.cpp

#include <iostream> 
#include <sys/types.h> 
#include <unistd.h> 
#include <string.h> 
#include <stdlib.h> 

using namespace std; 

int main(int argc ,char **argv) { 
    cout << "Child #" << getpid() << " has started" << endl; 
    int value; 
    /* Getting arguments */ 
    if (argc != 3) { 
     cerr << "ERROR : Wrong arguments" << endl; 
     exit(EXIT_FAILURE); 
    } 
    else { 
     if (strncmp(argv[1],"-m",2) == 0) 
      value = atoi(argv[2]); 
    } 
    cout << "Child has " << value << endl; 
    return EXIT_SUCCESS; 
} 

la sortie est

mypath/child -m 31 
mypath/child -m 23 
mypath/child -m 48 
mypath/child -m 23 
mypath/child -m 48 
[email protected]$ Child #13063 has started 
Child #13062 has started 
Child has 48 
Child has 23 
mypath/child -m 48 
Child #13064 has started 
Child has 48 

Alors ce que je comprends mal ici?

+0

La moitié de ce code est UB ou standard non conforme (à partir de l'utilisation des en-têtes ..) si vous apprenez C++., Oubliez C. Si les tuteurs ne doivent pas utiliser _features_ de C++, conformez-vous envisager d'envisager des solutions de rechange et de garder cela à l'esprit. – Swift

+0

par exemple. y compris stdlib.h est UB, bien que vous l'ayez fait après avoir déjà inclus l'en-tête droit, cstdlib. Y compris string.h est faux aussi. 'using namespace std' dans _global_ scope peut conduire à des UBs possibles mais pas dans cet extrait .. gardez juste à l'esprit que vous pouvez utiliser using dans la portée locale ou tirer les noms requis, comme' using std :: cin'. Il n'y a plus de STL, eh bien, STL est une bibliothèque séparée qui pourrait être disponible pour des raisons historiques, si vous avez iostream, vous utilisez déjà la bibliothèque de templates C++ (pas STL). N'utilisez pas malloc \ free \ realloc dans le code C++, utilisez new et delete (et placement new). – Swift

Répondre

1

Ce qui est mal compris ici, ce sont les principes généraux de l'écriture de code C++ moderne. Il n'y a aucune raison d'utiliser ces allocations de mémoire dynamiques de style C affreuses. Tout ce qui est fait ici peut être fait beaucoup plus propre, en utilisant des conteneurs, avec le code résultant étant au moins trois fois plus petit.

Oh, et le tableau de paramètres execv doit être terminé par un pointeur NULL. Ce n'est pas le cas, ce qui entraîne un comportement indéfini. Très probablement, l'appel système execv échoue, en raison de ce paramètre de poubelle - probablement avec un EFAULT selon ma lecture de sa page de manuel. Par conséquent, execv() retourne effectivement dans le processus enfant. Puisque le code indiqué ne vérifie pas sa valeur de retour: un processus enfant, aléatoirement, continuera à s'exécuter au retour de execv(), retournant à main() dans le processus fils, et continue avec son propre manège de la boucle externe for , résultant de la production en double.

+0

STL n'est pas autorisé et comme je veux un exec dynamique, je ne peux pas non plus utiliser execl, c'est pourquoi j'ai fini par le faire de cette façon. –