Entdecken Sie die VoIP-Engine von Mediastreamer2. Teil 12

Das Material des Artikels stammt von mir Zen-Kanal.

Entdecken Sie die VoIP-Engine von Mediastreamer2. Teil 12

In der Vergangenheit ArtikelIch habe versprochen, die Frage der Ticker-Auslastungsschätzung und Möglichkeiten zum Umgang mit übermäßiger Rechenlast im Medienstreamer zu prüfen. Aber ich entschied, dass es logischer wäre, die Probleme des Debuggens von Craft-Filtern im Zusammenhang mit der Datenbewegung zu behandeln und erst dann Fragen der Leistungsoptimierung zu berücksichtigen.

Debuggen von Craft-Filtern

Nachdem wir im vorherigen Artikel den Mechanismus der Datenbewegung in einem Medienstreamer untersucht haben, wäre es logisch, über die darin verborgenen Gefahren zu sprechen. Eines der Merkmale des „Datenfluss“-Prinzips besteht darin, dass die Zuweisung von Speicher aus dem Heap in den Filtern erfolgt, die sich an den Quellen des Datenflusses befinden, und die Filter, die sich am Ende des Flusspfads befinden, den Speicher bereits bei der Rückgabe freigeben zum Haufen. Darüber hinaus kann es irgendwo in den Zwischenpunkten zur Entstehung neuer Daten und deren Zerstörung kommen. Im Allgemeinen erfolgt die Freigabe des Speichers durch einen anderen Filter als den, der den Datenblock erstellt hat.

Unter dem Gesichtspunkt einer transparenten Speicherüberwachung wäre es sinnvoll, wenn der Filter beim Empfang eines Eingabeblocks diesen nach der Verarbeitung sofort zerstört, Speicher freigibt und einen neu erstellten Block mit Ausgabedaten am Ausgang ablegt. In diesem Fall könnte das Speicherleck im Filter leicht zurückverfolgt werden – wenn der Analysator ein Leck im Filter entdeckt, dann zerstört der folgende Filter eingehende Blöcke nicht ordnungsgemäß und es liegt ein Fehler darin vor. Unter dem Gesichtspunkt der Aufrechterhaltung einer hohen Leistung ist dieser Ansatz für die Arbeit mit Datenblöcken jedoch nicht produktiv – er führt zu einer großen Anzahl von Vorgängen zum Zuweisen/Freigeben von Speicher für Datenblöcke, ohne dass es zu einer sinnvollen Erschöpfung kommt.

Aus diesem Grund verwenden Media-Streamer-Filter, um die Datenverarbeitung nicht zu verlangsamen, Funktionen, die beim Kopieren von Nachrichten leichte Kopien erstellen (wir haben darüber in einem früheren Artikel gesprochen). Diese Funktionen erstellen lediglich eine neue Kopie des Nachrichtenkopfes, indem sie den Datenblock aus der kopierten „alten“ Nachricht daran „anhängen“. Dadurch werden zwei Header an einen Datenblock angehängt und der Referenzzähler im Datenblock erhöht. Aber es wird wie zwei Nachrichten aussehen. Mit einem solchen „öffentlichen“ Datenblock können mehrere Nachrichten vorhanden sein. Beispielsweise generiert der MS_TEE-Filter zehn solcher Light-Kopien auf einmal und verteilt sie auf seine Ausgänge. Wenn alle Filter in der Kette ordnungsgemäß funktionieren, sollte dieser Referenzzähler am Ende der Pipeline Null erreichen und die Funktion zur Speicherfreigabe wird aufgerufen: ms_free(). Wenn der Aufruf nicht erfolgt, wird dieser Speicher nicht mehr an den Heap zurückgegeben, d. h. er „leckt“. Die Kosten für die Verwendung von Light-Kopien bestehen darin, dass nicht mehr einfach ermittelt werden kann (wie es bei der Verwendung von regulären Kopien der Fall wäre), in welchem ​​Diagrammfilter der Speicher verloren geht.

Da die Verantwortung für das Auffinden von Speicherlecks in den „nativen“ Filtern bei den Entwicklern des Medienstreamers liegt, müssen Sie diese höchstwahrscheinlich nicht debuggen. Aber mit Ihrem Crafting-Filter sind Sie selbst die Heuschrecke Ihres eigenen Glücks, und die Zeit, die Sie mit der Suche nach Lecks in Ihrem Code verbringen, hängt von Ihrer Genauigkeit ab. Um Ihre Debugging-Zeit zu verkürzen, müssen wir beim Entwerfen von Filtern Techniken zur Lecklokalisierung berücksichtigen. Darüber hinaus kann es vorkommen, dass das Leck nur dann auftritt, wenn der Filter in einem realen System angewendet wird, wo die Anzahl der „Verdächtigen“ sehr groß sein kann und die Zeit zum Debuggen begrenzt ist.

