Toon posts:

[C] getchar() uit pipe moet blocken

Pagina: 1
Acties:

Verwijderd

Topicstarter
Hoe krijg ik getchar() zover dat hij blockt ook als ik uit een pipe lees?

Om mijn probleem te illustreren:

Ik heb de volgende code
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <string.h>

int main() {
  int i=0;
  char c;
  char *tomatch = "quit";
  while (i<strlen(tomatch)) {
    c=getchar();
    if (c==tomatch[i]) i++; else i=0;
    printf("%c",c);
  }
}

als ik nu opstart
code:
1
echo bla bla bla quit bla bla bla | a.out

dan werkt het perfect, maar als ik opstart
code:
1
echo bla bla bla | a.out

dan blijft getchar() rommel van uit de pipe lezen terwijl ik wil dat hij gaat wachten tot er weer wat inzit. Voorbeeld als je het opstart met
code:
1
tail -f file1 | a.out

dan moet ie de file1 blijven echo'en totdat ie quit tegen komt en moet getchar() dus netjes geblockt blijven wachten en geen rommel gaan spuien, zolang tail aan het wachten is.

iemand die mijn probleem begrijpt en me dan ook nog kan helpen?

  • D2k
  • Registratie: Januari 2001
  • Laatst online: 31-08 10:19

D2k

ik begrijp je probleem
ik heb alleen nog geen oplossing
is er niet een inlees iets dat blockt?

Doet iets met Cloud (MS/IBM)


Verwijderd

Topicstarter
Heb ik erg naar gezocht, een inlees die blockt, maar niet gevonden.
Op internet doet iedereen iets als
code:
1
while ((c=getchar())!=EOF);

maar dat gaat dus 100% CPU trekken en daar heb ik echt geen zin in.

  • D2k
  • Registratie: Januari 2001
  • Laatst online: 31-08 10:19

D2k

idd busy wait is vies

um is er niet een signal voor die je kan gebruiken?
* D2k doet ff wat voorstellen, geen id of het kan.

Doet iets met Cloud (MS/IBM)


Verwijderd

Topicstarter
Ja als ik wist dat ik op een signal kon wachten zou ik dat inderdaad doen, maar ik kan niks daarover vinden. Ik wil gewoon een getchar() hebben die gewoon blockt als er niks is.
Heb al gezocht maar kan echt niks vinden.

Verwijderd

:? :? :? :?
getchar() en de hele io-familie zijn blocking calls. Je code gebruikt 100% CPU omdat je voorbij de EOF leest, die rare tekens die je print zijn EOF chars!

Probeer dit eens:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <string.h>

const char TOMATCH[]= "quit";

int main() {
        int i= 0;
        int len= strlen(TOMATCH);
        char ch= getchar();
        while(ch != EOF) {
                putchar(ch);
                if(ch == TOMATCH[i]) {
                        if(++i == len) break;
                } else {
                        i= 0;
                }
                ch= getchar();
        }
        return i == len ? 0 : 1;
}

Verwijderd

Topicstarter
Ja ik weet dat die rare tekens EOF chars zijn.
Jouw voorbeeld echter kapt er mee zodra er een EOF char komt, maar dat wil ik niet. Ik wil mijn programma aan een pipe koppelen en die pipe kan bijvoorbeeld best eerste een uur leeg staan voordat er eindelijk een character aankomt. In jouw oplossing vliegt het programma er direct uit. Ik wil graag een oplossing met een call die eerst een uur wacht en dan weer vrolijk verder gaat.
Jouw idee werkt dus niet.

Verwijderd

Misschien is stdin gewoon non-blocking? fcntl() is je vriend!

Verwijderd

Zo lang als je de pipe niet closed zal de oproepende applicatie geen EOF sturen, en deze code zal dus gewoon blijven wachten op input totdat de oproepende applicatie de pipe sluit. Hoe denk je dat je met deze functies keyboard input kunt lezen? Die keyboards staan soms dagen te wachten op input...

Verwijderd

Topicstarter
Verwijderd schreef op 27 september 2002 @ 15:24:
Zo lang als je de pipe niet closed zal de oproepende applicatie geen EOF sturen
toch verschijnen er EOF tekens op het scherm, zoals ik in mijn eerst post aan gaf met
code:
1
echo bla | a.out

als volgens jouw de oproepende application geen EOF stuurt heb ik geen idee waar die dingen wel vandaan komen.
ALs je gelijk heb ben ik heel benieuwd wie en waarom en EOF's worden gestuurd.

