Heim  >  Artikel  >  Backend-Entwicklung  >  Zend-Speichermanager für PHP

Zend-Speichermanager für PHP

coldplay.xixi
coldplay.xixinach vorne
2020-07-29 17:14:152374Durchsuche

Zend-Speichermanager für PHP

Zend Memory Manager

Zend Memory Manager, oft abgekürzt als ZendMM oder ZMM, ist eine C-Schicht Entwickelt, um die Möglichkeit zu bieten, dynamischen anforderungsgebundenen Speicher zuzuweisen und freizugeben.

Achten Sie auf „Anfragebindung“ im obigen Satz.

ZendMM ist mehr als nur eine klassische Ebene auf der dynamischen Speicherzuweisung von libc, die hauptsächlich durch zwei API-Aufrufe repräsentiert wird malloc()/free(). Bei ZendMM geht es um den anfragegebundenen Speicher, den PHP bei der Verarbeitung einer Anfrage zuweisen muss.

Verwandte Lernempfehlungen: PHP-Programmierung vom Einstieg bis zur Beherrschung

Zwei dynamische Hauptspeicherpools in PHP

PHP Es ist eine Shared-Nothing-Architektur. Nun ja, nicht zu 100 %. Weitere Informationen zu Schritten und Zyklen.

PHP kann Hunderte oder Tausende von Anfragen im selben Prozess bearbeiten. Standardmäßig vergisst PHP alle Informationen über die aktuelle Anfrage, nachdem diese abgeschlossen ist.

Die „Vergessen“-Nachricht wird so interpretiert, dass alle dynamischen Puffer freigegeben werden, die während der Verarbeitung der Anfrage zugewiesen wurden. Dies bedeutet, dass Sie beim Verarbeiten einer Anforderung keine herkömmlichen libc-Aufrufe verwenden können, um dynamischen Speicher zuzuweisen. Das ist vollkommen richtig, aber Sie geben sich die Chance, zu vergessen, den Puffer freizugeben.

ZendMM verfügt über eine API, die den dynamischen Allokator von libc durch Kopieren seiner API ersetzt. Programmierer müssen bei der Bearbeitung von Anforderungen diese API anstelle des Allokators von libc verwenden.

Wenn PHP beispielsweise eine Anfrage verarbeitet, analysiert es die PHP-Datei. Diese führen beispielsweise zu Deklarationen von Funktionen und Klassen. Wenn der Compiler mit dem Kompilieren einer PHP-Datei beginnt, weist er dynamischen Speicher zum Speichern der erkannten Klassen und Funktionen zu. PHP gibt diese jedoch am Ende der Anfrage frei. Standardmäßig vergisst PHP

viele

Informationen von einer Anfrage zur anderen.

Es gibt jedoch einige sehr seltene Informationen, die Sie über mehrere Anfragen hinweg beibehalten müssen. Aber das ist nicht üblich.

Was kann durch Anfragen unverändert beibehalten werden? Was wir ein persistentes

Objekt nennen. Nochmals: Das ist keine alltägliche Situation. Beispielsweise ändert sich der aktuelle PHP-Ausführungspfad zwischen Anfragen nicht. Seine Informationen werden dauerhaft zugewiesen, was bedeutet, dass herkömmliche libcs ​​

aufgerufen werden, um sie zuzuweisen.

Was noch? etwas Schnur. Beispielsweise wird die Zeichenfolge "_SERVER" zwischen Anfragen wiederverwendet, da für jede Anfrage ein malloc () PHP-Array erstellt wird. Daher kann die Zeichenfolge

"_SERVER"

selbst dauerhaft zugewiesen werden, da sie nur einmal zugewiesen wird. Sie müssen Folgendes beachten:$_SERVER

