Beobachtung eines Festplattenschreibvorgangs im Kernel (mit Treibern / Modulen)

13

Ich entschuldige mich im Voraus, wenn dieser Beitrag ein wenig unübersichtlich ist, aber es fällt mir schwer, ihn besser zu formulieren.

  • Ist mein Verständnis unten richtig - und wenn nicht, wo gehe ich falsch?
  • Gibt es ein besseres Tool zum "Erfassen" von Protokolldaten zu allen Aspekten, die auf dem PC während eines Schreibvorgangs auftreten?

Genauer gesagt: Das Betriebssystem, das ich verwende, ist:

$ uname -a
Linux mypc 2.6.38-16-generic #67-Ubuntu SMP Thu Sep 6 18:00:43 UTC 2012 i686 i686 i386 GNU/Linux

So habe ich folgendes einfaches (zB die üblichen Prüfungen auf Betriebsstörungen werden übersprungen) User-Space-C-Programm wtest.c:

#include <stdio.h>
#include <fcntl.h>  // O_CREAT, O_WRONLY, S_IRUSR

int main(void) {
  char filename[] = "/tmp/wtest.txt";
  char buffer[] = "abcd";
  int fd;
  mode_t perms = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;

  fd = open(filename, O_RDWR|O_CREAT, perms);
  write(fd,buffer,4);
  close(fd);

  return 0;
}

Ich baue das mit gcc -g -O0 -o wtest wtest.c. Da ich nun versuche zu schreiben /tmp, stelle ich fest, dass es sich um ein Verzeichnis unter dem Stammverzeichnis handelt /- also überprüfe ich Folgendes mount:

$ mount
/dev/sda5 on / type ext4 (rw,errors=remount-ro,commit=0)
...
/dev/sda6 on /media/disk1 type ext4 (rw,uhelper=hal,commit=0)
/dev/sda7 on /media/disk2 type ext3 (rw,nosuid,nodev,uhelper=udisks,commit=0,commit=0,commit=0,commit=0,commit=0,commit=0)
...

Mein Root-Dateisystem /ist also eine Partition des /dev/sdaGeräts (und ich verwende auch andere Partitionen als "eigenständige" Festplatten / Mounts). Um den Treiber für dieses Gerät zu finden, benutze ich hwinfo:

$ hwinfo --disk
...
19: IDE 00.0: 10600 Disk
...
  SysFS ID: /class/block/sda
  SysFS BusID: 0:0:0:0
...
  Hardware Class: disk
  Model: "FUJITSU MHY225RB"
...
  Driver: "ata_piix", "sd"
  Driver Modules: "ata_piix"
  Device File: /dev/sda
...
  Device Number: block 8:0-8:15
...

Die /dev/sdaFestplatte wird also anscheinend vom ata_piix(und sd) Treiber verwaltet.

$ grep 'ata_piix\| sd' <(gunzip </var/log/syslog.2.gz)
Jan 20 09:28:31 mypc kernel: [    1.963846] ata_piix 0000:00:1f.2: version 2.13
Jan 20 09:28:31 mypc kernel: [    1.963901] ata_piix 0000:00:1f.2: PCI INT B -> GSI 19 (level, low) -> IRQ 19
Jan 20 09:28:31 mypc kernel: [    1.963912] ata_piix 0000:00:1f.2: MAP [ P0 P2 P1 P3 ]
Jan 20 09:28:31 mypc kernel: [    2.116038] ata_piix 0000:00:1f.2: setting latency timer to 64
Jan 20 09:28:31 mypc kernel: [    2.116817] scsi0 : ata_piix
Jan 20 09:28:31 mypc kernel: [    2.117068] scsi1 : ata_piix
Jan 20 09:28:31 mypc kernel: [    2.529065] sd 0:0:0:0: [sda] 488397168 512-byte logical blocks: (250 GB/232 GiB)
Jan 20 09:28:31 mypc kernel: [    2.529104] sd 0:0:0:0: Attached scsi generic sg0 type 0
Jan 20 09:28:31 mypc kernel: [    2.529309] sd 0:0:0:0: [sda] Write Protect is off
Jan 20 09:28:31 mypc kernel: [    2.529319] sd 0:0:0:0: [sda] Mode Sense: 00 3a 00 00
Jan 20 09:28:31 mypc kernel: [    2.529423] sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
Jan 20 09:28:31 mypc kernel: [    2.674783]  sda: sda1 sda2 < sda5 sda6 sda7 sda8 sda9 sda10 >
Jan 20 09:28:31 mypc kernel: [    2.676075] sd 0:0:0:0: [sda] Attached SCSI disk
Jan 20 09:28:31 mypc kernel: [    4.145312] sd 2:0:0:0: Attached scsi generic sg1 type 0
Jan 20 09:28:31 mypc kernel: [    4.150596] sd 2:0:0:0: [sdb] Attached SCSI removable disk

