Heim  >  Artikel  >  System-Tutorial  >  Erfahren Sie in einem Artikel mehr über die Speicherzuweisungsstrategie von Linux

Erfahren Sie in einem Artikel mehr über die Speicherzuweisungsstrategie von Linux

王林
王林nach vorne
2024-02-12 11:57:02900Durchsuche

Wie sieht die Speicherverteilung eines Linux-Prozesses aus?

Im Linux-Betriebssystem ist das Innere des virtuellen Adressraums in zwei Teile unterteilt: Kernelraum und Benutzerraum Systeme mit unterschiedlichen Ziffern haben unterschiedliche Adressraumbereiche. Die gängigsten 32-Bit- und 64-Bit-Systeme sind beispielsweise die folgenden:

一文读懂 Linux 内存分配策略

Sie können es hier sehen:

  • Der Kernel-Speicherplatz eines 32-Bit-Systems belegt 1 GB, also ganz oben, und die restlichen 3 GB sind Benutzerspeicherplatz
  • Der Kernelraum und der Benutzerraum von 64-Bit-Systemen sind beide 128T groß und belegen den höchsten bzw. niedrigsten Teil des gesamten Speicherraums, und der verbleibende mittlere Teil ist undefiniert.

Lassen Sie uns über den Unterschied zwischen Kernel-Space und User-Space sprechen:

  • Wenn sich der Prozess im Benutzermodus befindet, kann er nur auf den Benutzerspeicher zugreifen
  • Erst nach Eingabe des Kernel-Status können Sie auf den Speicher im Kernel-Bereich zugreifen

Obwohl jeder Prozess seinen eigenen unabhängigen virtuellen Speicher hat, Die Kerneladresse in jedem virtuellen Speicher ist tatsächlich demselben physischen Speicher zugeordnet. Auf diese Weise kann der Prozess nach dem Wechsel in den Kernel-Status problemlos auf den Kernel-Speicher zugreifen.

一文读懂 Linux 内存分配策略

Als nächstes erfahren wir mehr über die Aufteilung des virtuellen Raums und des Kernelraums. Über die Verteilung des Kernelraums werde ich nicht viel sagen.

Werfen wir einen Blick auf die Verteilung des Benutzerraums. Am Beispiel eines 32-Bit-Systems habe ich ein Bild gezeichnet, um ihre Beziehung zu zeigen:

Auf diesem Bild können Sie sehen, dass der Benutzerspeicher in 6 verschiedene Speichersegmente von niedrig bis hoch unterteilt ist:

一文读懂 Linux 内存分配策略
  • Programmdateisegment, einschließlich binärem ausführbarem Code
  • Initialisiertes Datensegment, einschließlich statischer Konstanten
  • Nicht initialisierte Datensegmente, einschließlich nicht initialisierter statischer Variablen
  • Heap-Segmente, einschließlich dynamisch zugewiesenem Speicher, beginnen bei niedrigen Adressen und wachsen nach oben
  • Dateizuordnungssegmente, einschließlich dynamischer Bibliotheken, gemeinsam genutzter Speicher usw., beginnen bei niedrigen Adressen und wachsen nach oben (abhängig von der Hardware- und Kernel-Version);
  • Stapelsegment, einschließlich lokaler Variablen und Funktionsaufrufkontext usw. Die Stapelgröße ist fest und beträgt normalerweise 8 MB. Natürlich stellt das System auch Parameter bereit, damit wir die Größe anpassen können

Unter diesen 6 Speichersegmenten wird der Speicher der Heap- und Dateizuordnungssegmente dynamisch zugewiesen. Mit malloc() oder mmap() der C-Standardbibliothek können Sie beispielsweise Speicher im Heap- bzw. Dateizuordnungssegment dynamisch zuweisen.

Wie reserviert malloc Speicher?

Tatsächlich ist malloc() kein Systemaufruf, sondern eine Funktion in der C-Bibliothek, die zum dynamischen Zuweisen von Speicher verwendet wird.

Wenn malloc Speicher beantragt, gibt es zwei Möglichkeiten, Heap-Speicher vom Betriebssystem zu beantragen.

  • Methode 1: Reservieren Sie Speicher vom Heap über den Systemaufruf brk()
  • Methode 2: Weisen Sie Speicher im Dateizuordnungsbereich über den Systemaufruf mmap() zu
  • Die Implementierung der ersten Methode ist sehr einfach. Sie besteht darin, die Funktion brk() zu verwenden, um den Zeiger „Top of Heap“ auf eine hohe Adresse zu verschieben, um neuen Speicherplatz zu erhalten. Wie unten gezeigt:

