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
Threads und UDP-Sockets / Timingprobleme
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
$ 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.
Ich mag die Schreie.
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:
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:
2) In einer IF-Schleife geguckt ob die Zeit erreicht ist:
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
Code: Alles auswählen
struct thread_data
{
int iThreadID; /*!< ID des Threads um den es geht */
int iStatus; /*!< Laeuft der Thread gerade? (0)Nein, (1)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 (Payload) das RTP-Paketes */
};
struct thread_data thread_data_array[MAX_THREADS]; /*!< 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: Alles auswählen
lTimeSum = thread_data_array[i].lLastSend + (long)thread_data_array[i].iInterval;
curtime = get_real_time(acct_time.tv_sec , acct_time.tv_usec); /* eigene Funktion */
Code: Alles auswählen
if ( curtime >= (lTimeSum-10) )
{
....Setzen des RTP-headers....
thread_data_array[i].lLastSend = curtime;
....Versenden des Pakets mit sendto(...);.....
}
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
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
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.
Ich mag die Schreie.
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
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.
Ich mag die Schreie.