Osservare una scrittura sul disco rigido nello spazio del kernel (con driver / moduli)


13

Mi scuso in anticipo se questo post è un po 'denso / disordinato, ma sto facendo fatica a formularlo meglio ... Fondamentalmente, vorrei studiare cosa succede su una scrittura su disco rigido e mi piacerebbe sapere:

  • La mia comprensione al di sotto è corretta - e in caso contrario, dove sbaglio?
  • Esiste uno strumento migliore per "acquisire" i dati di registro, su tutti gli aspetti che si verificano sul PC, durante una scrittura su disco?

Più in dettaglio, in primo luogo, il sistema operativo che sto usando è:

$ 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

Quindi, ho il seguente semplice programma C (spazio utente per i controlli falliti) 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;
}

Lo costruisco con gcc -g -O0 -o wtest wtest.c. Ora, dal momento che sto provando a scrivere /tmp, noto che è una directory sotto il root /- quindi controllo 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)
...

Quindi, il mio filesystem di root /è una partizione del /dev/sdadispositivo (e sto usando anche altre partizioni come dischi / supporti "standalone"). Per trovare il driver per questo dispositivo, utilizzo 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
...

Quindi, il /dev/sdadisco rigido è apparentemente gestito dal ata_piix(e sd) driver.

$ 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

Devo estrarre dal vecchio syslog mentre sospendo molto, ma quanto sopra sembra lo snippet corretto dallo syslog al momento dell'avvio, in cui il ata_piix(e sd) driver entra in funzione per la prima volta.

Il mio primo punto di confusione è che non posso altrimenti osservare i driver ata_piixo sd:

$ 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

Quindi la mia prima domanda è: perché non posso osservare il ata_piixmodulo qui, solo nei registri di avvio? È perché ata_piix(e sd) sono costruiti come driver integrati nel kernel (monolitico), anziché essere costruiti come .komoduli (caricabili) del kernel?

Bene, quindi ora sto cercando di osservare cosa succede quando si esegue il programma con il ftracetracciante funzioni integrato di Linux.

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
'

... ed ecco un frammento del ftraceregistro relativo a 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 | }

Questo è il mio secondo punto di confusione: posso osservare lo spazio utente write()ottenuto con uno spazio kernel sys_write(), come previsto; e all'interno di sys_write(), osservo le funzioni relative alla sicurezza (ad es. apparmor_file_permission()), le funzioni di scrittura "generiche" (ad es. generic_file_aio_write()), le ext4funzioni relative al filesystem (ad es. ext4_journal_start_sb()) - ma non osservo nulla relativo a ata_piix(o sd) driver ?!

La pagina Tracing and Profiling - Yocto Project suggerisce di utilizzare il blktracciante ftraceper ottenere maggiori informazioni sul funzionamento del dispositivo a blocchi, ma con questo esempio non riporta nulla per me. Inoltre, i driver del filesystem di Linux - Annon Inglorion (tutorfs) suggeriscono che i filesystem sono (possono?) Anche (essere) implementati come moduli / driver del kernel, e immagino che anche questo sia il caso ext4.

Infine, avrei potuto giurare di aver precedentemente osservato il nome del driver tra parentesi quadre accanto alla funzione mostrata dal function_graphtracciante, ma immagino di aver mescolato le cose - probabilmente può apparire così nelle tracce dello stack (indietro), ma non nel grafico delle funzioni. Inoltre, posso ispezionare /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]
...

... e verificando con il sorgente Linux / drivers / ata / ata_piix.c , confermare che ad esempio piix_init_sata_mapè effettivamente una funzione in ata_piix. Il che probabilmente dovrebbe dirmi che: i moduli che sono compilati nel kernel (così diventano parte del kernel monolitico) "perdono" le informazioni su quale modulo provengono; tuttavia, i moduli caricabili che sono costruiti come .kooggetti kernel separati , conservano tali informazioni (ad es. [psmouse]mostrate sopra tra parentesi quadre). Pertanto, ftracepotrebbe anche mostrare solo informazioni sul "modulo di origine", solo per quelle funzioni che provengono da moduli caricabili del kernel. È corretto?

Quanto sopra preso in considerazione, questa è la comprensione che ho del processo attualmente:

  • Al momento dell'avvio, il ata_piixdriver stabilisce una mappatura della memoria DMA (?) Tra /dev/sdae il disco rigido
    • per questo motivo, tutti gli accessi futuri a /dev/sdavia ata_piixsaranno trasparenti al kernel (cioè non rintracciabili) - poiché tutto il kernel vedrebbe, sono solo letture / scritture in posizioni di memoria (non necessariamente chiamate a specifiche funzioni del kernel tracciabili), che non sono segnalati dal function_graphtracciante
  • Al momento dell'avvio, il sddriver "analizzerà" anche le partizioni /dev/sda, le renderà disponibili e possibilmente gestirà i mapping di memoria tra le partizioni <-> dispositivo disco
    • di nuovo, ciò dovrebbe rendere sdtrasparenti le operazioni di accesso al kernel
  • Dal momento che sia ata_piixe sdsono compilati nel kernel, anche se alcune delle loro funzioni finisce per essere catturato dai ftrace, non possiamo ottenere un'informazione di quale modulo quelle funzioni sarebbe venuto da (a parte correlazione "manuale" con i file sorgente)
  • Successivamente, mountstabilisce una relazione / associazione tra una partizione e il driver del file system corrispondente (in questo caso ext4)
    • da questo punto in poi, tutti gli accessi al filesystem montato sarebbero gestiti da ext4funzioni - che sono rintracciabili dal kernel; ma come ext4è compilato nel kernel, il tracer non può darci le informazioni del modulo di origine
  • Quindi, le scritture "generiche" osservate, chiamate tramite ext4funzioni, alla fine avrebbero accesso alle posizioni di memoria, la cui mappatura è stabilita da ata_piix- ma a parte questo, ata_piixnon interferire direttamente con i trasferimenti di dati (probabilmente gestito da DMA (al di fuori del processore) (s), e quindi trasparente ad esso).

Questa comprensione è corretta?

Alcune domande secondarie correlate:

  • Nella mia configurazione sopra, posso identificare un driver di dispositivo PCI ( ata_piix) e un driver di file system ( ext4); ma ci sono driver di caratteri o blocchi usati da qualche parte nel percorso di esecuzione "write", e se sì, quali sono?
  • Quale di questi driver gestirà la memorizzazione nella cache (quindi le operazioni su disco non necessarie vengono ignorate o ottimizzate?)
  • So prima che /dev/shmè un filesystem nella RAM; mount | grep shmper me i rapporti: none on /dev/shm type tmpfs (rw,nosuid,nodev). Ciò significa che - al contrario di /dev/sda- al shmfilesystem manca semplicemente la mappatura (DMA) dai "suoi" indirizzi agli indirizzi bus verso un dispositivo; e quindi tutti gli accessi tramite il tmpfsdriver del filesystem finiscono nella RAM effettiva?

4
Ciao sdaau. Questa è una buona domanda, ma la lunghezza di questo post è eccessiva e ci sono diverse domande. È encomiabile che tu stia provando a capire le cose, piuttosto che fare semplicemente domande sull'help desk, che è ciò che principalmente arriviamo qui. Ognuna di queste domande meriterebbe una risposta lunga da sola. Raccomando almeno di scomporre il tuo post in pezzi chiaramente definiti e di mettere ogni pezzo in una domanda separata, creando così una serie di domande.
Faheem Mitha,

Quindi è possibile inviare queste domande insieme o in sequenza. Va bene, penso, se fai riferimento a un'altra domanda (o domande) all'interno di una domanda.
Faheem Mitha,

1
Se vuoi suggerimenti su come pulire la tua domanda, ti suggerisco di entrare nella chat room e parlare con le persone lì. Ne abbiamo già parlato qui. :-)
Faheem Mitha

Mille grazie per il commento, @FaheemMitha - avevo anche dubbi simili, ma non ero davvero sicuro di come tagliare le domande - e fino ad ora non ero consapevole di poter usare la chat (e non ero entusiasta usare meta per chiedere quel tipo di consiglio); sicuramente la chat la prossima volta. Per fortuna, questa volta ha funzionato con una risposta molto accettabile ... Saluti!
sdaau,

@sdaau, hai capito come monitorare l'accesso al disco?
riscatto

Risposte:


10

Hai fatto troppe domande in una domanda — beh, tecnicamente no, come immagino che "questa comprensione sia corretta" si può rispondere rapidamente: no. Ma questa non è una risposta utile.