Methode 2 verwendet die Methode „Private Anonymous Mapping“ im Systemaufruf mmap (), um einen Teil des Speichers im Dateizuordnungsbereich zuzuweisen, was bedeutet, dass ein Teil des Speichers aus dem Dateizuordnungsbereich „gestohlen“ wird. Wie unten gezeigt: 一文读懂 Linux 内存分配策略一文读懂 Linux 内存分配策略

Unter welchen Umständen reserviert malloc() Speicher über brk()? In welchem ​​Szenario wird Speicher über mmap() zugewiesen?

malloc() hat einen standardmäßig im Quellcode definierten Schwellenwert:

  • Wenn der vom Benutzer zugewiesene Speicher weniger als 128 KB beträgt, beantragen Sie Speicher über brk(); Wenn der vom Benutzer zugewiesene Speicher größer als 128 KB ist, beantragen Sie Speicher über mmap();
  • Beachten Sie, dass verschiedene Glibc-Versionen unterschiedliche Schwellenwerte definieren.
  • malloc() reserviert physischen Speicher?

Nein,

malloc() reserviert virtuellen Speicher

.

Wenn auf den zugewiesenen virtuellen Speicher nicht zugegriffen wird, wird der virtuelle Speicher nicht dem physischen Speicher zugeordnet, sodass er keinen physischen Speicher belegt. Nur beim Zugriff auf den zugewiesenen virtuellen Adressraum durchsucht das Betriebssystem die Seitentabelle und stellt fest, dass sich die dem virtuellen Speicher entsprechende Seite nicht im physischen Speicher befindet. Es löst einen Seitenfehler-Interrupt aus und erstellt dann einen Verbindung zwischen dem virtuellen Speicher und der physischen Speicherzuordnungsbeziehung zwischen.

Wie viel virtuellen Speicher wird malloc(1) zuweisen?

Wenn

malloc() Speicher zuweist, wird der Speicherplatz nicht entsprechend der vom Benutzer erwarteten Anzahl von Bytes zugewiesen, sondern wird vorab einen größeren Speicherplatz als Speicherpool zugewiesen.

Die spezifische Menge an Speicherplatz, die vorab zugewiesen wird, hängt vom von malloc verwendeten Speichermanager ab. Zur Analyse verwenden wir den Standardspeichermanager von malloc (Ptmalloc2). Als nächstes führen wir ein Experiment durch und verwenden den folgenden Code, um zu sehen, wie viel Speicherplatz tatsächlich vom Betriebssystem zugewiesen wird, wenn über malloc 1 Byte Speicher beantragt wird.

#include 
#include 

int main() {
  printf("使用cat /proc/%d/maps查看内存分配\n",getpid());
  
  //申请1字节的内存
  void *addr = malloc(1);
  printf("此1字节的内存起始地址:%x\n", addr);
  printf("使用cat /proc/%d/maps查看内存分配\n",getpid());
 
  //将程序阻塞,当输入任意字符时才往下执行
  getchar();

  //释放内存
  free(addr);
  printf("释放了1字节的内存,但heap堆并不会释放\n");
  
  getchar();
  return 0;
}

Führen Sie den Code aus (

Lassen Sie mich im Voraus erklären, die Version der Glibc-Bibliothek, die ich verwende, ist 2.17

):

Wir können die Speicherverteilung des Prozesses über die Datei /proc//maps anzeigen. Ich filtere den Bereich der Speicheradressen in der Maps-Datei anhand dieser 1-Byte-Speicherstartadresse heraus.

[root@xiaolin ~]# cat /proc/3191/maps | grep d730
00d73000-00d94000 rw-p 00000000 00:00 0                                  [heap]

Der in diesem Beispiel zugewiesene Speicher beträgt weniger als 128 KB, daher wird der Speicher über den Systemaufruf brk() auf den Heap-Speicherplatz angewendet, sodass Sie die Markierung [heap] ganz rechts sehen können. 一文读懂 Linux 内存分配策略

