[LINUX] Wann wird die mmap (2) -Datei aktualisiert? (2)

Letztes Mal beschreibt zwei Arten von Erkennungsmethoden zum Schreiben in den mMap-Bereich und drei Arten im Detail (1a Seitentabellen-Scanmethode, 1b). Die physikalische Seitenscanmethode (2-Schreibfehlererfassungsmethode) wurde vorhergesagt. In diesem Artikel werde ich den Betriebssystemcode des weit verbreiteten OSS untersuchen, um herauszufinden, welche Methode verwendet wird oder welche Methode verwendet wird.

Für Linux

Linux-Code für den virtuellen Speicher befindet sich im Quellbaumverzeichnis mm. Den Code zum Ersetzen der Seite finden Sie unter vmscan.c.

Ein Merkmal des Linux-mm-Codes ist, dass der modellunabhängige Teil die Seitentabelle direkt bedient. Natürlich variiert die Struktur der Seitentabelle je nach Modell, sie wird jedoch über das im modellabhängigen Teil definierte Makro bedient. Ich denke, dass es ein seltenes Merkmal ist, dass sogar die Struktur der mehrstufigen Seitentabelle im modellunabhängigen Teil erscheint. Es wird angenommen, dass dies auf die Tatsache zurückzuführen ist, dass Linux ursprünglich ein Kernel für x86 war. Da es sich um ein relativ neues Kernel handelt, wurden exzentrische MMUs eliminiert und entwickelt, nachdem nur MMUs mit ähnlichen Konfigurationen entwickelt wurden. Getan werden.

Seitentabelle scannen

