Hallo, ich würde gerne unter Linux mit einem C-Programm von der seriellen Schnittstelle lesen. Auf dem uC befüll ich im RX Interrupt einen Ringpuffer, von dem das Hauptprogramm dann lesen kann. Am PC hab ich ja erstmal keine Interrupts. Ich würd aber gerne ähnliche Schnittstellen zu meinem Puffer verwenden wie vom uC gewohnt. Deshalb frag ich mich, wie ich die Daten von der seriellen Schnittstelle holn kann. read() blockiert ja bis n Zeichen empfangen wurden. fread() ist bereits gepuffert (und returned sofort?). Ich überleg nun, in einem separaten pthread mit read() in einer Endlosschleife zu pollen. Immer nur auf 1 Zeichen warten und dieses dann in den Ringpuffer legen. Mein "Hauptprogramm" könnte ich dann so konzipieren wie vom uC gewohnt. Ich habe einen Ringpuffer, der im Hintergrund von irgendwas (Thread anstatt Interrupt) befüllt wird. Ist diese Strategie zu empfehlen? Oder Schwachsinn und Overkill? Vorteil: Ich hätt mal nen Grund mich mit pthreads zu beschäftigen. Grüße, lex
Wenn du in deinem Hauptprogramm Teile hast die auch mal mehrere Millisekunden dauern können dann kannst du nicht dort auf die Schnittstelle pollen. Wenn man also eine Schnittstelle hat die Zeichen nicht zwischenspeichert, rel. langsam ist und man nicht weiss wann genau Daten ankommen, dan ist es durchaus normal daß man einen eigenen Thread macht, der eben auf diese Schnittstelle pollt und die Daten dann in einen Puffer stellt. Man muss nur beachten, daß die Zugriffe auf den Puffer zwischen Thread und Hauptprogramm synchronisiert sind (Wie beim µC auch, Stcichwort "Atomar") Eleganter ist es natürlich wenn man bei dem Betriebssystem einen Schnittstellenhandler registrieren kann, den das Betriebssystem immer dann aufruft, wenn ein Zeichen angekommen ist bzw. wenn der Sendepuffer leer geworden ist. Diese Handler übernehmen dann das Befüllen des Puffers, bzw Senden des nächsten Bytes aus dem Sendepuffer des Programms.
le x. schrieb: > read() blockiert ja bis n Zeichen empfangen wurden. In der Voreinstellung ja. Man kann den Filedeskriptor aber auch auf "non blocking" setzen. Das sage ich nur ungern, weil ich fürchte, daß du jetzt damit in rinrt Endlosschleife pollst - in aller Regel ist das die schlechteste Lösung. Bessere Alternativen wären, wie bereits erwähnt, einen eigenen Thread zu machen, der beim Lesen ruhig blockieren kann, oder man nimmt select().
Danke euch erstmal! Also dieses nicht-blockierende Lesen lass ich glaub ich bleiben. Aber wenn ich in einem separaten Thread blockierend in einer Endlosschleife lese, das geht in Ordnung? Wird dadurch nicht die CPU-Last erhöht? Das ist meine Sorge. Wie würde die Methode mit select() funktionieren?
le x. schrieb: > Aber wenn ich in einem separaten Thread blockierend in einer > Endlosschleife lese, das geht in Ordnung? Wird dadurch nicht die > CPU-Last erhöht? Das ist meine Sorge. Nein. Der blockierte Thread braucht natürlich keine CPU-Zeit, solange nichts zu lesen ist, und die anderen Threads halt je nachdem, was sie machen. > > Wie würde die Methode mit select() funktionieren? Dafür gibt es viele Beispiele und Dolu im Netz. Kurz: Man füttert ein Feld mit Filedeskriptoren, von denen man lesen möchte, ein weiteres mit solchen, auf die man schreiben möchte, füllt eine struct aus, wie lange man maximal warten möchte, und ruft select() auf. Außerdem übergibt man noch den höchsten aller vorkommenden fd plus 1. Sobald einer oder mehrere der fd lesen bzw. schreiben können, ohne zu blockieren, kehrt select() zurück, in den Feldern stehen dann die bereiten fds. Mit denen kann man dann read() bzw write() zu machen, ohne hängen zu bleiben. select() springt schon vorher zurück, wenn ein angegebener Timeout abgelaufen ist, oder wenn ein Signal das Warten unterbricht. Ggf. füllt man die Felder wieder, und ruft damit nochmal select() auf. Zum Füllen und Abfragen der Felder gibt es Makros, die man nehmen sollte.
PS: Die fd, die man in die Felder schreibt und auf die man wartet, müssen keine Dateien sein unter Linux bzw. Unix. Es können alle möglichen fd sein: pipes, sockets, ... Nur unter Windows kann man damit leider nur auf sockets warten.
OK, danke dir nochmal. Denke dass da meine Thread-Lösung sowohl elegant als auch praktisch ist :-) War mir nur nicht sicher ob das "warten" im Thread rechenintensiv ist. Für mich ist read() ja erstmal ne Blackbox, ich weis ja nicht, WIE die Funktion wartet, deswegen die Nachfrage. Noch eine Frage: Wenn meine "Hauptschleife" grad nichts zu tun hat weil sie drauf wartet dass im Empfangspuffer zB ein komplettes Kommando liegt, kann ich sie irgendwie schlafen legen? Ich bin mir in Sachen PC-Programmierung recht unsicher da ich einfach nicht weis was noch alles passiert, bzw. was ein "freundliches" Programm so alles tun sollte...
$ man 3 usleep NAME usleep − suspend execution for microsecond intervals SYNOPSIS #include <unistd.h> int usleep(useconds_t usec);
le x. schrieb: > OK, danke dir nochmal. > Denke dass da meine Thread-Lösung sowohl elegant als auch praktisch ist > :-) Da bin ich mir nicht so sicher, denn du muß dann die gelesenen Daten wieder zwischen den Threads austauschen, mit passender Synchronisation. > War mir nur nicht sicher ob das "warten" im Thread rechenintensiv ist. > Für mich ist read() ja erstmal ne Blackbox, ich weis ja nicht, WIE die > Funktion wartet, deswegen die Nachfrage. Im Betriebssystem-Scheduler wird der Thread als wartend markiert und nicht mehr ausgeführt, bis Daten ankommen. > Noch eine Frage: > Wenn meine "Hauptschleife" grad nichts zu tun hat weil sie drauf wartet > dass im Empfangspuffer zB ein komplettes Kommando liegt, kann ich sie > irgendwie schlafen legen? Ahem, du könntest sie einfach in read() blockieren lassen, bis Daten ankommen. > Ich bin mir in Sachen PC-Programmierung recht unsicher da ich einfach > nicht weis was noch alles passiert, bzw. was ein "freundliches" Programm > so alles tun sollte... Norbert schrieb: > $ man 3 usleep > > NAME > usleep − suspend execution for microsecond intervals > SYNOPSIS > #include <unistd.h> > int usleep(useconds_t usec); Und wenn dann im anderen Thread Daten ankommen, muß hier in usleep gewartet werden, bis die Wartezeit zuende ist, bevor auf die Daten reagiert werden kann. Nicht grad sehr elegant.
Rolf Magnus schrieb: > Und wenn dann im anderen Thread Daten ankommen, muß hier in usleep > gewartet werden, bis die Wartezeit zuende ist, bevor auf die Daten > reagiert werden kann. Nicht grad sehr elegant. Die ursprüngliche Frage lautete: > Wenn meine "Hauptschleife" grad nichts zu tun hat weil sie drauf wartet > dass im Empfangspuffer zB ein komplettes Kommando liegt, kann ich sie > irgendwie schlafen legen? Ich denke das usleep/nanosleep usw. präzise zu diesem Wunsch passen. Man legt sie (die Hauptschleife) natürlich nicht ne viertel Stunde am Stück schlafen, sondern häppchenweise (mit kontinuierlichem nachschauen)... Natürlich kann man mit message queues, semaphores, usw. arbeiten, aber der OP schrieb ja das er sich gerade erst in die PC Programmierung einarbeitet. (und das macht er, wie ich finde, schon recht gut)
Norbert schrieb: > Rolf Magnus schrieb: >> Und wenn dann im anderen Thread Daten ankommen, muß hier in usleep >> gewartet werden, bis die Wartezeit zuende ist, bevor auf die Daten >> reagiert werden kann. Nicht grad sehr elegant. > > Die ursprüngliche Frage lautete: > >> Wenn meine "Hauptschleife" grad nichts zu tun hat weil sie drauf wartet >> dass im Empfangspuffer zB ein komplettes Kommando liegt, kann ich sie >> irgendwie schlafen legen? Nein, ursprünglich ging es darum: le x. schrieb: > Ich überleg nun, in einem separaten pthread mit read() in einer > Endlosschleife zu pollen. Das wollte er tun, damit er nicht in read() blockieren muß. Und erst danach wollte er wissen, wie er den Haupt-Thread schlafen legt, bis Daten angekommen sind. Die Kombination von beidem ist aber irgendwie Unsinn. Das Blockieren in read() macht ja eigentlich schon genau das, also wozu es überhaupt in eine separaten Thread auslagern? Das macht die Sache doch nur unnötig kompliziert. > Ich denke das usleep/nanosleep usw. präzise zu diesem Wunsch passen. Es wartet nicht, bis Daten da sind, sondern einfach für eine gewisse Zeit. > Man legt sie (die Hauptschleife) natürlich nicht ne viertel Stunde am > Stück schlafen, sondern häppchenweise (mit kontinuierlichem > nachschauen)... Diese Variante ist aber nie optimal. Einerseits wacht der Thread nicht sofort auf, wenn Daten da sind, sondern muß bis zum nächsten Zyklus warten, andererseits wacht er regelmäßig auf, owohl nix da ist. Man muß dann mit einem Kompromiss zwischen eigentlich unnötiger Verzögerung und ebenfalls unnötiger Prozessorlast leben. Je kürzer die Wartezeit, desto öfter wacht er auf, je länger, desto mehr Verzögerung habe ich. > Natürlich kann man mit message queues, semaphores, usw. arbeiten, aber > der OP schrieb ja das er sich gerade erst in die PC Programmierung > einarbeitet. > > (und das macht er, wie ich finde, schon recht gut) Er stellt auf jeden Fall die richtigen Fragen.
Rolf Magnus schrieb: > Man muß > dann mit einem Kompromiss zwischen eigentlich unnötiger Verzögerung und > ebenfalls unnötiger Prozessorlast leben. Rein theoretisch hast du natürlich Recht, aber mit weniger als einem einem Zehntel bis ein Prozent Prozessorlast kann man heutzutage wirklich gut leben.
1 | $ time ./a.out |
2 | proctime: 20.000ms |
3 | percentage: 0.067% |
4 | proctime: 290.000ms |
5 | percentage: 0.967% |
6 | |
7 | real 1m3.983s |
8 | user 0m0.036s |
9 | sys 0m0.288s |
Und hier der quick'n'dirty code:
1 | // kate:encoding utf8; tab-width 4; show-tabs 1; indent-width 4
|
2 | //
|
3 | // gcc -Wall testcode.c
|
4 | // time ./a.out
|
5 | |
6 | #include <stdio.h> |
7 | #include <time.h> |
8 | #include <unistd.h> |
9 | |
10 | volatile unsigned char the_buffer_contains_data = 0; |
11 | |
12 | int main(int argc, char * argv[]) |
13 | {
|
14 | int loopcount; |
15 | clock_t t0, t1; |
16 | double zeit; |
17 | struct timespec ts; |
18 | |
19 | //-----[ 30 second loop using usleep ]-----
|
20 | // 3000 loops 10ms sleep
|
21 | //
|
22 | loopcount = 3000; |
23 | t0 = clock(); |
24 | while(loopcount--) |
25 | {
|
26 | usleep(10000); // this systems granularity: 10ms |
27 | if(the_buffer_contains_data) |
28 | break; |
29 | }
|
30 | t1 = clock(); |
31 | zeit = (t1-t0) * 1000.0 / CLOCKS_PER_SEC; |
32 | printf("proctime: %.3fms\n", zeit); |
33 | printf("percentage: %.3f%%\n", zeit / 30000.0 * 100.0); |
34 | |
35 | //-----[ 30 second loop using nanosleep ]-----
|
36 | // 30000 loops 1ms sleep
|
37 | loopcount = 30000; |
38 | ts.tv_sec = 0; |
39 | ts.tv_nsec = 1000000; // 1ms |
40 | t0 = clock(); |
41 | while(loopcount--) |
42 | {
|
43 | nanosleep(&ts, NULL); |
44 | if(the_buffer_contains_data) |
45 | break; |
46 | }
|
47 | t1 = clock(); |
48 | zeit = (t1-t0) * 1000.0 / CLOCKS_PER_SEC; |
49 | printf("proctime: %.3fms\n", zeit); |
50 | printf("percentage: %.3f%%\n", zeit / 30000.0 * 100.0); |
51 | |
52 | return 0; |
53 | }
|
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.