Sie können sehen, dass der Speicheradressbereich des Heap-Speichers 00d73000-00d94000 beträgt und die Größe dieses Bereichs 132 KB beträgt, was bedeutet, dass

malloc(1) tatsächlich 132 KB Speicher vorab zuweist

.

Einige Schüler haben vielleicht bemerkt, dass die Startadresse des im Programm gedruckten Speichers d73010 ist, während die Kartendatei zeigt, dass die Startadresse des Heap-Speicherplatzes d73000 ist. Warum gibt es eine zusätzliche 0x10 (16 Bytes)? Lassen wir diese Frage zunächst beiseite und sprechen später darüber. #free Wird freigegebener Speicher an das Betriebssystem zurückgegeben?

Führen wir den obigen Prozess aus, um zu sehen, ob der Heap-Speicher noch vorhanden ist, nachdem der Speicher über die Funktion free() freigegeben wurde.

Wie Sie auf dem Bild unten sehen können, ist der Heap-Speicher nach dem Freigeben des Speichers noch vorhanden und wurde nicht an das Betriebssystem zurückgegeben.

一文读懂 Linux 内存分配策略Das liegt daran, dass es besser ist, dieses 1 Byte an das Betriebssystem weiterzugeben, es zwischenzuspeichern und in den Speicherpool von malloc zu legen. Wenn der Prozess erneut 1 Byte Speicher beansprucht, kann es direkt wiederverwendet werden ist viel schneller.

Wenn der Prozess beendet wird, beansprucht das Betriebssystem natürlich alle Ressourcen des Prozesses zurück. 一文读懂 Linux 内存分配策略

Der Heap-Speicher ist weiterhin vorhanden, nachdem der oben erwähnte freie Speicher für den von malloc über brk() angewendeten Speicher gilt.

Wenn malloc über mmap Speicher anfordert, wird dieser an das Betriebssystem zurückgegeben, nachdem der Speicher freigegeben wurde.

Lassen Sie uns ein Experiment durchführen, um zu überprüfen, ob wir 128 KB Speicher über malloc beantragen, sodass malloc Speicher über mmap zuweist.

#include 
#include 

int main() {
  //申请1字节的内存
  void *addr = malloc(128*1024);
  printf("此128KB字节的内存起始地址:%x\n", addr);
  printf("使用cat /proc/%d/maps查看内存分配\n",getpid());

  //将程序阻塞,当输入任意字符时才往下执行
  getchar();

  //释放内存
  free(addr);
  printf("释放了128KB字节的内存,内存也归还给了操作系统\n");

  getchar();
  return 0;
}

Ausführungscode:

一文读懂 Linux 内存分配策略

Wenn Sie sich die Speicherverteilung des Prozesses ansehen, können Sie feststellen, dass ganz rechts keine [Kopf]-Markierung vorhanden ist, was darauf hinweist, dass der anonyme Speicher aus dem Dateizuordnungsbereich über mmap durch anonyme Zuordnung zugewiesen wird.

一文读懂 Linux 内存分配策略

Dann lasst uns diese Erinnerung freigeben und sehen:

一文读懂 Linux 内存分配策略

Überprüfen Sie erneut die Startadresse des 128-KB-Speichers. Sie können feststellen, dass dieser nicht mehr vorhanden ist, was darauf hinweist, dass er an das Betriebssystem zurückgegeben wurde.

一文读懂 Linux 内存分配策略

Zur Frage „Wird der von malloc beantragte und von free freigegebene Speicher an das Betriebssystem zurückgegeben?“ können wir zusammenfassen:

  • Der von malloc über brk() angewendete Speicher gibt den Speicher nicht an das Betriebssystem zurück, sondern wird für die nächste Verwendung im Speicherpool von malloc zwischengespeichert
  • Wenn free den von malloc zugewiesenen Speicher über
  • mmap() freigibt, gibt den Speicher an das Betriebssystem zurück und der Speicher wird tatsächlich freigegeben.

Warum verwenden nicht alle mmap, um Speicher zuzuweisen?

Da die Beantragung von Speicher vom Betriebssystem einen Systemaufruf erfordert, müssen Sie in den Kernel-Status wechseln und dann in den Benutzerstatus zurückkehren.

