Esplorante la Mediastreamer2 VoIP-motoron. Parto 11

La materialo de la artikolo estas prenita el mia zen kanalo.

Esplorante la Mediastreamer2 VoIP-motoron. Parto 11

Mekanismo de movo de datumoj

  • Datumbloko dblk_t
  • Mesaĝo mblk_t
  • Funkcioj por labori kun mesaĝoj mblk_t
  • Queue queue_t
  • Funkcioj por labori kun vostoj queue_t
  • Konektante filtrilojn
  • Signalpunkto de datumtraktado grafiko
  • Malantaŭ la kulisaj agadoj de la teletajpilo
  • Bufferigilo (MSBufferizer)
  • Funkcioj por labori kun MSBufferizer

En la lasta artikolo ni evoluigis nian propran filtrilon. Ĉi tiu artikolo fokusiĝos pri la interna mekanismo por movi datumojn inter amaskomunikilaraj filtriloj. Ĉi tio permesos al vi skribi kompleksajn filtrilojn kun malpli da penado estonte.

Mekanismo de movo de datumoj

Movado de datumoj en la amaskomunikila streamer estas farita per vicoj priskribitaj de la strukturo queue_t. Ŝnuroj de mesaĝoj kiel mblk_t, kiuj mem ne enhavas signalajn datumojn, sed nur ligojn al la antaŭa, sekva mesaĝo kaj al la datumbloko. Krome mi volas precipe emfazi, ke ekzistas ankaŭ kampo por ligo al samtipa mesaĝo, kiu ebligas organizi unuope ligitan liston de mesaĝoj. Ni nomos grupon de mesaĝoj kunigitaj de tia listo opo. Tiel, ajna elemento de la atendovico povas esti ununura mesaĝo mblk_t, kaj eble la kapo de mesaĝa opo mblk_t. Ĉiu opomesaĝo povas havi sian propran hospitalan datumblokon. Ni diskutos kial opoj necesas iom poste.

Kiel menciite supre, la mesaĝo mem ne enhavas blokon da datenoj; anstataŭe, ĝi nur enhavas montrilon al la memorareo kie la bloko estas stokita. En ĉi tiu parto, la ĝenerala bildo de la laboro de la amaskomunikilaro rememorigas la pordan magazenon en la bildstrio "Monsters, Inc.", kie pordoj (ligiloj al datumoj - ĉambroj) moviĝas kun freneza rapido laŭ supraj transportiloj, dum la ĉambroj mem. resti senmova.

Nun, moviĝante laŭ la hierarkio de malsupre al supro, ni konsideru detale la listigitajn entojn de la mekanismo de transdono de datumoj en la amaskomunikila streamer.

Datumbloko dblk_t

La datumbloko konsistas el kaplinio kaj datumbufro. La kaplinio estas priskribita per la sekva strukturo,

typedef struct datab
{
unsigned char *db_base; // Указатель на начало буфер данных.
unsigned char *db_lim;  // Указатель на конец буфер данных.
void (*db_freefn)(void*); // Функция освобождения памяти при удалении блока.
int db_ref; // Счетчик ссылок.
} dblk_t;

La kampoj de la strukturo enhavas montrilojn al la komenco de la bufro, la fino de la bufro, kaj la funkcion por forigi la datumbufron. Lasta elemento en kaplinio db_ref — referenca nombrilo, se ĝi atingas nulon, tio servas kiel signalo por forigi ĉi tiun blokon el memoro. Se la datumbloko estis kreita de la funkcio datab_alloc() , tiam la datumbufro estos metita en memoron tuj post la kaplinio. En ĉiuj aliaj kazoj, la bufro povas troviĝi ie aparte. La datuma bufro enhavos signalajn specimenojn aŭ aliajn datumojn, kiujn ni volas prilabori per filtriloj.

Nova kazo de datumbloko estas kreita uzante la funkcion:

dblk_t *datab_alloc(int size);

Kiel eniga parametro, ĝi ricevas la grandecon de la datumoj kiujn la bloko stokos. Pli da memoro estas asignita por meti kaplinion - strukturon - komence de la asignita memoro datumojb. Sed dum uzado de aliaj funkcioj, tio ne ĉiam okazas; en kelkaj kazoj, la datumbufro povas troviĝi aparte de la datumbloka kaplinio. Kreante strukturon, la kampoj estas agordita tiel ke ĝia kampo db_bazo montris al la komenco de la datuma areo, kaj db_lim ĝis ĝia fino. Nombro de ligoj db_ref estas agordita al unu. La indikilo de puriga funkcio de datumoj estas agordita al nulo.

