get_ free_ page und Freunde

Wenn ein Modul große Speicherblocks allozieren muß, ist es besser, eine seitenorientierte Technik zu verwenden. Das hat auch noch weitere Vorteile, die in “the Section called Die Geräteoperation mmap in Kapitel 13” in Kapitel 13 eingeführt werden.

Die folgenden Funktionen stehen zur Allokation von Seiten zur Verfügung:

Die Prototypen für die Funktionen sind folgendermaßen definiert:



unsigned long get_zeroed_page(int flags);
unsigned long _ _get_free_page(int flags);
unsigned long _ _get_free_pages(int flags,
                              unsigned long order);
unsigned long _ _get_dma_pages(int flags,
                               unsigned long order);

Das Argument flags in den Allokationsfunktionen hat die gleiche Bedeutung wie bei kmalloc. Normalerweise wird entweder GFP_KERNEL oder GFP_ATOMIC verwendet, möglicherweise unter Hinzufügung von __GFP_DMA (für Speicher, auf den mit DMA-Operationen zugegriffen werden soll) oder __GFP_HIGHMEM für die Verwendung des oberen Speicherbereichs. order ist die Zweierpotenz der Anzahl der Seiten, die Sie anfordern oder freigeben wollen (also log2N). Beispielsweise nutzen Sie order 0, wenn Sie nur eine Seite haben wollen, oder 3 für acht Seiten. Wenn order zu groß ist, da kein zusammenhängender Speicherbereich der angegebenen Größe vorhanden ist, schlägt die Allokation fehl. In Linux 2.0 war der größte Wert für order 5 (entsprechend 32 Seiten) und in neueren Versionen 9 (entsprechend 512 Seiten, 2 MByte auf den meisten Plattformen). Natürlich ist es um so wahrscheinlicher, daß die Allokation fehlschlägt, je größer order ist.

Wenn ein Programm die Seiten nicht mehr benötigt, kann es sie mit einer der folgenden Funktionen zurückgeben. Die erste ist ein Makro, das die zweite verwendet:


void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned long order);

Wenn Sie versuchen, eine andere Zahl von Seiten freizugeben, als Sie alloziert haben, dann kommt die Speichertabelle durcheinander, und das System wird später in Schwierigkeiten geraten.

Es muß betont werden, daß get_free_pages und die anderen Funktionen jederzeit aufgerufen werden können, natürlich mit den gleichen Einschränkungen wie bei kmalloc. Es kann in gewissen Situationen passieren, daß kein Speicher alloziert werden kann; insbesondere dann, wenn die Priorität GFP_ATOMIC ist. Programme, die diese Allokationsfunktionen verwenden, müssen also in der Lage sein, Allokationsfehler behandeln zu können.

Es ist behauptet worden, daß Sie — wenn Sie das Risiko lieben — annehmen können, daß weder kmalloc noch das zugrundeliegende get_free_pages jemals fehlschlägt, wenn es mit der Priorität GFP_KERNEL verwendet wird. Das ist beinahe wahr, aber nicht ganz: Kleine Systeme mit wenig Speicher können immer noch in Schwierigkeiten geraten. Treiber-Autoren ignorieren die Gefahr des Fehlschlagens der Allokation auf eigene Gefahr (und auf Gefahr der Anwender).

Obwohl kmalloc(GFP_KERNEL) manchmal fehlschlägt, wenn kein Speicher zur Verfügung steht, versucht der Kernel doch sein Bestes, um Allokationsanforderungen zu erfüllen. Daher ist es ziemlich einfach, das Antwortverhalten durch das Anfordern von zuviel Speicher herabzusetzen. Beispielsweise können Sie den Computer in die Knie zwingen, wenn Sie zu viele Daten in ein scull-Gerät schreiben. Das System wird nur noch kriechen, wenn es versucht, so viele Seiten wie möglich auszulagern, um die Anforderung von kmalloc zu erfüllen. Da jede Ressource von dem wachsenden Gerät aufgesaugt wird, ist der Computer bald unbenutzbar. Das geht so weit, daß Sie irgendwann keinen neuen Prozeß von Ihrer Shell starten können. Wir gehen darauf in scull nicht ein, da es sich nur um ein Beispiel-Modul und nicht um ein echtes Werkzeug in einem Multiuser-System handelt. Als Programmierer müssen Sie trotzdem vorsichtig sein, weil ein Modul privilegierter Code ist und neue Sicherheitslöcher in das System reißen kann (das wahrscheinlichste dieser Löcher ist ein “Denial-of-service”-Loch, wie jenes, das wir gerade beschrieben haben).

Ein scull, das ganze Seiten verwendet: scullp

Um die Allokation der Testseiten im Ernstfall testen zu können, haben wir das Modul scullp geschrieben. Es ist wie das oben eingeführte scullc ein eingeschränktes scull.

Die von scullp allozierten Speicher-Quanta sind ganze Seiten oder Seitenmengen: Die Variable scullp_order hat den Defaultwert 0 und kann entweder beim Kompilieren oder beim Laden angegeben werden.

Die folgenden Zeilen zeigen, wie die Allokation vor sich geht:


/* Hier wird ein Quantum alloziert */
if (!dptr->data[s_pos]) {
    dptr->data[s_pos] =
      (void *)_ _get_free_pages(GFP_KERNEL, dptr->order);
    if (!dptr->data[s_pos])
        goto nomem;
    memset(dptr->data[s_pos], 0, PAGE_SIZE << dptr->order);
}

Der Code zum Deallozieren von Speicher in scullp sieht dagegen so aus:


/* Gibt eine ganze Quanten-Menge frei */
for (i = 0; i < qset; i++)
    if (dptr->data[i])
        free_pages((unsigned long)(dptr->data[i]),
                   dptr->order);

Als Benutzer kann man hauptsächlich einen Geschwindigkeitsgewinn und eine bessere Speicherverwendung feststellen, weil es keine interne Fragmentierung des Speichers gibt. Wir haben einige Tests laufen lassen, bei denen erst 4 Megabyte von scull0 nach scull1 und dann von scullp0 nach scullp1 kopiert wurden; dabei verringerte sich die Last des Prozessors im Kernel-Space leicht.

Der Performance-Gewinn ist nicht so dramatisch, weil kmalloc schon so entworfen wurde, daß es schnell ist. Der Hauptvorteil der Allokation auf Seitenebene ist nicht die Geschwindigkeit, sondern die effizientere Verwendung des Speichers. Werden ganze Seiten alloziert, wird dabei kein Speicher verschwendet, während kmalloc aufgrund der Allokations-Granularität eine nicht vorhersagbare Menge an Speicher verschwendet.

Der größte Vorteil von __get_free_page ist aber, daß Ihnen die Seite ganz allein gehört; Sie könnten theoretisch sogar die Seiten durch Manipulieren der Seitentabellen in einen linearen Speicherbereich bringen. Damit kann ein Benutzer-Prozeß Speicherbereiche, die als einzelne, nicht zusammenhängende Seiten angefordert wurden, mit mmap einblenden. Wir werden darauf in “the Section called Die Geräteoperation mmap in Kapitel 13” in Kapitel 13 eingehen, wo wir Ihnen zeigen werden, wie scullp das Abbilden von Speicher ermöglicht — etwas, was scull nicht kann.