Observer une écriture sur disque dur dans l'espace noyau (avec pilotes / modules)

13

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/sdaappareil (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/sdadisque 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_piixou 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_piixmodule 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 .komodules du noyau (chargeables) ?

Bon - maintenant j'essaie d'observer ce qui se passe lors de l'exécution du programme avec la ftracefonction 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 ftracejournal 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()), ext4Système de fichiers connexes (par exemple ext4_journal_start_sb()) - mais je ne pas observer tout ce qui concerne ata_piix(ou sdpilotes)?!

La page Tracing and Profiling - Yocto Project suggère d'utiliser le blktraceur ftracepour 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_graphtraceur, 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_mapc'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 .koqu'objets de noyau séparés , conservent ces informations (par exemple, [psmouse]montrés ci-dessus entre crochets). Ainsi, également ftracene 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_piixpilote établit un mappage de mémoire DMA (?) Entre /dev/sdaet le disque dur
    • pour cette raison, tous les futurs accès à /dev/sdavia ata_piixseront 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 le function_graphtraceur
  • Au démarrage, le sdpilote "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 sdtransparent pour le noyau
  • Étant donné que les deux ata_piixet sdsont compilés dans le noyau, même si certaines de leurs fonctions finissent par être capturées par ftrace, 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 cas ext4)
    • à partir de ce moment, tous les accès au système de fichiers monté seraient gérés par des ext4fonctions - qui sont traçables par le noyau; mais comme il ext4est compilé dans le noyau, le traceur ne peut pas nous donner les informations du module d'origine
  • Ainsi, les écritures «génériques» observées, appelées via des ext4fonctions, accéderaient finalement à des emplacements de mémoire, dont le mappage est établi par ata_piix- mais à part cela, ata_piixn'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/shmc'est un système de fichiers en RAM; mount | grep shmpour moi des rapports: none on /dev/shm type tmpfs (rw,nosuid,nodev). Est-ce à dire que - contrairement à /dev/sda- le shmsystè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 le tmpfspilote du système de fichiers se retrouvent dans la RAM réelle?
sdaau
la source
4
Salut sdaau. Ce sont de bonnes questions, mais la longueur de ce message est excessive et il y a plusieurs questions. Il est louable que vous essayiez de comprendre les choses, plutôt que de simplement poser des questions au service d'assistance, ce que nous obtenons principalement ici. Chacune de ces questions mériterait à elle seule une longue réponse. Je recommande au moins de diviser votre message en morceaux clairement définis et de placer chaque morceau dans une question distincte, créant ainsi une série de questions.
Faheem Mitha
Ensuite, vous pouvez publier ces questions ensemble ou séquentiellement. C'est correct, je pense, si vous référencez une autre question (ou des questions) dans une question.
Faheem Mitha
1
Si vous voulez des conseils sur le nettoyage de votre question, je vous suggère de monter dans le salon de discussion et de parler aux gens là-bas. Nous en avons déjà parlé ici. :-)
Faheem Mitha
Merci beaucoup pour le commentaire, @FaheemMitha - J'avais aussi des doutes similaires, mais je ne savais pas vraiment comment découper les questions - et je ne savais pas jusqu'à présent que je pouvais utiliser le chat pour cela (et je n'aimais pas utiliser des méta pour poser des questions sur ce genre de conseils); va certainement essayer le chat la prochaine fois. Heureusement, cette fois, cela a fonctionné avec une réponse très acceptable ... A bientôt!
sdaau
@sdaau, avez-vous compris comment surveiller l'accès au disque?
ransh

Réponses:

10

Vous en avez trop demandé dans une seule question - eh bien, techniquement non, car je suppose que «cette compréhension est-elle correcte», on peut y répondre rapidement: non. Mais ce n'est pas une réponse utile.

Tout d'abord, vous avez raison ata_piixet sd_modapparemment être compilé dans votre noyau. C'est un choix que vous faites en configurant le noyau - vous pouvez l'omettre, l'inclure ou l'inclure en tant que module. (Idem avec ext4).

Deuxièmement, vous avez supposé que les écritures étaient bien plus simples qu'elles ne le sont en réalité. L'aperçu de base du fonctionnement d'une écriture est que le code du système de fichiers place les données à écrire en mémoire, dans le cadre du cache-tampon, et les marque comme nécessitant d'être écrites ("sales"). (Sauf s'il y en a déjà trop dans la RAM, auquel cas il est en fait obligé de faire l'écriture ...)

Plus tard, diverses choses (telles que le bdflushthread du noyau) vident réellement les pages sales sur le disque. C'est à ce moment que vous voyez des appels via sd, scsi, libata, ata_piix, io schedulers, PCI, etc. Bien qu'il y ait très probablement du DMA impliqué dans cette écriture, il s'agit des données à transférer, et peut-être de la commande. Mais les écritures sur disque, au moins en SATA, sont gérées par l'envoi de commandes qui signifient essentiellement "écrire le secteur X avec les données Y". Mais ce n'est certainement pas géré par le mappage de la mémoire sur tout le disque (pensez: vous pouvez utiliser des disques bien plus grands que 4GiB sur des machines 32 bits).

La mise en cache est gérée par le sous-système de gestion de la mémoire (pas un pilote), conjointement avec le système de fichiers, la couche de bloc, etc.

tmpfsest spécial, il est essentiellement entièrement cache. Son cache juste spécial qui n'est jamais rejeté ou réécrit (bien qu'il puisse être échangé). Vous pouvez trouver le code dans mm/shmem.cet à plusieurs autres endroits (essayez ack-grep --cc CONFIG_TMPFSde les trouver).

