PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Problem Vaterprozess wartet auf Sohnprozesse


Cordell
13.04.2007, 21:22
Hi.

ich bin noch relativ neu in der Unix / Parallelprogrammierung und hab ein kleines Problem.
Ich habe folgenden Code:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>

int eval_wait_stat (int stat);

int main (void)
{
pid_t parent_pid, child_pid, /* process ID's */
fork_pid, wait_pid,
parent_grp, child_grp; /* process groups */
int child_stat, /* return status of child */
exit_val; /* "exit"-value for child */

exit_val = 10; /* value for testing */
parent_pid = getpid ();
parent_grp = getpgrp ();
printf ("\nParent process: process ID: %ld group ID: %ld\n",
(long) parent_pid, (long) parent_grp);
fork_pid = fork ();
switch (fork_pid)
{
case -1: /* error: no process created */
perror ("fork failed");
errno = 0;
break;

case 0: /* child process */
child_pid = getpid ();
child_grp = getpgrp ();
printf ("Child process: process ID: %ld group ID: %ld "
"parent process ID: %ld\n", (long) child_pid,
(long) child_grp, (long) getppid ());
printf ("Child process: terminate with \"exit\"-value: %d\n",
exit_val);
exit (exit_val);
break;

default: /* parent process */
printf ("Parent process: child process with ID %ld created.\n",
(long) fork_pid);
wait_pid = wait (&child_stat);
if (wait_pid == -1)
{
perror ("wait");
errno = 0;
}
else
{
printf ("Parent process: child process %ld has terminated.\n",
(long) wait_pid);
eval_wait_stat (child_stat);
}
}
return 0;
}


/* Evaluates the return status of a process.
*
* input parameter: process status from function wait or waitpid
* output parameter: none
* return value: 0: process has terminated
* 1: process has stopped or continued
* side effects: none
*
*/
int eval_wait_stat (int stat)
{
if (WIFEXITED (stat) != 0)
{
printf ("\t\tChild has terminated with status %d.\n",
WEXITSTATUS (stat));
return 0;
}
if (WIFSIGNALED (stat) != 0)
{
printf ("\t\tChild has been terminated using signal %d.\n",
WTERMSIG (stat));
return 0;
}
if (WIFSTOPPED (stat) != 0)
{
printf ("\t\tChild has been stopped using signal %d.\n",
WSTOPSIG (stat));
}
#if (defined(SunOS) || defined(IRIX)) && !defined(_POSIX_C_SOURCE)
if (WIFCONTINUED (stat) != 0)
{
printf ("\t\tChild has continued.\n");
}
#endif
return 1;
}Ich möchte diesen Code so umschreiben, dass der (Vater-)Prozess 3 parallele Sohnprozesse erzeugt, die Ihre PID, GID und PPID ausgeben und sich nach kurzer Wartezeit selbst beenden. Der Vaterprozess soll auf die Sohnprozesse warten.

Hier meine Frage, wie kann ich mir zeigen lassen wieviele Sohnprozesse erzeugt wurden? Ich könnte mir denken dass ich ne CHILDREN-Variable definieren und dann eine for-schleife drüber laufen lasse, jeodch weiß ich halt nicht wie ich nachschauen kann wieviele Sohnprozesse schon erzeugt wurden, da der Vater ja auf sie Warten soll!

Vielen Dank im Vorraus!

Gruß,
Cordell


butterkeks
13.04.2007, 21:29
ich würde dafür wirklich eine (globale) Variable verwenden und diese bei geglücktem fork() im Parent Prozess erhöhen bzw. im SIGCHLD Handler verringern

Cordell
13.04.2007, 21:41
ich würde dafür wirklich eine (globale) Variable verwenden und diese bei geglücktem fork() im Parent Prozess erhöhen bzw. im SIGCHLD Handler verringern

ja ich hab per #define eine globale CHILDREN Variable definiert. Also wäre es sinnvoll das fork() samt Switch/Case-Block mit einer for-Schleife zu umschließen und im Case 0 (geglücktes fork()) die Varible zu erhöhen und wenn sie 3 erreicht hat die Schleife zu verlassen ?

butterkeks
13.04.2007, 22:54
ach so, ja... eine while Schleife wäre vielleicht sinnvoller:

