Threads und UDP-Sockets / Timingprobleme

Post Reply
Message
Author
Ikarisan
Posts: 3
Joined: 20. Nov 2006 12:14

Threads und UDP-Sockets / Timingprobleme

#1 Post by Ikarisan »

Hallo!!

Ich muss für ein Projekt eine Software realisieren, die eine gegebene Datei als UDP/RTP-Datenstrom in das Netz schickt. Alles kein Problem habe ich zuerst gesagt, aber leider muss das Senden unter bestimmten Bedingungen erfolgen.

1) Es müssen mehrere Threads / Prozesse gleichzeitig die Daten senden können (bis zu 40).
2) Die UDP-Pakete müssen in festen Zeitabständen (mind. alle 20ms) gesendet werden.

Ich habe das momentan dadurch realisiert das ich ein Hauptprogramm habe, welches je nach Anforderung bis zu 40 Threads startet. Das Hauptprogramm guckt jetzt in einer WHILE-Schleife immer welcher Thread wieder bedient werden muss. Sind die 20ms vorbei sage ich dem entsprechenden Thread mit Hilfe von pthread_cond das er was zu senden hat und dieser liest dann aus einem globalen Puffer (da steht die Datei drin) die nächsten 160Byte aus und versendet diese.

Leider funktioniert das so überhaupt nicht da ich nicht auf die 20ms komme. Die Werte schwanken zwischen 19.999ms und 180ms. Und je mehr Threads vorhanden sind desto schlechter werden die Werte.

Habe dann einmal die ganze Zeitroutine, die kontrolliert ob es was zu senden gibt, in die Threads ausgelagert. Aber selbst dann habe ich noch Schwankungen zwischen 20ms und 50ms.

Ich weiß momentan nicht mehr weiter. Momentan scheitert alles an diesen Timingproblemen. Hat eventuell jemand einen Tip für mich?

Gruß
Thorsten

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#2 Post by Janka »

Ja. Für genaues Timing im Bereich <200ms benötigst du Echtzeitpriorität (SCHED_FIFO oder SCHED_RR). Ansonsten wertet der Prozess-Scheduler dein Programm mit der Zeit in der Priorität immer weiter runter, so dass auch mal andere Prozesse drankommen.

$ man sched_setscheduler

Du solltest dir beim Experimentieren immer auch eine Shell in eine noch höhere Echtzeitpriorität legen, damit du das System auf jeden Fall noch bedienen kannst. X ist beim Experimentieren mit Echtzeitpriorität deshalb auch tabu, also abschalten. Den X-Server mit Echtzeitpriorität zu betreiben erhöht meist die Latenz zu sehr.

Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

Ikarisan
Posts: 3
Joined: 20. Nov 2006 12:14

#3 Post by Ikarisan »

Eventuell liegt ein Teil meines Problems schon darin wie ich auswerte ob der Thread was senden muss. Ich habe ein globales Array mit einem Struct für jeden Thread:

Code: Select all

struct thread_data
&#123;
	int	iThreadID;	/*!< ID des Threads um den es geht */
	int  	iStatus;	/*!< Laeuft der Thread gerade? &#40;0&#41;Nein, &#40;1&#41;Ja */
	int 	iPackageSize;	/*!< Paketgroesse die der Thread momentan versendet */
	int	iPackages;	/*!< Anzahl der Pakete - abhaengig von Dateigroesse */
	int	iPort;		/*!< Port an den der Thread die Daten sendet */
	char	*cIP;		/*!< IP/Rechnername an den die Daten gesendet werden */
	int	iInterval;	/*!< Intervall in dem der Thread neue Nachrichten sendet */
	long 	lLastSend;	/*!< Zeitstempel der letzten gesendeten Nachricht */
	long	lOffset;	/*!< Startposition im Puffer bei der die Uebertragung beginnt */
	int  	iLastPosition;	/*!< Letzte Position im Puffer nach dem Senden */
	char 	*cMessage;	/*!< Die Nachricht &#40;Payload&#41; das RTP-Paketes */
&#125;;

struct thread_data thread_data_array&#91;MAX_THREADS&#93;; /*!< Struktur fuer die Threaddaten */

Und momentan schreibe ich halt ganz am Anfang in thread_data_array.lLastSend einen mit gettimeofday(&acct_time,NULL); ermittelten Wert. Dann wird im Thread selber in einer while(1) Schleife

1) Die Summe erzeugt aus LastSend und Intervall:

Code: Select all

lTimeSum = thread_data_array&#91;i&#93;.lLastSend + &#40;long&#41;thread_data_array&#91;i&#93;.iInterval;
curtime = get_real_time&#40;acct_time.tv_sec , acct_time.tv_usec&#41;; /* eigene Funktion */
2) In einer IF-Schleife geguckt ob die Zeit erreicht ist:

Code: Select all

if &#40; curtime >= &#40;lTimeSum-10&#41; &#41; 
&#123;
....Setzen des RTP-headers....
thread_data_array&#91;i&#93;.lLastSend = curtime;
....Versenden des Pakets mit sendto&#40;...&#41;;.....
&#125;
Nehme ich die Zeitabfrage raus, also z.B. mit if(1), dann liegt laut Wireshark der Abstand zwischen den Paketen bei 3.987ms bis 4.055ms. Mit 0,07ms Schwankungen kann ich leben.

Ich habe das dumme Gefühl das meine "muss ich was senden?"-Lösung nicht wirklich optimal funktioniert. Habe aber leider keine Ahnung ob das jetzt an dem Struct-Array liegt oder eher an der gettimeofday Funktion. Oder an beiden. :(

Gruß
Thorsten

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#4 Post by Janka »

Oder anders gesagt, du hast eine Busy-Loop gebaut. Das ist in einem Multiprozess-/Multithreadsystem ganz schlecht, dadurch wird alles langsam. In der Zeit, in der der eine Thread sich im Kreise dreht kann der andere schlißlich nicht arbeiten

Frag nicht selbst die Zeittakte ab, lass das auf jeden Fall den Kernel machen. Evtl. hilft dir da pthread_cond_timedwait.

Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

Ikarisan
Posts: 3
Joined: 20. Nov 2006 12:14

#5 Post by Ikarisan »

Danke!

Mit pthread_cond_timedwait() scheint es jetzt viel besser zu funktionieren. Habe jetzt Zeiten zwischen 19.992ms und 20.008ms. Allerdings sind ca. alle 30 Sekunden auch bestimmt 10 Pakete dabei, die einen Abstand von 30ms und mehr haben :(

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#6 Post by Janka »

Dieses letzte Problem kriegst du weg, indem du dem Prozess eine statische Priorität gibst, wie ich das oben schon angedeutet habe. Trau dich, es reißt dir deswegen keiner den Kopf ab.

Nachteil ist natürlich, dass der Prozess dann root-Rechte (echte, SUID oder sudo) braucht. Ist beim CD-Brennen ohne Burnfree ja aber auch nicht anders.

Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

Post Reply