Fondamentalement, l'écriture sur disque passe par une bonne partie des sous-systèmes du noyau; le réseautage est le seul grand auquel je puisse penser qui n'est pas impliqué dans votre exemple. Pour bien l'expliquer, il faut un effort de la longueur d'un livre; Je recommande d'en chercher un.

derobert
la source
Salut @derobert - merci beaucoup pour votre réponse; il contient le type exact d'informations qui me manquaient! Au départ, je cherchais une illustration simple de l'espace utilisateur par rapport à l'espace noyau, mais je me suis vite rendu compte qu'une écriture sur le disque dur n'était pas exactement quelque chose que je comprenais parfaitement, et n'était pas si simple - merci d'avoir confirmé qu'il s'agit en fait d'un livre - effort de longueur! À votre santé!
sdaau
Une petite note: une partie de l'explication dans cette réponse (par exemple le nettoyage des pages sales) est observable, si dans le sudo bash...script dans l'OP: la mémoire ftrace est augmentée ( echo 8192 > $KDBGPATH/buffer_size_kb); et sync ;est ajouté après l' ./wtest ;appel. Ensuite, je peux voir flush-8, kworker(sous kthreaddin ps axf), et synclui - même, comme des processus d' ftraceappel de fonctions comme par exemple ata_bmdma_setup()(qui fait partie de libata, qui ata_piixs'appuie sur), ou get_nr_dirty_inodes().
sdaau
4

Donc ma première question est - pourquoi ne puis-je pas observer le module ata_piix 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 modules de noyau .ko (chargeables)?

Vous n'avez pas à deviner quelle est votre configuration. Sur ma machine, j'ai

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

Le fichier de configuration est pour ce noyau est situé à /boot/config-3.2.0-4-amd64.

Vous avez demandé ata_piix. En recherchant le .configfichier ci-dessus , nous voyons CONFIG_ATA_PIIX=m. nous pouvons le confirmer en faisant

dlocate ata_piix.ko   

alternativement

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

Donc au moins dans mon noyau, c'est un module.

Faheem Mitha
la source
Merci beaucoup pour cela, @FaheemMitha - alors que j'ai entendu parler (et utilisé) le fichier de configuration avant, pour une raison quelconque, je l'ai complètement oublié dans cet exemple; joliment repéré! :)Sur mon système, grep ATA_PIIX /boot/config-2.6.38-16-genericdit CONFIG_ATA_PIIX=y, ce qui devrait probablement signifier sur ce noyau, ata_piixest construit "dans le noyau", et non en tant que module. À votre santé!
sdaau