Observar una escritura en el disco duro en el espacio del kernel (con controladores / módulos)

13

Disculpas de antemano si esta publicación es un poco densa / desordenada, pero me cuesta formularla mejor ... Básicamente, me gustaría estudiar qué sucede en una escritura en el disco duro, y me gustaría saber:

  • ¿Es correcto mi entendimiento a continuación? Y si no, ¿dónde me estoy equivocando?
  • ¿Existe una herramienta mejor para "capturar" datos de registro, sobre todos los aspectos que suceden en la PC, durante una escritura de disco?

Más detalladamente: primero, el sistema operativo que estoy usando es:

$ 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

Por lo tanto, tengo el siguiente programa simple de espacio de usuario C (por ejemplo, se omiten las comprobaciones habituales de falla de operaciones) 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;
}

Construyo esto con gcc -g -O0 -o wtest wtest.c. Ahora, ya que estoy tratando de escribir /tmp, noto que es un directorio bajo la raíz /, así que verifico 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)
...

Por lo tanto, mi sistema de archivos raíz /es una partición del /dev/sdadispositivo (y también estoy usando otras particiones como discos / montajes "independientes"). Para encontrar el controlador para este dispositivo, uso 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
...

Entonces, el /dev/sdadisco duro aparentemente es manejado por ata_piix(y sd) el controlador.

$ 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

Tengo que retirarme del syslog anterior ya que suspendo mucho, pero lo anterior parece ser el fragmento adecuado del syslog en el momento del arranque, donde el controlador ata_piix(y sd) se activa por primera vez.

Mi primer punto de confusión es que no puedo observar los controladores 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

Entonces, mi primera pregunta es: ¿por qué no puedo observar el ata_piixmódulo aquí, solo en los registros de tiempo de arranque? ¿Es porque ata_piix(y sd) están construidos como controladores incorporados en el núcleo (monolítico), en lugar de estar construidos como .komódulos de núcleo (cargables) ?

Bien, ahora estoy tratando de observar qué sucede al ejecutar el programa con el ftracerastreador de funciones incorporado de 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
'

... y aquí hay un fragmento del ftraceregistro sobre write:

