web-dev-qa-db-fra.com

Concernant les processus d'arrière-plan utilisant fork () et les processus enfants dans mon shell factice

J'essaie de créer un programme Shell simple en C. Ce dont j'ai besoin pour cela, c'est de fournir à l'utilisateur une invite dans laquelle il peut exécuter d'autres programmes locaux. Je peux faire cette partie très bien, en utilisant un fork () dans lequel le processus parent attend () l'enfant, et l'enfant execvp () est le programme.

Cependant, si le caractère "&" est ajouté à la fin de la commande de l'utilisateur, j'ai besoin que leur programme s'exécute en arrière-plan, ce qui signifie que j'ai besoin que le parent n'attende PAS le processus enfant mais renvoie à la place immédiatement l'invite à l'utilisateur, tout en permettant au processus d'arrière-plan de continuer à s'exécuter, mais sans lui permettre d'afficher quoi que ce soit à l'écran. Je veux juste pouvoir vérifier qu'il existe toujours via la commande ps.

J'ai essayé de comprendre l'idée derrière l'utilisation de fork () pour créer un enfant, puis de le redéfinir pour créer un petit-enfant, puis de quitter immédiatement () le processus enfant. c'est-à-dire Orphelin le petit-enfant. Soi-disant, cela permet au parent d'attendre toujours l'enfant, mais comme l'enfant se termine effectivement presque immédiatement, c'est comme s'il n'attendait pas du tout? Quelque chose à propos de la folie zombie? Je ne sais pas. Plusieurs sites que j'ai rencontrés semblent recommander cela comme un moyen d'exécuter un processus en arrière-plan.

Cependant, lorsque j'essaie de le faire, des choses folles se produisent avec le déroulement du programme, le processus `` d'arrière-plan '' continue d'afficher les entrées à l'écran, et je ne sais vraiment pas où aller à partir d'ici.

Ceci est mon implémentation du code, ce qui, j'en suis sûr, est tout à fait faux. Je me demande simplement si tout ce petit-enfant est même l'itinéraire que je dois emprunter, et si oui, quel est le problème avec mon code?

36 int main(int argc, char *argv[])
37 {
38     char buffer[512];
39     char *args[16];
40     int background;
41     int *status;
42     size_t arg_ct;
43     pid_t pid;
44 
45     while(1)
46     {
47         printf("> ");
48         fgets(buffer, 512, stdin);
49         parse_args(buffer, args, 16, &arg_ct);
50 
51         if (arg_ct == 0) continue;
52 
53         if (!strcmp(args[0], "exit"))
54         {
55             exit(0);
56         }
57 
58         pid = fork();  //here I fork and create a child process
61 
62         if (pid && !background)  //i.e. if it's the parent process and don't need to run in the background (this is the part that seems to work)
63         {
64             printf("Waiting on child (%d).\n", pid);
65             pid = wait(status);
66             printf("Child (%d) finished.\n", pid);
67         }
68         else
69         {
70             if (background && pid == 0) { //if it's a child process and i need to run the command in background
71                
72                 pid = fork(); //fork child and create a grandchild
73                 if (pid) exit(0); //exit child and Orphan grandchild
74                 
75                 execvp(args[0], args); //Orphan execs the command then exits
76                 exit(1);
77 
78             } else exit(0);
79         }
80     }
81     return 0;
82 }

P.S. Pour être clair, j'ai besoin du processus que j'exécute en arrière-plan pour ne plus jamais faire de son, même s'il a une boucle infinie d'instructions d'impression ou quelque chose du genre. Je veux juste m'assurer qu'il fonctionne toujours en arrière-plan via ps -a ou quelque chose.

Désolé pour l'explication déroutante, je ne sais tout simplement pas comment l'expliquer mieux.

Merci d'avance

P.P.S Je vais l'implémenter pour que chaque commande suivante détermine la valeur booléenne de 'background', désolé pour la confusion

20
tlw11591

Vous ne voulez pas utiliser la méthode double -fork() dans un Shell - c'est pour écrire des démons qui veulent spécifiquement échapper à la supervision par le Shell qui les a exécutés.

Au lieu de cela, la façon de dupliquer le comportement de & Dans les shells existants est d'appeler fork(), puis dans l'appel enfant setpgid(0, 0); pour mettre l'enfant dans un nouveau - groupe de processus. Le parent continue simplement (peut-être après avoir imprimé le PID de l'enfant) - il n'appelle pas wait().

Chaque terminal ne peut avoir qu'un seul groupe de processus de premier plan, qui est le groupe de processus actuellement autorisé à envoyer une sortie au terminal. Cela restera le groupe de processus dont votre Shell est membre, donc votre Shell restera au premier plan.

Si un groupe de processus d'arrière-plan tente de lire à partir du terminal, un signal lui sera envoyé pour l'arrêter. La sortie vers le terminal est toujours autorisée - c'est normal (essayez ls & Dans votre Shell habituel). Pour implémenter la commande fg dans votre Shell, vous aurez besoin de la fonction tcsetpgrp() pour changer le groupe de processus de premier plan.

Vous souhaiterez également appeler waitpid() avec l'option WNOHANG régulièrement - par exemple, juste avant d'afficher l'invite du shell. Cela vous permettra de détecter la fin ou l'arrêt du processus d'arrière-plan (ou vous pouvez également gérer le signal SIGCHLD).

22
caf