Das x86-MMU-Operationsmakro lautet [arch / x86 / include / asm / pgtable.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch Es ist in /x86/include/asm/pgtable.h usw. definiert. Das Makro, das prüft, ob das D-Bit einer Seite gesetzt ist, ist pte_dirty ().

vmscan ist ein sehr komplexer Code. Es gibt zwei Möglichkeiten, wie das Austauschen von Seiten funktioniert: Zum einen wird ein Hintergrundprozess (kthread) ausgeführt, der im Kernel mit dem Namen kswapd ausgeführt wird, und zum anderen wird versucht, Speicher ohne Linux abzurufen. Bei Ausführung im Vordergrund (direkte Rückforderung), wenn kein (sehr wenig) freier Speicher vorhanden ist. Beide werden schließlich shrink_node () Kommen Sie zu dem Ort namens .7 # n2683).

Es gibt keinen Ort, an dem pte_dirty () direkt oder indirekt aufgerufen wird, selbst wenn Sie danach jagen (es ist so schmerzhaft wie es ist). Daher ist ersichtlich, dass Linux eine Methode zur Erfassung von Schreibfehlern zu sein scheint.

Schreibfehler und Seitenschreiben

Für alle Fälle werfen wir einen Blick auf den Schreibfehlercode und den Exportteil der schmutzigen Seite. Der Linux-Seitenfehlercode durchläuft die Seitenfehlerroutine im modellabhängigen Teil und handle_mm_fault (). /mm/memory.c?h=v5.7#n4348). Zu diesem Zeitpunkt wird im Falle eines Schreibfehlers FAULT_FLAG_WRITE im Flag-Argument (Bitmap) angegeben. Danach kommt \ _ \ _ handle_mm_fault () → handle_pte_fault () (HugePages oder die Verarbeitung des oberen Teils der mehrstufigen Seitentabelle sind mir unterwegs egal). [Wenn FAULT_FLAG_WRITE und die Seite schreibgeschützt sind (! Pte_write (Eintrag))](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/memory. c? h = v5.7 # n4230) ist do_wp_page (). C? H = v5.7 # n2878). Wp_page_shared () Wenn die Datei mit MAP_SHARED zugeordnet ist? h = v5.7 # n2844).

vma-> vm_ops-> page_mkwrite ist [filemap_page_mkwrite ()](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/filemap.c?h = v5.7 # n2656), wodurch der Zeitstempel der Datei aktualisiert und die Seite verschmutzt wird (diese Verschmutzung ist ein Seitenattribut im Gegensatz zum PTE D-Bit verschmutzt). Wenn eine Seite aus mehreren Prozessen zugeordnet wird, gibt es mehrere entsprechende PTEs, und das D-Bit des PTE in der Seitentabelle, das dem virtuellen Bereich entspricht, in dem der Schreibvorgang ausgeführt wurde, wird festgelegt. Schreiben Sie beispielsweise (2). Es ist die PTE in der Seitentabelle im Kernelbereich, die im Systemaufruf fehlerhaft ist und nicht vmscan unterliegt. Da jedoch klar ist, wann sie fehlerhaft sein wird, ist das Seitenattribut zum Zeitpunkt des Systemaufrufs fehlerhaft Es kann festgelegt werden. In set_page_dirty () wird auch der Vorgang zum Verbinden der Seite mit der Liste ausgeführt, die fehlerhafte Seiten verwaltet.

Die Person, die diese schmutzige Seite schreibt, ist eine Person namens bdi flusher. Der Export per bdi flusher wird durch die sysctl-Variable vm.dirty_expire_centisecs usw. gesteuert. Es wurde entwickelt, um einige Speicherschreibvorgänge zu speichern und sie dann in den Speicher zu schreiben. Derzeit wird es mit workqueue implementiert. Wenn Daten in der Warteschlange registriert sind, wird die Rückruffunktion später ausgeführt. In diesem Fall werden die Daten für jedes Blockgerät erstellt (um genau zu sein, für jedes Memcg) [struct bdi_writeback (wb)](https: / /git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/backing-dev-defs.h?h=v5.7#n130) ist die Rückruffunktion wb_workfn ..

Da die Zukunft kompliziert ist, werde ich sie weglassen, aber das Schreiben des Seitencaches lautet do_writepages (). Die Writepages-Methode, die für jedes Dateisystem mit page-writeback.c? H = v5.7 # n2336 ([ext4_writepages ()] für ext4] vorbereitet wurde (https://git.kernel.org/pub/scm/linux/kernel) /git/torvalds/linux.git/tree/fs/ext4/inode.c?h=v5.7#n2610) für XFS [xfs_vm_writepages ()](https://git.kernel.org/pub/scm/ linux / kernel / git / torvalds / linux.git / tree / fs / xfs / xfs_aops.c? h = v5.7 # n570))) Hier folgen wir generic_writepages (), einem allgemeinen Zweck (aufgerufen, wenn die Writepages-Methode für jedes Dateisystem nicht registriert ist und auch ein vorlagenähnlicher Code ist). Der Hauptteil ist write_cache_pages () # n2127). Schreibt den Dirty-Page-Cache für die angegebene Datei.

In der while-Schleife sucht pagevec_lookup_range_tag () nach schmutzigen Cache-Seiten. Die Strukturseite vec, die die Ergebnisse enthält, ist ein Array von Strukturseiten fester Länge, die physische Seiten darstellen (Länge 15. /tree/include/linux/pagevec.h?h=v5.7#n15)), die for-Schleife ist für jedes Element von pagevec. pagevec_lookup_range_tag () dient zum Durchsuchen nur verschmutzter Seiten, jedoch nach der Verarbeitung, z. B. Überspringen, wenn das Leeren auf einer anderen CPU usw. durchgeführt wird.

                        if (!clear_page_dirty_for_io(page))
                                goto continue_unlock;

                        trace_wbc_writepage(wbc, inode_to_bdi(mapping->host));
                        error = (*writepage)(page, wbc, data);

Dieser Teil ist das Miso. Ich werde später clear_page_dirty_for_io () sehen, aber es wird aufgerufen, um die schmutzige Markierung zu entfernen, weil ich sie ausschreiben werde. writepage ist eine dateisystemabhängige Writepage-Methode, die Seiten schreibt, wie der Name schon sagt.

clear_page_dirty_for_io () befindet sich etwas dahinter in derselben Datei = v5.7 # n2639). Das Folgende ist Kimo.

                if (page_mkclean(page))
                        set_page_dirty(page);

page_mkclean () ist [in rmap.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/rmap.c?h=v5. Es ist in 6 # n986). rmap ist eine Abkürzung für Reverse Map, eine Struktur zum Abrufen von PTE von einer physischen Seite. Physische Seiten können von mehreren PTEs referenziert werden, also für jede page_mkclean_one (). Rufen Sie linux.git / tree / mm / rmap.c? H = v5.6 # n1979 auf. So endlich

			entry = ptep_clear_flush(vma, address, pte);
			entry = pte_wrprotect(entry);
			entry = pte_mkclean(entry);
			set_pte_at(vma->vm_mm, address, pte, entry);

Ich kam an. Gleichzeitig mit dem Löschen des D-Bits wird das R / W-Bit gelöscht und schreibgeschützt. Jetzt weiß ich, dass ich nur gelesen habe, bevor ich mit dem Schreiben des Seitencaches beginne.

Für NetBSD

NetBSD ist ein Betriebssystem, das auf 4.4BSD basiert. 4.4BSD scheint das virtuelle Speichersystem vom CMU-Mikrokern Mach (Mark) portiert zu haben, aber es scheint unmöglich zu sein, das virtuelle Speichersystem von Mach mit unterschiedlichen Anforderungen weiter zu verwenden. NetBSD verfügt über ein eigenes virtuelles Speichersystem Ersetzt mit. Dieser Code heißt UVM und wird in der Dissertation von Charles D. Cranor beschrieben, die auf der [Dokumentenseite] des NetBSD-Projekts (https://www.netbsd.org/docs/kernel/uvm.html) verlinkt ist. (Es ist ein Papier vor mehr als 20 Jahren, und es sollte beachtet werden, dass es sich von der aktuellen Situation unterscheidet).

In NetBSD UVM wird der modellabhängige Teil (MD: Maschinenabhängig in der NetBSD-Terminologie) als pmap-Schicht bezeichnet. Was im maschinenunabhängigen Teil (MI) erscheint, ist nicht die Seitentabelle selbst wie Linux, sondern eine undurchsichtige Struktur namens struct pmap (implementiert in der pmap-Schicht von MD), virtual address (VA) und physisch. Zum Beispiel Adresse (PA). Die mehrstufige Struktur der Seitentabelle ist in der pmap-Ebene ausgeblendet. UVM wurde implementiert, um die Schnittstelle zwischen dem virtuellen Mach-Speichersystem und der pmap-Schicht aufrechtzuerhalten.

Nicht viele Leute sind mit dem NetBSD-Quellcode vertraut, daher hier ein kurzes Glossar.

Der NetBSD-Quellcode ist von CVS verwaltet, verweist jedoch auf eine bestimmte Zeile, sodass Folgendes lautet: Github Mirror Siehe //github.com/NetBSD/src/tree/netbsd-9).

Seitentabelle scannen

In der pmap-Schnittstelle ist es pmap_is_modified (), um zu überprüfen, ob das D-Bit einer Seite gesetzt ist. /pmap.h#L332). Da das Argument eine physische Seite ist, können mehrere PTEs darauf verweisen. In diesem Fall wird true zurückgegeben, wenn für ein PTE ein D-Bit gesetzt ist. Ein anderer PTE (s) pmap_clear_modify () verweist auf eine Seite Löscht das D-Bit von (möglich) und gibt gleichzeitig zurück, ob ein PTE-Satz vorhanden war oder nicht (entspricht page_mkclean () unter Linux). UVM ist für die Seitenfreigabe durch [einen Kernel-Thread namens Page Daemon (pdaemon)] verantwortlich (https://github.com/NetBSD/src/blob/netbsd-9/sys/uvm/uvm_pdaemon.c#L233). Selbst wenn ich den gesamten UVM-Quellcode greife, gibt es jedoch keinen Ort, an dem pmap_is_modified () (oder pmap_clear_modify ()) aufgerufen wird. Daher stellt sich heraus, dass NetBSD auch eine Methode zur Erfassung von Schreibfehlern zu sein scheint.

Interessanterweise verfügt NetBSD über einen steckbaren Algorithmus zum Ersetzen von Seiten, mit dem Sie zwischen zwei Typen wählen können: Clock und Clock Pro.

Fehler schreiben und Seite schreiben

Für alle Fälle werfen wir einen Blick auf den Schreibfehlercode und den Exportteil der schmutzigen Seite. Die Fehlerbehandlung für NetBSD-Seiten finden Sie unter uvm / uvm_fault.c. Im Falle eines Schreibfehlers enthält das Argument access_type VM_PROT_WRITE, das in der Struktur uvm_faultctctx flt.access_type gespeichert ist. Gehen Sie gemäß Kommentar oben zu dem Teil, in dem Sie die Datei mit MAP_SHARED in mMap eingefügt haben Der Schreibfehler von wird als CASE 2A klassifiziert. Es wird unter uvm_fault_lower () verarbeitet. uobj repräsentiert die Datei, die mMapped ist.

Bei regulären Dateien ist uobj-> pgops-> pgo_fault NULL, uobj-> pgops-> pgo_get ist uvn_get () /uvm_vnode.c#L155) und vop_getpages, wenn das Dateisystem BSD FFS ist, ist genfs_getpages () Wenn Sie dem Code folgen und beachten, dass es sich um .c # L102) handelt, uvm_fault_lower () → uvm_fault_lower_lookup () /uvm_fault.c#L1764) → uvm_get () → VOP_GETPAGES () → genfs_getpages () Fortfahren mit. genfs_getpages () lädt Daten aus dem Speicher in den Seitencache. VM_PROT_WRITE von access_type ist Reflect in variable memwrite. Auch Zeitstempel wurden aktualisiert. Und Zeile 261.

                if (error == 0 && memwrite) {
                        genfs_markdirty(vp);
                }

Hier wird vnode durch den später beschriebenen Syncer verschmutzt und in der Liste der Rückschreibziele registriert. Es scheint keinen Teil zu geben, der die Seite schmutzig macht (löscht das PG_CLEAN-Flag von den Seitenflags) (das Lesen des Exportvorgangs einer schmutzigen Seite unten scheint sicherlich unnötig). Wenn Sie irgendwo schmutzig sind, lassen Sie es mich bitte wissen.

Als nächstes der Vorgang des Exportierens der schmutzigen Seite. Dies erfolgt durch einen Kernel-Thread namens Syncer. Der Hauptteil von Syncer ist sched_sync (). Von hier aus lazy_sync_vnode ()VOP_FSYNC () /NetBSD/src/blob/netbsd-9/sys/kern/vnode_if.c#L798) → [ffs_fsync ()](https://github.com/NetBSD/src/blob/netbsd-9/sys/ufs/ ffs / ffs_vnops.c # 325) → ffs_full_fsync ()vflushbuf () → [VOP_PUTPAGES ()](https://github.com/NetBSD/src/blob /netbsd-9/sys/kern/vnode_if.c#L1614) → [genfs_putpages ()](https://github.com/NetBSD/src/blob/netbsd-9/sys/miscfs/genfs/genfs_io.c# Geschrieben in L777). wapbl entspricht dem ext3 / 4-Journal, aber sagen wir es hier nicht. Da PGO_CLEANIT in vflushbuf () festgelegt ist, starten Sie die Transaktion zunächst mit einem Mechanismus namens fstrans und retry, wenn Sie dem Ablauf folgen. Gehen Sie zurück zu sys / miscfs / genfs / genfs_io.c # L885) und while-Schleife. hinein gehen. Die while-Schleife scheint abhängig von der Dichte der speicherinternen Seiten zwischen by_list (nach der Liste der Seiten) und Seiten nacheinander zu wechseln. In jedem Fall befindet sich die aktuelle Zielseite in der Struktur vm_page * pg.

Ich bin mir in Zukunft etwas unsicher. Zeile 1112

		if (flags & PGO_CLEANIT) {
 			needs_clean = pmap_clear_modify(pg) ||
			    (pg->flags & PG_CLEAN) == 0;
			pg->flags |= PG_CLEAN;
		} else {

Mit pmap_clear_modify () wird das D-Bit jedes PTE, das auf pg zeigt, gelöscht, und das D-Bit vor dem Löschen wird in need_clean wiedergegeben.

Es scheint auch, dass es erst kurz zuvor gelesen wurde.

			if (cleanall && wasclean &&
			    gp->g_dirtygen == dirtygen) {

				/*
				 * uobj pages get wired only by uvm_fault
				 * where uobj is locked.
				 */

				if (pg->wire_count == 0) {
					pmap_page_protect(pg,
					    VM_PROT_READ|VM_PROT_EXECUTE);
				} else {
					cleanall = false;
				}
			}

wasclean wird festgelegt, wenn das Attribut vp_numoutput des vnode 0 ist. Dieses Mitglied wurde zu Beginn des Schreibens der nächsten Seite erhöht, aber ist es zum Zeitpunkt der Ankunft immer 0? Dirtygen wird aus dem gleichnamigen Attribut in vnode am Anfang von genfs_do_putpages () kopiert. Dieses Attribut wird unter [genfs_markdirty ()] erhöht (https://github.com/NetBSD/src/blob/netbsd-9/sys/miscfs/genfs/genfs_io.c#L87). Wenn es sich um eine schmutzige Generation handelt und ein Schreibfehler auf einer anderen Seite derselben Datei hinter dem Schreiben der Seite auftritt, wird die Bedingung von gp-> g_dirtygen == dirtygen entfernt. In diesem Fall wird der Vorgang des Ausschlusses dieses Knotens vom Syncer-Ziel im späteren Prozess nicht ausgeführt, sodass dies sicherlich gut zu sein scheint.

Es stellt sich verdächtig heraus, dass NetBSD auch das Schreiben der Seite während eines Schreibfehlers zulässt, sie gleichzeitig verschmutzt und beim Ausschreiben nur wieder lesbar macht. Das D-Bit wird jedoch verwendet, um zu bestimmen, welche Seite des Seitencaches einer bestimmten Datei verschmutzt ist.

Es ist lange her, also ist es das für diesen Artikel. In Nächster Artikel werden wir zwei weitere sehen und der letzte sein. Zwei Betriebssysteme unterschiedlicher Herkunft, Linux und NetBSD, verwendeten dieselbe Methode. Bitte freuen Sie sich darauf, ob dies der Mainstream ist oder ob es andere Arten von Betriebssystemen gibt.

Recommended Posts

Wann wird die mmap (2) -Datei aktualisiert? (3)
Wann wird die mmap (2) -Datei aktualisiert? (2)
Wann wird die mmap (2) -Datei aktualisiert? (1)
Sagen Sie voraus, wann die ISS sichtbar sein wird
Seien Sie vorsichtig, wenn Sie mit gzip-komprimierten Textdateien arbeiten
[Python] Wann werden Variablen erstellt? Wann werden Klasseninstanzen gelöscht?
Reverse Pull Numpy / Pandas (wird jederzeit aktualisiert)
Wenn USB nicht formatiert werden konnte
Wenn pydub nicht installiert werden kann