Wie äußert sich ein Speicherverlust?

Es ist logisch, dies in der Ausgabe des Programms anzunehmen Top zeigt einen zunehmenden Prozentsatz des von Ihrer Anwendung belegten Speichers an.

Die äußere Erscheinung wird darin bestehen, dass das System irgendwann langsam auf die Bewegung der Maus reagiert und den Bildschirm langsam neu zeichnet. Es ist auch möglich, dass das Systemprotokoll wächst und Speicherplatz auf der Festplatte verbraucht. In diesem Fall verhält sich Ihre Anwendung seltsam, reagiert nicht auf Befehle, kann die Datei nicht öffnen usw.

Um die Tatsache eines Lecks zu identifizieren, verwenden wir einen Speicheranalysator (im Folgenden als Analysator bezeichnet). Das kann sein Valgrind (Gut Beitrag darüber) oder in den Compiler integriert gcc MemorySanitizer oder etwas anderes. Wenn der Analysator anzeigt, dass das Leck in einem der Diagrammfilter auftritt, bedeutet dies, dass es an der Zeit ist, eine der unten beschriebenen Methoden anzuwenden.

Drei-Kiefern-Methode

Wie oben erwähnt, verweist der Analysator im Falle eines Speicherverlusts auf den Filter, der die Speicherzuweisung vom Heap angefordert hat. Aber es wird nicht auf den Filter verweisen, der „vergessen“ hat, es zurückzugeben, woran tatsächlich die Schuld liegt. Somit kann der Analysator unsere Ängste nur bestätigen, aber nicht auf ihre Wurzel hinweisen.

Um die Position des „schlechten“ Filters im Diagramm herauszufinden, können Sie das Diagramm auf die minimale Anzahl von Knoten reduzieren, an denen der Analysator noch ein Leck erkennt, und den problematischen Filter in den verbleibenden drei Kiefern lokalisieren.

Es kann jedoch vorkommen, dass durch die Verringerung der Anzahl der Filter in der Säule die normale Interaktion zwischen Filtern und anderen Elementen Ihres Systems gestört wird und das Leck nicht mehr auftritt. In diesem Fall müssen Sie mit einem Diagramm in voller Größe arbeiten und den unten beschriebenen Ansatz verwenden.

Gleitisolatormethode

Zur Vereinfachung der Darstellung verwenden wir ein Diagramm, das aus einer einzelnen Filterkette besteht. Sie ist auf dem Bild abgebildet.

Entdecken Sie die VoIP-Engine von Mediastreamer2. Teil 12

Eine gewöhnliche Grafik, in der neben vorgefertigten Media-Streamer-Filtern vier Craft-Filter F1…F4 verwendet werden, vier verschiedene Typen, die Sie vor langer Zeit erstellt haben und an deren Richtigkeit kein Zweifel besteht. Nehmen wir jedoch an, dass bei mehreren von ihnen ein Speicherverlust vorliegt. Wenn wir unser Analyseprogramm zur Überwachung ausführen, erfahren wir aus seinem Bericht, dass ein bestimmter Filter eine bestimmte Menge an Speicher angefordert und diese nicht N-mal an den Heap zurückgegeben hat. Es ist leicht zu erraten, dass es einen Verweis auf die internen Filterfunktionen vom Typ MS_VOID_SOURCE geben wird. Seine Aufgabe ist es, Speicher aus dem Heap zu entnehmen. Andere Filter sollten es dorthin zurückgeben. Diese. Wir werden das Leck finden.

Um festzustellen, in welchem ​​Abschnitt der Pipeline Inaktivität aufgetreten ist, die zu einem Speicherverlust geführt hat, wird vorgeschlagen, einen zusätzlichen Filter einzuführen, der Nachrichten einfach von der Eingabe zur Ausgabe verschiebt, gleichzeitig aber eine nicht leichte Kopie der Eingabe erstellt Nachricht in eine normale „schwere“ Kopie umwandeln und dann die am Eingang angekommene Nachricht vollständig löschen. Wir nennen einen solchen Filter einen Isolator. Da der Filter einfach ist, sind wir davon überzeugt, dass Leckagen ausgeschlossen sind. Und noch eine positive Eigenschaft: Wenn wir sie an einer beliebigen Stelle in unserem Diagramm hinzufügen, hat dies keinerlei Auswirkungen auf den Betrieb der Schaltung. Wir stellen den Isolatorfilter als Kreis mit doppelter Kontur dar.