while(counter < 3) {
/* forken */
}

Ich würde mich aber wundern, warum ein fork(), das vorhin fehlschlug, plötzlich beim nächsten mal funktionieren sollte...

Meinst du nicht eher sowas?

int i;
for(i = 0; i < 3; ++i) {
/* forken */
}

Dadurch wird derselbe Block 3 mal aufgerufen (innerhalb kannst du natürlich auf Fehler reagieren)

Cordell
13.04.2007, 23:07
Danke schon mal für die Antwort!
Ja genau, ich muss halt genau wissen, wann 3 Sohnprozesse erzeugt wurden und wenn diese sich dann selber beendet haben, soll sich dann anschließend der Vaterprozess beenden!

Das fork() gibt ja im gegglückten Fall 0 zurück, deswegen war meine Frage ob ich nicht dann auch die Variable im Case 0
inkrementieren sollte.

butterkeks
14.04.2007, 11:53
Du musst inkrementieren, wenn der Rückgabewert >0 ist (sonst würdest du die Variable im Child verändern und dieses interessiert der Wert nicht).

Sobald sich ein Kindprozess beendet, bekommt der Elternprozess das Signal SIGCHLD; nähere Infos über den Prozess, von dem das Signal stammt, kriegst du mit wait()

Cordell
14.04.2007, 13:42
Du musst inkrementieren, wenn der Rückgabewert >0 ist (sonst würdest du die Variable im Child verändern und dieses interessiert der Wert nicht).

Sobald sich ein Kindprozess beendet, bekommt der Elternprozess das Signal SIGCHLD; nähere Infos über den Prozess, von dem das Signal stammt, kriegst du mit wait()

Ich bin jetz grade etwas verwirrt, ich habs bis jetzt mal so gemacht


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#inlcude <errno.h>

//#define CHILDREN 2 /* max. Anzahl Soehne */
int CHILDREN = 2;

int eval_wait_stat (int stat):

