Z góry przepraszam, jeśli ten post jest trochę gęsty / niechlujny, ale mam trudności z jego lepszym sformułowaniem ... Zasadniczo chciałbym przestudiować, co dzieje się podczas zapisu na dysku twardym i chciałbym wiedzieć:
- Czy moje rozumienie poniżej jest prawidłowe - a jeśli nie, to gdzie się mylę?
- Czy istnieje lepsze narzędzie do „przechwytywania” danych dziennika o wszystkich aspektach zachodzących na komputerze podczas zapisu na dysku?
Bardziej szczegółowo - po pierwsze, używam systemu operacyjnego:
$ 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
Mam więc następujący prosty (np. Zwykłe kontrole niepowodzenia operacji) program C w przestrzeni użytkownika 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;
}
Buduję to za pomocą gcc -g -O0 -o wtest wtest.c
. Teraz, skoro próbuję pisać /tmp
, zauważam, że jest to katalog pod rootem /
- więc sprawdzam 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)
...
Tak więc mój główny system plików /
to jedna partycja /dev/sda
urządzenia (i używam innych partycji jako „samodzielnych” dysków / montowań). Aby znaleźć sterownik dla tego urządzenia, używam 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
...
Tak więc /dev/sda
dysk twardy jest najwyraźniej obsługiwany przez sterownik ata_piix
(i sd
).
$ 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
Muszę pobierać ze starszego sysloga, ponieważ dużo zawieszam, ale powyższe wydaje się być właściwym fragmentem sysloga z sysloga w czasie uruchamiania, w którym sterownik ata_piix
(i sd
) uruchamia się po raz pierwszy.
Moim pierwszym zamieszaniem jest to, że nie mogę inaczej obserwować sterowników ata_piix
lub sd
sterowników:
$ 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
Moje pierwsze pytanie brzmi: dlaczego nie mogę obserwować ata_piix
modułu tutaj, tylko w dziennikach rozruchu? Czy to dlatego, że ata_piix
(i sd
) są wbudowane jako wbudowane sterowniki w (monolitycznym) jądrze, a nie jako (ładowane) .ko
moduły jądra?
Tak - teraz próbuję obserwować, co dzieje się po uruchomieniu programu z ftrace
wbudowanym modułem śledzenia funkcji w systemie 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
'
... a oto fragment ftrace
dziennika dotyczący 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 us | } 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 us | } 4604,352704 | 0) wtest-31632 | | __generic_file_aio_write () { ... 4604,352728 | 0) wtest-31632 | | file_update_time () { ... 4604,352732 | 0) wtest-31632 | 0,756 us | 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 us | 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 us | 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 us | 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 | }
To mój drugi punkt pomyłki - zgodnie z oczekiwaniami mogę obserwować przestrzeń użytkownika write()
wynikającą z przestrzeni jądra sys_write()
; a na terenie sys_write()
, ja obserwować funkcje związane z bezpieczeństwem (np apparmor_file_permission()
), „rodzajowe” funkcje zapisu (np generic_file_aio_write()
) ext4
filesystem funkcje towarzyszące (np ext4_journal_start_sb()
) - ale ja nie wszystko związane obserwować ata_piix
(lub sd
) kierowców?
Strona Śledzenie i profilowanie - Projekt Yocto sugeruje użycie modułu blk
śledzącego, ftrace
aby uzyskać więcej informacji o działaniu urządzenia blokowego, ale w tym przykładzie nic mi nie zgłasza. Również sterowniki systemu plików Linux - Annon Inglorion (tutorfs) sugerują, że systemy plików są (mogą?) Również być (być) implementowane jako moduły / sterowniki jądra, i przypuszczam, że ext4
tak też jest.
Na koniec mogłem przysiąc, że wcześniej obserwowałem nazwę sterownika w nawiasach kwadratowych obok funkcji pokazanej przez function_graph
znacznik, ale wydaje mi się, że pomieszałem rzeczy - prawdopodobnie może to wyglądać tak w śladach stosu (z tyłu), ale nie na wykresie funkcji. Ponadto mogę sprawdzić /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]
...
... i sprawdzając za pomocą źródłowego Linux / drivers / ata / ata_piix.c , potwierdź, że np. piix_init_sata_map
rzeczywiście jest funkcją w ata_piix
. Co prawdopodobnie powinno mi powiedzieć, że: moduły skompilowane w jądrze (aby stały się częścią jądra monolitycznego) „tracą” informacje o tym, z którego modułu pochodzą; jednak moduły ładowalne, które są zbudowane jako osobne .ko
obiekty jądra, zachowują tę informację (np. [psmouse]
pokazano powyżej w nawiasach kwadratowych). Dlatego też ftrace
mógł pokazywać tylko informacje o „module inicjującym”, tylko dla funkcji pochodzących z ładowalnych modułów jądra. Czy to jest poprawne?
Biorąc powyższe pod uwagę, rozumiem obecnie proces:
- W czasie uruchamiania
ata_piix
sterownik ustanawia mapowanie pamięci DMA (?) Pomiędzy/dev/sda
i dyskiem twardym- z tego powodu wszystkie przyszłe dostępy do
/dev/sda
viaata_piix
będą przezroczyste dla jądra (to znaczy, że nie można go śledzić) - ponieważ wszystkie jądra by to zobaczyły, są to tylko odczyty / zapisy do miejsc w pamięci (niekoniecznie wywołania określonych funkcji śledzenia jądra), które nie są zgłaszane przezfunction_graph
tracer
- z tego powodu wszystkie przyszłe dostępy do
- W czasie uruchamiania
sd
sterownik „parsuje” partycje/dev/sda
, udostępnia je i ewentualnie obsługuje mapowania pamięci między partycjami <-> urządzenia dyskowego- znowu powinno to uczynić operacje dostępu
sd
przejrzystymi dla jądra
- znowu powinno to uczynić operacje dostępu
- Ponieważ oba
ata_piix
isd
są kompilowane w jądra, nawet jeśli niektóre z ich funkcji nie kończy się rejestrowany przezftrace
nie możemy uzyskać informacje, z których te funkcje modułu będzie pochodzić z (oprócz „manual” korelacja z plików źródłowych) - Później
mount
ustanawia relację / powiązanie między partycją a odpowiednim sterownikiem systemu plików (w tym przypadkuext4
)- od tego momentu wszystkie dostępy do zamontowanego systemu plików będą obsługiwane przez
ext4
funkcje - które są identyfikowalne przez jądro; ale jakext4
skompilowano w jądrze, moduł śledzący nie może przekazać nam informacji o module źródłowym
- od tego momentu wszystkie dostępy do zamontowanego systemu plików będą obsługiwane przez
- Tak więc zaobserwowane „ogólne” zapisy, wywoływane przez
ext4
funkcje, ostatecznie uzyskałyby dostęp do lokalizacji pamięci, których mapowanie jest ustanawiane przezata_piix
- ale poza tym,ata_piix
nie zakłóca bezpośrednio transferu danych (prawdopodobnie jest obsługiwany przez DMA (poza procesorem) (s), a zatem przejrzyste dla niego).
Czy to zrozumienie jest prawidłowe?
Niektóre powiązane pytania:
- W powyższej konfiguracji mogę zidentyfikować sterownik urządzenia PCI (
ata_piix
) i sterownik systemu plików (ext4
); ale czy są jakieś sterowniki znaków lub bloków używane gdzieś na ścieżce wykonania „zapisu”, a jeśli tak, to jakie? - Który ze sterowników obsłużyłby buforowanie (więc niepotrzebne operacje na dysku są pomijane lub optymalizowane?)
- Wiem wcześniej, że
/dev/shm
jest to system plików w pamięci RAM;mount | grep shm
dla mnie raporty:none on /dev/shm type tmpfs (rw,nosuid,nodev)
. Czy to oznacza, że - w przeciwieństwie do/dev/sda
- wshm
systemie plików po prostu brakuje (DMA) mapowanie z „własnymi” adrresses do adresów autobusowego w kierunku urządzenia; a zatem wszystkie dostępy za pośrednictwemtmpfs
sterownika systemu plików kończą się w rzeczywistej pamięci RAM?
źródło
Odpowiedzi:
Za dużo zadałeś w jednym pytaniu - cóż, technicznie nie, jak sądzę „czy to zrozumienie jest prawidłowe” można szybko odpowiedzieć: nie. Ale to nie jest przydatna odpowiedź.
Po pierwsze, masz rację
ata_piix
isd_mod
prawdopodobnie jesteś wkompilowany w jądro. Jest to wybór dokonywany podczas konfigurowania jądra - możesz go pominąć, dołączyć lub włączyć jako moduł. (To samo z ext4).Po drugie, założyłeś, że zapisy są znacznie prostsze niż w rzeczywistości. Podstawowy zarys działania zapisu polega na tym, że kod systemu plików umieszcza dane do zapisania w pamięci jako część bufora pamięci podręcznej i oznacza je jako wymagające zapisania („brudne”). (Chyba że jest już zbyt dużo tego w pamięci RAM, w takim przypadku jest on faktycznie zmuszony do zapisu ...)
Później różne rzeczy (takie jak
bdflush
wątek jądra) faktycznie opróżniają brudne strony na dysk. To wtedy zobaczysz wywołania przez sd, scsi, libata, ata_piix, harmonogramy IO, PCI itp. Chociaż jest bardzo prawdopodobne, że DMA jest zaangażowany w ten zapis, to dane, które mają być przesłane, a może polecenie. Ale zapisy na dysku, przynajmniej w SATA, są obsługiwane przez wysyłanie poleceń, które w zasadzie oznaczają „zapis sektora X z danymi Y”. Ale zdecydowanie nie jest to obsługiwane przez mapowanie pamięci całego dysku (rozważ: możesz używać dysków znacznie większych niż 4GiB na komputerach 32-bitowych).Buforowanie jest obsługiwane przez podsystem zarządzania pamięcią (nie sterownik), w połączeniu z systemem plików, warstwą bloków itp.
tmpfs
jest wyjątkowy, to w zasadzie całkowicie pamięć podręczna. Jest to po prostu specjalna pamięć podręczna, której nigdy nie odrzuca się ani nie zapisuje (choć można ją wymienić). Możesz znaleźć kod wmm/shmem.c
kilku innych miejscach (spróbujack-grep --cc CONFIG_TMPFS
je znaleźć).Zasadniczo zapis na dysk przebiega przez sporą część podsystemów jądra; sieciowanie jest jedyną ważną, o której myślę, że nie jest uwzględnione w twoim przykładzie. Właściwe wyjaśnienie tego wymaga długiego wysiłku; Polecam poszukać jednego.
źródło
sudo bash...
Opróżnianie brudnych stron) są obserwowalne, jeśli w skrypcie w OP: pamięć ftrace jest zwiększona (echo 8192 > $KDBGPATH/buffer_size_kb
); isync ;
jest dodawany po zakończeniu./wtest ;
połączenia. Wtedy widzęflush-8
,kworker
(zgodniekthreadd
zps axf
), async
sam, jak w procesachftrace
wywoływania funkcji, takich jak na przykładata_bmdma_setup()
(który jest częściąlibata
, któraata_piix
opiera się na), lubget_nr_dirty_inodes()
.Nie musisz zgadywać, jaka jest twoja konfiguracja. Na mojej maszynie mam
Plik konfiguracyjny dla tego jądra znajduje się w
/boot/config-3.2.0-4-amd64
.Zapytałeś o
ata_piix
. Przeszukując powyższy.config
plik, widzimyCONFIG_ATA_PIIX=m
. możemy to potwierdzić, robiącalternatywnie
Więc przynajmniej w moim jądrze jest to moduł.
źródło
:)
,grep ATA_PIIX /boot/config-2.6.38-16-generic
że w moim systemie,CONFIG_ATA_PIIX=y
co prawdopodobnie powinno oznaczać to jądro,ata_piix
jest wbudowane „w jądrze”, a nie jako moduł. Twoje zdrowie!