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/sda
dispositivo (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/sda
disco 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_piix
o 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_piix
modulo qui, solo nei registri di avvio? È perché ata_piix
(e sd
) sono costruiti come driver integrati nel kernel (monolitico), anziché essere costruiti come .ko
moduli (caricabili) del kernel?
Bene, quindi ora sto cercando di osservare cosa succede quando si esegue il programma con il ftrace
tracciante 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 ftrace
registro 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 ext4
funzioni 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 blk
tracciante ftrace
per 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_graph
tracciante, 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 .ko
oggetti kernel separati , conservano tali informazioni (ad es. [psmouse]
mostrate sopra tra parentesi quadre). Pertanto, ftrace
potrebbe 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_piix
driver stabilisce una mappatura della memoria DMA (?) Tra/dev/sda
e il disco rigido- per questo motivo, tutti gli accessi futuri a
/dev/sda
viaata_piix
saranno 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 dalfunction_graph
tracciante
- per questo motivo, tutti gli accessi futuri a
- Al momento dell'avvio, il
sd
driver "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
sd
trasparenti le operazioni di accesso al kernel
- di nuovo, ciò dovrebbe rendere
- Dal momento che sia
ata_piix
esd
sono compilati nel kernel, anche se alcune delle loro funzioni finisce per essere catturato daiftrace
, non possiamo ottenere un'informazione di quale modulo quelle funzioni sarebbe venuto da (a parte correlazione "manuale" con i file sorgente) - Successivamente,
mount
stabilisce una relazione / associazione tra una partizione e il driver del file system corrispondente (in questo casoext4
)- da questo punto in poi, tutti gli accessi al filesystem montato sarebbero gestiti da
ext4
funzioni - che sono rintracciabili dal kernel; ma comeext4
è compilato nel kernel, il tracer non può darci le informazioni del modulo di origine
- da questo punto in poi, tutti gli accessi al filesystem montato sarebbero gestiti da
- Quindi, le scritture "generiche" osservate, chiamate tramite
ext4
funzioni, alla fine avrebbero accesso alle posizioni di memoria, la cui mappatura è stabilita daata_piix
- ma a parte questo,ata_piix
non 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 shm
per me i rapporti:none on /dev/shm type tmpfs (rw,nosuid,nodev)
. Ciò significa che - al contrario di/dev/sda
- alshm
filesystem manca semplicemente la mappatura (DMA) dai "suoi" indirizzi agli indirizzi bus verso un dispositivo; e quindi tutti gli accessi tramite iltmpfs
driver del filesystem finiscono nella RAM effettiva?