int main (void)
{
pid_t parent_pid, child_pid, /* Prozessnummern */
fork_pid, wait_pid,
parent_grp, child_grp; /* Prozessgruppen */
int child_stat, /* Zustand des Sohnes */
exit_val, /* "exit"-Wert des Sohnes */
i,j; /* Zählvariablen */

exit_val = 10; /* zum Testen beliebiger Wert */
parent_pid = getpid ();
parent_grp = getpgrp ();

for (i = 0; i < CHILDREN; i++)
{
fork_pid = fork ();

switch(fork_pid)
{
case -1: /* Fehler: kein Prozess erzeugt */
perror ("fork failed");
errno = 0;
CHILDREN--;
break;

case 0: /* Sohnprozesse */
child_pid = getpid ();
child_grp = getpgrp ();

printf ("Sohnprozess. Prozess-Id: %ld Gruppen-Id: %ld "
"Vaterprozess-Id: %ld ", (long) child_pid,
(long) child_grp, (long) getppid ());

exit (exit_val);
break;

default:
wait_pid = wait (&child_stat);
if (wait_pid == -1)
{
perror ("wait");
errno = 0;
}
else
{
printf ("Vaterprozess. Sohnprozess %ld endet.\n",
(long) wait_pid);
CHILDREN++;
eval_wait_stat (child_stat);
}
}
}
return 0;

int eval_wait_stat (int stat)
{
if (WIFEXITED (stat) != 0)
{
printf ("\t\tSohnprozess endet mit Status %d.\n",
WEXITSTATUS (stat));
return 0;
}
if (WIFSIGNALED (stat) != 0)
{
printf ("\t\tSohnprozess wurde durch Signal %d beendet.\n",
WTERMSIG (stat));
return 0;
}
if (WIFSTOPPED (stat) != 0)
{
printf ("\t\tSohnprozess wurde durch Signal %d gestoppt.\n",
WSTOPSIG (stat));
}
#if (defined(SunOS) || defined(IRIX)) && !defined(_POSIX_C_SOURCE)
if (WIFCONTINUED (stat) != 0)
{
printf ("\t\tSohnprozess wurde fortgesetzt\n.");
}
#endif
return 1;
}

Bin nicht sicher, ob ich dich richtig verstanden habe! Bin etwas durcheinander grad :(

Baegsch
14.04.2007, 23:59
Vielleicht versteh ich das Problem so spät auch nicht, aber irgendwie seh ich da jetzt keins. Ich stell mir das in etwa wie folgt vor. Korrigiert mich falls ich falsch liege :)


void function(void) {
int number_childs = 0;

while(number_childs < 3) {
switch(fork()) {
// behandle fehler.
// child code: ...
// parent code: erhöhe number childs um 1.
}
}

// parent code bevor in die warteschleife gegangen wird.

while(number_childs > 0) {
// wait().
// werte status aus
// verringere number childs bei beendigung eines childs um 1.
}


Solltest du in function mehrmals "einsteigen", kannst du number_childs auch static definieren. Aber vielleicht ist dein Problem auch etwas anders.

Wenn ich deinen Code richtig überflogen hab, so erzeugst du die childs doch linear. Im for loop wird ein child erzeugt. Der nächste code, den der parent prozess bekommt, ist der default block im switch. Also wird auf das child gewartet. Erst danach steigst du in die zweite Iteration deiner Schleife ein. Im Gegensatz dazu erzeuge ich erst alle childs (auf die Gefahr, dass Einige schon Zombies sind bevor die letzten erzeugt sind), und führe erst danach die Warteschleife aus. Netter Nebeneffekt: Jedes child kann über number_childs prüfen, das wievielte es ist. Denn erhöht der parent diese Variable, so wird für den child COW angewandt.

Netter ist natürlich einen signalhandler für SIGCHLD anzulegen. In diesem kannst du ebenso wait benutzen, und somit zeitnah, asynchron oder wie du es auch nennen magst deine Auswertungen treffen. Dabei entstehen dir auch keine Zombies. Und mit der Anzahl der zu erzeugenden childs hängt das auch nicht zusammen.

Alles klar?!

Wenn nicht, frag nach. :)

Warum ihr globale Variablen verwendet, verschließt sich mir übrigens grad total.

mfg Maik

Cordell
15.04.2007, 03:47
Danke für deine Antwort Baegsch! Ich hab das jetz so gemacht, nach deiner Anweisung:


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>

#define CHILDREN 3 /* max. Anzahl Soehne */

int eval_wait_stat (int stat):

int main (void)
{
pid_t parent_pid, child_pid, /* Prozessnummern */
fork_pid, wait_pid,
parent_grp, child_grp; /* Prozessgruppen */
int child_stat, /* Zustand des Sohnes */
exit_val, /* "exit"-Wert des Sohnes */
i,j, /* Zählvariablen */


exit_val = 10; /* zum Testen beliebiger Wert */
parent_pid = getpid ();
parent_grp = getpgrp ();

i = 0;
while (i < CHILDREN)
{
fork_pid = fork ();
switch(fork_pid)
{
case -1: /* Fehler: kein Prozess erzeugt */
perror ("fork failed");
errno = 0;
break;

case 0: /* Sohnprozesse */
child_pid = getpid ();
child_grp = getpgrp ();

printf ("Sohnprozess. Prozess-Id: %ld Gruppen-Id: %ld "
"Vaterprozess-Id: %ld ", (long) child_pid,
(long) child_grp, (long) getppid ());
i++;
exit (exit_val);
break;

default: /* Vaterprozess */


wait_pid = wait (&child_stat);
while(i > 0)
{
if (wait_pid == -1)
{
perror ("wait");
errno = 0;
}
else
{
printf ("Vaterprozess. Sohnprozess %ld endet.\n",
(long) wait_pid);
eval_wait_stat (child_stat);
i--;
}
}
}
}
return 0;
}
int eval_wait_stat (int stat)
{
if (WIFEXITED (stat) != 0)
{
printf ("\t\tSohnprozess endet mit Status %d.\n",
WEXITSTATUS (stat));
return 0;
}
if (WIFSIGNALED (stat) != 0)
{
printf ("\t\tSohnprozess wurde durch Signal %d beendet.\n",
WTERMSIG (stat));
return 0;
}
if (WIFSTOPPED (stat) != 0)
{
printf ("\t\tSohnprozess wurde durch Signal %d gestoppt.\n",
WSTOPSIG (stat));
}
#if (defined(SunOS) || defined(IRIX)) && !defined(_POSIX_C_SOURCE)
if (WIFCONTINUED (stat) != 0)
{
printf ("\t\tSohnprozess wurde fortgesetzt\n.");
}
#endif
return 1;
}
}