Beim Schreiben von PHP-Kern oder -Erweiterungen gibt es zwei Möglichkeiten der dynamischen Speicherzuweisung:

    Anforderungsbindung bestimmt dynamische Zuordnung.
  • Permanente dynamische Zuordnung.

    • Anfragegebundene dynamische Speicherzuweisung
  • wird nur durchgeführt, wenn PHP die Anfrage verarbeitet (nicht vorher oder nachher).
  • Sollte nur mit der dynamischen Speicherzuweisungs-API von ZendMM durchgeführt werden.

      ist im Erweiterungsdesign sehr verbreitet, grundsätzlich sind 95 % der dynamischen Zuweisungen anforderungsgebunden.
    • Wird von ZendMM verfolgt und benachrichtigt Sie über Lecks.
    • Permanente dynamische Speicherzuweisung
  • sollte nicht durchgeführt werden, während PHP die Anfrage verarbeitet (dies ist nicht verboten, aber eine schlechte Idee) .
  • Wird von ZendMM nicht verfolgt und Sie werden nicht über Lecks benachrichtigt.

      sollte in Erweiterungen selten sein.
    • Bedenken Sie außerdem, dass der gesamte PHP-Quellcode auf dieser Speicherebene basiert. Daher werden viele interne Strukturen mithilfe des Zend-Speichermanagers zugewiesen. Die meisten rufen eine „persistente“ API auf, deren Aufruf zu einer herkömmlichen libc-Zuweisung führt.
    Dies ist eine anforderungsgebundene Zuweisung zend_string:
  • zend_string *foo = zend_string_init("foo", strlen("foo"), 0);
Dies ist eine dauerhafte Zuweisung:

zend_string *foo = zend_string_init("foo", strlen("foo"), 1);

Die gleiche HashTable.

Anforderungsgebundene Zuweisung:

zend_array ar;
zend_hash_init(&ar, 8, NULL, NULL, 0);

Persistente Zuweisung:

zend_array ar;
zend_hash_init(&ar, 8, NULL, NULL, 1);

Es ist bei allen verschiedenen Zend-APIs immer das Gleiche. Normalerweise als letzter Parameter übergeben, bedeutet
„0“

„Ich möchte diese Struktur mit ZendMM zuordnen, also eine Bindung anfordern“, oder

„1“

bedeutet „Ich möchte ZendMM verwenden.“ um diese Struktur zuzuweisen, also wird die Bindung angefordert“ oder

„1“ bedeutet „Ich möchte ZendMM verwenden, um diese Struktur zuzuweisen, also wird die Bindung angefordert“ Rufen Sie traditionelle libcs ​​ auf, um diese Struktur zuzuweisen ". Anscheinend stellen diese Strukturen eine API bereit, die sich merkt, wie sie die Struktur zugewiesen hat, sodass bei ihrer Zerstörung die richtige Freigabefunktion verwendet wird. Also in Code wie diesem: malloc()

zend_string_release(foo);
zend_hash_destroy(&ar);

API weiß, ob diese Strukturen mithilfe der Anforderungsbindung zugewiesen oder dauerhaft zugewiesen werden. Im ersten Fall wird

verwendet, um sie freizugeben, im zweiten Fall libcs ​​

.

Zend Memory Manager API

Die API befindet sich in Zend/zend_alloc.h

Die API besteht hauptsächlich aus C-Makros, nicht aus Funktionen, wenn Sie sie also debuggen und ihre Funktionsweise verstehen möchten Prinzip: Bitte seien Sie vorbereitet. Diese APIs kopieren die Funktionen von libc, oft mit einem „e“ an den Funktionsnamen. Sie sollten daher nicht den Fehler machen, es gibt nicht viele Details über die API.

Grundsätzlich werden Sie am häufigsten emalloc(size_t) und efree(void *) verwenden.

bietet auch ecalloc(size_t nmemb,size_t size), das ein einzelnes size der Größe nmemb zuweist und die Region auf Null setzt. Wenn Sie ein erfahrener C-Programmierer sind, sollten Sie wissen, dass es nach Möglichkeit besser ist, emalloc() anstelle von ecalloc() zu verwenden, da ecalloc() den Speicherbereich auf Null setzt, was für die Erkennung von Zeigerfehlern wichtig ist und möglicherweise von großem Nutzen ist helfen. Beachten Sie, dass emalloc() grundsätzlich genauso funktioniert wie libc malloc(): Es sucht in verschiedenen Pools nach einem ausreichend großen Bereich und stellt Ihnen den am besten geeigneten Platz zur Verfügung. Daher kann es sein, dass Sie einen durch Garbage Collection gesammelten Zeiger erhalten.

