Udforskning af Mediastreamer2 VoIP-motoren. Del 12

Artiklens materiale er hentet fra min zen kanal.

Udforskning af Mediastreamer2 VoIP-motoren. Del 12

I fortiden artiklen, Jeg lovede at overveje spørgsmålet om at vurdere belastningen på tickeren og måder at bekæmpe overdreven computerbelastning i mediestreameren. Men jeg besluttede, at det ville være mere logisk at dække spørgsmålene om fejlfinding af håndværksfiltre relateret til databevægelse og først derefter overveje spørgsmål om ydeevneoptimering.

Fejlretning af håndværksfiltre

Efter at vi så på mekanismen til at flytte data i en mediestreamer i den forrige artikel, ville det være logisk at tale om farerne gemt i den. Et af funktionerne ved "dataflow"-princippet er, at hukommelsen allokeres fra heapen i filtre, der er placeret ved kilden til datastrømmen, og hukommelse frigives og returneres til heapen af ​​filtre placeret for enden af ​​flowet sti. Derudover kan skabelsen af ​​nye data og deres ødelæggelse forekomme et sted midt imellem. Generelt udføres hukommelsesfrigivelse af et andet filter end det, der skabte datablokken.

Ud fra et synspunkt om gennemsigtig hukommelsesovervågning ville det være rimeligt, at filteret, efter at have modtaget en inputblok, straks ødelægger den efter behandling, frigør hukommelsen og udsender en nyoprettet blok med outputdata. I dette tilfælde ville en hukommelseslækage i filteret let kunne spores - hvis analysatoren opdagede en lækage i filteret, så ødelægger det næste filter ikke indgående blokke korrekt, og der er en fejl i det. Men fra synspunktet om at opretholde høj ydeevne er denne tilgang til at arbejde med datablokke ikke produktiv - den fører til et stort antal operationer for at allokere/frigøre hukommelse til datablokke uden brugbart output.

Af denne grund bruger mediestreamerfiltre, for ikke at bremse databehandlingen, funktioner, der skaber nemme kopier, når du kopierer beskeder (vi talte om dem i den forrige artikel). Disse funktioner opretter kun en ny forekomst af meddelelseshovedet ved at "vedhæfte" en blok af data fra den "gamle" meddelelse, der kopieres. Som et resultat er to overskrifter knyttet til én datablok, og referencetælleren i datablokken øges. Men det vil ligne to beskeder. Der kan være flere meddelelser med sådan en "socialiseret" datablok, for eksempel genererer MS_TEE-filteret et dusin af sådanne lette kopier på én gang, og fordeler dem mellem dets output. Hvis alle filtre i kæden fungerer korrekt, skal denne referencetæller ved slutningen af ​​pipelinen nå nul, og hukommelsesfrigivelsesfunktionen vil blive kaldt: ms_free(). Hvis opkaldet ikke sker, så vil dette stykke hukommelse ikke længere blive returneret til dyngen, dvs. det vil "lække". Prisen for at bruge lette kopier er tabet af evnen til nemt at bestemme (som det ville være tilfældet med almindelige kopier), hvilket graffilter der lækker hukommelse.

Da mediestreamerudviklerne er ansvarlige for at finde hukommelseslækager i native filtre, behøver du højst sandsynligt ikke at fejlsøge dem. Men med dit håndværksfilter er du din egen lykkes græshoppe, og den tid, du bruger på at søge efter lækager i din kode, vil afhænge af din nøjagtighed. For at reducere din debugging-tid er vi nødt til at se på lækagedetektionsteknikker, når vi udvikler filtre. Derudover kan det ske, at lækagen kun vil manifestere sig, når filteret anvendes i et rigtigt system, hvor antallet af "mistænkte" kan være enormt og tiden til fejlretning begrænset.

Hvordan kommer et hukommelseslæk til udtryk?

Det er logisk at antage, at i programmet output top vil vise den stigende procentdel af hukommelsen, der optages af din applikation.

Den ydre manifestation vil være, at systemet på et tidspunkt vil begynde at reagere langsomt på musebevægelser og langsomt gentegne skærmen. Det er også muligt, at systemloggen vil vokse og æde plads på din harddisk. I dette tilfælde vil din applikation begynde at opføre sig mærkeligt, ikke reagere på kommandoer, kan ikke åbne en fil osv.

For at opdage forekomsten af ​​en lækage, vil vi bruge en hukommelsesanalysator (herefter benævnt analysatoren). Det kunne være valgrind (godt artiklen om det) eller indbygget i compileren gcc Memory Sanitizer eller noget andet. Hvis analysatoren viser, at der opstår en lækage i et af graffiltrene, betyder det, at det er tid til at anvende en af ​​metoderne beskrevet nedenfor.

Three Pines metode