Es geht zu kompilieren jedoch macht er nicht nur 3 Prozesse, sondern endlos!
Sehe den Fehler einfach nicht!

Cordell
16.04.2007, 23:33
Es hat sich erledigt, habe es jetz hinbekommen! Danke für die Antworten!!

Gruß,
Cordell

Baegsch
17.04.2007, 09:51
Hallo,

dein Fehler war wohl, dass du im Kindprozess die Variable i inkrementierst, was aber im Vaterprozess geschehen müsste ;) Trotzdem erzeugst du deine Kindprozesse immernoch einer nach dem anderen. Es laufen bei dir nie 2 oder mehr Kindprozesse parallel - ich weiss ja nicht, ob das so gewollt ist.

mfg Maik

Cordell
18.04.2007, 00:13
Ich hab die aktuelle version hier noch nicht gepostet!
Es läuft parallel, alles so wie ich es wollte :)

Gruß,
Cordell

Klobert
18.04.2007, 15:05
Hi, kannst du bitte mal deinen fertigen Code hier posten, habe das auch so ähnlich gemacht wie oben, aber wie hast du das mit der Parallelität gelöst??

Cordell
19.04.2007, 18:51
Hi, kannst du bitte mal deinen fertigen Code hier posten, habe das auch so ähnlich gemacht wie oben, aber wie hast du das mit der Parallelität gelöst??

Ja klar, hier bitte schön :


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>

#define CHILDREN 3 /* max. Anzahl Soehne */

int eval_wait_stat (int stat):

int main (void)
{
pid_t parent_pid, child_pid, /* Prozessnummern */
fork_pid, wait_pid,
parent_grp, child_grp; /* Prozessgruppen */
int child_stat, /* Zustand des Sohnes */
exit_val, /* "exit"-Wert des Sohnes */
i,j, /* Zählvariablen */


exit_val = 10; /* zum Testen beliebiger Wert */
parent_pid = getpid ();
parent_grp = getpgrp ();

for (i = 0; i < CHILDREN; i++)
{
fork_pid = fork ();
switch(fork_pid)
{
case -1: /* Fehler: kein Prozess erzeugt */
perror ("fork failed");
errno = 0;
break;

case 0: /* Sohnprozesse */
child_pid = getpid ();
child_grp = getpgrp ();

printf ("Sohnprozess. Prozess-Id: %ld Gruppen-Id: %ld "
"Vaterprozess-Id: %ld ", (long) child_pid,
(long) child_grp, (long) getppid ());
sleep(3);
exit (exit_val);
break;

default: /* Vaterprozess */

printf ("Vaterprozess: Sohnprozess mit ID %ld wurde erzeugt.\n",
(long) fork_pid);

if (i == CHILDREN -1)
{
for (j = 0; j < CHILDREN; j++)
{
wait_pid = wait (&child_stat);
if (wait_pid == -1)
{
perror ("wait");
errno = 0;
}
else
{
printf ("Vaterprozess. Sohnprozess %ld endet.\n",
(long) wait_pid);
eval_wait_stat (child_stat);
}
}
}
}
}
return 0;
}
int eval_wait_stat (int stat)
{
if (WIFEXITED (stat) != 0)
{
printf ("\t\tSohnprozess endet mit Status %d.\n",
WEXITSTATUS (stat));
return 0;
}
if (WIFSIGNALED (stat) != 0)
{
printf ("\t\tSohnprozess wurde durch Signal %d beendet.\n",
WTERMSIG (stat));
return 0;
}
if (WIFSTOPPED (stat) != 0)
{
printf ("\t\tSohnprozess wurde durch Signal %d gestoppt.\n",
WSTOPSIG (stat));
}
#if (defined(SunOS) || defined(IRIX)) && !defined(_POSIX_C_SOURCE)
if (WIFCONTINUED (stat) != 0)
{
printf ("\t\tSohnprozess wurde fortgesetzt\n.");
}
#endif
return 1;
}
}