4604.352690 | 0) wtest-31632 | El | sys_write () {
 4604.352690 | 0) wtest-31632 | 0.750 nosotros | fget_light ();
 4604.352692 | 0) wtest-31632 | El | vfs_write () {
 4604.352693 | 0) wtest-31632 | El | rw_verify_area () {
 4604.352693 | 0) wtest-31632 | El | security_file_permission () {
 4604.352694 | 0) wtest-31632 | El | apparmor_file_permission () {
 4604.352695 | 0) wtest-31632 | 0.811 nosotros | common_file_perm ();
 4604.352696 | 0) wtest-31632 | 2.198 nosotros | }
 4604.352697 | 0) wtest-31632 | 3.573 nosotros | }
 4604.352697 | 0) wtest-31632 | 4.979 nosotros | }
 4604.352698 | 0) wtest-31632 | El | do_sync_write () {
 4604.352699 | 0) wtest-31632 | El | ext4_file_write () {
 4604.352700 | 0) wtest-31632 | El | generic_file_aio_write () {
 4604.352701 | 0) wtest-31632 | El | mutex_lock () {
 4604.352701 | 0) wtest-31632 | 0.666 nosotros | _cond_resched ();
 4604.352703 | 0) wtest-31632 | 1.994 nosotros | }
 4604.352704 | 0) wtest-31632 | El | __generic_file_aio_write () {
...
 4604.352728 | 0) wtest-31632 | El | file_update_time () {
...
 4604.352732 | 0) wtest-31632 | 0.756 nosotros | mnt_want_write_file ();
 4604.352734 | 0) wtest-31632 | El | __mark_inode_dirty () {
...
 4604.352750 | 0) wtest-31632 | El | ext4_mark_inode_dirty () {
 4604.352750 | 0) wtest-31632 | 0.679 nosotros | _cond_resched ();
 4604.352752 | 0) wtest-31632 | El | ext4_reserve_inode_write () {
...
 4604.352777 | 0) wtest-31632 | El | __ext4_journal_get_write_access () {
...
 4604.352795 | 0) wtest-31632 | El | ext4_mark_iloc_dirty () {
...
 4604.352806 | 0) wtest-31632 | El | __ext4_journal_stop () {
...
 4604.352821 | 0) wtest-31632 | 0.684 nosotros | mnt_drop_write ();
 4604.352822 | 0) wtest-31632 | + 93.541 nosotros | }
 4604.352823 | 0) wtest-31632 | El | generic_file_buffered_write () {
 4604.352824 | 0) wtest-31632 | 0.654 nosotros | iov_iter_advance ();
 4604.352825 | 0) wtest-31632 | El | generic_perform_write () {
 4604.352826 | 0) wtest-31632 | 0,709 nosotros | iov_iter_fault_in_readable ();
 4604.352828 | 0) wtest-31632 | El | ext4_da_write_begin () {
 4604.352829 | 0) wtest-31632 | El | ext4_journal_start_sb () {
...
 4604.352847 | 0) wtest-31632 | 1.453 nosotros | __block_write_begin ();
 4604.352849 | 0) wtest-31632 | + 21.128 nosotros | }
 4604.352849 | 0) wtest-31632 | El | iov_iter_copy_from_user_atomic () {
 4604.352850 | 0) wtest-31632 | El | __kmap_atomic () {
...
 4604.352863 | 0) wtest-31632 | 0.672 nosotros | mark_page_accessed ();
 4604.352864 | 0) wtest-31632 | El | ext4_da_write_end () {
 4604.352865 | 0) wtest-31632 | El | generic_write_end () {
 4604.352866 | 0) wtest-31632 | El | block_write_end () {
...
 4604.352893 | 0) wtest-31632 | El | __ext4_journal_stop () {
...
 4604.352909 | 0) wtest-31632 | 0.655 nosotros | mutex_unlock ();
 4604.352911 | 0) wtest-31632 | 0.727 nosotros | generic_write_sync ();
 4604.352912 | 0) wtest-31632 | ! 212.259 nosotros | }
 4604.352913 | 0) wtest-31632 | ! 213.845 nosotros | }
 4604.352914 | 0) wtest-31632 | ! 215.286 nosotros | }
 4604.352914 | 0) wtest-31632 | 0.685 nosotros | __fsnotify_parent ();
 4604.352916 | 0) wtest-31632 | El | fsnotify () {
 4604.352916 | 0) wtest-31632 | 0.907 nosotros | __srcu_read_lock ();
 4604.352918 | 0) wtest-31632 | 0.685 nosotros | __srcu_read_unlock ();
 4604.352920 | 0) wtest-31632 | 3.958 nosotros | }
 4604.352920 | 0) wtest-31632 | ! 228.409 nosotros | }
 4604.352921 | 0) wtest-31632 | ! 231.334 nosotros | }

Este es mi segundo punto de confusión: puedo observar el espacio de usuario write()resultante con un espacio de núcleo sys_write(), como se esperaba; y dentro de sys_write(), observo funciones relacionadas con la seguridad (p apparmor_file_permission(). ej. ), funciones de escritura "genéricas" (p generic_file_aio_write(). ej. ), ext4funciones relacionadas con el sistema de archivos (p ext4_journal_start_sb(). ej. ), pero no observo nada relacionado con ata_piix(o sd) controladores.

La página Tracing and Profiling - Yocto Project sugiere usar el blktrazador ftracepara obtener más información sobre el funcionamiento del dispositivo de bloque, pero no informa nada para mí con este ejemplo. Además, Controladores del sistema de archivos de Linux: Annon Inglorion (tutorfs) sugiere que los sistemas de archivos también se pueden implementar como módulos / controladores de kernel, y supongo que ese es el caso ext4también.

Finalmente, podría haber jurado que anteriormente había observado el nombre del conductor entre corchetes junto a la función mostrada por el function_graphtrazador, pero supongo que había mezclado las cosas, probablemente puede parecer así en los trazados de pila (atrás), pero no en el gráfico de funciones. Además, puedo inspeccionar /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]
...

... y comprobando con la fuente Linux / drivers / ata / ata_piix.c , confirme que, por ejemplo, sí piix_init_sata_mapes una función en ata_piix. Lo que probablemente debería decirme que: los módulos que se compilan en el núcleo (para que se conviertan en parte del núcleo monolítico) "pierden" la información sobre el módulo del que provienen; sin embargo, los módulos cargables que se construyen como .koobjetos de kernel separados , conservan esa información (por ejemplo, se [psmouse]muestra entre corchetes). Por lo tanto, también ftracesolo podría mostrar información del "módulo de origen", solo para aquellas funciones que provienen de módulos del núcleo cargables. ¿Es esto correcto?

Teniendo en cuenta lo anterior, esta es la comprensión que tengo del proceso actualmente:

  • En el momento del arranque, el ata_piixcontrolador establece una asignación de memoria DMA (?) Entre /dev/sday el disco duro
    • debido a esto, todos los accesos futuros a /dev/sdavia ata_piixserán transparentes para el kernel (es decir, no rastreables), ya que todo el kernel vería, son solo lecturas / escrituras en ubicaciones de memoria (no necesariamente llamadas a funciones específicas del kernel rastreable), que no son reportados por el function_graphtrazador
  • En el momento del arranque, el sdcontrolador además "analizará" las particiones /dev/sda, las hará disponibles y posiblemente manejará las asignaciones de memoria entre particiones <-> dispositivo de disco
    • nuevamente, esto debería hacer que las operaciones de acceso sean sdtransparentes para el núcleo
  • Dado que ambos ata_piixy sdestán compilados en el núcleo, incluso si algunas de sus funciones terminan siendo capturadas por ftrace, no podemos obtener una información de qué módulo vendrían esas funciones (aparte de la correlación "manual" con los archivos fuente)
  • Más adelante, mountestablece una relación / enlace entre una partición y el controlador del sistema de archivos correspondiente (en este caso ext4)
    • a partir de este momento, todos los accesos al sistema de archivos montado serían manejados por ext4funciones, que el núcleo puede rastrear; pero como ext4se compila en el núcleo, el trazador no puede proporcionarnos la información del módulo de origen
  • Entonces, las escrituras "genéricas" observadas, llamadas a través de ext4funciones, finalmente accederían a ubicaciones de memoria, cuyo mapeo se establece por ata_piix, pero aparte de eso, ata_piixno interfiere directamente con las transferencias de datos (probablemente sea manejado por DMA (fuera del procesador (s), y por lo tanto transparente para ello).

¿Es correcto este entendimiento?

Algunas preguntas secundarias relacionadas:

  • En mi configuración anterior, puedo identificar un controlador de dispositivo PCI ( ata_piix) y un controlador de sistema de archivos ( ext4); pero, ¿hay controladores de caracteres o bloques utilizados en alguna parte de la ruta de ejecución de "escritura" y, de ser así, cuáles son?
  • ¿Cuál de esos controladores manejaría el almacenamiento en caché (por lo que se saltan u optimizan las operaciones innecesarias del disco?)
  • Sé de antes que /dev/shmes un sistema de archivos en RAM; mount | grep shmpara mí informa: none on /dev/shm type tmpfs (rw,nosuid,nodev). ¿Significa eso que, en contraste con /dev/sda, el shmsistema de archivos simplemente carece de la asignación (DMA) de direcciones "propias" a direcciones de bus hacia un dispositivo; y por lo tanto todos los accesos a través del tmpfscontrolador del sistema de archivos terminan en la RAM real?
sdaau
fuente
44
Hola sdaau Estas son buenas preguntas, pero la duración de esta publicación es excesiva, y hay varias preguntas allí. Es recomendable que estés tratando de entender las cosas, en lugar de solo hacer preguntas a la mesa de ayuda, que es lo que generalmente recibimos. Cada una de estas preguntas merecería una respuesta larga por sí mismas. Recomiendo al menos dividir su publicación en partes claramente definidas y colocar cada pieza en una pregunta separada, creando así una serie de preguntas.
Faheem Mitha
Luego puede publicar estas preguntas juntas o secuencialmente. Está bien, creo, si hace referencia a otra pregunta (o preguntas) dentro de una pregunta.
Faheem Mitha
1
Si desea consejos sobre cómo resolver su pregunta, le sugiero que entre a la sala de chat y hable con la gente de allí. Ya hemos estado hablando de eso aquí. :-)
Faheem Mitha
Muchas gracias por el comentario, @FaheemMitha: también tenía dudas similares, pero no estaba muy seguro de cómo cortar las preguntas, y hasta ahora no sabía que podía usar el chat (y no estaba interesado en usando meta para preguntar sobre ese tipo de consejo); Definitivamente probaré el chat la próxima vez. Afortunadamente, esta vez funcionó con una respuesta muy aceptable ... ¡Salud!
sdaau
@sdaau, ¿descubriste cómo monitorear el acceso al disco?
ransh

Respuestas:

10