Verwijderd

:) Ik ken het probleem...

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const char TOMATCH[] = "quit";

int main ( ) {
  int i = 0;
  int len = strlen (TOMATCH);
  while ( 1 )
  {
    char ch = getchar ( )
    if ( ch != EOF )
    {
      putchar ( ch );
      if ( ch == TOMATCH[ i ] )
      {
         i++;
         if (i == len) break;
      } //if (ch == TOMATCH[ i ]
      else
      {
         i = 0;
      } //else
    } //if ( ch != EOF)
    else
    {
      sleep ( 50 );
    } // else
  } // while
  return 0;
}


Zo zou 'ie moeten wachten als 'ie EOF's leest en niet quitten...

Verwijderd

Verwijderd schreef op 27 september 2002 @ 15:28:
toch verschijnen er EOF tekens op het scherm, zoals ik in mijn eerst post aan gaf met
code:
1
echo bla | a.out

als volgens jouw de oproepende application geen EOF stuurt heb ik geen idee waar die dingen wel vandaan komen.
1) echo opent een pipe naar a.out
2) echo schrijft "bla" naar de pipe
3) echo closed de pipe (en stuurt een EOF)

Die ene EOF blijf jij vervolgens uitlezen en afdrukken...

Verwijderd

als je doet
echo bla bla | a.out
dan krijg je natuurlijk een eof, namelijk aan het einde van 'bla bla', dan is je echo namelijk klaar :)
Een applicatie die actief blijft en output blijft geven kan je op deze manier niet redirecten. Dat komt omdat zolang die applicatie niet is afgesloten, de 'gepipete' applicatie niet wordt gestart.
ptail -f bla.log | a.out
heeft dus geen zin, aangezien ptail niet afsluit en a.out niet wordt gestart voordat ptail afgesloten is.

edit:

hmm, das dus niet waar onder multiprocessing OSen... Op winXP hier wordt de gepipete applicatie METEEN gestart... De door mij aangeleverde voorbeeldcode zou dus moeten werken in dit geval :)

Verwijderd

Topicstarter
Als reactie op Akhorahil

Ja dit is dus dezelfde oplossing die al gaf, alleen heb jij er nog een sleep in gebouwd. Opzich een werkende oplossing, maar ik vind het pollende karakter echt ontzettend lelijk. ipv van een sleep wik dus heel graag iets hebben dat gewoon wacht op een actie ipv telkens te kijken of er wat gebeurd.

Verwijderd

Topicstarter
Verwijderd schreef op 27 september 2002 @ 15:35:
Dat komt omdat zolang die applicatie niet is afgesloten, de 'gepipete' applicatie niet wordt gestart.
Sorry hoor, maar volgens mij is dit grote onzin.
als ik doe
code:
1
tail -f file1 | grep test

dan krijg ik direct al allerlei regels met test als ze worden toegevoegd. grep draait dus allang voordat tail is afgesloten. Sterker nog, je kan tail -f zelfs dingen meegeven dat hij ook moet doodgaan als het proces achter hem doodgaat ofzoiets....

en als reactie op je edit
hmm, das dus niet waar onder multiprocessing OSen... Op winXP hier wordt de gepipete applicatie METEEN gestart... De door mij aangeleverde voorbeeldcode zou dus moeten werken in dit geval
Code werkt dus ook wel, maar het is ontzettend lelijk om telkens te gaan checken. In een noodgeval kan dat wel, maar ik wil gewoon een blocking call hebben...


en als reactie op mietje
1) echo opent een pipe naar a.out
2) echo schrijft "bla" naar de pipe
3) echo closed de pipe (en stuurt een EOF)

Die ene EOF blijf jij vervolgens uitlezen en afdrukken
Vind ik een leuke theorie en klinkt ook aannemelijk in het begin, maar van bijvoorbeeld tail -f weet ik dat hij de pipe niet closed maar dat mijn proggie toch voltijd EOF's aan het lezen is. Dus misschien heb je met echo "bla" | a.out wel gelijk toch lost dit mijn probleem niet op.
Ik wil graag doen
tail -f | a.out
en dan moet a.out niet als een dwaas EOF's gaan printen zolang de pipe van tail-f open doch leeg is.

Verwijderd

