Hallo,
ich suche nach einer Möglichkeit einen laufenden C-Programm befehle über
die Comandline zu geben.
Ich arbeite auf einen PI3 mit Raspbian Jessy
ich habe ein kleines C-Programm welches "ständig" für node den Status
von ADCs und einigen GPIOs auszulesen (über I2C).
jetzt möchte ich das Tool so erweitern, dass ich dem zwischendurch
befehle geben kann <- ohne dass diese pausiert...
ein paar Ideen hatte ich z.B. mit Dateien welche ständig ausgelesen
werden oder mit einer Datenbank welche abgearbeitet werden... aber
irgendwie habe ich das Gefühl, dass es sich negativ an der Zeit
auswirkt.
Folgendes Problem, wenn ich ein 2. Programmchen starte welches GPIOs
schaltet, kommt dieses MANCHMAL durcheinander... immer wenn das Lese C
mit dem Schreib C "gleichzeitig" arbeitet, schaltet er falsch...
Hat jemand eine Idee was ich machen könnte?
=> Ich brauche entweder die möglichkeit, möglichst schnell mit einem
Tool STÄNDIG lesen und gleichzeitig zwischendurch schreiben, oder statt
schreiben das lesetool kurzzeitig pausieren <- was mir fast noch lieber
wäre!
Vielen Dank
baer schrieb:> ich suche nach einer Möglichkeit einen laufenden C-Programm befehle über> die Comandline zu geben.
Beschissene Idee, denn dafür würde man echte IPC brauchen. Die Command
Line laufender Prozesse lässt sich AFAIK nach dem Start nicht mehr
ändern.
Normalerweise macht man dann ein Config File und einen Signal Handler
für das USR1 Signal, dass das Programm anweist seine Config neu
einzulesen. Das ist deutlich einfacher zu programmieren.
baer schrieb:> ein paar Ideen hatte ich z.B. mit Dateien welche ständig ausgelesen
Dazu wurden named pipes erfunden. Minimalbeispiel:
1
#include<fcntl.h>
2
#include<stdio.h>
3
#include<sys/stat.h>
4
#include<unistd.h>
5
6
intmain(void)
7
{
8
intfd;
9
constchar*myfifo="/tmp/myfifo";
10
charbuf;
11
12
mkfifo(myfifo,0666);
13
fd=open(myfifo,O_RDONLY);
14
while(1)
15
{
16
// falls etwas in die named pipe geschrieben wurde:
17
if(read(fd,&buf,1)>0)// normalerweise würde man mehr als ein zeichen lesen
18
{
19
switch(buf)
20
{
21
case'q':
22
gotoexit_loop;
23
case's':
24
fprintf(stderr,"befehl=s\n");
25
break;
26
default:
27
fprintf(stderr,"unbekannt: %c\n",buf);
28
break;
29
}
30
}
31
32
// normaler ablauf:
33
fprintf(stderr,"Temperatur = 17 Grad\t");
34
fprintf(stderr,"Wasserstand = 23 m\n");
35
sleep(1);
36
}
37
38
exit_loop:
39
close(fd);
40
unlink(myfifo);
41
return0;
42
}
Jetzt kann man mit
1
echo "q" > /tmp/myfifo
oder einem aufwändigeren Programm Dinge in die named pipe schreiben, die
dann im obigen Beispiel auftauchen. Im Beispiel ist ein Bug, der die
Endlosschleife erst startet, wenn ein Zeichen in die named pipe
geschrieben wurde.
> irgendwie habe ich das Gefühl, dass es sich negativ an der Zeit> auswirkt.
Solche low-level-Unix-Fileoperationen sind nicht mit dem Öffnen einer
Word-Datei vergleichbar ;).
Möglichkeiten gibt es viele:z.B. Programm bleibt im Vordergrund und man
macht eine Schleife die die Eingaben abfragt (und die eigentliche
Aufgabe in einem extra Thread). Dabei kann man z.B. mit ncurses
arbeiten, damit kann man auch komplette Textmenüs u.ä. erzeugen, aber
auch so einfache Dinge wie bleib in einer Zeile während der Eingabe und
lösche sie wieder. Oder man macht zwei Programm die z.B. Netzwerkports
kommunizieren. Eines läuft permanent und das andere führt man nur mit
dem entsprechenden Parameter aus und dieser wird dann an das
Hauptprogramm weitergegeben.
Tom schrieb:>> int main(void)> {> int fd;> const char* myfifo = "/tmp/myfifo";> char buf;>> mkfifo(myfifo, 0666);> fd = open(myfifo, O_RDONLY);> while(1)> {> // falls etwas in die named pipe geschrieben wurde:> if (read(fd, &buf, 1) > 0) // normalerweise würde man mehr als> ein zeichen lesen> {> switch(buf)> {> case 'q':
...
> }> [/c]>> Jetzt kann man mit>
1
echo "q" > /tmp/myfifo
> oder einem aufwändigeren Programm Dinge in die named pipe schreiben
Das obige Programm ist leider falsch, weil der einzige Thread durch
einen blockierenden Systemcall wie read() blockiert wird, und daher
nichts anderes machen kann. Man muss je blockierenden filedescriptor
einen eigene thread starten. D.h. einen ggf. für die "normale" Arbeit
und einen Thread, um von der command-pipe/socket zu lesen. Die
Synchronisation der Datenstrukturen, die gemeinsam verwendet werden,
darf man dann natürlich auch nicht vergessen. Oder man versetzt den fd
in den non-blocking-mode und macht ein polling, was aber eigentlich
nicht schön ist.
Also besser, je blockierendem fd einen eigenen thread mit pthread_create
starten... nicht schwer, aber man muss eben einige Grundprinzipien
beachten.
Sende den Befehl in eine Datei (z.B. .txt) unter einem vorher
definierten Namen und Verzeichnis. Sperren bzw. warten falls bereits
existent.
Frage im Programm nach dieser Datei; existiert diese Lese sie; lösche
sie; kümmre Dich um den Inhalt.
Das Gleiche kann man mit einem Eintrag in der INI machen.
Sinnvollerweise sollte das Programm, bei seinem Start, einen eventuellen
Eintrag bzw. eine existente Datei, löschen.
Es geht, ich habe mehrere Hintergrundprogramme (Dämone), die das können.
Ganz simpel ist es allerdings nicht. Wenn das Programm im Hintergrund
läuft, kann man über dasselbe Programm Einstellungen des laufenden
Dämons abfragen oder auch ändern.
Dafür gibt es MessageQeues, eine Form von Interprozesskommunikation.
Wenn das Programm gestartet wird, informiert es sich, ob schon eine
Instanz am Laufen ist. Wenn ja, geht es nicht in den Hintergrund,
sondern kommuniziert mit dem laufenden Dämon.
baer schrieb:> ich habe ein kleines C-Programm welches "ständig" für node den Status> von ADCs und einigen GPIOs auszulesen (über I2C).> jetzt möchte ich das Tool so erweitern, dass ich dem zwischendurch> befehle geben kann <- ohne dass diese pausiert...
Ein select mit einem Timeout auf stdin. Kommt der Timeout, dann messen,
sonst input bearbeiten
MfG Klaus
Ein select oder poll mit timeout oder auch eine periodische
Unterbrechung mittels eines Signals (alarm) für zu einer polling
Strategie: das ist nicht gut.
Entweder sind alle DatenQuellen Dateideskriptoren, dann kann man
erfolgreich mit select / poll ein Multiplexen über die blockierenden
Quellen machen.
Ist aber eine Quelle keine fd, dann braucht man mehrere Threads.
Meistens führen Programme mit mehreren Threads zu strukturell
einfacheren Programmen.
Man kann die Threads auch auf mehrere Prozesse aufteilen: dann braucht
man allerdings IPC-Mechnismen (semaphores / mutex, message-queues /
pipes, shared memory) für den Datenaustausch.
Ein gutes Buch zu der Thematik ist der Klassiker:
APUE:
https://www.amazon.de/Programming-Environment-Addison-Wesley-Professional-Computing/dp/0321637739
Ich mache immer einen Socket auf. Einfach AScii, Telnet-Stil.
Dann kann man von der Kommandozeile mit netcat dahin etwas schicken.
Das geht dann auch von extern, also einem Steuerrechner.
Ich würde 2 Threads machen - einer, der die GPIOs bearbeitet, und einer,
der die Konsole bedient. Kommunikation zwischen den Threads dann über
Variablen. Da du auf einem Raspi arbeitest, müsste es auch keine Kunst
sein, fork und Konsorten zu benutzen.
Ja klar, aber er "baer" weiß ja nicht, wie er über mehrere
file-descriptoren in seinem(!) Programm multiplexen soll, wenn er
blockierende systemcalls zum Lesen seiner Daten benutzen muss.
Deswegen:
a) select/poll-Lösung
b) meherere Aktivitätsträger (Threads)
Wilhelm M. schrieb:> Das obige Programm ist leider falsch, weil der einzige Thread durch> einen blockierenden Systemcall wie read() blockiert wird, und daher> nichts anderes machen kann. Man muss je blockierenden filedescriptor> einen eigene thread starten.
Oder man nutzt select(), um auf mehrere Filedeskriptoren gleichzeitig zu
warten.
Wilhelm M. schrieb:> Aber jetzt wiederholen sich die Antworten ... (s.o.)
Ups. Ich hätte vielleicht die Seite vor dem Antworten nochmal neu laden
sollen.
Wilhelm M. schrieb:> Ein select oder poll mit timeout oder auch eine periodische> Unterbrechung mittels eines Signals (alarm) für zu einer polling> Strategie: das ist nicht gut.
Wenn er sowieso regelmäßig seinen ADC einliest, passt das doch. Ob er
nun in einem Thread regelmäßig seine Messung macht und dann mit
irgendeinem nanosleep oder so wartet und in einem separaten Thread dann
auf das File wartet oder select() nutzt, um beides zu machen, ist doch
Jacke wie Hose.
> Ist aber eine Quelle keine fd, dann braucht man mehrere Threads.
Auch dann geht's ohne. Schau dir z.B. an, was die Eventloops von
modernen GUI-Frameworks so alles ohne Threads machen können.
> Meistens führen Programme mit mehreren Threads zu strukturell> einfacheren Programmen.
Kommt sehr drauf an. Man muss sich dann um Synchronisation kümmern und
kann sich auch schnell mal einen fiesen Fehler einfangen, der schwer zu
finden ist. Meines Erachtens machen Threads die Programme oft genug eher
komplizierter als einfacherer - zumindest wenn sie auch miteinander
kommunizieren sollen. Deshalb gehe ich damit eher sparsam um und
vermeide sie, wenn sie nicht nötig sind.
> Man kann die Threads auch auf mehrere Prozesse aufteilen: dann braucht> man allerdings IPC-Mechnismen (semaphores / mutex, message-queues /> pipes, shared memory) für den Datenaustausch.
Dafür hat er ja dann gleich das, was er ursprünglich mal wollte: Das
Programm von außen steuern.
Rolf M. schrieb:>> Wenn er sowieso regelmäßig seinen ADC einliest, passt das doch. Ob er> nun in einem Thread regelmäßig seine Messung macht und dann mit> irgendeinem nanosleep oder so wartet und in einem separaten Thread dann> auf das File wartet oder select() nutzt, um beides zu machen, ist doch> Jacke wie Hose.
Ich habe gesagt, wenn er blockierende (!) systemcalls zum Lesen seiner
Daten verwendet! Dann nützt ihm ein select() nichts, wenn er sein device
nicht als filedescriptor abstrahiert hat. Wenn ja, ist ein multiplexen
mit select() ok (s.o.)
Rolf M. schrieb:>> Auch dann geht's ohne. Schau dir z.B. an, was die Eventloops von> modernen GUI-Frameworks so alles ohne Threads machen können.
Mal so mal so: bspw. hat Qt entsprechende ManagerThreads, die Du nicht
sofort zu Gesicht bekommst ...
Rolf M. schrieb:>> Meistens führen Programme mit mehreren Threads zu strukturell>> einfacheren Programmen.>> Kommt sehr drauf an. Man muss sich dann um Synchronisation kümmern und> kann sich auch schnell mal einen fiesen Fehler einfangen, der schwer zu> finden ist. Meines Erachtens machen Threads die Programme oft genug eher> komplizierter als einfacherer - zumindest wenn sie auch miteinander> kommunizieren sollen. Deshalb gehe ich damit eher sparsam um und> vermeide sie, wenn sie nicht nötig sind.
Gemeint war: strukturell.
Natürlich braucht man Synchronisation (s.o.!), und auch diesen
Werkzeugkasten sollte man beherrschen. Aber auch hier gilt m.E.: wenn
man die Grundprinzipien kann, ist es nicht so schwer. Und Fehler: ja,
die kann man überall machen.
Wilhelm M. schrieb:> Ich habe gesagt, wenn er blockierende (!) systemcalls zum Lesen seiner> Daten verwendet!
Wie kommst du darauf, dass er solche hat? Und welche Systemcalls könnten
das denn sein, um Daten einzulesen, aber nicht von einem Filedeskriptor?
Mir würde da keiner einfallen.
Probleme sehe ich eher, wenn der Filedeskriptor und sein ganzes Handling
hinter einer API versteckt und nicht zugänglich ist. Dann ist man ggf.
gezwungen, auf Threads auszuweichen.
Wilhelm M. schrieb:> Natürlich braucht man Synchronisation (s.o.!), und auch diesen> Werkzeugkasten sollte man beherrschen. Aber auch hier gilt m.E.: wenn> man die Grundprinzipien kann, ist es nicht so schwer. Und Fehler: ja,> die kann man überall machen.
Ich muss mir ja nicht ohne Not zusätzliche potenzielle Fehlerquellen
einbauen.
Rolf M. schrieb:> Wilhelm M. schrieb:>> Ich habe gesagt, wenn er blockierende (!) systemcalls zum Lesen seiner>> Daten verwendet!>> Wie kommst du darauf, dass er solche hat? Und welche Systemcalls könnten> das denn sein, um Daten einzulesen, aber nicht von einem Filedeskriptor?> Mir würde da keiner einfallen.
Ich weiß ja nun nicht, was der TO hat ...
Beispiel aus einer anderen Ecke, die mir sofort einfällt: mq_receive()
Wilhelm M. schrieb:> Beispiel aus einer anderen Ecke, die mir sofort einfällt: mq_receive()
Aus "man mq_overview":
On Linux, a message queue descriptor is actually a file descriptor.
(POSIX does not require such an implementation.) This means that a
message queue descriptor can be monitored using select(2), poll(2), or
epoll(7).
Genaues Lesen: das gilt nur auf Linux!!!!
Schau bitte unter:
http://pubs.opengroup.org/onlinepubs/9699919799/toc.htm
Der DT ist auch mqd_t und nicht int.
Das sind ganz tolle Fehlerquellen, wenn man so etwas annimmt!
Anderes Beispiel: msgrcv()
ööhhhhhh,
erstmal 10000 Dank für die Millionen Antworten ;)
es freut mich wenn einem Freundlich geholfen wird, ist heute keine
Selbstverständlichkeit mehr.
joa, ich bin da eher "einsteiger" im Gebiet. Aber die idee mit der Datei
hatte ich auch...
also mach ich das so
1
while(1){
2
3
dateilesen(inwelcherggf.Steuerbefehlesind)
4
=>wennSteuerbefehledanndieseabarbeiten
5
6
adc&gpioseinlesen
7
8
kurzepauseeinlegen
9
10
}
das Problem was hierbei entsteht ist, dass ich hier eine Datei ja im
falle eines Falles gleichzeitig lesen und beschreiben muss... geht das
"so einfach"
ich habe oben was von "Polling" gelesen, soweit ich weiß, geht das, dass
man die Datei nur zum lesen öffnet und jemand anderes nur zum schreiben.
leider habe ich die Erfahrung gemacht, das Konzepte (die man falsch
angeht) ggf. sehr lange funktionieren... und in einem "ungünstigen"
Moment bekommt man dann Fehler.
Vielen Dank
>
Ich nehme mal jetzt an, dass adc und gpio einlesen nicht blockierend
sind (unter blockierenden Systemcalls versteht man solche, bei denen man
keine Obergrenze an Ausführungszeit angeben kann).
1) das Lesen der Datei und das Beschreiben durch ein anderes Programm
ist nicht grundsätzlich falsch, produziert hier jedoch eine
race-condition (Wettlaufsituation): die Datei ist eine gemeinsame
Ressource, die von zwei Threads bearbeitet wird. Hier muss man
Massnahmen ergreifen.
2) kurze Pause einlegen: der Thread blockiert und kann die Datei nicht
neu lesen. Je nachdem was erwartet wird, kann das zu unerwünschtem
Verhalten führen (weil ja das Steuerkommando nicht sofort bearbeitet
wird).
> Ich nehme mal jetzt an, dass adc und gpio einlesen nicht blockierend> sind (unter blockierenden Systemcalls versteht man solche, bei denen man> keine Obergrenze an Ausführungszeit angeben kann).
ja, das sind zwei unterschiedliche über i2c angebundene module...
> 1) das Lesen der Datei und das Beschreiben durch ein anderes Programm> ist nicht grundsätzlich falsch, produziert hier jedoch eine> race-condition (Wettlaufsituation): die Datei ist eine gemeinsame> Ressource, die von zwei Threads bearbeitet wird. Hier muss man> Massnahmen ergreifen.
was mir noch eingefallen ist, ich muss ja noch irgendwie verhindern,
dass ein befehl "mehrfach" ausgeführt wird :/ d.h. ich müsste doch die
Datei "nochmals" beschreiben!?!? oder hat da jemand ne bessere idee?
> 2) kurze Pause einlegen: der Thread blockiert und kann die Datei nicht> neu lesen. Je nachdem was erwartet wird, kann das zu unerwünschtem> Verhalten führen (weil ja das Steuerkommando nicht sofort bearbeitet> wird).
naja mit kurz mein ich,... meine GPIO schaffen nur alle ~50ms gelesen zu
werden! also hab ich ne Pause von 150ms eingelegt um hier nicht
falschwerte zu erhalten... diese Latenz ist noch in Ordnung für die
ausgabe
man könnte auch
1
longleseAbtand=150//ms
2
3
while(0){
4
5
dateilesen
6
wenndateisteuerbefhel{
7
steuerbefehlabarbeiten
8
***
9
}
10
11
wennlasttime+leseAbtand<now{
12
longlastTime=now()
13
lesegpio&adc
14
}
15
16
}
*** die frage ist jetzt nur, wie kann ich jetzt die Steuerbefehl Datei
leeren?
Das über eine Datei zu machen, wird ziemlicher Krampf.
Tom schrieb:> Dazu wurden named pipes erfunden.
Was gefällt dir denn an der Lösung mit den pipes nicht?
Ansonsten gibt es stdin, das liest von der Eingabe. In einem zweiten
Thread parallel zum Hauptprogramm abgefragt tut das auch, was du suchst.
Oliver
@Oliver S.
öh nein... so ist es nicht dass mir das nicht gefällt...
ich dachte, das ist genau das.
und die erste "Lösung" die ich finde:
http://www.gnu.org/software/libc/manual/html_node/Creating-a-Pipe.html
macht auch nichts anderes als eine Datei schreiben oder lesen!?
=> wo ich trotzdem dran hänge, dass ich ein tool habe das befüllt
und ein 2. tool habe das liest und leert!? <- geht das?
die idee mit dem 2. thread gefällt mir...
aber wie schaut das "konkret" aus?
> stdin ...
Wilhelm M. schrieb:> Genaues Lesen: das gilt nur auf Linux!!!!
Das habe ich und bin einfach mal davon ausgegangen, dass der TE auf
seinem RPI nicht so viele verschiedene Unix-Derivate laufen lassen will,
so wie du einfach mal davon ausgegangen bist, dass er zum Auslesen der
Hardware Message-Queues verwenden will.
Ich war zugegebenermaßen etwas überrascht, dass das in POSIX nicht als
filedescriptor festgelegt ist, denn der Charme ist ja eigentlich gerade,
dass man praktisch alle Kommunikationskanäle mit select() nutzen kann.
baer schrieb:> joa, ich bin da eher "einsteiger" im Gebiet. Aber die idee mit der Datei> hatte ich auch...>> also mach ich das so> while(1) {>> datei lesen (in welcher ggf. Steuerbefehle sind)> => wenn Steuerbefehle dann diese abarbeiten>> adc & gpios einlesen>> kurze pause einlegen>> }> das Problem was hierbei entsteht ist, dass ich hier eine Datei ja im> falle eines Falles gleichzeitig lesen und beschreiben muss... geht das> "so einfach"
Für sowas drängen sich pipes geradezu auf. Die kannst du zur
Kommunikation zwischen Programnen nutzen und Daten da reinschreiben und
rauslesen wie bei Dateien, aber ohne den Umweg über eine echte Datei zu
gehen. Der eine schreibt einfach immer rein, und beim anderen kommt es
genau so wieder an. Was rausgelesen wurde, ist automatisch auch weg, und
wenn nichts reingeschrieben wird, gibt's auch nix zu lesen.
Im einfachsten Fall kann dein Programm sogar von stdin lesen, und du
kannst die Pipe von außen verbinden. Wenn du auf der Shell sowas
schreibst wie:
1
ls | grep "hallo"
ist das nämlich nichts anderes als das Verbinden der Standardausgabe von
ls mit der Standardeingabe von grep über eine pipe.
Alternativ kann man eine pipe auch einfach in der Kommandozeile mit
mkfifo erzeugen. Dann bekommt sie einen Namen, über den man sie sogar
wie eine Datei öffnen kann.
Rolf Magnus schrieb:> Wilhelm M. schrieb:>> Genaues Lesen: das gilt nur auf Linux!!!!>> Das habe ich und bin einfach mal davon ausgegangen, dass der TE auf> seinem RPI nicht so viele verschiedene Unix-Derivate laufen lassen will,> so wie du einfach mal davon ausgegangen bist, dass er zum Auslesen der> Hardware Message-Queues verwenden will.
Das Problem dabei ist ja bei solchen Dingen, also einen mqd_t als int
als filedescriptor zu benutzen, dass dann auf Linux funktioniert, und
dann gegebenenfalls als source irgendwoanders hin übernommen wird. Und
schon haben wir das Problem. Deswegen empfehle ich das niemals.
Rolf Magnus schrieb:> ist das nämlich nichts anderes als das Verbinden der Standardausgabe von> ls mit der Standardeingabe von grep über eine pipe.> Alternativ kann man eine pipe auch einfach in der Kommandozeile mit> mkfifo erzeugen. Dann bekommt sie einen Namen, über den man sie sogar> wie eine Datei öffnen kann.
(Eine unnamed-pipe geht nur mit verwandten Prozessen: pipe())
Also ein named-pipe! Die kann man selbst im Programm per systemcall
mkfifo erstellen. Der CommandProzess öffnet die dann wie eine Datei und
schreibt sein Kommando da rein. Zudem könnte man die named-pipe aka fifo
auch per socat oder ähnlichem im Netzwerk bekannt machen. Auch ganz
hübsch.
Trotzdem: ich würde den Kommandoteil und den Meßwert-Verarbeitungsteil
in unterschiedliche Threads packen. Aber - wie schon gesagt - wenn Du
mit der Verzögerung über dein sleep() / nanosleep() leben kannst, dann
brauchst Du das natürlich nicht ...
Hallo, ich benutze für solche Aufgaben einen Namedpipe-Server z.B. mit
Winform GUI und der Nampedpipeclient ist ein Consolenprogramm, welches
über ein Protokoll Daten an den Server sendet. Der Server beantwortet in
meinem Protokoll die Befehle etc und zeigt dann die Daten grafisch an,
funktioniert seit Monaten ohne Probleme.
Das könnte man gut über ein Terminalserver machen, das heisst Dein
Programm implementiert eine kleine Konsole. Dann kannst Du über
telnet localhost <Dein Port e.g. 16100>
verbinden und die Kommandos übergeben. Das ganze läuft dann in einem
eigenen Thread.
Grüsse,
René
Wilhelm M. schrieb:>> Alternativ kann man eine pipe auch einfach in der Kommandozeile mit>> mkfifo erzeugen. Dann bekommt sie einen Namen, über den man sie sogar>> wie eine Datei öffnen kann.>> (Eine unnamed-pipe geht nur mit verwandten Prozessen: pipe())>> Also ein named-pipe!
Ja, natürlich. Was sonst stellst du dir unter einer Pipe mit einem Namen
vor, als eine named pipe?
> Die kann man selbst im Programm per systemcall mkfifo erstellen.
Oder eben mit dem Kommandozeilentool mkfifo, das ja auch nix anderes
macht.
> Trotzdem: ich würde den Kommandoteil und den Meßwert-Verarbeitungsteil> in unterschiedliche Threads packen. Aber - wie schon gesagt - wenn Du> mit der Verzögerung über dein sleep() / nanosleep() leben kannst, dann> brauchst Du das natürlich nicht ...
Nun, wenn man sowieso eine zyklisch durchlaufene Hauptschleife braucht,
warum nicht? Ich würde das aber eher mit clock_nanosleep() machen, da
man dort auch absolute Zeiten angeben kann.
Offenbar macht der TE das Einlesen der Daten von seinen Schnittstellen
ja sowieso schon nonblocking und zyklisch mit einer Timing-Steuerung.
Dann kann man in der Schleife auch gleich das Kommando einlesen. Wozu
dann extra in einem separaten Thread blockierend lesen und dann in einen
anderen Thread weitergeben, der dann sowieso pollt? Einzig wenn die
Auswertung der Befehle so exorbitant lange dauert, dass die Ausführung
zu weit verzögert wird, wäre es sinnvoll, das in einen anderen Thread
auszulagern und es damit zu entkoppeln.
Rolf Magnus schrieb:>> Ja, natürlich. Was sonst stellst du dir unter einer Pipe mit einem Namen> vor, als eine named pipe?
habe ich geschrieben, weil Dein Beispiel
ls | grep "hallo"
eine unnamed benutzt.
> Nun, wenn man sowieso eine zyklisch durchlaufene Hauptschleife braucht,> warum nicht? Ich würde das aber eher mit clock_nanosleep() machen, da> man dort auch absolute Zeiten angeben kann.
Mmh...absolut, also 11.11.16, 20:00:00. Nein, es ist immer eine
Zeitspanne ...
> Offenbar macht der TE das Einlesen der Daten von seinen Schnittstellen> ja sowieso schon nonblocking und zyklisch mit einer Timing-Steuerung.
Mag sein, dass das non-blocking geschieht.
Dreht man allerdings an der Verzögerungsszeit, beeinflusst man damit
implizit die längste Antwortzeit auf ein Kommando. Das(!) finde ich halt
unschön.
Hallo,
leider find ich online nicht das richtige HowTo oder ich stell mich zu
sehr an...
hat jemand evtl. eine kurze Beschreibung zur Hand wie ich das mit der
"pipe" hinbekomme
eigentlich will ich zuerst einfach mal ein tool, dass in einer Instanz
"dauerläuft" und in der 2. Instanz will ich befehle eingeben
also hab ich 2x Putty offen... im einen fenster läuft ./pipeTest
1
intmain(intargc,char**argv){
2
FILE*stream;
3
intfile,c;
4
5
stream=fdopen(file,"r");
6
7
while(1){
8
9
c=fgetc(stream);
10
printf("%i \n",c);
11
12
}
13
14
return(0);
15
}
zwar ist das Programm ein "dauerläufer"
aber auf ein
> ls | grep 2
in einer anderen Puttyinstanz reagiert er nicht :/
wo sind meine Denkfehler?
Danke
Wilhelm M. schrieb:>> Nun, wenn man sowieso eine zyklisch durchlaufene Hauptschleife braucht,>> warum nicht? Ich würde das aber eher mit clock_nanosleep() machen, da>> man dort auch absolute Zeiten angeben kann.>> Mmh...absolut, also 11.11.16, 20:00:00. Nein, es ist immer eine> Zeitspanne ...
Es ist eine Zeitspanne, aber diese sollte exakt mit Ende der letzten
Zeitspanne beginnen und nicht mit Aufruf von nanosleep(). Das heißt, ich
lese einmal die Uhrzeit aus, sage dann, der nächste Zyklus soll zu
dieser Zeit + x starten. Und dann addiere ich für jeden Zyklus immer die
gewünschte Zykluszeit drauf. So bekomme ich für eine gewünschte
Zykluszeit von x immer sehr genau alle x einen Zyklus und nicht erst
immer nach x + y für y = (ggf. variable) Laufzeit meiner Zyklusfunktion.
Voraussetzung ist natürlich, dass diese Funktion selbst nicht länger
braucht als x.
Mir ist schon klar was clock_nanosleep() macht: nur hatte ich schlicht
nur nanosleep() in Deinem Post gelesen ... das habe ich aber auch
korrigiert (s.o.)
baer schrieb:> hat jemand evtl. eine kurze Beschreibung zur Hand wie ich das mit der> "pipe" hinbekomme>> eigentlich will ich zuerst einfach mal ein tool, dass in einer Instanz> "dauerläuft" und in der 2. Instanz will ich befehle eingeben>> also hab ich 2x Putty offen...
Du musst entsprechend den obigen Antworten unterscheiden zwischen einer
named pipe und einer ... pipe ohne Namen. Letztere kannst du nur von
einem Prozess an einen anderen vererben, wenn du per fork() einen neuen
Prozess startest. Das kannst du aber dann nicht von deinen zwei
Putty-Fenstern aus machen.
Die named pipe dagegen hat einen Namen im Dateisystem, wie eine reguläre
Datei, und die kannst du in deinem Programm auch wie eine aufmachen.
Einmal nur zum lesen, das andere mal nur zum schreiben.
Siehe mkfifo. Der Einfachheit halber kannst du das mal einfach auf der
Konsole aufrufen:
1
mkfifo /tmp/meinfifo
ein
1
ls -l /tmp
sollte dann in etwa sowas zeigen:
1
prw-rw-r-- 1 rolf rolf 0 Nov 15 20:38 meinfifo
Das p ganz am Anfang steht für pipe. Die öffnest du dann in deinem
Programm einfach wie eine reguläre Datei zum lesen, und dann kannst du
z.B. mit
1
echo "Hallo" > /tmp/meinfifo
etwas reinschreiben, das von deinem Programm dann wieder rausgelesen
werden
kann.
Zu deinem Programm:
> int main(int argc, char **argv){> FILE *stream;> int file, c;>> stream = fdopen (file, "r");
Wie kommst du auf fdopen(), noch dazu mit einem uninitialisierten
Filedeskriptor? fdopen() macht was ganz anderes, als du offenbar denkst.
Wilhelm M. schrieb:> Mir ist schon klar was clock_nanosleep() macht: nur hatte ich schlicht> nur nanosleep() in Deinem Post gelesen ... das habe ich aber auch> korrigiert (s.o.)
Ja Himmel... liegt das an meinem Browser, dass ich alte Antworten immer
erst nach dem Posten sehe? Ich vergess doch nicht jedesmal, den
Update-Knopf zu drücken...
Sorry.
danke... ihr seid die besten :)
@Rolf Magnus & Tom
=> läuft genau so... leider habe ich hier zuerst verschiedene Vorschläge
"vermischt" ... das war "doof"
bierchen ausgeb
Frage...
warum hängt das Programm bis zur "ersten" Eingabe*???
1
intfd,i;
2
constchar*myfifo="myfifo";
3
charbuf[64];
4
mkfifo(myfifo,0666);
5
fd=open(myfifo,O_RDONLY);
6
while(1)
7
{
8
9
if((i=read(fd,buf,64))>0){
10
11
buf[i]='\0';
12
fprintf(stderr,"Ausgabe: %s",buf);
13
14
}
15
16
//hier ist noch mehr
17
18
return0;
19
}
wenn ich das Programm starte macht er erstmal NICHTS!!!
"hier ist noch mehr", *wird erst angesprochen sobald ich etwas in das
Programm schicke => also echo "ein befehl" > myfifo
das funktioniert auch super! Aber irgendwie WILL er zuerst dass die
"Datei" erzeugt wird oder ... was auch immer...
hat da jemand eine Idee?
Du liest blockierend, d.h. read() wartet so lange, bis Daten aus der
Pipe kommen. Deshalb geht's natürlich erst weiter, wenn was gelesen
wurde. Du musst die Pipe non-blocking öffnen.
1
fd=open(myfifo,O_RDONLY|O_NONBLOCK);
Übrigens hat dein Programm einen buffer-overflow-error. Mal sehen, ob du
ihn selber findest. ;-)
Rolf M. schrieb:> Du liest blockierend, d.h. read() wartet so lange, bis Daten aus der> Pipe kommen.
Interessant wäre, warum es nach der ersten Eingabe auch ohne O_NONBLOCK
geht und nichtblockierend einfach 0 Byte liest.
Tom schrieb:> Rolf M. schrieb:>> Du liest blockierend, d.h. read() wartet so lange, bis Daten aus der>> Pipe kommen.>> Interessant wäre, warum es nach der ersten Eingabe auch ohne O_NONBLOCK> geht und nichtblockierend einfach 0 Byte liest.
Weil dein Programm ein EOF bekommt, wenn der Schreiber seine Seite
schließt. Das heißt, dass read() 0 zurückliefert und immer wieder sofort
zurückkehrt, bis jemand die andere Seite der Pipe wieder öffnet.
Pipes modellieren einen protokoll-losen Datenstrom. Da es kein Protokoll
gibt und auch keine Möglichkeit, out-of-band-data zu übertragen, muss es
eine andere Möglichkeit geben, das Kommunikationsende anzuzeigen. Dies
geschieht dadurch, dass der Schreiber die Pipe schließt: dann kehrt beim
Leser read() mit 0 zurück (also kein Fehler und keine Nutzdaten). Will
der Leser die Kommunikation beenden und schließt die Pipe, so bekommt
der Schreiber beim nächsten write() in die Pipe ein Signal SIG_PIPE
(default action ist Programmabbruch) und (falls das Signal behandelt
oder ignoeriert wird) kehrt write() mit Fehler EPIPE zurück.
(Gemeint ist der jeweils letzte Leser oder Schreiber: es kann ja mehrere
geben)
baer schrieb:> @Rolf Magnus>> ich kann aus einem Array mit 64 Zellen nur 0-63 Werte lesen!?>> **danke**
Da passen schon 64 Werte rein (mit dem Index 0 bis 63). Allerdings
darfst du dann keinen 65. Wert mehr hinten anhängen, was dein buf[i] =
'\0' aber tut, falls tatsächlich 64 Bytes gelesen wurden.