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/sdaviaata_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 pelofunction_graphrastreador
- por isso, todos os acessos futuros ao
- 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
- novamente, isso deve tornar as operações de acesso
- Como ambos
ata_piixesdsã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,
mountestabelece 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
ext4funçõ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
ext4funções, finalmente acessariam os locais de memória, cujo mapeamento é estabelecido porata_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- oshmsistema 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 dotmpfsdriver do sistema de arquivos acabam na RAM real?