und dann safe_emalloc(size_t nmemb,size_t size,size_t offset), was emalloc(size * nmemb + offset) ist, aber es prüft für Sie, ob ein Überlauf vorliegt. Dieser API-Aufruf sollte verwendet werden, wenn die anzugebende Nummer von einer nicht vertrauenswürdigen Quelle (z. B. Userland) stammt.

In Bezug auf Zeichenfolgen ermöglichen estrdup(char *) und estrndup(char *, size_t len) das Kopieren von Zeichenfolgen oder binären Zeichenfolgen.

Egal was passiert, der von ZendMM zurückgegebene Zeiger muss durch den Aufruf von ZendMMs efree() freigegeben werden, und ist nicht free() von libc.

Hinweis

zur dauerhaften Zuordnung. Dauerhafte Zuweisungen bleiben zwischen Anfragen gültig. Normalerweise verwenden Sie dazu die übliche libc malloc/ free, aber ZendMM hat einige Abkürzungen für den libc-Allokator: die „persistente“ API. Die API beginnt mit „p“ -Buchstaben und lässt Sie zwischen ZendMM-Zuweisung und dauerhafter Zuweisung wählen. Daher ist pemalloc(size_t, 1) nur malloc(), pefree(void *, 1) ist free() und pestrdup(void *, 1) ist strdup(). Ich sage es nur.

Zend Memory Manager Debug Shield

ZendMM bietet die folgenden Funktionen:

  • Speicherverbrauchsverwaltung.
  • Speicherleckverfolgung und automatische Freigabe.
  • Beschleunigen Sie Zuweisungen, indem Sie Puffer bekannter Größe vorab zuweisen und Hot-Caches im Leerlauf halten.

Speicherverbrauchsverwaltung

ZendMM ist ein PHP-Benutzerland, das dem „ „memory_limit“-Funktion. Jedes einzelne Byte, das mithilfe der ZendMM-Schicht zugewiesen wird, wird gezählt und summiert. Sie wissen, was passiert, wenn das memory_limit der INI erreicht ist. Dies bedeutet auch, dass alle über ZendMM durchgeführten Zuweisungen in memory_get_usage() im PHP-Benutzerland widergespiegelt werden.

Als Erweiterungsentwickler ist das eine gute Sache, denn so behalten Sie den Überblick über die Heap-Größe Ihres PHP-Prozesses.

Wenn ein Speicherlimitfehler ausgelöst wird, wird die Engine von der aktuellen Codeposition in den Capture-Block freigegeben und dann ordnungsgemäß beendet. Es ist jedoch nicht möglich, zu einem Codespeicherort zurückzukehren, der das Limit überschreitet. Darauf muss man vorbereitet sein.

Theoretisch bedeutet dies, dass ZendMM Ihnen keinen NULL-Zeiger zurückgeben kann. Wenn die Zuweisung durch das Betriebssystem fehlschlägt oder die Zuweisung einen Speicherlimitfehler erzeugt, stößt der Code auf einen Catch-Block und kehrt nicht zu Ihrem Zuweisungsaufruf zurück.

Wenn Sie diesen Schutz aus irgendeinem Grund umgehen müssen, müssen Sie herkömmliche libc-Aufrufe wie malloc() verwenden. Seien Sie auf jeden Fall vorsichtig und wissen Sie, was Sie tun. Wenn Sie ZendMM verwenden, müssen Sie möglicherweise viel Speicher zuweisen und überschreiten möglicherweise PHPs memory_limit. Verwenden Sie also einen anderen Allokator (wie libc), aber seien Sie vorsichtig: Ihre Erweiterung erhöht die aktuelle Größe des Prozess-Heapspeichers. memory_get_usage() kann in PHP nicht gesehen werden, aber der aktuelle Heap kann mithilfe von Betriebssystemfunktionen analysiert werden (z. B. /proc/{pid}/maps)

Hinweis

Wenn Sie ZendMM vollständig deaktivieren müssen, können Sie PHP mit der Umgebungsvariablen USE_ZEND_ALLOC = 0 starten. Auf diese Weise wird jeder Aufruf der ZendMM-API (z. B. emalloc()) an den libc-Aufruf weitergeleitet und ZendMM wird deaktiviert. Dies ist besonders nützlich beim Debuggen des Speichers.