Daher sollten bei der Beantragung von Speicher häufige Systemaufrufe vermieden werden. Wenn mmap zum Zuweisen von Speicher verwendet wird, bedeutet dies, dass jedes Mal Systemaufrufe ausgeführt werden müssen.

Da außerdem der von mmap zugewiesene Speicher bei jeder Freigabe an das Betriebssystem zurückgegeben wird, befindet sich die von mmap zugewiesene virtuelle Adresse jedes Mal in einem Seitenfehlerzustand, und zwar auch dann, wenn zum ersten Mal auf die virtuelle Adresse zugegriffen wird Zu diesem Zeitpunkt wird ein Seitenfehler-Interrupt ausgelöst.

Mit anderen Worten:

Wenn Speicher häufig über mmap zugewiesen wird, wird nicht nur jedes Mal der Betriebsstatus geändert, sondern es kommt auch zu einem Seitenfehler-Interrupt (nach dem ersten Zugriff auf die virtuelle Adresse), der zu einem hohen CPU-Verbrauch führt .

Um diese beiden Probleme zu verbessern, weist malloc beim Freigeben des Speichers direkt größeren Speicher als Speicherpool zu, wenn er über den Systemaufruf brk () Speicher im Heap-Speicherplatz beantragt , Im Speicherpool zwischengespeichert.

Wenn Sie das nächste Mal Speicher beantragen, entnehmen Sie einfach den entsprechenden Speicherblock direkt aus dem Speicherpool. Die Zuordnungsbeziehung zwischen der virtuellen Adresse und der physischen Adresse dieses Speicherblocks besteht möglicherweise weiterhin. Dadurch wird nicht nur die Anzahl reduziert Systemaufrufe, Es reduziert auch die Anzahl der Seitenfehler-Interrupts, was den CPU-Verbrauch erheblich reduziert.

Da brk so großartig ist, warum nicht brk für alle Zuweisungen verwenden?

Wir haben bereits erwähnt, dass der über brk vom Heap-Speicher zugewiesene Speicher nicht an das Betriebssystem zurückgegeben wird. Betrachten wir also ein solches Szenario.

Wenn wir nacheinander drei Speicherstücke mit 10 KB, 20 KB und 30 KB beantragen, wenn 10 KB und 20 KB freigegeben werden und zu freiem Speicherplatz werden, und wenn der für das nächste Mal beantragte Speicher weniger als 30 KB beträgt, kann dieser freie Speicherplatz wiederverwendet werden .

一文读懂 Linux 内存分配策略Aber wenn der beim nächsten Mal angeforderte Speicher größer als 30 KB ist, ist kein freier Speicherplatz verfügbar und Sie müssen einen Antrag auf das Betriebssystem stellen, und der tatsächlich verwendete Speicher erhöht sich weiter.

Da das System häufig Mallocs und Freigaben durchführt, insbesondere bei kleinen Speicherblöcken, werden immer mehr unbrauchbare Fragmente im Heap generiert, was zu „Speicherlecks“ führt. Dieses „Leckage“-Phänomen kann mit Valgrind nicht erkannt werden.

Daher werden bei der Implementierung von malloc die Unterschiede, Vor- und Nachteile im Verhalten von brk und mmap vollständig berücksichtigt und standardmäßig ein großer Speicherblock (128 KB) zugewiesen, bevor mmap zum Zuweisen von Speicherplatz verwendet wird.

Die Funktion free() übergibt nur eine Speicheradresse. Warum kann sie wissen, wie viel Speicher freigegeben werden muss?

Erinnern Sie sich, ich habe bereits erwähnt, dass die Startadresse des von malloc an den Benutzermodus zurückgegebenen Speichers 16 Byte größer ist als die Startadresse des Heap-Speicherplatzes des Prozesses?

In den zusätzlichen 16 Bytes werden die Beschreibungsinformationen des Speicherblocks gespeichert, beispielsweise die Größe des Speicherblocks.

一文读懂 Linux 内存分配策略

Auf diese Weise versetzt free beim Ausführen der Funktion free () die eingehende Speicheradresse um 16 Bytes nach links und analysiert dann die Größe des aktuellen Speicherblocks aus diesen 16 Bytes und weiß natürlich, wie groß er ist Der Speicher muss freigegeben werden.

Das obige ist der detaillierte Inhalt vonErfahren Sie in einem Artikel mehr über die Speicherzuweisungsstrategie von Linux. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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