[C] job control

Bonjour,

Je suis en train de m’initier au job control en C et je rencontre quelques petits problème. J’ai donc crée un “simulateur” de shell fort simple afin d’y implémenter ce fameux job control. Objectif : faire en sorte que lors d’un ^C ce soit le processus fils qui se le mange et non le père … et bien entendu que le père reprenne la main a la fin.

Voici mon code :

[code]#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define PROMPT "?> "

void sig_handler(int sig);
int disp_infos(const char *name);
int do_parent_process(pid_t child);
int do_child_process(void);

void sig_handler(int sig)
{
signal(SIGINT, &sig_handler);
printf(“Signal %d received by %d\n”, sig, getpid());
}

int disp_infos(const char *name)
{
if (name == NULL)
return (-1);
printf("%-6s: pid %d, pgrp %d, tty pgrp %d\n",
name, getpid(), getpgrp(), tcgetpgrp(STDOUT_FILENO));
return 0;
}

int do_parent_process(pid_t child)
{
int status;

disp_infos(“Parent”);
if (tcsetpgrp(STDOUT_FILENO, child) == -1)
fprintf(stderr, “tcsetpgrp failed\n”);
waitpid(child, &status, WCONTINUED);
if (tcsetpgrp(STDOUT_FILENO, getpgrp()) == -1)
fprintf(stderr, “tcsetpgrp failed\n”);
return status;
}

int do_child_process(void)
{
setpgid(0, 0);
disp_infos(“Child”);
sleep(2);
return 0;
}

int main(void)
{
pid_t p;
char b[4];

signal(SIGINT, &sig_handler);
printf(PROMPT); fflush(stdout);
while (read(STDIN_FILENO, b, sizeof(b)) > 0 && b[0] != ‘q’)
{
p = fork();
if (p < 0)
{
perror(“fork”);
return 1;
}
else if (p == 0)
exit(do_child_process());
else
do_parent_process§;
printf(PROMPT); fflush(stdout);
}
if (b[0] != ‘q’)
printf("\n");
return 0;
}[/code]
Et un exemple d’utilisation :

[code]tycho@uraniborg-> make
cc -W -Wall -Werror -Wshadow -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Winline -ansi -pedantic -O3 -c -o job.o job.c
cc -o job job.o
tycho@uraniborg-> ./job
?> 42
Parent: pid 12589, pgrp 12589, tty pgrp 12589
Child : pid 12591, pgrp 12591, tty pgrp 12589
^CSignal 2 received by 12591

[1]+ Stopped ./job
tycho@uraniborg-> fg
./job
?> 42
Child : pid 12592, pgrp 12592, tty pgrp 12589
Parent: pid 12589, pgrp 12589, tty pgrp 12589

[1]+ Stopped ./job
tycho@uraniborg->[/code]

Comme vous pouvez le constater, seul le fils se prend le signal dans la figure, c’est une bonne chose. Le seul problème est que dès que le file se termine d’une manière ou d’une autre, le père se barre dans le background :confused: Pour avoir mis un exit alternativement avant et après le second tcsetpgrp du père je sais que c’est durant ce dit tcsetpgrp qu’il passe en background, ce qui est exactement l’effet inverse de ce qui est souhaité (le but est de repasser le père en foreground). Soit dit en passant, un disp_infos juste après ce tcsetpgrp m’indique qu’il a bien réussi a en reprendre la main.
Est-ce que quelqu’un saurai dire d’où provient ce problème svp ?

Hum, c’est aléatoire:

[quote]francois@bling:/tmp$ fg
./job
?> ^CYoh!, Signal 2 received by 6223
^CYoh!, Signal 2 received by 6223
^CYoh!, Signal 2 received by 6223
^CYoh!, Signal 2 received by 6223
^CYoh!, Signal 2 received by 6223
^CYoh!, Signal 2 received by 6223
^CYoh!, Signal 2 received by 6223
^CYoh!, Signal 2 received by 6223
^CYoh!, Signal 2 received by 6223
67
Parent: pid 6223, pgrp 6223, tty pgrp 6223
Child : pid 6226, pgrp 6226, tty pgrp 6226
fils fini

[2]+ Stopped ./job
francois@bling:/tmp$ fg
./job
?> ^CYoh!, Signal 2 received by 6223

[/quote]

  1. Dès que tu as fait ton entrée clavier, il se met en background
  2. Le père reçoit également le ^C, cf ci dessus.
    Ici tu t’aper’çois que le père et le fils ne sont pas dans le même groupe de processus à l’avant plan. Il te faudrait mettre le père et le fils dans le même plan, ou en tout cas qu’un processus à l’arrière plan ne fasse pas de sortie (sinon il se met en attente):
    En bricolant (2s), ça donne ça ou rien ne se met en arrière plan:

[code]#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define PROMPT "?> "

void sig_handler(int sig);
int disp_infos(const char *name);
int do_parent_process(pid_t child);
int do_child_process(pid_t parent);

void sig_handler(int sig)
{
signal(SIGINT, &sig_handler);
printf(“Yoh!, Signal %d received by %d\n”, sig, getpid());
}

int disp_infos(const char *name)
{
if (name == NULL)
return (-1);
printf("%-6s: pid %d, pgrp %d, tty pgrp %d\n",
name, getpid(), getpgrp(), tcgetpgrp(STDOUT_FILENO));
return 0;
}

int do_parent_process(pid_t child)
{
int status;

/* if (tcsetpgrp(STDOUT_FILENO, child) == -1)
fprintf(stderr, “tcsetpgrp failed\n”);*/
disp_infos(“Parent”);
waitpid(child, &status, WCONTINUED);
fprintf(stderr,“fils fini\n”);
if (tcsetpgrp(STDOUT_FILENO, getpgrp()) == -1)
fprintf(stderr, “tcsetpgrp failed\n”);
return status;
}

int do_child_process(pid_t parent)
{
if (tcsetpgrp(STDOUT_FILENO, parent) == -1)
fprintf(stderr, “tcsetpgrp failed\n”);
setpgid(0, 0);
disp_infos(“Child”);
sleep(2);
return 0;
}

int main(void)
{
pid_t p,pr;
char b[4];

signal(SIGINT, &sig_handler);
printf(PROMPT); fflush(stdout);
while (read(STDIN_FILENO, b, sizeof(b)) > 0 && b[0] != ‘q’)
{
p = fork();
if (p < 0)
{
perror(“fork”);
return 1;
}
else if (p == 0)
exit(do_child_process(pr));
else
fprintf(stderr,“glop glop\n”);
do_parent_process§;
printf(PROMPT); fflush(stdout);
}
if (b[0] != ‘q’)
printf("\n");
return 0;
}
[/code]

Salut,

Merci beaucoup de ta réponse :slightly_smiling:
Grâce a tes explications je vois mieux le soucis et ta solution fonctionne bien. J’ai juste remplacé le pr (dans le main) par un getppid(), sinon la variable n’était pas initialisée (vu les flags que j’utilise ça ne compilais pas ^^).