Skúmame VoIP motor Mediastreamer2. Časť 12

Materiál článku je prevzatý z môjho zen kanála.

Skúmame VoIP motor Mediastreamer2. Časť 12

V minulosti článku, sľúbil som preskúmať otázku hodnotenia záťaže na ticker a spôsoby boja proti nadmernému výpočtovému zaťaženiu v mediastreameri. Ale rozhodol som sa, že bude logickejšie zamerať sa na problémy ladenia krafterských filtrov súvisiacich s presunom dát a až potom sa zaoberať otázkami optimalizácie výkonu.

Ladenie krafterských filtrov

Po tom, čo sme v predchádzajúcom článku preskúmali mechanizmus presunu dát v mediastreameri, bude logické hovoriť o nebezpečenstvách, ktoré sa v ňom skrývajú. Jednou z charakteristík princípu "data flow" je, že alokácia pamäte z haldy prebieha vo filtroch, ktoré sa nachádzajú na začiatku prúdu dát, zatiaľ čo oslobodenie pamäte so spätným prenosom do haldy vykonávajú filtre umiestnené na konci toku. Navyše, vytváranie nových dát a ich zničenie môže prebiehať niekde v medzičlánkoch. Vo všeobecnosti uvoľňuje pamäť nie ten filter, ktorý vytvoril blok dát.

Z hľadiska transparentného monitorovania pamäte by bolo rozumné, aby filter, ktorý prijíma vstupný blok, po spracovaní okamžite zničil tento blok s uvoľnením pamäte a na výstupe predložil opäť vytvorený blok s výstupnými dátami. V takom prípade by sa únik pamäte vo filtri ľahko trasoval — ak analyzátor objaví únik vo filtre, znamená to, že nasledujúci filter nezničí prichádzajúce bloky riadne a chyba je v ňom. Ale z pohľadu zachovania vysokej výkonnosti, taký prístup k práci s blokmi dát nie je produktívny — vedie to k veľkému počtu operácií na alokáciu/uvoľnenie pamäte pod bloky dát bez akéhokoľvek užitočného výstupu.

Z tohto dôvodu mediastreamer filtre, aby neznižovali spracovanie údajov, pri kopírovaní správ využívajú funkcie, ktoré vytvárajú ľahké kópie (o ktorých sme hovorili v predchádzajúcom článku). Tieto funkcie len vytvárajú novú inštanciu hlavičky správy „pripínajúc“ k nej blok údajov z kopírovanej „staršej“ správy. V dôsledku toho sú k jednému bloku údajov priradené dve hlavičky a počítadlo odkazov v bloku údajov sa inkrementuje. Avšak to bude vyzerať ako dve správy. Správ s takýmto „zdielaným“ blokom údajov môže byť aj viac, napríklad filter MS_TEE vytvára hneď desiatky takých ľahkých kópií, ktoré rozdeľuje po svojich výstupoch. Pri správnej práci všetkých filtrov v reťazci by toto počítadlo odkazov malo na konci pásu dosiahnuť nulu a spustí sa funkcia uvoľnenia pamäte: ms_free(). Ak sa volanie neuskutoční, znamená to, že tento kus pamäte sa už nevráti do haldy, t.j. „unikne“. Cena za používanie ľahkých kópií je strata možnosti ľahko zistiť (ako by to bolo v prípade použitia bežných kópií), v ktorom filtri graf uniká pamäť.

Keďže zodpovednosť za vyhľadávanie únikov pamäte v „nativních“ filtroch leží na vývojároch mediastreamera, pravdepodobne ich nebudete musieť ladit. Ale s vaším remeselným filtrom — ste sami kováčom svojho šťastia a od vašej starostlivosti bude závisieť čas, ktorý strávite hľadaním únikov vo vašom kóde. Aby sme skrátili váš čas mučenia s ladením, musíme preskúmať techniky lokalizácie únikov pri vývoji filtrov. Navyše sa môže stať, že únik sa prejaví len pri aplikácii filtra v reálnej sústave, kde môže byť množstvo „podozrivých“ obrovské a čas na ladenie obmedzený.

Ako sa prejavuje únik pamäte?

Logicky by sa dalo predpokladať, že v výstupe programu top bude zobrazovaný narastajúci percentuálny podiel pamäte, ktorý zaberá vaša aplikácia.

Vonkajší prejav sa bude prejavovať tým, že systém začne pomaly reagovať na pohyb myši a pomaly prekresľovať obrazovku. Môže tiež rásť systémový log, ktorý zaberá miesto na pevnom disku. Vaša aplikácia sa začne správať zvláštne, neodpovedať na príkazy, nebude môcť otvoriť súbor atď.

Na zistenie vzniku úniku budeme používať analýzátor pamäte (ďalej analýzátor). Môže to byť Valgrind (dobré článok o ňom) alebo zabudovaný v kompilátore gcc MemorySanitizer alebo niečo iné. Ak analýzátor ukáže, že únik nastáva v jednom z filtrov grafu, znamená to, že je čas použiť niektorú z nižšie opísaných metód.

