Наблюдение за записью на жесткий диск в пространстве ядра (с драйверами / модулями)

13

Заранее извиняюсь, если этот пост немного дремучий / грязный, но мне трудно его лучше сформулировать ... По сути, я хотел бы изучить, что происходит при записи на жесткий диск, и я хотел бы знать:

  • Правильно ли мое понимание ниже - и если нет, то где я иду не так?
  • Есть ли лучший инструмент для «записи» данных журнала обо всех аспектах, происходящих на ПК, во время записи на диск?

Более подробно - во-первых, ОС, которую я использую:

$ 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_piixsd) драйвером.

$ 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_piixsd) запускается впервые.

Мой первый пункт путаницы в том , что я не могу иначе наблюдать 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_piixsd) встроены как (встроенные) драйверы в (монолитное) ядро, а не встроены как (загружаемые) .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/sdavia ata_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драйвер файловой системы в конечном итоге в оперативной памяти?
sdaau
источник
4
Привет, Сдау. Это хорошие вопросы, но длина этого поста чрезмерна, и там есть несколько вопросов. Похвально, что вы пытаетесь что-то понять, а не просто задавать вопросы в службу поддержки, что мы чаще всего получаем здесь. Каждый из этих вопросов заслуживает длинного ответа. Я рекомендую, по крайней мере, разбить ваш пост на четко определенные фрагменты и поместить каждый фрагмент в отдельный вопрос, создав таким образом ряд вопросов.
Фахим Митха
Затем вы можете опубликовать эти вопросы вместе или последовательно. Это нормально, я думаю, если вы ссылаетесь на другой вопрос (или вопросы) внутри вопроса.
Фахим Митха
1
Если вы хотите получить советы по уборке вашего вопроса, я предлагаю вам зайти в чат и поговорить с людьми там. Мы уже говорили об этом здесь. :-)
Фахим Митха
Большое спасибо за комментарий, @FaheemMitha - у меня тоже были подобные сомнения, но я не был уверен, как урезать вопросы - и не знал до сих пор, что могу использовать чат для него (и я не был заинтересован в использование мета для выяснения такого рода совета); в следующий раз обязательно дадим чат. К счастью, на этот раз все получилось с очень приемлемым ответом ... Ура!
sdaau
@sdaau, вы выяснили, как контролировать доступ к диску?
ransh

Ответы:

10

Вы задали слишком много в одном вопросе - ну, технически нет, так как, я думаю, «правильно ли это понимание», можно быстро ответить: нет. Но это не полезный ответ.

Во-первых, вы правы 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их найти).

По сути, запись на диск проходит через большую часть подсистем ядра; Единственное, что я могу себе представить, - это работа с сетью, которая не связана с вашим примером. Правильное объяснение этого требует усилий на всю книгу; Я рекомендую искать один.

derobert
источник
Привет @derobert - большое, большое спасибо за твой ответ; он содержит точную информацию, которую я пропустил! Первоначально я начал с поиска простой иллюстрации пространства пользователя и ядра, но вскоре понял, что запись на жесткий диск - это не совсем то, что я полностью понимаю, и это не так тривиально - спасибо, что подтвердили, что на самом деле это книга. длина усилия! Ура!
sdaau
Небольшое примечание: некоторые объяснения в этом ответе (например, 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().
sdaau
4

Итак, мой первый вопрос - почему я не могу наблюдать модуль ata_piix здесь, только в журналах загрузки? Это потому, что ata_piix (и sd) встроены как (встроенные) драйверы в (монолитное) ядро, а не встроены как (загружаемые) модули ядра .ko?

Вам не нужно угадывать, какова ваша конфигурация. На моей машине у меня есть

$ uname -a
Linux orwell 3.2.0-4-amd64 #1 SMP Debian 3.2.51-1 x86_64 GNU/Linux

Конфигурационный файл для этого ядра находится по адресу /boot/config-3.2.0-4-amd64.

Вы спрашивали о ata_piix. Ища указанный выше .configфайл мы видим CONFIG_ATA_PIIX=m. мы можем подтвердить это, сделав

dlocate ata_piix.ko   

альтернативно

dpkg -S ata_piix.ko

linux-image-3.2.0-4-amd64: /lib/modules/3.2.0-4-amd64/kernel/drivers/ata/ata_piix.ko

Так что по крайней мере в моем ядре это модуль.

Фахим Митха
источник
Большое спасибо за это, @FaheemMitha - хотя я слышал (и использовал) файл конфигурации раньше, по какой-то причине я полностью забыл об этом в этом примере; приятно заметили! :)В моей системе, grep ATA_PIIX /boot/config-2.6.38-16-genericговорит CONFIG_ATA_PIIX=y, что, вероятно, должно означать в этом ядре, ata_piixэто сборка "в ядре", а не как модуль. Ура!
sdaau