Innanzitutto, hai ragione ata_piixe sd_modapparentemente stai compilando il kernel. È una scelta che fai configurando il kernel: puoi ometterlo, includerlo o includerlo come modulo. (Lo stesso con ext4).

In secondo luogo, hai assunto che le scritture siano molto più semplici di quanto non siano in realtà. Lo schema di base di come funziona una scrittura è che il codice del filesystem mette i dati da scrivere in memoria, come parte della cache del buffer, e li contrassegna come necessità da scrivere ("sporco"). (A meno che non ci sia già troppo di quello nella RAM, nel qual caso è effettivamente costretto a fare la scrittura ...)

Successivamente, varie cose (come il bdflushthread del kernel) scaricano effettivamente le pagine sporche sul disco. Questo è quando vedresti le chiamate tramite sd, scsi, libata, ata_piix, io scheduler, PCI, ecc. Mentre è molto probabile che DMA sia coinvolto in quella scrittura, si tratta dei dati da trasferire e forse del comando. Ma le scritture su disco, almeno in SATA, vengono gestite inviando comandi che sostanzialmente significano "scrivere il settore X con i dati Y". Ma non è sicuramente gestito dalla mappatura della memoria dell'intero disco (considerare: è possibile utilizzare dischi molto più grandi di 4GiB su macchine a 32 bit).

La memorizzazione nella cache è gestita dal sottosistema di gestione della memoria (non da un driver), insieme al filesystem, al livello di blocco, ecc.

tmpfsè speciale, è praticamente interamente cache. È solo cache speciale che non viene mai scartata o riscritta (anche se può essere sostituita). Puoi trovare il codice in mm/shmem.ce molti altri posti (prova ack-grep --cc CONFIG_TMPFSa trovarli).

Fondamentalmente, la scrittura su disco passa attraverso una buona parte dei sottosistemi del kernel; il networking è l'unico a cui riesco a pensare che non è coinvolto nel tuo esempio. Spiegarlo correttamente richiede uno sforzo lungo il libro; Consiglio di cercarne uno.


Ciao @derobert - molte, molte grazie per la tua risposta; contiene il tipo esatto di informazioni che mi mancavano! Inizialmente ho iniziato a cercare una semplice illustrazione dello spazio utente vs. kernel, ma presto mi sono reso conto che una scrittura sul disco rigido non è esattamente qualcosa che capisco completamente, e non è così banale - grazie per aver confermato che in realtà è un libro- lungo sforzo! Saluti!
sdaau,

Una piccola nota: alcune delle spiegazioni contenute in questa risposta (ad es. Flushing di pagine sporche) sono osservabili, se nello sudo bash...script nell'OP: la memoria di ftrace è aumentata ( echo 8192 > $KDBGPATH/buffer_size_kb); e sync ;viene aggiunto dopo la ./wtest ;chiamata. Quindi posso vedere flush-8, kworker(sotto kthreaddin ps axf), e syncse stesso, come processi nel ftracechiamare funzioni come ad es. ata_bmdma_setup()(Di cui fa parte libata, su cui si ata_piixbasa), oppure get_nr_dirty_inodes().
sdaau,

4

Quindi la mia prima domanda è: perché non posso osservare il modulo ata_piix qui, solo nei registri di avvio? È perché ata_piix (e sd) sono compilati come driver integrati nel kernel (monolitico), anziché essere costruiti come moduli kernel caricabili .ko?

Non devi indovinare quale sia la tua configurazione. Sulla mia macchina ho

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

Il file di configurazione è per questo kernel si trova in /boot/config-3.2.0-4-amd64.

Hai chiesto di ata_piix. Cercando il .configfile sopra , vediamo CONFIG_ATA_PIIX=m. possiamo confermarlo facendo

dlocate ata_piix.ko   

in alternativa

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

Quindi almeno nel mio kernel, è un modulo.


Mille grazie per questo, @FaheemMitha - mentre ho già sentito parlare (e usato) il file di configurazione prima, per qualche motivo in questo esempio me ne sono completamente dimenticato; ben individuato! :)Sul mio sistema, grep ATA_PIIX /boot/config-2.6.38-16-genericdice CONFIG_ATA_PIIX=y, che probabilmente dovrebbe significare su questo kernel, ata_piixè compilato "in-kernel", e non come un modulo. Saluti!
sdaau,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.