Ich muss aus älterem Syslog ziehen, da ich viel aussetze, aber das obige scheint das richtige Snippet aus dem Syslog beim Booten zu sein, wo der ata_piix(und sd) Treiber zum ersten Mal einschaltet.

Mein erster Punkt der Verwirrung ist, dass ich die ata_piixoder sdFahrer nicht anders beobachten kann :

$ lsmod | grep 'ata_piix\| sd'
$
$ modinfo sd
ERROR: modinfo: could not find module sd
$ modinfo ata_piix
ERROR: modinfo: could not find module ata_piix

Meine erste Frage lautet also: Warum kann ich das ata_piixModul hier nicht nur in Startprotokollen beobachten ? Liegt es daran, dass ata_piix(und sd) als integrierte Treiber im (monolithischen) Kernel erstellt werden und nicht als (ladbare) .koKernelmodule?

Ich versuche jetzt zu beobachten, was passiert, wenn das Programm mit dem in ftraceLinux integrierten Funktionstracer ausgeführt wird.

sudo bash -c '
KDBGPATH="/sys/kernel/debug/tracing"
echo function_graph > $KDBGPATH/current_tracer
echo funcgraph-abstime > $KDBGPATH/trace_options
echo funcgraph-proc > $KDBGPATH/trace_options
echo 0 > $KDBGPATH/tracing_on
echo > $KDBGPATH/trace
echo 1 > $KDBGPATH/tracing_on ; ./wtest ; echo 0 > $KDBGPATH/tracing_on
cat $KDBGPATH/trace > wtest.ftrace
'

... und hier ist ein Auszug aus dem ftraceProtokoll über write:

4604.352690 | 0) wtest-31632 | | sys_write () {
 4604.352690 | 0) wtest-31632 | 0,750 us | fget_light ();
 4604.352692 | 0) wtest-31632 | | vfs_write () {
 4604.352693 | 0) wtest-31632 | | rw_verify_area () {
 4604.352693 | 0) wtest-31632 | | security_file_permission () {
 4604.352694 | 0) wtest-31632 | | apparmor_file_permission () {
 4604.352695 | 0) wtest-31632 | 0,811 us | common_file_perm ();
 4604.352696 | 0) wtest-31632 | 2.198 us | }
 4604.352697 | 0) wtest-31632 | 3,573 us | }
 4604.352697 | 0) wtest-31632 | 4.979 us | }
 4604.352698 | 0) wtest-31632 | | do_sync_write () {
 4604.352699 | 0) wtest-31632 | | ext4_file_write () {
 4604.352700 | 0) wtest-31632 | | generic_file_aio_write () {
 4604.352701 | 0) wtest-31632 | | mutex_lock () {
 4604.352701 | 0) wtest-31632 | 0,666 us | _cond_resched ();
 4604.352703 | 0) wtest-31632 | 1.994 us | }
 4604.352704 | 0) wtest-31632 | | __generic_file_aio_write () {
...
 4604.352728 | 0) wtest-31632 | | file_update_time () {
...
 4604.352732 | 0) wtest-31632 | 0,756 us | mnt_want_write_file ();
 4604.352734 | 0) wtest-31632 | | __mark_inode_dirty () {
...
 4604.352750 | 0) wtest-31632 | | ext4_mark_inode_dirty () {
 4604.352750 | 0) wtest-31632 | 0,679 us | _cond_resched ();
 4604.352752 | 0) wtest-31632 | | ext4_reserve_inode_write () {
...
 4604.352777 | 0) wtest-31632 | | __ext4_journal_get_write_access () {
...
 4604.352795 | 0) wtest-31632 | | ext4_mark_iloc_dirty () {
...
 4604.352806 | 0) wtest-31632 | | __ext4_journal_stop () {
...
 4604.352821 | 0) wtest-31632 | 0,684 us | mnt_drop_write ();
 4604.352822 | 0) wtest-31632 | + 93.541 us | }
 4604.352823 | 0) wtest-31632 | | generic_file_buffered_write () {
 4604.352824 | 0) wtest-31632 | 0,654 us | iov_iter_advance ();
 4604.352825 | 0) wtest-31632 | | generic_perform_write () {
 4604.352826 | 0) wtest-31632 | 0,709 us | iov_iter_fault_in_readable ();
 4604.352828 | 0) wtest-31632 | | ext4_da_write_begin () {
 4604.352829 | 0) wtest-31632 | | ext4_journal_start_sb () {
...
 4604.352847 | 0) wtest-31632 | 1,453 us | __block_write_begin ();
 4604.352849 | 0) wtest-31632 | + 21.128 us | }
 4604.352849 | 0) wtest-31632 | | iov_iter_copy_from_user_atomic () {
 4604.352850 | 0) wtest-31632 | | __kmap_atomic () {
...
 4604.352863 | 0) wtest-31632 | 0,672 us | mark_page_accessed ();
 4604.352864 | 0) wtest-31632 | | ext4_da_write_end () {
 4604.352865 | 0) wtest-31632 | | generic_write_end () {
 4604.352866 | 0) wtest-31632 | | block_write_end () {
...
 4604.352893 | 0) wtest-31632 | | __ext4_journal_stop () {
...
 4604.352909 | 0) wtest-31632 | 0,655 us | mutex_unlock ();
 4604.352911 | 0) wtest-31632 | 0,727 us | generic_write_sync ();
 4604.352912 | 0) wtest-31632 | ! 212.259 us | }
 4604.352913 | 0) wtest-31632 | ! 213.845 us | }
 4604.352914 | 0) wtest-31632 | ! 215.286 us | }
 4604.352914 | 0) wtest-31632 | 0,685 us | __fsnotify_parent ();
 4604.352916 | 0) wtest-31632 | | fsnotify () {
 4604.352916 | 0) wtest-31632 | 0,907 us | __srcu_read_lock ();
 4604.352918 | 0) wtest-31632 | 0,685 us | __srcu_read_unlock ();
 4604.352920 | 0) wtest-31632 | 3.958 us | }
 4604.352920 | 0) wtest-31632 | ! 228.409 us | }
 4604.352921 | 0) wtest-31632 | ! 231.334 us | }

Dies ist mein zweiter Punkt der Verwirrung - ich kann den User-Space beobachten, der sich erwartungsgemäß write()mit einem Kernel-Space ergibt sys_write(); und in der sys_write()ich sicherheitsrelevante Funktionen (zB beobachten apparmor_file_permission()), „generische“ Schreibfunktionen (zB generic_file_aio_write()), ext4Dateisystem verwandte Funktionen (zB ext4_journal_start_sb()) - aber ich weiß nicht beobachte alles im Zusammenhang mit ata_piix(oder sd) -Treiber?

Auf der Seite Nachverfolgung und Profilerstellung - Yocto Project wird vorgeschlagen , den blkTracer in ftracezu verwenden, um weitere Informationen über den Betrieb von Blockgeräten zu erhalten. In diesem Beispiel wird jedoch nichts gemeldet . Außerdem schlagen Linux-Dateisystemtreiber - Annon Inglorion (tutorfs) vor, dass Dateisysteme auch als Kernelmodule / -treiber (implementiert) werden (können?), Und ich vermute, dass dies auch der Fall ist ext4.

Schließlich hätte ich schwören können, dass ich früher den Treibernamen in eckigen Klammern neben der vom function_graphTracer angezeigten Funktion gesehen habe , aber ich glaube, ich hatte etwas verwechselt - es kann wahrscheinlich in Stack- (Back-) Traces so aussehen, aber nicht im Funktionsgraphen. Außerdem kann ich Folgendes einsehen /proc/kallsyms:

$ grep 'piix\| sd\|psmouse' /proc/kallsyms
...
00000000 d sd_ctl_dir
00000000 d sd_ctl_root
00000000 d sdev_class
00000000 d sdev_attr_queue_depth_rw
00000000 d sdev_attr_queue_ramp_up_period
00000000 d sdev_attr_queue_type_rw
00000000 d sd_disk_class
...
00000000 t piix_init_sata_map
00000000 t piix_init_sidpr
00000000 t piix_init_one
00000000 t pci_fixup_piix4_acpi
...
00000000 t psmouse_show_int_attr        [psmouse]
00000000 t psmouse_protocol_by_type     [psmouse]
00000000 r psmouse_protocols    [psmouse]
00000000 t psmouse_get_maxproto [psmouse]
...

... und überprüfe mit Linux / drivers / ata / ata_piix.c , ob zB piix_init_sata_maptatsächlich eine Funktion in ist ata_piix. Was mir wahrscheinlich sagen sollte: Module, die im Kernel kompiliert sind (damit sie Teil des monolithischen Kernels werden), "verlieren" die Informationen darüber, von welchem ​​Modul sie stammen; Bei den ladbaren Modulen, die als separate .koKernelobjekte erstellt wurden, bleiben diese Informationen jedoch erhalten (z. B. [psmouse]oben in eckigen Klammern angegeben). Somit ftracekönnten auch nur "Ursprungsmodul" -Informationen angezeigt werden, und zwar nur für die Funktionen, die von ladbaren Kernelmodulen stammen. Ist das richtig?

Unter Berücksichtigung des oben Gesagten ist dies das Verständnis, das ich derzeit von dem Prozess habe:

  • Beim Booten erstellt der ata_piixTreiber eine DMA-Speicherzuordnung (?) Zwischen /dev/sdaund der Festplatte
    • Aus diesem Grund sind alle zukünftigen Zugriffe auf /dev/sdaVia ata_piixfür den Kernel transparent (dh nicht nachvollziehbar) - da jeder Kernel nur Lese- / Schreibzugriffe auf Speicherorte ausführen würde (nicht unbedingt Aufrufe bestimmter nachvollziehbarer Kernelfunktionen) werden vom function_graphTracer nicht gemeldet
  • Beim Booten sd"analysiert" der Treiber außerdem die Partitionen von /dev/sda, stellt sie zur Verfügung und verarbeitet möglicherweise die Speicherzuordnungen zwischen den Partitionen <-> Plattenlaufwerken
    • Auch dies sollte die Zugriffsoperationen über sdfür den Kernel transparent machen
  • Da beide ata_piixund sdim Kernel kompiliert sind ftrace, können wir keine Informationen darüber erhalten, von welchem ​​Modul diese Funktionen stammen würden (abgesehen von der "manuellen" Korrelation mit Quelldateien) , auch wenn einige ihrer Funktionen erfasst werden.
  • Baut später mounteine Beziehung / Bindung zwischen einer Partition und dem entsprechenden Dateisystemtreiber auf (in diesem Fall ext4)
    • Ab diesem Zeitpunkt würden alle Zugriffe auf das bereitgestellte Dateisystem von ext4Funktionen verwaltet, die vom Kernel nachverfolgt werden können. aber wie ext4im Kernel kompiliert wird, kann der Tracer gibt uns nicht die Ursprungsmodul Informationen
  • Somit würden die beobachteten "generischen" Schreibvorgänge, die über ext4Funktionen aufgerufen werden , letztendlich auf Speicherorte zugreifen, deren Zuordnung durch ata_piix- aber anders als das - festgelegt wird, ata_piixdie Datenübertragung nicht direkt stören (sie wird wahrscheinlich von DMA (außerhalb des Prozessors) verarbeitet (s) und damit für sie transparent).

Ist dieses Verständnis richtig?

Einige verwandte Unterfragen:

  • In meinem obigen Setup kann ich einen PCI-Gerätetreiber ( ata_piix) und einen Dateisystemtreiber ( ext4) identifizieren . Aber gibt es Zeichen- oder Blocktreiber, die irgendwo im Ausführungspfad "Schreiben" verwendet werden, und wenn ja, welche?
  • Welcher dieser Treiber würde das Caching handhaben (so dass unnötige Festplattenoperationen übersprungen oder optimiert werden?)
  • Ich kenne von /dev/shmvorhin ein Dateisystem im RAM; mount | grep shmfür mich berichtet: none on /dev/shm type tmpfs (rw,nosuid,nodev). Bedeutet das, dass /dev/sdadem shmDateisystem - im Gegensatz zu - einfach die (DMA-) Zuordnung von "seinen" Adressen zu Busadressen zu einem Gerät fehlt? und somit tmpfslanden alle Zugriffe über den Dateisystemtreiber im eigentlichen RAM?
sdaau
quelle
4
Hi sdaau. Dies sind gute Fragen, aber die Länge dieses Beitrags ist zu lang und es gibt mehrere Fragen. Es ist empfehlenswert, dass Sie versuchen, Dinge zu verstehen, anstatt nur Helpdesk-Fragen zu stellen, was wir meistens hier erfahren. Jede dieser Fragen verdient eine lange Antwort für sich. Ich empfehle, Ihren Beitrag zumindest in klar definierte Teile zu zerlegen und jedes Teil in eine separate Frage zu stellen, um so eine Reihe von Fragen zu erstellen.
Faheem Mitha
Dann können Sie diese Fragen zusammen oder nacheinander stellen. Ich denke, es ist in Ordnung, wenn Sie auf eine andere Frage (oder Fragen) innerhalb einer Frage verweisen.
Faheem Mitha
1
Wenn Sie Tipps zur Bereinigung Ihrer Frage wünschen, empfehlen wir Ihnen, in den Chatroom zu springen und mit den Leuten dort zu sprechen. Wir haben hier bereits darüber gesprochen. :-)
Faheem Mitha
Vielen Dank für den Kommentar, @FaheemMitha - ich hatte auch ähnliche Zweifel, aber ich war mir nicht sicher, wie ich die Fragen zerschneiden sollte - und war mir bis jetzt nicht bewusst, ob ich den Chat dafür nutzen kann (und ich war nicht scharf darauf Verwendung von Meta, um nach solchen Ratschlägen zu fragen); Ich werde den Chat auf jeden Fall das nächste Mal ausprobieren. Zum Glück hat es diesmal mit einer sehr akzeptablen Antwort geklappt ... Prost!
Sdaau
@sdaau, hast du herausgefunden, wie der Festplattenzugriff überwacht werden kann?
Ransh

Antworten:

10

Sie haben in einer Frage viel zu viel gefragt - nun ja, technisch gesehen nicht, da ich denke, dass "dieses Verständnis richtig ist", kann dies schnell beantwortet werden: Nein. Aber das ist keine nützliche Antwort.

Erstens haben Sie Recht ata_piixund werden sd_modanscheinend in Ihren Kernel kompiliert. Das ist eine Wahl, die Sie treffen, um den Kernel zu konfigurieren - Sie können ihn weglassen, einschließen oder als Modul einschließen. (Dasselbe mit ext4).

Zweitens haben Sie angenommen, dass Schreibvorgänge viel einfacher sind als sie tatsächlich sind. Der grundlegende Umriss der Funktionsweise eines Schreibvorgangs besteht darin, dass der Dateisystemcode die zu schreibenden Daten als Teil des Puffercaches in den Speicher legt und sie als zu schreibend markiert ("unsauber"). (Es sei denn, es befindet sich bereits zu viel davon im RAM. In diesem Fall ist es tatsächlich gezwungen, den Schreibvorgang auszuführen ...)

Später werden bdflushdie verschmutzten Seiten von verschiedenen Dingen (wie dem Kernel-Thread) auf die Festplatte geschrieben. Dies ist der Fall, wenn Aufrufe über sd, scsi, libata, ata_piix, io-Scheduler, PCI usw. angezeigt werden. Es ist zwar sehr wahrscheinlich, dass DMA an dieser Ausgabe beteiligt ist, es handelt sich jedoch um die zu übertragenden Daten und möglicherweise um den Befehl. Festplattenschreibvorgänge werden jedoch zumindest in SATA durch Senden von Befehlen behandelt, die im Grunde genommen "Sektor X mit Daten Y schreiben" bedeuten. Es wird jedoch definitiv nicht durch die Speicherzuordnung der gesamten Festplatte verarbeitet.