Memory Leak Tracing

Bedenken Sie die Hauptregel von ZendMM: Es startet, wenn die Anfrage startet, und erwartet dann, dass Sie seine API aufrufen, wenn Sie dynamischen Speicher benötigen, um die Anfrage zu bearbeiten. Wenn die aktuelle Anfrage endet, wird ZendMM heruntergefahren.

Durch das Schließen werden alle Live-Zeiger durchsucht und bei Verwendung des PHP-Debug-Builds werden Sie vor Speicherlecks gewarnt.

Lass es uns etwas klarer erklären: Wenn ZendMM am Ende der aktuellen Anfrage einige aktive Speicherblöcke findet, bedeutet das, dass diese Speicherblöcke lecken. Am Ende der Anfrage sollten keine aktiven Speicherblöcke auf dem ZendMM-Heap vorhanden sein, da derjenige, der Speicher zugewiesen hat, ihn hätte freigeben müssen.

如果您忘记释放块,它们将全部显示在 stderr上。此内存泄漏报告进程仅在以下情况下有效:

  • 你正在使用 PHP 的调试构建
  • 在 php.ini 中具有 report_memleaks = On(默认)

这是一个简单泄漏到扩展中的示例:

PHP_RINIT_FUNCTION(example)
{
    void *foo = emalloc(128);
}

在启动该扩展的情况下启动 PHP,在调试版本上会在 stderr 上生成:

[Fri Jun 9 16:04:59 2017]  Script:  '/tmp/foobar.php'
/path/to/extension/file.c(123) : Freeing 0x00007fffeee65000 (128 bytes), script=/tmp/foobar.php
=== Total 1 memory leaks detected ===

当 Zend 内存管理器关闭时,在每个已处理请求的末尾,将生成这些行。

但是要当心:

  • 显然,ZendMM 对持久分配或以不同于使用持久分配的方式执行的分配一无所知。因此,ZendMM 只能警告你有关它知道的分配信息,在这里不会报告每个传统的 libc 分配信息。
  • 如果 PHP 以错误的方式关闭(我们称之为不正常关闭),ZendMM 将报告大量泄漏。这是因为引擎在错误关闭时会使用longjmp()调用 catch 块,防止清理所有内存的代码运行。因此,许多泄漏得到报告。尤其是在调用 PHP 的 exit()/ die()之后,或者在 PHP 的某些关键部分触发了致命错误时,就会发生这种情况。
  • 如果你使用非调试版本的 PHP,则 stderr 上不会显示任何内容,ZendMM 是愚蠢的,但仍会清除程序员尚未明确释放的所有分配的请求绑定缓冲区

你必须记住的是 ZendMM 泄漏跟踪是一个不错的奖励工具,但它不能代替真正的 C 内存调试器。

ZendMM 内部设计

常见错误和错误

这是使用 ZendMM 时最常见的错误,以及你应该怎么做。

  1. 不处理请求时使用 ZendMM。

获取有关 PHP 生命周期的信息,以了解在扩展中何时处理请求,何时不处理。如果在请求范围之外使用 ZendMM(例如在MINIT()中),在处理第一个请求之前,ZendMM 会静默清除分配,并且可能会使用after-after-free:根本没有。

  1. 缓冲区上溢和下溢。

使用内存调试器。如果你在 ZendMM 返回的内存区域以下或过去写入内容,则将覆盖关键的 ZendMM 结构并触发崩溃。如果 ZendMM 能够为你检测到混乱,则可能会显示“zend_mm_heap损坏”的消息。堆栈追踪将显示从某些代码到某些 ZendMM 代码的崩溃。ZendMM 代码不会自行崩溃。如果你在 ZendMM 代码中间崩溃,那很可能意味着你在某个地方弄乱了指针。插入你喜欢的内存调试器,查找有罪的部分并进行修复。

  1. 混合 API 调用

如果分配一个 ZendMM 指针(即emalloc())并使用 libc 释放它(free()),或相反的情况:你将崩溃。要严谨对待。另外,如果你将其不知道的任何指针传递给 ZendMM 的efree():将会崩溃。

Das obige ist der detaillierte Inhalt vonZend-Speichermanager für PHP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:learnku.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen