Je m'excuse à l'avance si ce billet est un peu dense / désordonné, mais j'ai du mal à mieux le formuler ... En gros, j'aimerais étudier ce qui se passe sur une écriture sur le disque dur, et j'aimerais savoir:
- Ma compréhension ci-dessous est-elle correcte - et sinon, où vais-je me tromper?
- Existe-t-il un meilleur outil pour «capturer» les données du journal, sur tous les aspects qui se produisent sur le PC, lors d'une écriture sur disque?
Plus en détail - premièrement, le système d'exploitation que j'utilise est:
$ 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
Donc, j'ai le programme C en espace utilisateur simple (par exemple, les contrôles habituels pour l'échec des opérations sont ignorés) 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;
}
Je construis cela avec gcc -g -O0 -o wtest wtest.c
. Maintenant, puisque j'essaie d'écrire dans /tmp
, je note qu'il s'agit d'un répertoire sous la racine /
- je vérifie donc 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)
...
Donc, mon système de fichiers racine /
est une partition de l' /dev/sda
appareil (et j'utilise également d'autres partitions comme disques / montages "autonomes"). Pour trouver le pilote de cet appareil, j'utilise 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
...
Ainsi, le /dev/sda
disque dur est apparemment géré par ata_piix
(et sd
) le pilote.
$ 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
Je dois retirer d'anciens syslog car je suspend beaucoup, mais ce qui précède semble être l'extrait approprié du syslog au démarrage, où le pilote ata_piix
(et sd
) entre en action pour la première fois.
Mon premier point de confusion est que je ne peux pas autrement observer les pilotes ata_piix
ou 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
Donc, ma première question est - pourquoi ne puis-je pas observer le ata_piix
module ici, uniquement dans les journaux de démarrage? Est-ce parce que ata_piix
(et sd
) sont construits en tant que pilotes intégrés dans le noyau (monolithique), par opposition à être construits en tant que .ko
modules du noyau (chargeables) ?
Bon - maintenant j'essaie d'observer ce qui se passe lors de l'exécution du programme avec la ftrace
fonction intégrée de traçage 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
'
... et voici un extrait du ftrace
journal concernant 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 nous | } 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 nous | } 4604.352704 | 0) wtest-31632 | | __generic_file_aio_write () { ... 4604.352728 | 0) wtest-31632 | | file_update_time () { ... 4604.352732 | 0) wtest-31632 | 0,756 nous | 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 nous | 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 nous | 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 nous | 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 | }
C'est mon deuxième point de confusion - je peux observer l'espace utilisateur write()
résultant avec un espace noyau sys_write()
, comme prévu; et dans le sys_write()
, je vois les fonctions liées à la sécurité (par exemple apparmor_file_permission()
), les fonctions d'écriture « génériques » (par exemple generic_file_aio_write()
), ext4
Système de fichiers connexes (par exemple ext4_journal_start_sb()
) - mais je ne pas observer tout ce qui concerne ata_piix
(ou sd
pilotes)?!
La page Tracing and Profiling - Yocto Project suggère d'utiliser le blk
traceur ftrace
pour obtenir plus d'informations sur le fonctionnement du périphérique de bloc, mais il ne rapporte rien pour moi avec cet exemple. En outre, les pilotes de systèmes de fichiers Linux - Annon Inglorion (tutorfs) suggèrent que les systèmes de fichiers sont (peuvent?) Également (être) mis en œuvre en tant que modules / pilotes du noyau, et je suppose que c'est également le cas ext4
.
Enfin, j'aurais juré avoir déjà observé le nom du pilote entre crochets à côté de la fonction indiquée par le function_graph
traceur, mais je suppose que j'avais mélangé les choses - cela peut probablement apparaître comme ça dans les traces de pile (arrière), mais pas dans le graphique de fonction. De plus, je peux inspecter /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]
...
... et en vérifiant avec la source Linux / drivers / ata / ata_piix.c , confirmez par exemple que piix_init_sata_map
c'est bien une fonction dans ata_piix
. Ce qui devrait probablement me dire que: les modules qui sont compilés dans le noyau (afin qu'ils deviennent une partie du noyau monolithique) "perdent" les informations sur le module dont ils proviennent; cependant, les modules chargeables qui sont construits en tant .ko
qu'objets de noyau séparés , conservent ces informations (par exemple, [psmouse]
montrés ci-dessus entre crochets). Ainsi, également ftrace
ne pouvait afficher que des informations sur le "module d'origine", uniquement pour les fonctions provenant des modules du noyau chargeables. Est-ce correct?
Ce qui précède pris en considération, c'est la compréhension que j'ai du processus actuellement:
- Au démarrage, le
ata_piix
pilote établit un mappage de mémoire DMA (?) Entre/dev/sda
et le disque dur- pour cette raison, tous les futurs accès à
/dev/sda
viaata_piix
seront transparents pour le noyau (c'est-à-dire non traçables) - puisque tout le noyau verrait, ne sont que des lectures / écritures aux emplacements de mémoire (pas nécessairement des appels à des fonctions spécifiques du noyau traçables), qui ne sont pas signalés par lefunction_graph
traceur
- pour cette raison, tous les futurs accès à
- Au démarrage, le
sd
pilote "analysera" en outre les partitions de/dev/sda
, les rendra disponibles et gérera éventuellement les mappages de mémoire entre les partitions <-> périphérique de disque- encore une fois, cela devrait rendre les opérations d'accès via
sd
transparent pour le noyau
- encore une fois, cela devrait rendre les opérations d'accès via
- Étant donné que les deux
ata_piix
etsd
sont compilés dans le noyau, même si certaines de leurs fonctions finissent par être capturées parftrace
, nous ne pouvons pas obtenir d'informations sur le module dont ces fonctions proviendraient (à part la corrélation "manuelle" avec les fichiers source) - Plus tard,
mount
établit une relation / liaison entre une partition et le pilote de système de fichiers correspondant (dans ce casext4
)- à partir de ce moment, tous les accès au système de fichiers monté seraient gérés par des
ext4
fonctions - qui sont traçables par le noyau; mais comme ilext4
est compilé dans le noyau, le traceur ne peut pas nous donner les informations du module d'origine
- à partir de ce moment, tous les accès au système de fichiers monté seraient gérés par des
- Ainsi, les écritures «génériques» observées, appelées via des
ext4
fonctions, accéderaient finalement à des emplacements de mémoire, dont le mappage est établi parata_piix
- mais à part cela,ata_piix
n'interfère pas directement avec les transferts de données (il est probablement géré par DMA (en dehors du processeur) (s), et donc transparent pour elle).
Cette compréhension est-elle correcte?
Quelques sous-questions connexes:
- Dans ma configuration ci-dessus, je peux identifier un pilote de périphérique PCI (
ata_piix
) et un pilote de système de fichiers (ext4
); mais y a-t-il des pilotes de caractères ou de blocs utilisés quelque part sur le chemin d'exécution "d'écriture", et si oui, lesquels sont-ils? - Lequel de ces pilotes gérerait la mise en cache (donc les opérations inutiles sur le disque sont ignorées ou optimisées?)
- Je sais d'avant que
/dev/shm
c'est un système de fichiers en RAM;mount | grep shm
pour moi des rapports:none on /dev/shm type tmpfs (rw,nosuid,nodev)
. Est-ce à dire que - contrairement à/dev/sda
- leshm
système de fichiers n'a tout simplement pas le mappage (DMA) de "ses propres" adresses aux adresses de bus vers un périphérique; et donc tous les accès via letmpfs
pilote du système de fichiers se retrouvent dans la RAM réelle?