Hinweise zur Speicherverwaltung von Linux-Kernel-Gerätetreibern

Hinweise zur Speicherverwaltung von Linux-Kernel-Gerätetreibern
/**********************
 * Linux-Speicherverwaltung **************************/

Die Speicherverwaltung ist die mit Abstand komplexeste Aktivität im Unix-Kernel. Wir stellen kurz die Speicherverwaltung vor und veranschaulichen anhand von Beispielen, wie man im Kernelmodus Speicher erhält.

(1) Verschiedene Adressen

Bei x86-Prozessoren muss zwischen folgenden drei Adresstypen unterschieden werden:

*Logische Adresse

Es wird nur x86 unterstützt. Jede logische Adresse besteht aus einem Segment und einem Offset, der die Entfernung vom Segmentanfang bis zur eigentlichen Adresse angibt.

Die logische Adresse beträgt 48 Bit, der Segmentselektor 16 Bit und der Offset 32 ​​Bit. Linux bietet nur eingeschränkte Unterstützung für logische Adressen

*Lineare Adresse

Wird auch als virtuelle Adresse bezeichnet.

Vorzeichenloser 32-Bit-Ganzzahlwert von 0x0000,0000 bis 0xffff,ffff, ein Gesamtadressbereich von 4 GB. Unabhängig davon, ob es sich um eine Anwendung oder einen Treiber handelt, sind die Adressen, die wir im Programm verwenden, virtuelle Adressen.

* Physische Adresse

Eine vorzeichenlose 32-Bit-Ganzzahl, die den elektrischen Signalen entspricht, die von den Adresspins der CPU an den Speicherbus gesendet werden. Wird zur Speicheradressierung verwendet.

Suchen Sie ein Programm wie scanf.c, führen Sie es zweimal aus und befolgen Sie dann die folgenden Anweisungen zur Beobachtung:

$>pmap $(pid)
$>cat /proc/$(pid)/maps

(2) Physischer Speicher und virtueller Speicher

a. Physischer Speicher

Es handelt sich um den RAM, der tatsächlich im System vorhanden ist, wie z. B. der 256 MB RAM, von dem wir oft sprechen. Der x86-Prozessor und der physische Speicher sind über tatsächliche physische Leitungen verbunden.

Darüber hinaus ist der x86-Prozessor über die Hauptplatine mit vielen Peripheriegeräten verbunden, und diese Peripheriegeräte sind wiederum über tatsächliche physische Leitungen mit dem Prozessor verbunden.

Für den Prozessor ist die Zugriffsmethode für die meisten Peripheriegeräte und den RAM dieselbe, d. h. das Programm gibt eine physische Adresse aus, um auf das tatsächliche physische Gerät zuzugreifen.

Peripheriegeräte und RAM teilen sich einen physischen 4G-Speicherplatz.

b. Virtueller Speicher

Es handelt sich um einen logischen Speicher, der für jeden Prozess über dem physischen Speicher zwischen der Speicheranforderung der Anwendung und der Hardware-Speicherverwaltungseinheit (MMU) erstellt wird. Die MMU konvertiert den von der Anwendung verwendeten virtuellen Speicher gemäß einer vordefinierten Seitentabelle in physische Adressen und greift dann über die physischen Adressen auf die tatsächlichen Peripheriegeräte oder den RAM zu.

Virtueller Speicher hat viele Einsatzmöglichkeiten und Vorteile:

  • *Mehrere Prozesse können gleichzeitig ausgeführt werden
  • * Die Anwendung kann auch dann ausgeführt werden, wenn der erforderliche Speicher größer ist als der physische Speicher
  • * Ein Prozess kann ein Programm ausführen, wenn nur ein Teil seines Codes in den Speicher geladen wird
  • * Erlauben Sie jedem Prozess, auf eine Teilmenge des verfügbaren physischen Speichers zuzugreifen
  • *Prozesse können ein einzelnes Speicherabbild einer Bibliotheksfunktion oder eines Programms gemeinsam nutzen
  • * Das Programm ist verschiebbar, d. h. das Programm kann an einer beliebigen Stelle im physischen Speicher abgelegt werden
  • * Programmierer können maschinenunabhängigen Code schreiben, ohne sich um die Organisation des physischen Speichers kümmern zu müssen