Metóda troch sosien

Ako už bolo spomenuté, pri úniku pamäte analýzátor ukáže na filter, ktorý požiadal o pridelenie pamäte z hromady. Ale neukáže na filter, ktorý "zabudol" ju vrátiť, ten, ktorý je teda vinný. Tým pádom, analýzator môže iba potvrdiť naše obavy, ale nie naznačiť ich koreň.

Aby sme zistili umiestnenie "nežiadúceho" filtra v grafe, môžeme ísť cestou zmenšovania grafu na minimum uzlov, pri ktorom analýzátor ešte odhalí únik a v zostávajúcich troch sosnách lokalizovať problematický filter.

Môže sa však stať, že skrátením počtu filtrov v grafe narušíte bežný tok interakcie filtrov s ďalšími prvkami vašej systému a únik sa prestane prejavovať. V takom prípade bude potrebné pracovať s plnohodnotným grafom a použiť prístup, ktorý je uvedený nižšie.

Metóda kĺzavého izolátora

Pre jednoduchosť opisu využijeme graf, ktorý sa skladá z jedného reťazca filtrov. Je znázornený na obrázku.

Skúmame VoIP motor Mediastreamer2. Časť 12

Bežný graf, v ktorom sú vedľa hotových filtrov mediastreamera aplikované štyri remeselné filtre F1…F4, štyroch rôznych typov, ktoré ste vytvorili dávno a v ich správnosti nepochybujete. Predpokladajme však, že v niektorých z nich dochádza k úniku pamäte. Spustením nášho programu pomocou analyzátora sa v jeho správe dozvieme, že nejaký filter požiadal o určitý objem pamäte a nevrátil ju do haldy N-krát. Ľahko sa dá dedukovať, že bude odkaz na vnútorné funkcie filtra typu MS_VOID_SOURCE. Jeho úlohou je získavať pamäť z haldy. Vrátiť ju tam musia iné filtre. T.j. zistíme fakt úniku.

Aby sme určili, na ktorej časti výrobného procesu skončila nečinnosť, ktorá viedla k úniku pamäte, navrhujeme zaviesť dodatočný filter, ktorý jednoducho prenáša správy z vstupu na výstup, ale pritom vytvára neľahkú, v normálnej "ťažkej" kópii vstupnej správy a následne úplne maže správu, ktorá prišla na vstup. Takýto filter nazveme izolátorom. Predpokladáme, že keďže je filter jednoduchý, únik v ňom je vylúčený. A ešte jedna pozitívna vlastnosť — ak ho pridáme na akékoľvek miesto nášho grafu, nijako to neovplyvní prácu schémy. Budeme zobrazovať filter-izolátor vo forme kruhu s dvojitým kontúrom.

Zaraďujeme izolátor hneď po filtri voidsource:
Skúmame VoIP motor Mediastreamer2. Časť 12

Znova spúšťame program s analyzátorom a vidíme, že tentokrát analyzátor hodí vinu na izolátor. Veď práve on teraz vytvára bloky dát, ktoré sa potom strácajú neznámym nedbalým filtrom (alebo filtrami). Ďalším krokom posúvame izolátor v reťazci doprava, na jeden filter a opäť spúšťame analýzu. Tak, krok za krokom posúvajúc izolátor doprava, dostaneme situáciu, keď v ďalšej správe analyzátora sa množstvo "uniknutých" blokov pamäte zníži. To znamená, že na tomto kroku bol izolátor v reťazci hneď po problematickom filtre. Ak bol "zlý" filter jediný, únik sa úplne stratí. Takto sme lokalizovali problematický filter (alebo jeden z viacerých). "Opravením" filtra môžeme pokračovať v posúvaní izolátora doprava v reťazci až do úplného víťazstva nad únikmi pamäte.

Implementácia filtra-izolátora

Implementácia izolátora vyzerá rovnako ako bežný filter. Hlavičkový súbor:

/* Файл 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

Samotný filter:

/* Файл 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)

Metóda prepisovania funkcií správy pamäte

Pre podrobnejšie skúmania je v mediastreameri predpokladaná možnosť prepisovania funkcií prístupu k pamäti na vaše vlastné, ktoré okrem základnej práce budú monitorovať "Kto, kam a prečo". Prepíšu sa tri funkcie. Robí sa to nasledovne:

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

Takáto možnosť pomáha v prípadoch, keď analyzátor spomaľuje prácu filtrov natolko, že narušuje fungovanie systému, do ktorého je naša schéma integrovaná. V takejto situácii sa musí od analyzátora upustiť a použiť prepisovanie funkcií práce s pamäťou.

Preskúmali sme algoritmus činnosti pre jednoduchý graf bez rozvetvení. Ale tento prístup je možné aplikovať aj na iné prípady, samozrejme so zložitými úpravami, ale myšlienka zostane rovnaká.

V nasledujúcom článku preskúmame otázku hodnotenia zaťaženia na ticker a spôsoby boja s nadmernou výpočtovou záťažou v mediastreameri.

Zdroj: habr.com

Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster