Utforsker Mediastreamer2 VoIP-motoren. Del 12

Stoffet til artikkelen er hentet fra min zen-kanal.

Utforsker Mediastreamer2 VoIP-motoren. Del 12

I den siste artikkel, lovet jeg å vurdere spørsmålet om å vurdere belastningen på tickeren og måter å bekjempe overdreven databelastning i mediastreameren. Men jeg bestemte meg for at det ville være mer logisk å dekke problemene med feilsøking av håndverksfiltre relatert til databevegelse og først da vurdere problemer med ytelsesoptimalisering.

Feilsøking av håndverksfiltre

Etter at vi så på mekanismen for å flytte data i en mediastreamer i forrige artikkel, ville det være logisk å snakke om farene som er skjult i den. En av funksjonene til "dataflyt"-prinsippet er at minne tildeles fra haugen i filtre som er plassert ved kilden til datastrømmen, og minne frigjøres og returneres til haugen av filtre plassert på slutten av strømmen. sti. I tillegg kan opprettelsen av nye data og ødeleggelsen av dem skje et sted i mellom. Generelt utføres minnefrigjøring av et annet filter enn det som opprettet datablokken.

Fra et synspunkt om gjennomsiktig minneovervåking, vil det være rimelig at filteret, ved mottak av en inngangsblokk, umiddelbart ødelegger den etter prosessering, frigjør minnet og sender ut en nyopprettet blokk med utdata. I dette tilfellet vil en minnelekkasje i filteret lett kunne spores - hvis analysatoren oppdaget en lekkasje i filteret, ødelegger ikke neste filter innkommende blokker ordentlig, og det er en feil i det. Men fra synspunktet om å opprettholde høy ytelse, er denne tilnærmingen til å jobbe med datablokker ikke produktiv - den fører til et stort antall operasjoner for å allokere/frigjøre minne for datablokker uten noen nyttig utgang.

Av denne grunn bruker mediastreamerfiltre, for ikke å bremse databehandlingen, funksjoner som lager enkle kopier når du kopierer meldinger (vi snakket om dem i forrige artikkel). Disse funksjonene oppretter bare en ny forekomst av meldingshodet ved å "feste" til den en blokk med data fra den "gamle" meldingen som kopieres. Som et resultat blir to overskrifter knyttet til en datablokk og referansetelleren i datablokken økes. Men det vil se ut som to meldinger. Det kan være flere meldinger med en slik "sosialisert" datablokk, for eksempel genererer MS_TEE-filteret et dusin av slike lette kopier på en gang, og distribuerer dem mellom utgangene. Hvis alle filtrene i kjeden fungerer som de skal, skal denne referansetelleren nå null ved slutten av rørledningen, og minnefrigjøringsfunksjonen vil bli kalt: ms_free(). Hvis anropet ikke oppstår, vil denne minnebiten ikke lenger bli returnert til haugen, dvs. det vil "lekke". Prisen for å bruke lette kopier er tapet av evnen til enkelt å bestemme (som tilfellet vil være med vanlige kopier) hvilket graffilter som lekker minne.

Siden mediestreamerutviklerne er ansvarlige for å finne minnelekkasjer i native filtre, trenger du mest sannsynlig ikke å feilsøke dem. Men med håndverksfilteret ditt er du gresshoppen for din egen lykke, og tiden du bruker på å søke etter lekkasjer i koden din vil avhenge av nøyaktigheten din. For å redusere feilsøkingstiden din, må vi se på lekkasjedeteksjonsteknikker når vi utvikler filtre. I tillegg kan det skje at lekkasjen vil manifestere seg bare når filteret brukes i et reelt system, hvor antallet "mistenkte" kan være stort og tiden for feilsøking begrenset.

Hvordan viser en minnelekkasje seg?

Det er logisk å anta at i programmets utgang topp vil vise den økende prosentandelen av minnet som er opptatt av applikasjonen.

Den ytre manifestasjonen vil være at systemet på et tidspunkt vil begynne å reagere sakte på musebevegelser og sakte tegne skjermen på nytt. Det er også mulig at systemloggen vil vokse og spise opp plass på harddisken din. I dette tilfellet vil applikasjonen din begynne å oppføre seg merkelig, ikke svare på kommandoer, ikke åpne en fil, etc.

For å oppdage forekomsten av en lekkasje vil vi bruke en minneanalysator (heretter kalt analysatoren). Det kan være valgrind (flink artikkel om det) eller innebygd i kompilatoren gcc Memory Sanitizer eller noe annet. Hvis analysatoren viser at det oppstår en lekkasje i et av graffiltrene, betyr dette at det er på tide å bruke en av metodene beskrevet nedenfor.

Three Pines-metoden

Som nevnt ovenfor, hvis det er en minnelekkasje, vil analysatoren peke på filteret som ba om minneallokering fra heapen. Men det vil ikke påpeke filteret som "glemte" å returnere det, som faktisk er den skyldige. Dermed kan analysatoren bare bekrefte frykten vår, men ikke indikere roten deres.

For å finne ut plasseringen av det "dårlige" filteret i grafen, kan du gå ved å redusere grafen til minimum antall noder der analysatoren fortsatt oppdager en lekkasje og lokalisere det problematiske filteret i de resterende tre furuene.

Men det kan skje at ved å redusere antall filtre i grafen, vil du forstyrre det normale samhandlingsforløpet mellom filtre og andre elementer i systemet ditt, og lekkasjen vil ikke lenger vises. I dette tilfellet må du jobbe med en graf i full størrelse og bruke tilnærmingen som er skissert nedenfor.

Skyve isolator metode

For enkelhets skyld vil vi bruke en graf som består av én kjede med filtre. Hun er vist på bildet.

Utforsker Mediastreamer2 VoIP-motoren. Del 12

En vanlig graf der det sammen med ferdige mediastreamer-filtre brukes fire håndverksfiltre F1...F4, av fire forskjellige typer, som du har laget for lenge siden og ikke er i tvil om riktigheten. La oss imidlertid anta at flere av dem har minnelekkasjer. Når vi kjører programmet vårt for å overvåke analysatoren, lærer vi fra rapporten at et bestemt filter ba om en viss mengde minne og ikke returnerte det til haugen N antall ganger. Du kan enkelt gjette at det vil være en lenke til de interne filterfunksjonene av typen MS_VOID_SOURCE. Hans oppgave er å ta minne fra haugen. Andre filtre må returnere det dit. De. vi vil oppdage lekkasjen.

For å bestemme i hvilken del av rørledningen det var passivitet som førte til en minnelekkasje, foreslås det å introdusere et ekstra filter som ganske enkelt skifter meldinger fra inngang til utgang, men som samtidig lager en kopi av inngangsmeldingen som ikke er lys , men heller en vanlig "tung", sletter deretter meldingen som ble mottatt ved inngangen. Vi vil kalle et slikt filter en isolator. Vi mener at siden filteret er enkelt, er det ingen lekkasje i det. Og enda en positiv egenskap - hvis vi legger den til hvor som helst i grafen vår, vil dette ikke påvirke driften av kretsen på noen måte. Vi vil skildre filterisolatoren i form av en sirkel med en dobbel krets.

Vi slår på isolatoren umiddelbart etter voidsource-filteret:
Utforsker Mediastreamer2 VoIP-motoren. Del 12

Vi kjører programmet med analysatoren igjen, og vi ser at denne gangen vil analysatoren skylde på isolatoren. Tross alt er det han som nå lager blokker med data, som deretter går tapt av et ukjent uforsiktig filter (eller filtre). Neste trinn er å flytte isolatoren langs kjeden til høyre, med ett filter, og starte analysen på nytt. Så, trinn for trinn ved å flytte isolatoren til høyre, vil vi få en situasjon der i neste analysatorrapport vil antallet "lekkede" minneblokker reduseres. Dette betyr at på dette trinnet var isolatoren i kjeden umiddelbart etter problemfilteret. Hvis det bare var ett "dårlig" filter, vil lekkasjen forsvinne helt. Dermed lokaliserte vi det problematiske filteret (eller ett av flere). Etter å ha "fikset" filteret, kan vi fortsette å flytte isolatoren til høyre langs kjeden til vi fullstendig overvinner minnelekkasjer.

Implementering av isolatorfilter

Isolatorimplementeringen ser akkurat ut som et vanlig filter. Overskriftsfil:

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

Selve filteret:

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

Metode for å erstatte minneadministrasjonsfunksjoner

For mer subtil forskning gir mediestreameren muligheten til å erstatte minnetilgangsfunksjoner med dine egne, som i tillegg til hovedarbeidet vil ta opp "Hvem, hvor og hvorfor". Tre funksjoner er erstattet. Dette gjøres som følger:

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

Denne funksjonen hjelper i tilfeller der analysatoren bremser driften av filtrene så mye at driften av systemet som kretsen vår er bygget i blir forstyrret. I en slik situasjon må du forlate analysatoren og bruke erstatning av funksjoner for å arbeide med minne.

Vi har vurdert en handlingsalgoritme for en enkel graf som ikke inneholder grener. Men denne tilnærmingen kan brukes på andre saker, selvfølgelig med mer kompleksitet, men ideen forblir den samme.

I den neste artikkelen vil vi se på spørsmålet om å estimere belastningen på en ticker og måter å bekjempe overdreven databelastning i en mediestreamer.

Kilde: www.habr.com

Legg til en kommentar