(3) RAM-Nutzung

Linux teilt den eigentlichen physischen RAM in zwei Teile. Mehrere Megabyte werden zum Speichern des Kernel-Images verwendet (also des Kernel-Codes und der statischen Kernel-Datenstrukturen). Der restliche RAM wird normalerweise vom virtuellen Speichersystem verwaltet und auf die folgenden drei Arten verwendet:

  • * Erfüllen Sie Kernelanforderungen für Caches, Deskriptoren und andere dynamische Kernel-Datenstrukturen
  • * Erfüllen Sie die Anforderungen des Prozesses hinsichtlich allgemeiner Speicherbereiche und Dateispeicherzuordnungen
  • * Erzielen Sie mithilfe des Cache eine bessere Leistung von Festplatten und anderen Puffergeräten

Eines der Hauptprobleme, mit denen sich der virtuelle Speicher befassen muss, ist die Speicherfragmentierung, da der Kernel normalerweise zusammenhängenden physischen Speicher verwendet und eine zu starke Fragmentierung daher zum Fehlschlagen von Anforderungen führen kann.

/**********************
 * Speicher im Kernel abrufen************************/

Wie im Userspace kann Speicher im Kernel dynamisch zugewiesen und freigegeben werden, allerdings unterliegt er stärkeren Einschränkungen als im Userspace.

(1) Speicherverwaltung im Kernel

Der Kernel verwendet physische Seiten als Grundeinheit der Speicherverwaltung. Dies liegt hauptsächlich daran, dass die Speicherverwaltungseinheit (MMU) virtuelle Adressen und physische Adressen in Seiten umwandelt. Aus der Perspektive des virtuellen Speichers ist eine Seite die kleinste Einheit. Die meisten 32-Bit-Architekturen unterstützen 4-KB-Seiten.

a. Seite

Der Kernel verwendet die Strukturseite, um jede physische Seite im System darzustellen.

Durch das Einbinden von <linux/mm.h> können Sie die Seite verwenden, die eigentlich in <linux/mm_types.h> definiert ist.

Strukturseite{
 page_flags_t-Flags;
 atomare_t_Anzahl;
 Atom_t_Mapanzahl;
 unsigniert, lange privat;
 Struktur Adressraum *Mapping;
 pgoff_t-Index;
 Struktur list_head lru;
 ungültig *virtuell;
};

Flags werden verwendet, um den Status der Seite zu speichern, der in <linux/page-flags.h> definiert ist. Der Status umfasst, ob die Seite schmutzig ist, ob sie im Speicher gesperrt ist usw. _count speichert die Referenzanzahl der Seite.

Die Seitenstruktur bezieht sich auf physische Seiten, nicht auf virtuelle Seiten. Der Zweck der Struktur besteht darin, den physischen Speicher selbst zu beschreiben, nicht die darin enthaltenen Daten.

Der Kernel verwaltet alle Seiten im System basierend auf der Seitenstruktur. Über die Seite kann der Kernel erkennen, ob eine Seite frei ist (d. h. ob die Seite zugewiesen wurde).

Wenn die Seite zugewiesen wurde, muss der Kernel auch wissen, wem die Seite gehört.

Der Besitzer kann ein Userspace-Prozess, dynamisch zugewiesene Kerneldaten, statischer Kernelcode oder der Seitencache usw. sein.

Eine solche Struktur wird jeder physischen Seite im System zugewiesen. Wenn die Struktur 40 Byte groß ist, muss 1 MB von 128 MB physischem Speicher (4-KB-Seiten) für die Seitenstruktur zugewiesen werden.

b. Fläche

Aufgrund von Hardwarebeschränkungen kann der Kernel nicht alle Seiten gleich behandeln. Der Kernel verwendet Zonen, um Seiten mit ähnlichen Eigenschaften zu gruppieren. Zu diesen Funktionen gehören:

  • *Einige Hardware kann DMA nur mit bestimmten Speicheradressen durchführen
  • *Einige Architekturen verfügen über einen Speicheradressbereich, der viel größer ist als der virtuelle Adressbereich, sodass ein Teil des Speichers nicht dauerhaft in den Kernelspeicher abgebildet werden kann.

Um diese Einschränkungen zu beheben, verwendet Linux drei Zonen (<linux/mmzone.h>):

  • ZONE_DMA: Diese Zone enthält Seiten, die DMA-Operationen durchführen können
  • ZONE_NORMAL: Diese Zone enthält Seiten, die normal zugeordnet werden können.
  • ZONE_HIGHMEM: Diese Zone enthält High-End-Speicher (größer als 896 MB), dessen Seiten nicht dauerhaft in den Adressraum des Kernels abgebildet werden können.

Für x86 entspricht der physische Speicher diesen drei Bereichen:

  • ZONE_DMA: <16 MB
  • ZONE_NORMAL: 16~896 MB
  • ZONE_HIGHMEM: >896 MB

Siehe Strukturzone in <linux/mmzone.h>.

Es gibt im System nur drei solcher Zonenstrukturen.

(2) Seitenzuordnung

Der Kernel verwendet Seiten zur Speicherverwaltung, daher können wir das System auch auffordern, uns Speicher in Seiten im Kernel zuzuweisen. Natürlich kann die seitenweise Zuweisung zu Speicherverschwendung führen, daher rufen wir sie nur auf, wenn wir sicher sind, dass wir eine ganze Speicherseite benötigen.

a. Zuordnung

#include <linux/gfp.h>
1. Strukturseite * alloc_pages(
    vorzeichenlose int gfp_mask, 
    vorzeichenlose int-Reihenfolge);
//2 aufeinanderfolgende physische Seiten zuordnen.
2. void *Seitenadresse(
    Strukturseite *Seite);
//Gibt einen Zeiger auf die aktuelle virtuelle Adresse einer bestimmten physischen Seite zurück 3. unsigned long __get_free_pages(
    vorzeichenlose int gfp_mask, 
    vorzeichenlose int-Reihenfolge);
//Entspricht der Kombination der beiden obigen Funktionen 4. struct page * alloc_page(
    vorzeichenlose int gfp_mask);
5. unsigned long __get_free_page(
    vorzeichenlose int gfp_mask);
6. unsigned long get_zeroed_page(
    vorzeichenlose int gfp_mask);
//Nur eine Seite zuordnen

b.gfp_mask-Flagge

Dieses Flag bestimmt, wie sich der Kernel bei der Speicherzuweisung verhält und von wo aus er den Speicher zuweist.

#include <linux/gfp.h>
#GFP_ATOMIC definieren
//Atomare Zuweisung, kein Ruhezustand, kann für die Interrupt-Verarbeitung verwendet werden.
#define GFP_KERNEL 
//Bevorzugt, der Kernel kann schlafen, wird im Prozesskontext verwendet

c. Release-Seite

void __free_pages(Struktur Seite *Seite,
    vorzeichenlose int-Reihenfolge);
void free_pages(unsigned long addr,
    vorzeichenlose int-Reihenfolge);
void free_page(unsigned long addr);

Beachten! Sie können nur Seiten freigeben, die Ihnen gehören. Falsche Parameter können einen Kernel-Panic verursachen.

(3) Speicherbeschaffung über kmalloc

kmalloc ist malloc sehr ähnlich und ist die am häufigsten verwendete Speicherzuweisungsfunktion im Kernel.

kmalloc löscht den zugewiesenen Speicherbereich nicht auf 0 und der zugewiesene Bereich ist im physischen Speicher fortlaufend.

a. Zuordnung

#include <linux/slab.h>
void *kmalloc(size_t Größe, int Flags)

Größe ist die Größe des Speichers, der zugewiesen werden muss

Der Flags-Parameter von kmalloc kann das Verhalten von kmalloc während der Zuweisung steuern. Die verwendeten Flags stimmen mit denen in alloc_page verwendeten überein. Beachten Sie, dass kmalloc keinen High-End-Speicher zuordnen kann.