Has hecho demasiadas preguntas en una pregunta, bueno, técnicamente no, ya que supongo que "esta comprensión es correcta" se puede responder rápidamente: no. Pero esa no es una respuesta útil.

Primero, tienes razón ata_piixy sd_modaparentemente estás compilado en tu núcleo. Esa es una elección que realiza al configurar el kernel: puede omitirlo, incluirlo o incluirlo como módulo. (Lo mismo con ext4).

En segundo lugar, ha asumido que las escrituras son mucho más simples de lo que realmente son. El esquema básico de cómo funciona una escritura es que el código del sistema de archivos coloca los datos que se escribirán en la memoria, como parte de la memoria caché del búfer, y los marca como necesarios para ser escritos ("sucios"). (A menos que ya haya demasiado de eso en la RAM, en cuyo caso se ve obligado a escribir ...)

Más tarde, varias cosas (como el bdflushhilo del núcleo) en realidad descargan las páginas sucias en el disco. Esto es cuando vería llamadas a través de sd, scsi, libata, ata_piix, io Schedulers, PCI, etc. Si bien es muy probable que DMA esté involucrado en esa escritura, son los datos a transferir, y tal vez el comando. Pero las escrituras en disco, al menos en SATA, se manejan enviando comandos que básicamente significan "escribir sector X con datos Y". Pero definitivamente no se maneja mediante el mapeo de memoria de todo el disco (considere: puede usar discos mucho más grandes que 4GiB en máquinas de 32 bits).

El subsistema de administración de memoria (no un controlador) maneja el almacenamiento en caché, junto con el sistema de archivos, la capa de bloques, etc.

tmpfses especial, básicamente es completamente caché. Es solo un caché especial que nunca se descarta ni se vuelve a escribir (aunque se puede cambiar). Puede encontrar el código en mm/shmem.cy en otros lugares (intente ack-grep --cc CONFIG_TMPFSencontrarlos).

Básicamente, escribir en el disco pasa por una buena parte de los subsistemas del núcleo; La creación de redes es la única importante que se me ocurre que no está involucrada en su ejemplo. Explicarlo adecuadamente requiere un esfuerzo de larga duración; Recomiendo buscar uno.

derobert
fuente
Hola @derobert. Muchas, muchas gracias por tu respuesta. ¡contiene el tipo exacto de información que me faltaba! Originalmente comencé a buscar una ilustración simple de espacio de usuario versus kernel, pero pronto me di cuenta de que una escritura en el disco duro no es exactamente algo que entiendo completamente, y no es tan trivial, gracias por confirmar que en realidad es un libro. esfuerzo de longitud! ¡Salud!
sdaau
Una pequeña nota: parte de la explicación en esta respuesta (por ejemplo, lavado de páginas sucias) es observable, si en el sudo bash...script en el OP: ftrace memory ( echo 8192 > $KDBGPATH/buffer_size_kb); y sync ;se agrega después de la ./wtest ;llamada. Entonces puedo ver flush-8, kworker(debajo kthreaddde ps axf), y en syncsí mismo, como procesos en ftracellamadas a funciones como por ejemplo ata_bmdma_setup()(que es parte de libata, que se ata_piixbasa en), o get_nr_dirty_inodes().
sdaau
4

Entonces, mi primera pregunta es: ¿por qué no puedo observar el módulo ata_piix aquí, solo en los registros de tiempo de arranque? ¿Es porque ata_piix (y sd) se construyen como controladores integrados en el kernel (monolítico), en lugar de construirse como módulos de kernel .ko (cargables)?

No tiene que adivinar cuál es su configuración. En mi máquina tengo

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

El archivo de configuración para este núcleo se encuentra en /boot/config-3.2.0-4-amd64.

Usted preguntó ata_piix. Buscando el .configarchivo anterior , vemos CONFIG_ATA_PIIX=m. podemos confirmar esto haciendo

dlocate ata_piix.ko   

alternativamente

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

Entonces, al menos en mi kernel, es un módulo.

Faheem Mitha
fuente
Muchas gracias por eso, @FaheemMitha, aunque he oído hablar (y he usado) el archivo de configuración antes, por alguna razón me he olvidado por completo en este ejemplo; muy bien visto! :)En mi sistema, grep ATA_PIIX /boot/config-2.6.38-16-genericdice CONFIG_ATA_PIIX=y, lo que probablemente debería significar en este núcleo, ata_piixes construir "en el núcleo", y no como un módulo. ¡Salud!
sdaau