Som nævnt ovenfor, hvis der er en hukommelseslækage, vil analysatoren pege på det filter, der anmodede om hukommelsesallokering fra heapen. Men det vil ikke påpege filteret, der "glemte" at returnere det, hvilket faktisk er synderen. Således kan analysatoren kun bekræfte vores frygt, men ikke angive deres rod.

For at finde ud af placeringen af ​​det "dårlige" filter i grafen, kan du gå ved at reducere grafen til det mindste antal knudepunkter, hvor analysatoren stadig registrerer en lækage og lokalisere det problematiske filter i de resterende tre fyrretræer.

Men det kan ske, at du ved at reducere antallet af filtre i grafen forstyrrer det normale interaktionsforløb mellem filtre og andre elementer i dit system, og lækagen vil ikke længere dukke op. I dette tilfælde bliver du nødt til at arbejde med en graf i fuld størrelse og bruge den fremgangsmåde, der er skitseret nedenfor.

Glidende isolator metode

For at gøre præsentationen enkel, vil vi bruge en graf, der består af en kæde af filtre. Hun er vist på billedet.

Udforskning af Mediastreamer2 VoIP-motoren. Del 12

En almindelig graf, hvor der sammen med færdige mediestreamerfiltre bruges fire håndværksfiltre F1...F4 af fire forskellige typer, som du har lavet for længe siden og ikke er i tvivl om deres rigtighed. Lad os dog antage, at flere af dem har hukommelseslækager. Når vi kører vores program til at overvåge analysatoren, lærer vi fra dens rapport, at et bestemt filter anmodede om en vis mængde hukommelse og ikke returnerede det til heap N antal gange. Du kan nemt gætte, at der vil være et link til de interne filterfunktioner af typen MS_VOID_SOURCE. Hans opgave er at tage hukommelsen fra dyngen. Andre filtre skal returnere det der. De der. vi vil opdage lækagen.

For at bestemme, i hvilken sektion af rørledningen, der var passivitet, der førte til en hukommelseslækage, foreslås det at indføre et ekstra filter, der blot flytter beskeder fra input til output, men samtidig opretter en kopi af inputmeddelelsen, der ikke er lys , men snarere en normal "tung", sletter så fuldstændig beskeden modtaget ved indgangen. Vi vil kalde et sådant filter en isolator. Vi mener, at da filteret er enkelt, er der ingen lækage i det. Og endnu en positiv egenskab - hvis vi tilføjer det hvor som helst i vores graf, vil dette ikke påvirke driften af ​​kredsløbet på nogen måde. Vi vil afbilde filterisolatoren i form af en cirkel med et dobbeltkredsløb.

Vi tænder isolatoren umiddelbart efter voidsource-filteret:
Udforskning af Mediastreamer2 VoIP-motoren. Del 12

Vi kører programmet med analysatoren igen, og vi ser, at denne gang vil analysatoren give isolatoren skylden. Det er trods alt ham, der nu opretter datablokke, som så går tabt af et ukendt skødesløst filter (eller filtre). Næste trin er at flytte isolatoren langs kæden til højre med ét filter og starte analysen igen. Så trin for trin flytter isolatoren til højre, vil vi få en situation, hvor antallet af "lækkede" hukommelsesblokke vil falde i den næste analysatorrapport. Det betyder, at på dette trin var isolatoren i kæden umiddelbart efter problemfilteret. Hvis der kun var ét "dårligt" filter, forsvinder lækagen helt. Således lokaliserede vi det problematiske filter (eller et af flere). Efter at have "fikset" filteret, kan vi fortsætte med at flytte isolatoren til højre langs kæden, indtil vi fuldstændig besejrer hukommelseslækager.

Implementering af et isolatorfilter

Isolatorimplementeringen ligner et almindeligt 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 til at erstatte hukommelsesstyringsfunktioner

For mere subtil research giver mediestreameren mulighed for at erstatte hukommelsesadgangsfunktioner med dine egne, som ud over hovedarbejdet optager "Hvem, hvor og hvorfor". Tre funktioner udskiftes. Dette gø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 funktion hjælper i tilfælde, hvor analysatoren sænker driften af ​​filtrene så meget, at driften af ​​systemet, som vores kredsløb er bygget i, forstyrres. I en sådan situation skal du opgive analysatoren og bruge substitution af funktioner til at arbejde med hukommelse.

Vi har overvejet en handlingsalgoritme for en simpel graf, der ikke indeholder grene. Men denne tilgang kan anvendes på andre sager, selvfølgelig med mere kompleksitet, men ideen forbliver den samme.

I den næste artikel vil vi se på spørgsmålet om at estimere belastningen på en ticker og måder at bekæmpe overdreven computerbelastning i en mediestreamer.

Kilde: www.habr.com

Tilføj en kommentar