mesaĝo mblk_t

Kiel dirite, vostoelementoj estas de tipo mblk_t, ĝi estas difinita jene:

typedef struct msgb
{
  struct msgb *b_prev;   // Указатель на предыдущий элемент списка.
  struct msgb *b_next;   // Указатель на следующий элемент списка.
  struct msgb *b_cont;   // Указатель для подклейки к сообщению других сообщений, для создания кортежа сообщений.
  struct datab *b_datap; // Указатель на структуру блока данных.
  unsigned char *b_rptr; // Указатель на начало области данных для чтения данных буфера b_datap.
  unsigned char *b_wptr; // Указатель на начало области данных для записи данных буфера b_datap.
  uint32_t reserved1;    // Зарезервированное поле1, медиастример помещает туда служебную информацию. 
  uint32_t reserved2;    // Зарезервированное поле2, медиастример помещает туда служебную информацию.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

strukturo mblk_t enhavas montrilojn ĉe la komenco b_antaŭa, b_sekva, kiuj estas necesaj por organizi duoble ligitan liston (kiu estas vico queue_t).

Poste venas la montrilo b_kont, kiu estas uzata nur kiam la mesaĝo estas parto de opo. Por la lasta mesaĝo en la opo, ĉi tiu montrilo restas nula.

Poste ni vidas montrilon al datumbloko b_datap, por kiu la mesaĝo ekzistas. Ĝi estas sekvita per montriloj al la areo ene de la blokdatumbufro. Kampo b_rptr specifas la lokon de kiu datumoj de la bufro estos legitaj. Kampo b_wptr indikas la lokon de kiu skribos al la bufro estos farita.

La ceteraj kampoj estas de servo-naturo kaj ne rilatas al la funkciado de la datumtransiga mekanismo.

Malsupre estas ununura mesaĝo kun la nomo m1 kaj datumbloko d1.
Esplorante la Mediastreamer2 VoIP-motoron. Parto 11
La sekva figuro montras opon de tri mesaĝoj m1, m1_1, m1_2.
Esplorante la Mediastreamer2 VoIP-motoron. Parto 11

Mesaĝaj funkcioj mblk_t

Nova mesaĝo mblk_t kreita de la funkcio:

mblk_t *allocb(int size, int pri); 

ŝi metas novan mesaĝon en memoron mblk_t kun datumbloko de la specifita grandeco grandeco, dua argumento - pri ne uzata en ĉi tiu versio de la biblioteko. Ĝi devus resti nulo. Dum la funkciado de la funkcio, memoro estos asignita por la strukturo de la nova mesaĝo kaj la funkcio estos vokita mblk_init(), kiu restarigos ĉiujn kampojn de la kreita kazo de la strukturo kaj poste, uzante la supre menciitajn datab_alloc(), kreos datuman bufron. Post tio la kampoj en la strukturo estos agorditaj:

mp->b_datap=datab;
mp->b_rptr=mp->b_wptr=datab->db_base;
mp->b_next=mp->b_prev=mp->b_cont=NULL;

Ĉe la eligo ni ricevas novan mesaĝon kun pravigitaj kampoj kaj malplena datumbufro. Por aldoni datumojn al mesaĝo, vi devas kopii ĝin al la datumbloka bufro:

memcpy(msg->b_rptr, data, size);

kie datumoj estas montrilo al la datumfonto, kaj grandeco - ilia grandeco.
tiam vi devas ĝisdatigi la montrilon al la skriba punkto, por ke ĝi montru denove al la komenco de la libera areo en la bufro:

msg->b_wptr = msg->b_wptr + size

Se vi bezonas krei mesaĝon el ekzistanta bufro, sen kopii, tiam uzu la funkcion:

mblk_t *esballoc(uint8_t *buf, int size, int pri, void (*freefn)(void*)); 

La funkcio, post kreado de la mesaĝo kaj la strukturo de la datumbloko, agordos siajn montrilojn al la datumoj ĉe la adreso buff. Tiuj. en ĉi tiu kazo, la datumbufro ne troviĝas post la kapkampoj de la datumbloko, kiel estis la kazo dum kreado de datumbloko kun la funkcio datab_alloc(). La bufro kun datumoj transdonitaj al la funkcio restos kie ĝi estis, sed helpe de montriloj ĝi estos alfiksita al la nove kreita kaplinio de la datumbloko, kaj tio, laŭe, al la mesaĝo.

Al unu mesaĝo mblk_t Pluraj datumblokoj povas esti kunligitaj sinsekve. Ĉi tio estas farita per la funkcio:

mblk_t * appendb(mblk_t *mp, const char *data, int size, bool_t pad); 

mp — mesaĝo al kiu estos aldonita alia datumbloko;
datumoj — montrilo al la bloko, kies kopio estos aldonita al la mesaĝo;
grandeco - grandeco de datumoj;
pad — flago, ke la grandeco de la asignita memoro devas esti vicigita laŭ 4-bajta limo (plenigo estos farita per nuloj).

Se estas sufiĉe da spaco en la ekzistanta mesaĝdatumbufro, tiam la novaj datumoj estos gluitaj malantaŭ la datumoj jam tie. Se estas malpli libera spaco en la mesaĝdatumbufro ol grandeco, tiam nova mesaĝo estas kreita kun sufiĉa bufrograndeco kaj la datumoj estas kopiitaj al ĝia bufro. Ĉi tio estas nova mesaĝo, ligita al la originala per montrilo b_kont. En ĉi tiu kazo, la mesaĝo iĝas opo.

Se vi bezonas aldoni alian blokon da datumoj al la opo, tiam vi devas uzi la funkcion:

void msgappend(mblk_t *mp, const char *data, int size, bool_t pad);

ŝi trovos la lastan mesaĝon en la opo (li havas b_kont estos nula) kaj vokos la funkcion por ĉi tiu mesaĝo appendb ().

Vi povas ekscii la grandecon de datumoj en mesaĝo aŭ opo uzante la funkcion:

int msgdsize(const mblk_t *mp);

ĝi trapasos ĉiujn mesaĝojn en la opo kaj resendos la totalan kvanton da datumoj en la datumbufroj de tiuj mesaĝoj. Por ĉiu mesaĝo, la kvanto de datumoj estas kalkulita jene:

 mp->b_wptr - mp->b_rptr

Por kombini du opoj, uzu la funkcion:

mblk_t *concatb(mblk_t *mp, mblk_t *newm);

ŝi aldonas la opon newm al la vosto de la opo mp kaj resendas montrilon al la lasta mesaĝo de la rezulta opo.

Se necese, opo povas esti igita unu mesaĝo kun ununura bloko de datumoj; tio estas farita per la funkcio:

void msgpullup(mblk_t *mp,int len);

se argumento len estas -1, tiam la grandeco de la asignita bufro estas determinita aŭtomate. Se len estas pozitiva nombro, bufro de ĉi tiu grandeco estos kreita kaj la opoj-mesaĝo datumoj estos kopiitaj en ĝi. Se la bufro finiĝas, kopiado ĉesos tie. La unua mesaĝo de la opo ricevos novan grandecan bufron kun la kopiitaj datumoj. La ceteraj mesaĝoj estos forigitaj kaj la memoro resendita al la amaso.

Kiam vi forigas strukturon mblk_t la referenckalkulo de la datumbloko estas konsiderata se, dum vokado freeb () ĝi montriĝas nul, tiam la datuma bufro estas forigita kune kun la petskribo mblk_t, kiu montras al ĝi.

Komencante la kampojn de nova mesaĝo:

void mblk_init(mblk_t *mp);

Aldonante alian datumon al la mesaĝo:

mblk_t * appendb(mblk_t *mp, const char *data, size_t size, bool_t pad);

Se la novaj datumoj ne konvenas en la liberan spacon de la mesaĝdatumbufro, tiam aparte kreita mesaĝo kun bufro de la bezonata grandeco estas alfiksita al la mesaĝo (montrilo al la aldonita mesaĝo estas metita en la unua mesaĝo) kaj la mesaĝo iĝas opo.

Aldonante datumon al opo:

void msgappend(mblk_t *mp, const char *data, size_t size, bool_t pad); 

La funkcio vokas appendb() en buklo.

Kombinante du opoj en unu:

mblk_t *concatb(mblk_t *mp, mblk_t *newm);

mesaĝo newm estos alfiksita al mp.

Farante kopion de ununura mesaĝo:

mblk_t *copyb(const mblk_t *mp);

Kompleta kopiado de opo kun ĉiuj datumblokoj:

mblk_t *copymsg(const mblk_t *mp);

La elementoj de la opo estas kopiitaj per la funkcio kopiib().

Kreu facilan kopion de mesaĝo mblk_t. En ĉi tiu kazo, la datumbloko ne estas kopiita, sed ĝia referenca nombrilo estas pliigita db_ref:

mblk_t *dupb(mblk_t *mp);

Farante malpezan kopion de opo. Datumblokoj ne estas kopiitaj, nur iliaj referencaj nombriloj estas pliigitaj db_ref:

mblk_t *dupmsg(mblk_t* m);

Gluante ĉiujn mesaĝojn de opo en unu mesaĝon:

void msgpullup(mblk_t *mp,size_t len);

Se la argumento len estas -1, tiam la grandeco de la asignita bufro estas determinita aŭtomate.

Forigante mesaĝon, opo:

void freemsg(mblk_t *mp);

La referenckalkulo de la datumbloko malpliiĝas je unu. Se ĝi atingas nulon, la datumbloko ankaŭ estas forigita.

Kalkulo de la totala kvanto de datumoj en mesaĝo aŭ opo.

size_t msgdsize(const mblk_t *mp);

Prenante mesaĝon el la vosto de la atendovico:

mblk_t *ms_queue_peek_last (q);

Kopiante la enhavon de la rezervitaj kampoj de unu mesaĝo en alian mesaĝon (fakte, ĉi tiuj kampoj enhavas flagojn, kiuj estas uzataj de la amaskomunikila streamer):

mblk_meta_copy(const mblk_t *source, mblk *dest);

Kolo queue_t

La mesaĝvico en la amaskomunikila streamer estas efektivigita kiel cirkla duoble ligita listo. Ĉiu listelemento enhavas montrilon al datumbloko kun signalspecimenoj. Montriĝas, ke nur montriloj al la datumbloko moviĝas laŭvice, dum la datumoj mem restas senmovaj. Tiuj. nur ligiloj al ili estas movitaj.
Strukturo priskribanta la atendovicon queue_t, montrita malsupre:

typedef struct _queue
{
   mblk_t _q_stopper; /* "Холостой" элемент очереди, не указывает на данные, используется только для управления очередью. При инициализации очереди (qinit()) его указатели настраиваются так, чтобы они указывали на него самого. */
   int q_mcount;        // Количество элементов в очереди.
} queue_t;

La strukturo enhavas kampon - montrilon _q_ŝtopilo tajpu *mblk_t, ĝi montras al la unua elemento (mesaĝo) en la vosto. La dua kampo de la strukturo estas la nombrilo de mesaĝoj en la atendovico.
La suba figuro montras vicon nomitan q1 enhavanta 4 mesaĝojn m1, m2, m3, m4.
Esplorante la Mediastreamer2 VoIP-motoron. Parto 11
La sekva figuro montras voston nomitan q1 enhavantan 4 mesaĝojn m1,m2,m3,m4. Mesaĝo m2 estas la kapo de opo kiu enhavas du pliajn mesaĝojn m2_1 kaj m2_2.

Esplorante la Mediastreamer2 VoIP-motoron. Parto 11

Funkcioj por labori kun vostoj queue_t

Vicovicigo:

void qinit(queue_t *q);

kampo _q_ŝtopilo (ĉi-poste ni nomos ĝin "ŝtopilo") estas pravigita per la funkcio mblk_init(), ĝia antaŭa elemento kaj sekva elementmontrilo estas alĝustigitaj por montri al si. La vosto-elementa nombrilo estas rekomencigita al nulo.

Aldonante novan elementon (mesaĝoj):

void putq(queue_t *q, mblk_t *m);

Nova elemento m estas aldonita al la fino de la listo, la elementmontriloj estas alĝustigitaj tiel ke la ŝtopilo iĝas la sekva elemento por ĝi, kaj ĝi iĝas la antaŭa elemento por la ŝtopilo. La vosto-elementa nombrilo estas pliigita.

Prenante elementon el la atendovico:

mblk_t * getq(queue_t *q); 

La mesaĝo kiu venas post la ŝtopilo estas prenita, kaj la elementa nombrilo estas malpliigita. Se ne estas elementoj en la atendovico krom la ŝtopilo, tiam 0 estas resendita.

Enmetante mesaĝon en atendovico:

void insq(queue_t *q, mblk_t *emp, mblk_t *mp); 

Ero mp enigita antaŭ la elemento EMP. Se EMP=0, tiam la mesaĝo estas aldonita al la vosto de la vosto.

Prenante mesaĝon de la ĉefo de la vico:

void remq(queue_t *q, mblk_t *mp); 

La elementa nombrilo estas malpliigita.

Legante montrilon al la unua elemento en la atendovico:

mblk_t * peekq(queue_t *q); 

Forigante ĉiujn elementojn el la atendovico dum vi forigas la elementojn mem:

void flushq(queue_t *q, int how);

argumenton kiom ne uzata. La vosto-elementa nombrilo estas agordita al nulo.

Makroo por legi montrilon al la lasta elemento de la vico:

mblk_t * qlast(queue_t *q);

Kiam vi laboras kun mesaĝvostoj, konsciu tion kiam vi vokas ms_queue_put(q, m) kun nula montrilo al la mesaĝo, la funkcio loops. Via programo frostiĝos. kondutas simile ms_queue_next(q, m).

Konektante filtrilojn

La vico priskribita supre estas uzata por pasi mesaĝojn de unu filtrilo al alia aŭ de unu al pluraj filtriloj. Filtriloj kaj iliaj ligoj formas direktitan grafeon. La enigo aŭ eligo de la filtrilo estos nomita la ĝenerala vorto "pinglo". Por priskribi la ordon en kiu filtriloj estas konektitaj unu al la alia, la amaskomunikila streamer uzas la koncepton de "signa punkto". Signalpunkto estas strukturo _MSCPoint, kiu enhavas montrilon al la filtrilo kaj la nombron de unu el ĝiaj pingloj; sekve, ĝi priskribas la ligon de unu el la enigaĵoj aŭ eliroj de la filtrilo.

Signalpunkto de datumtraktado grafiko

typedef struct _MSCPoint{
struct _MSFilter *filter; // Указатель на фильтр медиастримера.
int pin;                        // Номер одного из входов или выходов фильтра, т.е. пин.
} MSCPoint;

Filtrilaj stiftoj estas numeritaj ekde nulo.

La ligo de du pingloj per mesaĝvico estas priskribita per la strukturo _MSQueue, kiu enhavas mesaĝvicon kaj montrilojn al la du signalpunktoj kiujn ĝi ligas:

typedef struct _MSQueue
{
queue_t q;
MSCPoint prev;
MSCPoint next;
}MSQueue;

Ni nomos ĉi tiun strukturon signalligo. Ĉiu amaskomunikilara streamer filtrilo enhavas tabelon de eniga ligiloj kaj tabelo de eligo ligiloj (MSQueue). La grandeco de tabeloj estas agordita dum kreado de filtrilo; ni jam faris tion uzante eksportitan variablon de tipo MSFilterDesc, kiam ni evoluigis nian propran filtrilon. Malsupre estas strukturo, kiu priskribas ajnan filtrilon en amaskomunikila streamer, MSFilter:


struct _MSFilter{
    MSFilterDesc *desc;    /* Указатель на дескриптор фильтра. */
    /* Защищенные атрибуты, их нельзя сдвигать или убирать иначе будет нарушена работа с плагинами. */
    ms_mutex_t lock;      /* Семафор. */
    MSQueue **inputs;     /* Таблица входных линков. */
    MSQueue **outputs;    /* Таблица выходных линков. */
    struct _MSFactory *factory; /* Указатель на фабрику, которая создала данный экземпляр фильтра. */
    void *padding;              /* Не используется, будет задействован если добавятся защищенные поля. */
    void *data;                 /* Указатель на произвольную структуру для хранения данных внутреннего состояния фильтра и промежуточных вычислений. */
    struct _MSTicker *ticker;   /* Указатель на объект тикера, который не должен быть нулевым когда вызывается функция process(). */
    /*private attributes, they can be moved and changed at any time*/
    MSList *notify_callbacks; /* Список обратных вызовов, используемых для обработки событий фильтра. */
    uint32_t last_tick;       /* Номер последнего такта, когда выполнялся вызов process(). */ 
    MSFilterStats *stats;     /* Статистика работы фильтра.*/
    int postponed_task; /*Количество отложенных задач. Некоторые фильтры могут откладывать обработку данных (вызов process()) на несколько тактов.*/
    bool_t seen;  /* Флаг, который использует тикер, чтобы помечать что этот экземпляр фильтра он уже обслужил на данном такте.*/
};
typedef struct _MSFilter MSFilter;

Post kiam ni konektis la filtrilojn en la C-programon laŭ nia plano (sed ne konektis la ticker), ni tiel kreis direktitan grafeon, kies nodoj estas ekzemploj de la strukturo. MSFilter, kaj randoj estas ekzemploj de ligiloj MSQueue.

Malantaŭ la kulisaj agadoj de la teletajpilo

Kiam mi diris al vi, ke la teletajpilo estas filtrilo por la fonto de iksodoj, ĝi ne estis la tuta vero pri ĝi. Ticker estas objekto, kiu funkciigas funkciojn sur la horloĝo procezo () ĉiuj filtriloj de la cirkvito (grafo) al kiu ĝi estas konektita. Kiam ni konektas ticker al grafea filtrilo en C-programo, ni montras al la teletajpilo la grafeon, kiun ĝi kontrolos de nun ĝis ni malŝaltos ĝin. Post konekto, la teletajpilo komencas ekzameni la grafeon konfiditan al sia zorgo, kompilante liston de filtriloj, kiuj inkluzivas ĝin. Por ne "kalkuli" la saman filtrilon dufoje, ĝi markas la detektitajn filtrilojn metante markobutonon en ili. vidis. La serĉo estas farita per la ligaj tabeloj kiujn ĉiu filtrilo havas.

Dum ĝia enkonduka turneo de la grafeo, la teletajpilo kontrolas ĉu inter la filtriloj ekzistas almenaŭ unu, kiu funkcias kiel fonto de datumblokoj. Se ne ekzistas, tiam la grafikaĵo estas konsiderata malĝusta kaj la teletajpilo kraŝas.

Se la grafeo montriĝas "ĝusta", por ĉiu trovita filtrilo, la funkcio estas vokita por inicialigo antaŭprocezo (). Tuj kiam alvenas la momento por la sekva pretiga ciklo (defaŭlte ĉiujn 10 milisekundojn), la teletajpilo vokas la funkcion procezo () por ĉiuj antaŭe trovitaj fontfiltriloj, kaj poste por la ceteraj filtriloj en la listo. Se la filtrilo havas enigajn ligilojn, tiam rulu la funkcion procezo () ripetas ĝis la enirligaj atendovicoj estas malplenaj. Post ĉi tio, ĝi moviĝas al la sekva filtrilo en la listo kaj "rulumas" ĝin ĝis la eniga ligiloj estas liberaj de mesaĝoj. La teletajpilo moviĝas de filtrilo al filtrilo ĝis la listo finiĝas. Ĉi tio kompletigas la prilaboradon de la ciklo.

Nun ni revenos al opoj kaj parolos pri kial tia ento estis aldonita al la amaskomunikila streamer. Ĝenerale, la kvanto de datumoj postulitaj de la algoritmo funkciiganta ene de la filtrilo ne koincidas kaj ne estas oblo de la grandeco de la datenbufroj ricevitaj ĉe la enigo. Ekzemple, ni skribas filtrilon kiu elfaras rapidan transformon de Fourier, kiu laŭ difino povas nur prilabori datumblokojn kies grandeco estas potenco de du. Estu 512 kalkuloj. Se la datumoj estas generitaj de telefona kanalo, tiam la datuma bufro de ĉiu mesaĝo ĉe la enigo alportos al ni 160 signalspecimenojn. Tente ne kolekti datumojn de la enigo ĝis la bezonata kvanto da datumoj estas tie. Sed en ĉi tiu kazo, kolizio okazos kun la teletajpilo, kiu malsukcese provos rulumi la filtrilon ĝis la eniga ligilo estas malplena. Antaŭe, ni nomumis ĉi tiun regulon kiel la tria principo de la filtrilo. Laŭ ĉi tiu principo, la funkcio process() de la filtrilo devas preni ĉiujn datumojn de la enigvostoj.

Krome, ne eblos preni nur 512 specimenojn el la enigo, ĉar vi povas preni nur tutajn blokojn, t.e. la filtrilo devos preni 640 specimenojn kaj uzi 512 el ili, la reston antaŭ amasigi novan parton de datumoj. Tiel, nia filtrilo, krom sia ĉefa laboro, devas provizi helpajn agojn por meza stokado de enigo-datumoj. La programistoj de la amaskomunikilara streamer kaj solvo al ĉi tiu ĝenerala problemo evoluigis specialan objekton - MSBufferizer (bufferilo), kiu solvas ĉi tiun problemon uzante opoj.

Bufferigilo (MSBufferizer)

Ĉi tio estas objekto, kiu amasigos enigajn datumojn ene de la filtrilo kaj komencos sendi ĝin por prilaborado tuj kiam la kvanto de informoj sufiĉas por ruli la filtrilgoritmon. Dum la bufro amasigas datumojn, la filtrilo funkcios en neaktiva reĝimo, sen eluzi la pretigpovon de la procesoro. Sed tuj kiam la legofunkcio de la bufro resendas valoron krom nulo, la procezo () funkcio de la filtrilo komencas preni kaj prilabori datumojn de la bufro en partoj de la bezonata grandeco, ĝis ĝi estas elĉerpita.
Datenoj kiuj ankoraŭ ne estas postulataj restas en la bufro kiel la unua elemento de la opo, al kiu postaj blokoj de enigdatenoj estas alkroĉitaj.

La strukturo kiu priskribas la bufron:

struct _MSBufferizer{
queue_t q; /* Очередь сообщений. */
int size; /* Суммарный размер данных находящихся в буферизаторе в данный момент. */
};
typedef struct _MSBufferizer MSBufferizer;

Funkcioj por labori kun MSBufferizer

Kreante novan bufrokazaĵon:

MSBufferizer * ms_bufferizer_new(void);

Memoro estas asignita, pravalorigita en ms_bufferizer_init() kaj montrilo estas resendita.

Inicialiga funkcio:

void ms_bufferizer_init(MSBufferizer *obj); 

La vico estas pravaloriganta q, kampo grandeco estas agordita al nulo.

Aldonante mesaĝon:

void ms_bufferizer_put(MSBufferizer *obj, mblk_t *m); 

Mesaĝo m estas aldonita al la vico. La kalkulita grandeco de datumblokoj estas aldonita al grandeco.

Transdonante ĉiujn mesaĝojn de la ligdatumvico al la bufro q:

void ms_bufferizer_put_from_queue(MSBufferizer *obj, MSQueue *q);   

Transdono de mesaĝoj de ligilo q en la bufro estas farita uzante la funkcion ms_bufferizer_put().

Legado el la bufro:

int ms_bufferizer_read(MSBufferizer *obj, uint8_t *data, int datalen); 

Se la grandeco de la datumoj akumulitaj en la bufro estas malpli ol la petita (datalen), tiam la funkcio resendas nulon, datumoj ne estas kopiitaj al datumoj. Alie, sinsekva kopiado de datumoj de opoj situantaj en la bufro estas farita. Post kopiado, la opo estas forigita kaj la memoro estas liberigita. Kopiado finiĝas en la momento kiam datenbajtoj estas kopiitaj. Se spaco finiĝas en la mezo de datumbloko, tiam en ĉi tiu mesaĝo, la datumbloko estos reduktita al la restanta nekopiita parto. La venontan fojon kiam vi vokas, kopiado daŭros de ĉi tiu punkto.

Legante la kvanton da datumoj nuntempe disponeblaj en la bufro:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Redonas la kampon grandeco bufro.

Forĵetante parton de la datumoj en la bufro:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

La specifita nombro da bajtoj da datumoj estas prenitaj kaj forĵetitaj. La plej malnovaj datumoj estas forĵetitaj.

Forigante ĉiujn mesaĝojn en la bufro:

void ms_bufferizer_flush(MSBufferizer *obj); 

La datuma nombrilo estas rekomencigita al nulo.

Forigante ĉiujn mesaĝojn en la bufro:

void ms_bufferizer_uninit(MSBufferizer *obj); 

La nombrilo ne estas rekomencigita.

Forigante la bufron kaj liberigante memoron:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Ekzemploj de uzado de la bufro troviĝas en la fontkodo de pluraj amaskomunikilaraj filtriloj. Ekzemple, en la MS_L16_ENC-filtrilo, kiu rearanĝas la bajtojn en la specimenoj de la reto-ordo al la gastiga ordo: l16.c

En la sekva artikolo, ni rigardos la aferon pri taksado de la ŝarĝo sur teletajpilo kaj manieroj kontraŭbatali troan komputikan ŝarĝon en amaskomunikila streamer.

fonto: www.habr.com

Aldoni komenton