Forum: PC-Programmierung laufendes C-Programm befehle geben


von baer (Gast)


Lesenswert?

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

von Jim M. (turboj)


Lesenswert?

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.

von Tom (Gast)


Lesenswert?

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
int main(void)
7
{
8
    int fd;
9
    const char* myfifo = "/tmp/myfifo";
10
    char buf;
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
                goto exit_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
    return 0;
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 ;).

von physiker (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Sebastian S. (amateur)


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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.

: Bearbeitet durch User
von Jobst Q. (joquis)


Lesenswert?

Hier eine recht informative Seite über MessageQueues:

http://openbook.rheinwerk-verlag.de/linux_unix_programmierung/Kap09-005.htm

von Klaus (Gast)


Lesenswert?

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

von Wilhelm M. (wimalopaan)


Lesenswert?

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

: Bearbeitet durch User
von PittyJ (Gast)


Lesenswert?

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.

von Tobias P. (hubertus)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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)

von Rolf M. (rmagnus)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

Siehe oben, wenn er wirklich fds hat. Denn select / poll geht nur mit 
file-descriptoren.

Aber jetzt wiederholen sich die Antworten ... (s.o.)

von Rolf M. (rmagnus)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.)

von Wilhelm M. (wimalopaan)


Lesenswert?

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 ...

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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()

von Rolf M. (rmagnus)


Lesenswert?

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).

von Wilhelm M. (wimalopaan)


Lesenswert?

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()

von baer (Gast)


Lesenswert?

öö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
datei lesen (in welcher ggf. Steuerbefehle sind)
4
=> wenn Steuerbefehle dann diese abarbeiten
5
6
adc & gpios einlesen
7
8
kurze pause einlegen
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

von Wilhelm M. (wimalopaan)


Lesenswert?

baer schrieb:
>
> also mach ich das so
>
>
1
while(1) {
2
> 
3
> datei lesen (in welcher ggf. Steuerbefehle sind)
4
> => wenn Steuerbefehle dann diese abarbeiten
5
> 
6
> adc & gpios einlesen
7
> 
8
> kurze pause einlegen
9
> 
10
> }
>

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).

von baer (Gast)


Lesenswert?

> 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
long leseAbtand = 150 //ms
2
3
while(0) {
4
5
datei lesen
6
wenn datei steuerbefhel {
7
  steuerbefehl abarbeiten
8
  ***
9
}
10
11
wenn lasttime + leseAbtand < now {
12
  long lastTime = now()
13
  lese gpio & adc
14
}
15
16
}

*** die frage ist jetzt nur, wie kann ich jetzt die Steuerbefehl Datei 
leeren?

von baer (Gast)


Lesenswert?

evtl. als Zusatzinformation...
ich arbeite mit diesen Modulen:
https://www.abelectronics.co.uk/p/71/IO-Pi-Zero

hier auch der ADC <- wobei dieser gerade keine Probleme macht, da er nur 
als "input" dient!
https://www.abelectronics.co.uk/p/56/ADC-Pi-Plus-Raspberry-Pi-Analogue-to-Digital-converter

von Oliver S. (oliverso)


Lesenswert?

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

von baer (Gast)


Lesenswert?

@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 ...

von Rolf Magnus (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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 ...

: Bearbeitet durch User
von Thorsten (Gast)


Lesenswert?

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.

von René H. (Gast)


Lesenswert?

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é

von Rolf Magnus (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

Oh sorry, da habe ich überlesen, dass Du clock_nanosleep() geschrieben 
hast ...

Denke aber, dass für die Anwendung eine Zeitspanne reichen würde.

: Bearbeitet durch User
von baer (Gast)


Lesenswert?

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
int main(int argc, char **argv){
2
  FILE *stream;
3
  int file, 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

von Rolf M. (rmagnus)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.)

von Rolf M. (rmagnus)


Lesenswert?

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.

: Bearbeitet durch User
von baer (Gast)


Lesenswert?

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

von David G. (rosco)


Lesenswert?

Ja ich hätte jetzt auch an Threadding gedacht. Gibt es da gute Tutorials 
für C?

von Wilhelm M. (wimalopaan)


Lesenswert?

Für C gibt es den Klassiker : Steve Kleiman: "Programming with Threads"

: Bearbeitet durch User
von baer (Gast)


Lesenswert?

Frage...

warum hängt das Programm bis zur "ersten" Eingabe*???
1
  int fd, i;
2
  const char* myfifo = "myfifo";
3
  char buf[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
  return 0;
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?

von Rolf M. (rmagnus)


Lesenswert?

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. ;-)

von Tom (Gast)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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)

: Bearbeitet durch User
von baer (Gast)


Lesenswert?

@Rolf Magnus

ich kann aus einem Array mit 64 Zellen nur 0-63 Werte lesen!?

**danke**

von Rolf M. (rmagnus)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.