Verwijderd schreef op 27 september 2002 @ 15:35:
Een applicatie die actief blijft en output blijft geven kan je op deze manier niet redirecten. Dat komt omdat zolang die applicatie niet is afgesloten, de 'gepipete' applicatie niet wordt gestart.
ptail -f bla.log | a.out
heeft dus geen zin, aangezien ptail niet afsluit en a.out niet wordt gestart voordat ptail afgesloten is.
Dit is natuurlijk niet waar, anders zou je blind alle input vanaf een keyboard moeten typen voordat de applicatie er iets mee kan.

Ik denk eerder dat het probleem te maken heeft met de line-orientedness van de getchar() familie; getchar() en consorten buffert de input totdat er een '\n' newline gelezen wordt.

Verwijderd

Ja dit is dus dezelfde oplossing die al gaf, alleen heb jij er nog een sleep in gebouwd. Opzich een werkende oplossing, maar ik vind het pollende karakter echt ontzettend lelijk. ipv van een sleep wik dus heel graag iets hebben dat gewoon wacht op een actie ipv telkens te kijken of er wat gebeurd.
Dan moet je een non-blocking read in combinatie met een event gebruiken... Ik weet niet hoe dat onder *nix gaat (prog alleen voor dos, windows en OS/2). Maar jij dus vast wel ;)
Sorry hoor, maar volgens mij is dit grote onzin.
Je hebt gelijk. Sorry. |:( Dit gaat alleen op voor non-multiprocessing OSen (zoals DOS)

Verwijderd

Topicstarter
Dan moet je een non-blocking read in combinatie met een event gebruiken... Ik weet niet hoe dat onder *nix gaat (prog alleen voor dos, windows en OS/2). Maar jij dus vast wel
Dat is precies mijn vraag :)
Als ik het wist zou ik het niet vragen, het feit dat ik het vraag geeft dus aan dat ik niet weet.
Toch bedankt dat je meedenkt, maar ik wacht nog even op de goude tip die gewoon de naam van de blocking call noemt.

Verwijderd

Verwijderd schreef op 27 september 2002 @ 15:37:
Vind ik een leuke theorie en klinkt ook aannemelijk in het begin, maar van bijvoorbeeld tail -f weet ik dat hij de pipe niet closed maar dat mijn proggie toch voltijd EOF's aan het lezen is.
NEEEE, LEES nu wat ik schrijf. Dit is namelijk geen theorie maar zoals het in werkelijkheid gaat. Jou proggie leest een EOF, waarna de stream op error gaat. Die ene EOF blijf je vervolgens inlezen, omdat je er niet op checked en niet stopt met lezen wanneer er geen input meer is.
en dan moet a.out niet als een dwaas EOF's gaan printen zolang de pipe van tail-f open doch leeg is.
Dan moet je maar checken op die EOF, zoals dat hoort. Zoals al aangegeven ligt je probleem er waarschijnlijk aan dat je je "tail -f" output niet scheidt met newlines.

Verwijderd

getchar() en consorten buffert de input totdat er een '\n' newline gelezen wordt
getch ( ) doet dat gelukkig niet (niet onder win/dos iig).
getchar ( ) is hier dus niet geschikt, aangezien er nooit een '\n' gelezen wordt, en de functie dus steeds EOF's blijft ophalen uit de pipe :(

Verwijderd

Topicstarter
NEEEE, LEES nu wat ik schrijf. Dit is namelijk geen theorie maar zoals het in werkelijkheid gaat. Jou proggie leest een EOF, waarna de stream op error gaat. Die ene EOF blijf je vervolgens inlezen, omdat je er niet op checked en niet stopt met lezen wanneer er geen input meer is.
Ja ik weet dat het zo werkt, maar jij beweert dat een EOF een teken is, dat de pipe dichtgaat. Daar ben ik niet zo zeker van, met tail -f weet ik dat de pipe niet dichtgaat maar er verschijnen wel EOF's.
Aangezien ik het verschil niet kan zien tussen een EOF van echo of van tail weet ik dus niet of de pipe nog open staat.

Als het waar is dat het komt dat getchar() linebased is (wat ik niet begrijp, volgens mij is dat juist het verschil tussen getchar() en het read-line commando) dan bedoel ik in mijn vraag dat ik de naam van een call zoek die en character based is en blocking.

Akhorahil:
getch ( ) doet dat gelukkig niet (niet onder win/dos iig).
getchar ( ) is hier dus niet geschikt, aangezien er nooit een '\n' gelezen wordt, en de functie dus steeds EOF's blijft ophalen uit de pipe
getch() leest alleen van keyboard en niet van pipes, geloof ik als ik de man-page zo eens lees.

Verwijderd

Mensen, wakker worden!

PROBEER nu gewoon mijn code eens uit met een tail -f, en kijk of er EOF's geprint worden, of de applicatie te vroeg afsluit, of 100% CPU gebruikt. (Hint: dat doet ze niet, mijn code doet exact wat je wilt.)
Verwijderd schreef op 27 september 2002 @ 15:47:
getch ( ) doet dat gelukkig niet (niet onder win/dos iig).
getchar ( ) is hier dus niet geschikt, aangezien er nooit een '\n' gelezen wordt, en de functie dus steeds EOF's blijft ophalen uit de pipe :(
:Z :z Je hebt nu juist een blocking call nodig, anders moet je (RAW) sockets gaan pollen, en dat wil je niet, geloof me.
Verwijderd schreef op 27 september 2002 @ 15:50:
Als het waar is dat het komt dat getchar() linebased is (wat ik niet begrijp, volgens mij is dat juist het verschil tussen getchar() en het read-line commando) dan bedoel ik in mijn vraag dat ik de naam van een call zoek die en character based is en blocking.
getchar(0 is wel degelijk line-based, probeer mijn code maar uit zonder er iets naartoe te pipen. Hij wacht dan op keyboard input, en print die uit zodra je een enter geeft.

Als het echt te moeilijk is om je input te scheiden met newlines dan zul je met sockets moeten werken (hetgeen veel complexer is en hoogst waarschijnlijk niet nodig).

Verwijderd

Topicstarter
PROBEER nu gewoon mijn code eens uit met een tail -f, en kijk of er EOF's geprint worden, of de applicatie te vroeg afsluit, of 100% CPU gebruikt. (Hint: dat doet ze niet, mijn code doet exact wat je wilt.)
Ja klopt. Het lijkt inderdaad te werken, al kreeg ik tijdens het direct printen wel EOF's op mijn scherm. Daarom dacht ik dat ze er wel door kwamen.

Goed het probeem is bijna opgelost, maar nog steeds line based geloof ik, misschien is het nu meer een linux-script probleem dan

als ik
code:
1
tail -f | a.out
doe werkt het wel, maar het hele zaakje gaat pas dood als tail -f eindelijk zijn \n genereert en in mijn probleem kan dat wel even duren. Ik kan inderdaad zelfs zien dat a.out afgelopen is, toch gaat het zaakje niet plat...

Verwijderd

Verwijderd schreef op 27 september 2002 @ 16:18:
als ik
code:
1
tail -f | a.out
doe werkt het wel, maar het hele zaakje gaat pas dood als tail -f eindelijk zijn \n genereert en in mijn probleem kan dat wel even duren.
Mja, ik weet dus niet hoe je output er uit moet zien, maar als hij niet "human-readable" moet zijn kun je bv. alles wat je nu als spaties uitvoert veranderen in newlines. Zo weet je zeker dat je applicatie na elk compleet woord ff wakker wordt.

Verwijderd

Topicstarter
Of ik begrijp jouw niet, of jij begrijpt mij niet, daar ben ik nog niet uit. :)
Mijn applicatie moet niet wakker worden, maar tail -f moet dood gaan als a.out ook doodgaat.
als ik bijvoorbeeld a.out "ik ga dood" laat afdrukken voor de return en ik doe
tail -f file1 | a.out
dan krijg ik bijvoorbeeld als uitvoer

bla bla bla quit ik ga dood

maar blijft de tail -f nog leven tot dat er bij file1 een nieuwe regel komt.
Omdat ik voor de tail -f niks kan veranderen kan ik daar ook geen spaties en enters vervangen.

Verwijderd

Hmm, het wordt duidelijker :)
Je wilt dus de output van een tail -f lezen, en die tail -f stoppen wanneer er een bepaald woord in voorkomt? Dan werk je verkeerd om, je moet vanuit jouw applicatie een FILE *pipe= popen("tail -f /var/log/blah", "r"); doen, waarna je van die pipe kunt lezen. Als je die pipe closed met pclose(pipe); gaat je tail -f ook dood.

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 15:49
Dat tail niet 'dood' gaat komt omdat 'ie pas doorheeft dat de pipe stuk is, als 'ie er naar wil schrijven. Hoe je dat oplost weet ik niet - een signaal sturen lijkt me het meest logisch, maar dan moet je de process id van tail wel doorgeven aan je applicatie (misschien is 't wel te zien - geen idee hoe though).

Verwijderd

Je hebt nu juist een blocking call nodig, anders moet je (RAW) sockets gaan pollen, en dat wil je niet, geloof me.
Sorry maar sockets hebben erg weinig te maken met of je calls wel of niet blocking zijn... Een non-blocking call kan je gewoon blocking maken m.b.v. een event, en dat heb je ineens geen raw sockets meer nodig?
Sowieso gaat het hier over pipes, en niet over sockets.

Verwijderd

Verwijderd schreef op 27 september 2002 @ 19:13:
Sorry maar sockets hebben erg weinig te maken met of je calls wel of niet blocking zijn... Een non-blocking call kan je gewoon blocking maken m.b.v. een event, en dat heb je ineens geen raw sockets meer nodig?
Sowieso gaat het hier over pipes, en niet over sockets.
IO in *nix is altijd non-blocking, tenzij het niet anders kan ivm. orthogonaliteit. Maw. input van ttys en pipes is blocking, en daar kun je verdomd weinig aan doen. Als je dus dit soort dingen non-blocking wilt realiseren zul je met een andere vorm van IPC moeten werken, en dan liggen sockets voor de hand.

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Je kan toch gewoon stdin als file openen, en dan ben je van dat hele newline gedoe af?

Verwijderd

Probeer dit eens, dit is code die een popen() simuleert, maar het child process (de fail -f) meteen killed als het woord quit in de output zit.

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char TAILCMD[]= "tail";
const char STOPWORD[]= "quit";

int main(int argc, char *argv[])
{
        int     ret;
        int     pipes[2];
        pid_t   child;
        if(argc != 2) {
                fputs("No argument specified: Specify a file to watch.\n", stderr);
                return EXIT_FAILURE;
        }
        if(pipe(pipes)) {
                fputs("Couldn\'t create pipe.\n", stderr);
                return EXIT_FAILURE;
        }
        child= fork();
        if(child == (pid_t)0) {
                /* child code */
                if(dup2(pipes[1], STDOUT_FILENO) == -1) {
                        fputs("Couldn\'t duplicate stdout.\n", stderr);
                } else {
                        char    error[65];
                        execlp(TAILCMD, TAILCMD, "-f", argv[1], NULL);
                        sprintf(error, "Couldn\'t execute \"%s\"", TAILCMD);
                        perror(error);
                }
                ret= EXIT_FAILURE;
         } else if(child > (pid_t)0) {
                /* parent code */
                FILE    *fp= fdopen(pipes[0], "r");
                pid_t   p;
                int     len= strlen(STOPWORD);
                int     i= 0;
                int     ch= fgetc(fp);
                while(ch != EOF) {
                        putchar(ch);
                        if(ch == STOPWORD[i]) {
                                if(++i == len) break;
                        } else {
                                i= 0;
                        }
                        ch= fgetc(fp);
                }
                fclose(fp);
                if(i == len) {
                        putchar('\n');
                        kill(child, SIGTERM);
                }
                p= waitpid(child, &ret, 0);
                if(WIFEXITED(ret)) {
                        ret= WEXITSTATUS(ret);
                } else if(WIFSIGNALED(ret) && WTERMSIG(ret) == SIGTERM) {
                        ret= EXIT_SUCCESS;
                } else {
                        fputs("Child process terminated abnormally.\n", stderr);
                        ret= EXIT_FAILURE;
                }
        } else {
                perror("Couldn\'t fork child process");
                ret= EXIT_FAILURE;

        }
        return ret;
}


Als dit niet werkt heb je nog maar een optie volgens mij, nl. je eigen filterende versie van tail -f schrijven.

Verwijderd

Mietje :

Jij reageert op mijn opmerking
getch ( ) doet dat gelukkig niet (niet onder win/dos iig).
getchar ( ) is hier dus niet geschikt, aangezien er nooit een '\n' gelezen wordt, en de functie dus steeds EOF's blijft ophalen uit de pipe
met
Je hebt nu juist een blocking call nodig, anders moet je (RAW) sockets gaan pollen, en dat wil je niet, geloof me.
Ik heb het hele woord blocking dan niet eens genoemd. Waarom ga je dan juist daar op in als reactie op mijn bovenstaande commentaar, wat gaat over het wel of niet afsluiten met een newline character? Het verband ontgaat me echt volledig...

En wat betreft blocking pipes : zoals ik al zei ik weet niet hoe dat onder *nix werkt, maar pipe calls hoeven echt niet blocking te zijn. In win32 is er voor alle pipe reads (pipe = file overigens) een blocking en een overlapped (non-blocking) variant.
Pagina: 1