Aktivieren Sie den Isolator direkt nach dem voidsource-Filter:
Entdecken Sie die VoIP-Engine von Mediastreamer2. Teil 12

Wir führen das Programm erneut mit dem Analysator aus und stellen fest, dass der Analysator dieses Mal die Schuld dem Isolator zuweist. Schließlich ist er es, der nun Datenblöcke erstellt, die dann durch einen unbekannten fahrlässigen Filter (oder Filter) verloren gehen. Der nächste Schritt besteht darin, den Isolator entlang der Kette um einen Filter nach rechts zu verschieben und die Analyse erneut zu starten. Wenn wir also den Isolator Schritt für Schritt nach rechts bewegen, erhalten wir eine Situation, in der die Anzahl der „durchgesickerten“ Speicherblöcke im nächsten Analysebericht abnimmt. Dies bedeutet, dass der Isolator in diesem Schritt unmittelbar nach dem problematischen Filter in der Kette landete. Wenn es nur einen „schlechten“ Filter gab, verschwindet das Leck vollständig. Somit haben wir den problematischen Filter (oder einen von mehreren) lokalisiert. Nachdem wir den Filter „repariert“ haben, können wir den Isolator entlang der Kette weiter nach rechts bewegen, bis die Speicherlecks vollständig beseitigt sind.

Implementierung eines Isolatorfilters

Die Isolator-Implementierung sieht genauso aus wie ein normaler Filter. Header-Datei:

/* Файл iso_filter.h  Описание изолирующего фильтра. */

#ifndef iso_filter_h
#define iso_filter_h

/* Задаем идентификатор фильтра. */
#include <mediastreamer2/msfilter.h>

#define MY_ISO_FILTER_ID 1024

extern MSFilterDesc iso_filter_desc;

#endif

Der Filter selbst:

/* Файл iso_filter.c  Описание изолирующего фильтра. */

#include "iso_filter.h"

    static void
iso_init (MSFilter * f)
{
}
    static void
iso_uninit (MSFilter * f)
{
}

    static void
iso_process (MSFilter * f)
{
    mblk_t *im;

    while ((im = ms_queue_get (f->inputs[0])) != NULL)
    {
        ms_queue_put (f->outputs[0], copymsg (im));
        freemsg (im);
    }
}

static MSFilterMethod iso_methods[] = {
    {0, NULL}
};

MSFilterDesc iso_filter_desc = {
    MY_ISO_FILTER_ID,
    "iso_filter",
    "A filter that reads from input and copy to its output.",
    MS_FILTER_OTHER,
    NULL,
    1,
    1,
    iso_init,
    NULL,
    iso_process,
    NULL,
    iso_uninit,
    iso_methods
};

MS_FILTER_DESC_EXPORT (iso_desc)

Methode zum Ersetzen von Speicherverwaltungsfunktionen

Für eine subtilere Recherche bietet der Media Streamer die Möglichkeit, die Speicherzugriffsfunktionen durch eigene zu ersetzen, die neben der Hauptarbeit auch „Wer, wo und warum“ festlegen. Drei Funktionen werden ersetzt. Dies geschieht auf folgende Weise:

OrtpMemoryFunctions reserv;
OrtpMemoryFunctions my;

reserv.malloc_fun = ortp_malloc;
reserv.realloc_fun = ortp_realloc;
reserv.free_fun = ortp_free;

my.malloc_fun = &my_malloc;
my.realloc_fun = &my_realloc;
my.free_fun = &my_free;

ortp_set_memory_functions(&my);

Diese Funktion kommt in Fällen zum Einsatz, in denen der Analysator die Filter so stark verlangsamt, dass der Betrieb des Systems, in dem unser Schaltkreis aufgebaut ist, gestört wird. In einer solchen Situation müssen Sie den Analysator aufgeben und die Speicherersatzfunktionen verwenden.

Wir haben einen Aktionsalgorithmus für einen einfachen Graphen betrachtet, der keine Zweige enthält. Aber dieser Ansatz kann auf andere Fälle angewendet werden, natürlich mit Komplikationen, aber die Idee bleibt dieselbe.

Im nächsten Artikel befassen wir uns mit dem Thema der Ticker-Lastschätzung und dem Umgang mit übermäßiger Rechenlast im Media-Streamer.

Source: habr.com

Kommentar hinzufügen