Toon posts:

[C++/*nix] Problemen met pipes

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik ben voor school bezig met het programmeren van een shell. Het parse gedeelte is al klaar (was niet denderend moeilijk), maar nu ben ik het uitvoeren van de geparsede commando's aan het uitwerken. Ergens in mijn programma maak ik met behulp van de pipe() systemcall een pipe aan (of meerdere, afhankelijk van het aantal pipelines). Dit werkt op zich prima en de uitvoer van bijvoorbeeld 't onderstaande commando is ook zoals verwacht:

code:
1
ls -l | cat | wc


Nu loop ik echter tegen een aantal problemen aan. In de eerste plaatst viel mij op dat de uitvoer van wc één teken minder gaf dan wanneer ik hetzelfde commando uitvoerde in bash. Dus ben ik even op onderzoek gegaan en nu blijkt dat het ls en het wc commando blijven hangen op de één of andere manier. ps -A geeft namelijk de volgende uitvoer:

code:
1
2
 4176 pts/0    00:00:00 ls <defunct>
 4178 pts/0    00:00:00 wc <defunct>


Nou weet ik dat voordat een programma daadwerkelijk wordt beïndigd je een wait moet uitvoeren om de PCB te clearen, maar aangezien ik bij een pipe alle commando's tegelijkertijd moet uitvoeren kan ik natuurlijk niet wachten tot een proces klaar is voordat ik het volgende uit ga voeren. Dus, zijn er mensen met ideeën hoe ik dat kan oplossen?

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Hoe start je die processen op dan ?

Ik weet dat je bij een pthread je met een pthread_join oid moet wachten totdat je child processen afgelopen zijn, omdat je anders die zombies krijgt. ( Je krijgt dan ook een signaal dat je child is afgestorven )

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Verwijderd

Topicstarter
farlane schreef op 06 april 2004 @ 08:53:
Hoe start je die processen op dan ?

Ik weet dat je bij een pthread je met een pthread_join oid moet wachten totdat je child processen afgelopen zijn, omdat je anders die zombies krijgt. ( Je krijgt dan ook een signaal dat je child is afgestorven )
Ik start de processen gewoon op met execvp, signature daarvan is:

void execvp(char * file, char * argv[]);

Ik zorg er dus eerst voor dat ik mijn commando kan parsen naar iets wat daar in past, vervolgens voer ik een fork() uit en in m'n kind proces ga die execvp doen en in m'n parent proces ga ik wachten, mits er geen gebruik gemaakt wordt van een pipe, want dan moet ik meteen doorgaan met het uitvoeren van de volgende pipeline.

Gister heb ik even zitten kloten met het afvangen van het SIG_CHLD signal, maar dat werkt de ene keer wel en de andere keer niet, dus dat is niet echt een betrouwbare oplossing, maar misschien ligt dat aan mijn implementatie. Ik gebruik nu namelijk 't volgende stukje code:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Catch the termination of a child process
void catchchild(int);
signal(SIGCHLD, catchchild);

// uitvoer code

/**
 * Occurs when a child process has exited.
 */
void    catchchild(int signo)
{
    // Reinitialize the signal handler
    signal(SIGCHLD, catchchild);
    
    // Clean up the remainders of the process
    int ret;
    wait(&ret);
    
    // Display relevant information
    cout << "Return value: " << ret << endl;
    cout << "Signo: " << signo << endl;
}


Nou schijnen er nogal wat problemen te zijn met deze aanpak als er meerdere signals tegelijk komen, maar hoe ik het dan wel moet oplossen kan ik niet echt vinden.

Verwijderd

Topicstarter
Ik ben nog weer eens wezen kloten en ik heb een wat ander techniekje gebruikt voor het afhandelen van signals. Ik gebruik nu het volgende stukje code:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Set up a handler for handling te termination of a child proces
void catchchild(int, siginfo_t *, void *);
static struct sigaction act;
act.sa_sigaction = catchchild;
act.sa_flags = SA_SIGINFO;
sigfillset(&(act.sa_mask));
sigaction(SIGCHLD, &act, NULL);

// uitvoer code

/**
 * Occurs when a child process has exited.
 */
void    catchchild(int signo, siginfo_t * siginfo, void *)
{
    cout << "Called catchchild. PID: " << siginfo->si_pid << " Code: " << siginfo->si_code << endl;
    // Clean up the remainders of the process
    int ret;
    waitpid(siginfo->si_pid, &ret, 0);  
}


Dat werkt op zich prima, alleen vallen me nu een aantal dingen op als ik kijk naar de volgende debug uitvoer:

code:
1
2
3
4
5
6
7
8
9
10
New PID: 2263
New PID: 2264
New PID: 2265
Waiting for last process. PID: 2265
New PID: 0
New PID: 0
New PID: 0
     20     173    1278
Called catchchild. PID: 2263 Code: 1
Return value: 0


Zoals te zien worden er drie processen opgestart ('k gebruik nog steeds 't commando van m'n startpost voor 't testen), vervolgens wordt er gewacht tot het laatste proces (dat is wc dus) afgelopen is. Daarna gaan de kind procesjes aan de gang en vervolgens komt de uitvoer van wc, daarna vang ik nog 1 SIG_CHLD af voor het eerste proces (ls -l), maar ik krijg nooit een SIG_CHLD voor proces 2 (cat) en die blijft dan dus ook vrolijk hangen. Misschien dat zo wat duidelijker is wat het probleem precies is.

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Misschien kun je wat met wait3 ? waitpid is volgens mij niet de oplossing ...

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.