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/sda
dispositivo (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/sda
disco 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_piix
ou sd
drivers:
$ 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_piix
mó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 .ko
kernel (carregáveis) ?
Certo - agora, estou tentando observar o que acontece ao executar o programa com o ftrace
rastreador 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 ftrace
log 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()
), ext4
funçõ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 blk
rastreador ftrace
para 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_graph
rastreador, 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 .ko
objetos separados do kernel preservam essas informações (por exemplo, [psmouse]
mostradas acima entre colchetes). Assim, também ftrace
só 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_piix
driver estabelece um mapeamento de memória DMA (?) Entre/dev/sda
e o disco rígido- por isso, todos os acessos futuros ao
/dev/sda
viaata_piix
serã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 pelofunction_graph
rastreador
- por isso, todos os acessos futuros ao
- No momento da inicialização, o
sd
driver 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
sd
transparentes ao kernel
- novamente, isso deve tornar as operações de acesso
- Como ambos
ata_piix
esd
são compilados no kernel, mesmo que algumas de suas funções acabem sendo capturadasftrace
, 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,
mount
estabelece um relacionamento / ligação entre uma partição e o driver do sistema de arquivos correspondente (neste casoext4
)- a partir deste momento, todos os acessos ao sistema de arquivos montado seriam tratados por
ext4
funções - que são rastreáveis pelo kernel; mas comoext4
é compilado no kernel, o rastreador não pode nos fornecer as informações do módulo de origem
- a partir deste momento, todos os acessos ao sistema de arquivos montado seriam tratados por
- Portanto, as gravações "genéricas" observadas, chamadas por meio de
ext4
funções, finalmente acessariam os locais de memória, cujo mapeamento é estabelecido porata_piix
- mas fora isso,ata_piix
nã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 shm
para me informa:none on /dev/shm type tmpfs (rw,nosuid,nodev)
. Isso significa que - em contraste com/dev/sda
- oshm
sistema 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 dotmpfs
driver do sistema de arquivos acabam na RAM real?