Observando uma gravação no disco rígido no espaço do kernel (com drivers / módulos)

13

Peço desculpas antecipadamente se este post for um pouco denso / confuso, mas estou tendo dificuldades para formulá-lo melhor ... Basicamente, eu gostaria de estudar o que acontece com a gravação de um disco rígido e gostaria de saber:

  • Meu entendimento abaixo está correto - e se não, onde estou errado?
  • Existe uma ferramenta melhor para "capturar" dados de log, sobre todos os aspectos que acontecem no PC, durante uma gravação em disco?

Em mais detalhes - primeiro, o sistema operacional que estou 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

Então, eu tenho o seguinte programa simples (por exemplo, as verificações usuais de falha de operações são ignoradas) no espaço C do usuário 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;
}

Eu construo isso com gcc -g -O0 -o wtest wtest.c. Agora, como estou tentando escrever /tmp, notei que é um diretório sob a raiz /- então, verifiquei 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)
...

Portanto, meu sistema de arquivos raiz /é uma partição do /dev/sdadispositivo (e também estou usando outras partições como discos / montagens "independentes"). Para encontrar o driver para este dispositivo, eu 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
...

Portanto, o /dev/sdadisco rígido é aparentemente tratado pelo 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

Eu tenho que puxar do syslog mais antigo porque suspiro muito, mas o acima parece o snippet adequado do syslog no momento da inicialização, onde o driver ata_piix(e sd) entra em ação pela primeira vez.

Meu primeiro ponto de confusão é que, de outra forma, não posso observar os ata_piixou sddrivers:

$ 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

Portanto, minha primeira pergunta é - por que não consigo observar o ata_piixmódulo aqui, apenas nos logs de inicialização? É porque ata_piix(e sd) são construídos como drivers internos no kernel (monolítico), em vez de serem construídos como módulos do .kokernel (carregáveis) ?

Certo - agora, estou tentando observar o que acontece ao executar o programa com o ftracerastreador de funções interno do 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
'

... e aqui está um trecho do ftracelog referente a write:

4604.352690 | 0) wtest-31632 | | sys_write () {
 4604.352690 | 0) wtest-31632 | 0,750 nos | 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 nos | common_file_perm ();
 4604.352696 | 0) wtest-31632 | 2.198 nos | }
 4604.352697 | 0) wtest-31632 | 3.573 nos | }
 4604.352697 | 0) wtest-31632 | 4.979 nos | }
 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 nos | }
 4604.352704 | 0) wtest-31632 | | __generic_file_aio_write () {
...
 4604.352728 | 0) wtest-31632 | | file_update_time () {
...
 4604.352732 | 0) wtest-31632 | 0,756 nos | 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 nós | _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 nos | mnt_drop_write ();
 4604.352822 | 0) wtest-31632 | + 93.541 nos | }
 4604.352823 | 0) wtest-31632 | | generic_file_buffered_write () {
 4604.352824 | 0) wtest-31632 | 0,654 nós | 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 nos | __block_write_begin ();
 4604.352849 | 0) wtest-31632 | + 21.128 nos | }
 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 nós | 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 nós | mutex_unlock ();
 4604.352911 | 0) wtest-31632 | 0,727 us | generic_write_sync ();
 4604.352912 | 0) wtest-31632 | ! 212.259 nos | }
 4604.352913 | 0) wtest-31632 | ! 213.845 nos | }
 4604.352914 | 0) wtest-31632 | ! 215.286 nos | }
 4604.352914 | 0) wtest-31632 | 0.685 nos | __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 nos | __srcu_read_unlock ();
 4604.352920 | 0) wtest-31632 | 3.958 nos | }
 4604.352920 | 0) wtest-31632 | ! 228.409 nós | }
 4604.352921 | 0) wtest-31632 | ! 231.334 nos | }

Este é o meu segundo ponto de confusão - posso observar o espaço do usuário write()resultante com um espaço do kernel sys_write(), conforme o esperado; e dentro do sys_write(), observo funções relacionadas à segurança (por exemplo apparmor_file_permission()), funções de gravação "genéricas" (por exemplo generic_file_aio_write()), ext4funções relacionadas ao sistema de arquivos (por exemplo ext4_journal_start_sb()) - mas não observo nada relacionado a ata_piix(ou sd) drivers ?!

A página Rastreamento e criação de perfil - Projeto Yocto sugere o uso do blkrastreador ftracepara obter mais informações sobre a operação do dispositivo de bloco, mas não informa nada para mim neste exemplo. Além disso, os Drivers de sistema de arquivos do Linux - Annon Inglorion (tutorfs) sugerem que os sistemas de arquivos também (podem?) (Podem) ser implementados como módulos / drivers do kernel, e eu acho que esse também é o caso ext4.

Finalmente, eu poderia jurar que observei anteriormente o nome do driver entre colchetes ao lado da função mostrada pelo function_graphrastreador, mas acho que misturei tudo - provavelmente pode aparecer assim nos rastreamentos de pilha (de trás), mas não no gráfico da função. Além disso, posso inspecionar /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 com a fonte Linux / drivers / ata / ata_piix.c , confirme se, por exemplo, piix_init_sata_mapé realmente uma função no ata_piix. O que provavelmente deve me dizer que: os módulos compilados no kernel (para que se tornem parte do kernel monolítico) "perdem" as informações sobre de que módulo eles vêm; no entanto, os módulos carregáveis ​​que são criados como .koobjetos separados do kernel preservam essas informações (por exemplo, [psmouse]mostradas acima entre colchetes). Assim, também ftracesó poderia mostrar informações sobre "módulo de origem", apenas para as funções provenientes de módulos carregáveis ​​do kernel. Isso está correto?

O exposto acima, levado em consideração, é o entendimento que tenho do processo atualmente:

  • No momento da inicialização, o ata_piixdriver estabelece um mapeamento de memória DMA (?) Entre /dev/sdae o disco rígido
    • por isso, todos os acessos futuros ao /dev/sdavia ata_piixserão transparentes para o kernel (ou seja, não rastreáveis) - já que todo o kernel veria, são apenas leituras / gravações em locais de memória (não necessariamente chamadas para funções específicas do kernel rastreáveis), que não são relatados pelo function_graphrastreador
  • No momento da inicialização, o sddriver também "analisa" as partições /dev/sda, as disponibiliza e possivelmente manipula os mapeamentos de memória entre as partições <-> dispositivo de disco
    • novamente, isso deve tornar as operações de acesso sdtransparentes ao kernel
  • Como ambos ata_piixe sdsão compilados no kernel, mesmo que algumas de suas funções acabem sendo capturadas ftrace, não podemos obter informações de qual módulo essas funções viriam (além da correlação "manual" com os arquivos de origem)
  • Posteriormente, mountestabelece um relacionamento / ligação entre uma partição e o driver do sistema de arquivos correspondente (neste caso ext4)
    • a partir deste momento, todos os acessos ao sistema de arquivos montado seriam tratados por ext4funções - que são rastreáveis ​​pelo kernel; mas como ext4é compilado no kernel, o rastreador não pode nos fornecer as informações do módulo de origem
  • Portanto, as gravações "genéricas" observadas, chamadas por meio de ext4funções, finalmente acessariam os locais de memória, cujo mapeamento é estabelecido por ata_piix- mas fora isso, ata_piixnão interfere diretamente nas transferências de dados (provavelmente sendo tratado pelo DMA (fora do processador) (s) e, portanto, transparente).

Esse entendimento está correto?

Algumas subquestões relacionadas:

  • Na minha configuração acima, posso identificar um driver de dispositivo PCI ( ata_piix) e um driver de sistema de arquivos ( ext4); mas existem drivers de caracteres ou de bloco usados ​​em algum lugar no caminho de execução "gravação" e, em caso afirmativo, quais são?
  • Qual desses drivers lidaria com o cache (para operações desnecessárias em disco são ignoradas ou otimizadas?)
  • Eu sei de antes que /dev/shmé um sistema de arquivos na RAM; mount | grep shmpara me informa: none on /dev/shm type tmpfs (rw,nosuid,nodev). Isso significa que - em contraste com /dev/sda- o shmsistema de arquivos simplesmente não possui o mapeamento (DMA) dos endereços "próprios" para os endereços de barramento em direção a um dispositivo; e assim todos os acessos através do tmpfsdriver do sistema de arquivos acabam na RAM real?
sdaau
fonte
4
Oi sdaau. Essas são boas perguntas, mas a duração desta postagem é excessiva e há várias perguntas. É recomendável que você esteja tentando entender as coisas, em vez de apenas fazer perguntas ao suporte técnico, que é o que mais chegamos aqui. Cada uma dessas perguntas mereceria uma resposta longa por si só. Eu recomendo pelo menos dividir sua postagem em partes claramente definidas e colocar cada parte em uma pergunta separada, criando assim uma série de perguntas.
Faheem Mitha
Em seguida, você pode postar essas perguntas juntas ou sequencialmente. Tudo bem, eu acho, se você fizer referência a outra pergunta (ou perguntas) dentro de uma pergunta.
Faheem Mitha
1
Se você quiser dicas sobre como limpar sua pergunta, sugiro que você entre na sala de bate-papo e converse com as pessoas de lá. Já falamos sobre isso aqui. :-)
Faheem Mitha
Muito obrigado pelo comentário, @FaheemMitha - eu também tinha dúvidas semelhantes, mas não tinha muita certeza de como cortar as perguntas - e não sabia até agora que posso usar o bate-papo (e não estava interessado em usando meta para perguntar sobre esse tipo de conselho); definitivamente experimentará o chat da próxima vez. Felizmente, desta vez funcionou com uma resposta muito aceitável ... Saúde!
Sdaau
@sdaau, você descobriu como monitorar o acesso ao disco?
Ransh #

Respostas:

10

Você pediu demais em uma pergunta - bem, tecnicamente não, pois acho que "esse entendimento está correto" pode ser respondido rapidamente: não. Mas essa não é uma resposta útil.

Primeiro, você está certo ata_piixe sd_modaparentemente está sendo compilado no seu kernel. Essa é uma escolha que você faz ao configurar o kernel - você pode omiti-lo, incluí-lo ou incluí-lo como um módulo. (Mesmo com ext4).

Segundo, você supôs que as gravações eram muito mais simples do que realmente são. O esquema básico de como uma gravação funciona é que o código do sistema de arquivos coloca os dados a serem gravados na memória, como parte do cache do buffer, e os marca como precisa ser gravado ("sujo"). (A menos que já exista muito disso na RAM, nesse caso, ele é realmente forçado a fazer a gravação ...)

Mais tarde, várias coisas (como o bdflushthread do kernel) liberam as páginas sujas no disco. É quando você vê as chamadas através de sd, scsi, libata, ata_piix, agendadores io, PCI, etc. Embora exista muito provavelmente DMA envolvido nessa gravação, os dados devem ser transferidos e talvez o comando. Mas as gravações em disco, pelo menos no SATA, são tratadas enviando comandos que basicamente significam "setor de gravação X com dados Y". Mas definitivamente não é tratado com o mapeamento de memória de todo o disco (considere: você pode usar discos muito maiores que o 4GiB em máquinas de 32 bits).

O armazenamento em cache é tratado pelo subsistema de gerenciamento de memória (não um driver), em conjunto com o sistema de arquivos, a camada de blocos, etc.

tmpfsé especial, é basicamente inteiramente cache. É apenas um cache especial que nunca é descartado ou gravado de volta (embora possa ser trocado). Você pode encontrar o código em mm/shmem.cvários outros lugares (tente ack-grep --cc CONFIG_TMPFSencontrá-los).

Basicamente, a gravação no disco passa por boa parte dos subsistemas do kernel; a rede é a única das principais em que consigo pensar que não está envolvida no seu exemplo. Explicar corretamente exige um esforço no tamanho de um livro; Eu recomendo procurar um.

derobert
fonte
Olá @derobert - muito, muito obrigado pela sua resposta; contém o tipo exato de informação que estava faltando! Inicialmente, comecei a procurar uma ilustração simples do espaço do usuário versus o kernel, mas logo percebi que uma gravação no disco rígido não é exatamente algo que eu entendo completamente e não é trivial - obrigado por confirmar que é realmente um livro- esforço de comprimento! Felicidades!
Sdaau
Uma pequena observação: algumas das explicações nesta resposta (por exemplo, limpeza de páginas sujas) são observáveis, se no sudo bash...script no OP: a memória ftrace é aumentada ( echo 8192 > $KDBGPATH/buffer_size_kb); e sync ;é adicionado após a ./wtest ;chamada. Em seguida, pode ver flush-8, kworker(sob kthreadda ps axf), e syncsi, como em processos de ftracechamar funções, como por exemplo ata_bmdma_setup()(que faz parte do libata, que ata_piixtem por base), ou get_nr_dirty_inodes().
Sdaau
4

Portanto, minha primeira pergunta é - por que não consigo observar o módulo ata_piix aqui, apenas nos logs de inicialização? É porque ata_piix (e sd) são criados como drivers internos no kernel (monolítico), em vez de serem construídos como módulos do kernel (carregáveis) .ko?

Você não precisa adivinhar qual é a sua configuração. Na minha máquina eu tenho

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

O arquivo de configuração é para este kernel localizado em /boot/config-3.2.0-4-amd64.

Você perguntou sobre ata_piix. Pesquisando no .configarquivo acima , vemos CONFIG_ATA_PIIX=m. podemos confirmar isso fazendo

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

Então, pelo menos no meu kernel, é um módulo.

Faheem Mitha
fonte
Muito obrigado por isso, @FaheemMitha - enquanto eu já ouvi falar (e usei) o arquivo de configuração antes, por algum motivo eu o esqueci completamente neste exemplo; bem visto! :)No meu sistema, grep ATA_PIIX /boot/config-2.6.38-16-genericdiz CONFIG_ATA_PIIX=y, o que provavelmente deveria significar neste kernel, ata_piixé compilar "in-kernel", e não como um módulo. Felicidades!
Sdaau