b. Freigabe

#include <linux/slab.h>
 void kfree(const void *ptr);

Dies kann schwerwiegende Folgen haben, wenn der freigegebene Speicher bereits freigegeben wurde oder wenn Speicher freigegeben wird, der zu anderen Teilen des Kernels gehört. Es ist sicher, kfree(NULL) aufzurufen.

Seien Sie vorsichtig! Der Kernel kann nur Byte-Arrays einer vordefinierten, festen Größe zuordnen. Der kleinste Speicherblock, den kmalloc verarbeiten kann, ist 32 oder 64. Da der von kmalloc zugewiesene Speicher physisch kontinuierlich ist, gibt es eine Obergrenze für die Zuweisung, die normalerweise 128 KB nicht überschreiten sollte.

(4) Speicher über vmalloc abrufen

Die virtuellen Adressen des von vmalloc() zugewiesenen Speichers sind kontinuierlich, die physischen Adressen müssen jedoch nicht kontinuierlich sein. Dies ist auch die Art und Weise, wie malloc() zuweist. vmalloc weist nicht zusammenhängende Speicherblöcke zu und ändert dann die Seitentabelle, um den Speicher in einem zusammenhängenden Bereich im logischen Raum abzubilden.

In den meisten Fällen müssen nur Hardwaregeräte Speicher mit kontinuierlichen physischen Adressen erhalten, und der Kernel kann den über vmalloc erhaltenen Speicher verwenden. Allerdings wird kmalloc hauptsächlich aus Leistungsgründen im Kernel verwendet, da vmalloc zu starkem TLB-Jitter führt, es sei denn, vmalloc wird beim Zuordnen großer Speicherblöcke verwendet. Wenn beispielsweise ein Modul dynamisch geladen wird, wird es in den von vmalloc zugewiesenen Speicher geladen.

vmalloc wird in <linux/vmalloc.h> deklariert und in <mm/vmalloc.c> definiert. Die Verwendung ist dieselbe wie bei malloc().

 void*vmalloc(vorzeichenlose lange Größe);
 void vfree(void *Adresse);

vmalloc führt zum Ruhezustand

(5) Speicherbeschaffung durch den Slab-Mechanismus

Das Zuweisen und Freigeben von Datenstrukturen ist eine der häufigsten Operationen im Kernel.

Eine gängige Methode besteht darin, eine freie Liste zu erstellen, die die zugewiesenen, zur Verwendung verfügbaren Datenstrukturblöcke enthält.

Sie müssen nicht jedes Mal, wenn Sie eine Datenstruktur zuordnen müssen, erneut Speicher beantragen. Stattdessen können Sie den Datenblock direkt aus dieser freien verknüpften Liste zuordnen und den Speicher beim Freigeben der Struktur an diese verknüpfte Liste zurückgeben.

Dabei handelt es sich eigentlich um eine Art Objekt-Cache (Zwischenspeichern von Objekten).

Linux bietet einen Slab-Allocator, um diese Aufgabe zu erfüllen.

Der Slab-Allocator sucht nach einem Gleichgewicht zwischen mehreren Grundprinzipien:

  • * Häufig verwendete Datenstrukturen werden häufig zugewiesen und freigegeben und müssen zwischengespeichert werden
  • *Häufige Zuweisung und Wiederverwendung führen zwangsläufig zu einer Speicherfragmentierung. Um dieses Phänomen zu vermeiden, wird der Cache in der freien Liste kontinuierlich gespeichert, um eine Fragmentierung zu vermeiden.
  • * Der Allocator kann basierend auf Objektgröße, Seitengröße und Gesamtcachegröße optimiert werden

kmalloc wird auf der Platte aufgebaut.

a. Einen neuen Cache erstellen

#include <linux/slab.h>
Struktur kmem_cache *kmem_cache_create(
   const char *Name, 
   size_t Größe,
   size_t ausrichten,
   unsignierte lange Flags,
   void(*ctor)(...));

Name: Der Name des Caches. Erscheint in /proc/slabinfo
Größe: Die Größe jedes Elements im Cache
align: der Offset des ersten Objekts im Cache, normalerweise 0
Flags: Zuordnungsflags. Häufig verwendetes SLAB_HWCACHE_ALIGH, das die Cache-Zeilenausrichtung angibt, siehe slab.h

b. Zerstöre den Cache

#include <linux/slab.h>
void kmem_cache_destroy(struct kmem_cache *cachep);

Diese Methode muss aufgerufen werden, nachdem alle Objekte im Cache freigegeben wurden.

c. Holen Sie das Objekt aus dem Cache

ungültig *kmem_cache_alloc(
   Struktur kmem_cache *cachep, int flags);
Flaggen:
   GFP_KERNEL

d. Geben Sie das Objekt wieder in den Cache frei.

ungültig kmem_cache_free(
   Struktur kmem_cache *cachep, void *objp);

Siehe kernel/fork.c

(6) Zuordnung von High-End-Speicher

Seiten im oberen Speicherbereich können nicht dauerhaft in den Kernel-Adressraum abgebildet werden. Daher können Seiten, die durch alloc_pages() mit dem Flag __GFP_HIGHMEM abgerufen werden, keine virtuellen Adressen haben. Es muss dynamisch über eine Funktion zugewiesen werden.

a. Zuordnung

Um eine gegebene Seitenstruktur in den Kernel-Adressraum abzubilden, verwenden Sie:

void *kmap(Struktur Seite *Seite);

Funktionen können schlafen

b. Zuordnung aufheben

void kunmap(Struktur Seite*Seite);

Zusammenfassen

Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Lernwert für Ihr Studium oder Ihre Arbeit hat. Vielen Dank für Ihre Unterstützung von 123WORDPRESS.COM. Wenn Sie mehr darüber erfahren möchten, schauen Sie sich bitte die folgenden Links an

Das könnte Sie auch interessieren:
  • Tutorial und Praxis zu den virtuellen Speichereinstellungen unter Linux
  • Linux-Systemdiagnose: Speichergrundlagen im Detail
  • So überprüfen Sie die Speichernutzung unter Linux
  • Warum frisst das Linux-System meinen „Speicher“ auf?
  • Lösen Sie das Problem des gemeinsam genutzten Speichers im Linux-System
  • Linux-System zum Anzeigen von CPU, Maschinenmodell, Speicher und anderen Informationen
  • Methoden zur Optimierung von Oracle-Datenbanken mit großen Speicherseiten unter Linux
  • Eine kurze Diskussion über den virtuellen Speicher von Linux

<<:  Detaillierte Erklärung zum Remotezugriff auf die MySQL-Datenbank über Workbench

>>:  Get/Delete-Methode zum Übergeben von Array-Parametern in Vue

Artikel empfehlen

Verwendung des Linux-Befehls ifconfig

1. Befehlseinführung Der Befehl ifconfig (Netzwer...

Einführung in geplante Aufgaben im Linux-System

Inhaltsverzeichnis 1. Planaufgaben anpassen 2. Ze...

mysql 5.7.5 m15 winx64.zip Installations-Tutorial

So installieren und konfigurieren Sie mysql-5.7.5...

Detaillierte Erklärung des Linux-Texteditors Vim

Vim ist ein leistungsstarker Vollbild-Texteditor ...

Warum sollten MySQL-Felder NOT NULL verwenden?

Ich habe vor Kurzem in einer neuen Firma angefang...

Docker-Installation Tomcat Dubbo-Admin-Instanz-Kenntnisse

1. Laden Sie das Tomcat-Image herunter Docker zie...

Tutorial zur Installation der entpackten Version von mysql5.7 auf CentOS 7

1. Entpacken Sie das komprimierte MySQL-Paket in ...

So verwenden Sie React zur Implementierung einer Bilderkennungs-App

Lassen Sie mich Ihnen zuerst das Effektbild zeige...

Drei gängige Stilselektoren in HTML-CSS

1: Tag-Selektor Der Tag-Selektor wird für alle Ta...

Ausführliche Erklärung zu Slots und Filtern in Vue

Inhaltsverzeichnis Spielautomaten Was sind Slots?...