探索 Mediastreamer2 VoIP 引擎。 第 12 部分

文章素材取自我 禪頻道.

探索 Mediastreamer2 VoIP 引擎。 第 12 部分

在過去 文章,我答應考慮股票負載估計的問題以及處理媒體流媒體中過多計算負載的方法。 但我認為更合乎邏輯的是,先討論與數據移動相關的調試工藝過濾器問題,然後再考慮性能優化問題。

調試工藝過濾器

在我們在上一篇文章中研究了媒體流媒體中的數據移動​​機制之後,我們就可以順理成章地討論其中隱藏的危險了。 “數據流”原理的特點之一是,從堆中分配內存發生在位於數據流源頭的過濾器中,位於流路徑末端的過濾器已經釋放了內存並返回到堆。 此外,新數據的創建及其銷毀可能發生在中間點的某個地方。 一般來說,內存的釋放是由創建數據塊的過濾器以外的過濾器執行的。

從內存透明監控的角度來看,過濾器在接收輸入塊時,在處理後立即銷毀它,釋放內存,並將帶有輸出數據的新創建的塊放在輸出上是合理的。 在這種情況下,可以輕鬆追踪過濾器中的內存洩漏 - 如果分析器檢測到過濾器中的洩漏,則其後面的過濾器不會正確銷毀傳入的塊,並且其中存在錯誤。 但從保持高性能的角度來看,這種處理數據塊的方法效率不高——它會導致大量為數據塊分配/釋放內存的操作,而沒有任何有用的消耗。

因此,媒體流過濾器為了不減慢數據處理速度,在復制消息時使用創建輕量副本的函數(我們在上一篇文章中討論過它們)。 這些函數僅通過將復制的“舊”消息中的數據塊“附加”到消息頭來創建消息頭的新副本。 結果,兩個標頭被附加到一個數據塊,並且數據塊中的引用計數器遞增。 但它看起來像兩條消息。 可以有更多具有此類“共享”數據塊的消息,例如,MS_TEE 過濾器一次生成十個這樣的輕副本,並將它們分發到其輸出中。 如果鏈中的所有過濾器都正常工作,則在管道結束時,該引用計數應達到零,並且將調用內存釋放函數: ms_free()。 如果沒有發生調用,那麼這塊內存將不再返回到堆中,即他“洩漏”。 使用輕量副本的成本是無法輕鬆確定(就像使用常規副本的情況一樣)哪個圖形過濾器內存洩漏。

由於查找“本機”過濾器中的內存洩漏的責任在於媒體流處理程序的開發人員,因此您很可能無需調試它們。 但有了你的手工過濾器,你自己就是自己幸福的蚱蜢,你花在尋找代碼漏洞上的時間將取決於你的準確性。 為了減少調試時間,我們需要在設計濾波器時考慮洩漏定位技術。 此外,可能只有在實際系統中應用過濾器時,洩漏才會顯現出來,其中“嫌疑人”的數量可能很大,並且調試時間有限。

內存洩漏如何表現出來?

可以邏輯地假設在程序的輸出中 最佳 將顯示您的應用程序佔用的內存百分比不斷增加。

外部表現在於,在某個時刻系統會對鼠標的移動做出緩慢的反應,緩慢地重繪屏幕。 系統日誌也可能會增長,佔用硬盤空間。 在這種情況下,您的應用程序將開始表現奇怪,不響應命令,無法打開文件等。

為了識別洩漏的事實,我們將使用內存分析器(以下簡稱分析器)。 它可能是 瓦爾格朗德 (好的 文章 關於它)或內置到編譯器中 GCC 內存消毒器 或者是其他東西。 如果分析儀顯示洩漏發生在圖形過濾器之一中,則意味著是時候應用下面描述的方法之一了。

三松法

如上所述,如果發生內存洩漏,分析器將指向從堆請求內存分配的過濾器。 但它不會指向“忘記”返回它的過濾器,這實際上是罪魁禍首。 因此,分析器只能證實我們的恐懼,但無法指出其根源。

要找出圖表中“壞”過濾器的位置,您可以將圖表減少到分析器仍檢測到洩漏的最小節點數,並在其餘三個松樹中找到有問題的過濾器。

但是,通過減少色譜柱中過濾器的數量,您可能會破壞過濾器與系統其他元件之間的正常相互作用過程,並且洩漏將不再出現。 在這種情況下,您將必須使用全尺寸圖表並使用下面描述的方法。

滑動絕緣子法

為了簡化演示,我們將使用由單個過濾器鏈組成的圖表。 她如圖所示。

探索 Mediastreamer2 VoIP 引擎。 第 12 部分

一個普通的圖表,其中除了現成的媒體流過濾器之外,還使用了四個工藝過濾器F1…F4,四種不同類型的您很早以前製作的並且毫無疑問它們的正確性。 然而,假設其中有幾個存在內存洩漏。 當運行我們的分析器監控程序時,我們從其報告中了解到,某個過濾器請求了一定量的內存,但 N 次沒有將其返回到堆。 很容易猜測,其中會引用MS_VOID_SOURCE類型的內部過濾函數。 他的任務是從堆中取出內存。 其他過濾器應該將其返回那裡。 那些。 我們會找到洩漏點的。

為了確定管道的哪個部分發生不活動導致內存洩漏,建議引入一個額外的過濾器,該過濾器簡單地將消息從輸入轉移到輸出,但同時創建一個非輕型、正常的過濾器。輸入消息的“重”副本,然後完全刪除到達輸出入口的消息。 我們將這樣的過濾器稱為絕緣體。 我們認為,由於過濾器很簡單,因此可以排除洩漏。 還有一個積極的特性 - 如果我們將它添加到圖表中的任何位置,那麼這不會以任何方式影響電路的運行。 我們將絕緣體濾波器描繪為具有雙輪廓的圓形。

在 voidsourse 過濾器之後立即啟用隔離器:
探索 Mediastreamer2 VoIP 引擎。 第 12 部分

我們再次用分析器運行程序,我們看到這一次,分析器將責任歸咎於隔離器。 畢竟,現在是他創建了數據塊,然後這些數據塊被未知的疏忽過濾器(或多個過濾器)丟失了。 下一步是將絕緣體沿鏈向右移動一個濾波器,然後再次開始分析。 因此,逐步將隔離器向右移動,我們會得到這樣一種情況:分析器的下一個報告中“洩漏”內存塊的數量減少。 這意味著在此步驟中,絕緣體最終出現在有問題的過濾器之後的鏈中。 如果只有一個“壞”過濾器,那麼洩漏就會完全消失。 因此,我們定位了有問題的過濾器(或多個過濾器之一)。 “修復”過濾器後,我們可以繼續將隔離器沿著鏈向右移動,直到完全消除內存洩漏。

實施隔離濾波器

隔離器的實現看起來就像一個普通的過濾器。 頭文件:

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

過濾器本身:

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

更換內存管理功能的方法

對於更微妙的研究,媒體流提供了用您自己的內存訪問功能替換內存訪問功能的能力,除了主要工作之外,還可以修復“誰、哪里和為什麼”。 三個功能正在被替換。 這是通過以下方式完成的:

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

當分析儀減慢濾波器速度以至於構建電路的系統的運行受到干擾時,此功能可以發揮作用。 在這種情況下,你必須放棄分析器並使用內存功能替代。

我們考慮了一種針對不包含分支的簡單圖的動作算法。 但這種方法可以應用於其他情況,當然會很複雜,但想法是一樣的。

在下一篇文章中,我們將討論股票負載估計問題以及如何處理媒體流處理器中過多的計算負載。

來源: www.habr.com

添加評論