Заранее извиняюсь, если этот пост немного дремучий / грязный, но мне трудно его лучше сформулировать ... По сути, я хотел бы изучить, что происходит при записи на жесткий диск, и я хотел бы знать:
- Правильно ли мое понимание ниже - и если нет, то где я иду не так?
- Есть ли лучший инструмент для «записи» данных журнала обо всех аспектах, происходящих на ПК, во время записи на диск?
Более подробно - во-первых, ОС, которую я использую:
$ 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
драйвер файловой системы в конечном итоге в оперативной памяти?
источник
Ответы:
Вы задали слишком много в одном вопросе - ну, технически нет, так как, я думаю, «правильно ли это понимание», можно быстро ответить: нет. Но это не полезный ответ.
Во-первых, вы правы
ata_piix
и, по-sd_mod
видимому, компилируете свое ядро. Это выбор, который вы делаете при настройке ядра - вы можете его опустить, включить или включить как модуль. (То же самое с ext4).Во-вторых, вы предположили, что записи намного проще, чем они есть на самом деле. Основная схема того, как работает запись, состоит в том, что код файловой системы помещает данные, которые должны быть записаны в память, как часть буферного кеша, и помечает их как необходимые для записи («грязные»). (Если в оперативной памяти этого уже слишком много, в этом случае он фактически вынужден выполнять запись ...)
Позже, различные вещи (такие как
bdflush
поток ядра) фактически сбрасывают грязные страницы на диск. Это когда вы видите вызовы через sd, scsi, libata, ata_piix, io-планировщики, PCI и т. Д. Хотя весьма вероятно, что DMA участвует в этой записи, это данные, которые должны быть переданы, и, возможно, команда. Но запись на диск, по крайней мере, в SATA, осуществляется путем отправки команд, которые в основном означают «запись сектора X с данными Y». Но это определенно не обрабатывается отображением памяти на весь диск (учтите: на 32-битных машинах можно использовать диски гораздо больше 4 ГБ).Кэширование осуществляется подсистемой управления памятью (а не драйвером) совместно с файловой системой, блочным уровнем и т. Д.
tmpfs
это особенное, это в основном полностью кэш. Это просто специальный кеш, который никогда не сбрасывается и не записывается обратно (хотя его можно заменить). Вы можете найти код вmm/shmem.c
нескольких других местах (попробуйтеack-grep --cc CONFIG_TMPFS
их найти).По сути, запись на диск проходит через большую часть подсистем ядра; Единственное, что я могу себе представить, - это работа с сетью, которая не связана с вашим примером. Правильное объяснение этого требует усилий на всю книгу; Я рекомендую искать один.
источник
sudo bash...
удаление грязных страниц) можно наблюдать, если в сценарии в OP: увеличена память ftrace (echo 8192 > $KDBGPATH/buffer_size_kb
); иsync ;
добавляется после./wtest ;
звонка. Тогда я могу видетьflush-8
,kworker
(подkthreadd
вps axf
), аsync
сам, как процессы вftrace
вызове функции , как , например ,ata_bmdma_setup()
(который является частьюlibata
, котораяata_piix
опирается на), илиget_nr_dirty_inodes()
.Вам не нужно угадывать, какова ваша конфигурация. На моей машине у меня есть
Конфигурационный файл для этого ядра находится по адресу
/boot/config-3.2.0-4-amd64
.Вы спрашивали о
ata_piix
. Ища указанный выше.config
файл мы видимCONFIG_ATA_PIIX=m
. мы можем подтвердить это, сделавальтернативно
Так что по крайней мере в моем ядре это модуль.
источник
:)
В моей системе,grep ATA_PIIX /boot/config-2.6.38-16-generic
говоритCONFIG_ATA_PIIX=y
, что, вероятно, должно означать в этом ядре,ata_piix
это сборка "в ядре", а не как модуль. Ура!