Заранее извиняюсь, если этот пост немного дремучий / грязный, но мне трудно его лучше сформулировать ... По сути, я хотел бы изучить, что происходит при записи на жесткий диск, и я хотел бы знать:
- Правильно ли мое понимание ниже - и если нет, то где я иду не так?
- Есть ли лучший инструмент для «записи» данных журнала обо всех аспектах, происходящих на ПК, во время записи на диск?
Более подробно - во-первых, ОС, которую я использую:
$ 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
Итак, у меня есть следующая простая (например, обычные проверки на провал операций пропущены) программы на C в пользовательском пространстве 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;
}
Я строю это с gcc -g -O0 -o wtest wtest.c
. Теперь, поскольку я пытаюсь писать /tmp
, я отмечаю, что это каталог в корневом каталоге /
- поэтому я проверяю 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)
...
Итак, моя корневая файловая система /
- это один раздел /dev/sda
устройства (и я использую другие разделы в качестве «автономных» дисков / монтируемых дисков). Чтобы найти драйвер для этого устройства, я использую 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
...
Таким образом, /dev/sda
жесткий диск, по-видимому, обрабатывается ata_piix
(и 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
Я должен извлечь из старого системного журнала, поскольку я много приостанавливаю, но вышеупомянутое похоже на правильный фрагмент из системного журнала во время загрузки, где драйвер ata_piix
(и sd
) запускается впервые.
Мой первый пункт путаницы в том , что я не могу иначе наблюдать ata_piix
или 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
Итак, мой первый вопрос - почему я не могу наблюдать ata_piix
модуль здесь, только в журналах загрузки? Это потому, что ata_piix
(и sd
) встроены как (встроенные) драйверы в (монолитное) ядро, а не встроены как (загружаемые) .ko
модули ядра?
Правильно - теперь я пытаюсь наблюдать за тем, что происходит при запуске программы со ftrace
встроенной функцией трассировки 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
'
... и вот фрагмент ftrace
журнала о 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 с нами | common_file_perm (); 4604.352696 | 0) wtest-31632 | 2.198 нас | } 4604.352697 | 0) wtest-31632 | 3,557 нас | } 4604.352697 | 0) wtest-31632 | 4,979 нас | } 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 нас | _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 с нами | _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 нас | mnt_drop_write (); 4604.352822 | 0) wtest-31632 | + 93,541 с нами | } 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 нас | } 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 сша | 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 сша | mutex_unlock (); 4604.352911 | 0) wtest-31632 | 0,727 us | generic_write_sync (); 4604.352912 | 0) wtest-31632 | ! 212,259 с нами | } 4604.352913 | 0) wtest-31632 | ! 213,845 с нами | } 4604.352914 | 0) wtest-31632 | ! 215,286 с нами | } 4604.352914 | 0) wtest-31632 | 0,685 с нами | __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 с нами | __srcu_read_unlock (); 4604.352920 | 0) wtest-31632 | 3,995 США | } 4604.352920 | 0) wtest-31632 | ! 228,409 нас | } 4604.352921 | 0) wtest-31632 | ! 231,334 us | }
Это моя вторая путаница - я могу наблюдать за пользовательским пространством, write()
полученным с пространством ядра sys_write()
, как и ожидалось; и в рамках sys_write()
я наблюдаю связанные с безопасностью функции (например apparmor_file_permission()
), «общие» функции записи (например generic_file_aio_write()
), ext4
функции, связанные с файловой системой (например ext4_journal_start_sb()
) - но я не наблюдаю ничего, связанного с ata_piix
(или sd
) драйверами ?!
На странице « Трассировка и профилирование - проект Yocto» предлагается использовать blk
трассировщик ftrace
для получения дополнительной информации о работе блочных устройств, но в этом примере мне ничего не сообщается. Кроме того, драйверы файловой системы Linux - Аннон Инглорион (tutorfs) предполагает, что файловые системы (можно?) Также (могут быть) реализованы как модули / драйверы ядра, и я предполагаю, что это также относится и к ext4
.
Наконец, я мог бы поклясться, что ранее наблюдал имя драйвера в квадратных скобках рядом с функцией, показанной function_graph
трассировщиком, но я думаю, что я все перепутал - это может выглядеть так же в трассировках стека (назад), но не в графе функций. Кроме того, я могу проверить /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]
...
... и проверяя исходный код Linux / drivers / ata / ata_piix.c , убедитесь, что eg piix_init_sata_map
действительно является функцией в ata_piix
. Что, вероятно, должно сказать мне, что: модули, скомпилированные в ядре (так что они становятся частью монолитного ядра), «теряют» информацию о том, из какого модуля они берутся; однако загружаемые модули, которые построены как отдельные .ko
объекты ядра, сохраняют эту информацию (например, [psmouse]
показанную выше в квадратных скобках). Таким образом, также ftrace
может отображаться только информация о «исходном модуле», только для тех функций, которые поступают из загружаемых модулей ядра. Это верно?
Принимая во внимание вышеизложенное, это понимание, которое у меня есть в настоящее время процесса:
- Во время загрузки
ata_piix
драйвер устанавливает отображение памяти DMA (?) Между/dev/sda
жестким диском и- из-за этого все будущие обращения к
/dev/sda
viaata_piix
будут прозрачны для ядра (то есть не отслеживаются) - поскольку все ядро увидит, это просто чтение / запись в области памяти (необязательно вызовы определенных отслеживаемых функций ядра), которыеfunction_graph
трассировщик не сообщает
- из-за этого все будущие обращения к
- Во время загрузки
sd
драйвер, кроме того, "анализирует" разделы/dev/sda
, делает их доступными и, возможно, обрабатывает сопоставления памяти между разделами <-> дискового устройства.- опять же, это должно сделать операции доступа
sd
прозрачными для ядра
- опять же, это должно сделать операции доступа
- Поскольку оба
ata_piix
иsd
скомпилированы в ядре, даже если некоторые их функции в конечном итоге будут захваченыftrace
, мы не можем получить информацию о том, из какого модуля эти функции поступят (кроме «ручной» корреляции с исходными файлами) - Позже
mount
устанавливает связь / связь между разделом и соответствующим драйвером файловой системы (в данном случаеext4
)- с этого момента все обращения к смонтированной файловой системе будут обрабатываться
ext4
функциями, которые отслеживаются ядром; но, какext4
это скомпилировано в ядре, трассировщик не может дать нам информацию о исходном модуле
- с этого момента все обращения к смонтированной файловой системе будут обрабатываться
- Таким образом, наблюдаемые «общие» записи, вызываемые через
ext4
функции, в конечном итоге будут иметь доступ к областям памяти, отображение которых устанавливается,ata_piix
но кроме этого,ata_piix
не влияет непосредственно на передачу данных (это, вероятно, обрабатывается DMA (вне процессора). (s), и, следовательно, прозрачный для него).
Это понимание правильно?
Некоторые связанные подвопросы:
- В приведенной выше настройке я могу определить драйвер устройства PCI (
ata_piix
) и драйвер файловой системы (ext4
); но есть ли драйверы символов или блоков, используемые где-то на пути выполнения записи, и если да, то какие они? - Какой из этих драйверов будет обрабатывать кэширование (поэтому ненужные операции с дисками пропускаются или оптимизируются?)
- Я знаю, что раньше
/dev/shm
это файловая система в оперативной памяти;mount | grep shm
для меня сообщает:none on /dev/shm type tmpfs (rw,nosuid,nodev)
. Означает ли это, что в отличие/dev/sda
отshm
файловой системы просто отсутствует сопоставление (DMA) «своих» адресов с адресами шины по отношению к устройству; и, таким образом, все обращения черезtmpfs
драйвер файловой системы в конечном итоге в оперативной памяти?