Die Zwischenspeicherung wird vom Speicherverwaltungssubsystem (kein Treiber) in Verbindung mit dem Dateisystem, der Blockschicht usw. übernommen.

tmpfsist speziell, es ist im Grunde ganz Cache. Es ist nur ein spezieller Cache, der niemals verworfen oder zurückgeschrieben wird (obwohl er ausgetauscht werden kann). Sie können den Code an mm/shmem.cund an mehreren anderen Stellen finden (versuchen Sie ack-grep --cc CONFIG_TMPFS, sie zu finden).

Grundsätzlich durchläuft das Schreiben auf die Festplatte einen Großteil der Subsysteme des Kernels. Networking ist die einzige große Sache, an die ich denken kann, die nicht in Ihr Beispiel verwickelt ist. Um es richtig zu erklären, ist ein Aufwand in Buchgröße erforderlich. Ich empfehle, nach einem zu suchen.

derobert
quelle
Hallo @derobert - vielen, vielen Dank für deine Antwort; es enthält genau die Informationen, die ich vermisst habe! Ursprünglich suchte ich nach einer einfachen Darstellung von Benutzer- und Kernel-Speicherplatz, aber bald wurde mir klar, dass ein Festplattenschreibvorgang nicht genau das ist, was ich vollständig verstehe, und dass er nicht so trivial ist - danke, dass ich bestätigt habe, dass es sich tatsächlich um ein Buch handelt. Längenaufwand! Prost!
Sdaau
Ein kleiner Hinweis: Einige der Erklärungen in dieser Antwort (z. B. "Dirty Pages Flushing") sind zu beobachten, wenn im sudo bash...Skript im OP: ftrace memory erhöht wird ( echo 8192 > $KDBGPATH/buffer_size_kb); und sync ;wird nach dem ./wtest ;Anruf hinzugefügt . Dann kann ich sehen flush-8, kworker(unter kthreaddin ps axf), und syncselbst, wie Prozessen in den ftraceAnruffunktionen wie zB ata_bmdma_setup()(der Teil ist libata, der ata_piixauf Builds), oder get_nr_dirty_inodes().
Sdaau
4

Meine erste Frage lautet also: Warum kann ich das ata_piix-Modul hier nicht nur in Startprotokollen beobachten? Liegt es daran, dass ata_piix (und sd) als integrierte Treiber im (monolithischen) Kernel erstellt werden und nicht als (ladbare) .ko-Kernelmodule?

Sie müssen nicht raten, wie Ihre Konfiguration lautet. Auf meiner Maschine habe ich

$ uname -a
Linux orwell 3.2.0-4-amd64 #1 SMP Debian 3.2.51-1 x86_64 GNU/Linux

Die Konfigurationsdatei für diesen Kernel befindet sich unter /boot/config-3.2.0-4-amd64.

Sie fragten nach ata_piix. Beim Durchsuchen der obigen .configDatei sehen wir CONFIG_ATA_PIIX=m. wir können dies dadurch bestätigen

dlocate ata_piix.ko   

Alternative

dpkg -S ata_piix.ko

linux-image-3.2.0-4-amd64: /lib/modules/3.2.0-4-amd64/kernel/drivers/ata/ata_piix.ko

Zumindest in meinem Kernel handelt es sich also um ein Modul.

Faheem Mitha
quelle
Vielen Dank dafür, @FaheemMitha - obwohl ich schon einmal von der Konfigurationsdatei gehört habe (und sie verwendet habe), habe ich sie in diesem Beispiel aus irgendeinem Grund völlig vergessen. schön entdeckt! :)Auf meinem System grep ATA_PIIX /boot/config-2.6.38-16-genericheißt das CONFIG_ATA_PIIX=y, was auf diesem Kernel wohl heißen sollte ata_piix, "In-Kernel" bauen, und nicht als Modul. Prost!
Sdaau