В ядре Linux я написал код, напоминающий copy_page_range
(mm/memory.c), поэтому копируйте память из одного процесса в другой с оптимизацией COW. Адреса назначения и источника могут быть смещены на PAGE_SIZE
, и COW все еще работает. Однако я заметил, что в пользовательской программе, когда я копирую с одного и того же адреса источника на разные адреса назначения, TLB не очищается должным образом. На высоком уровне мой код пользовательского уровня делает следующее (я копирую ровно одну страницу, 0x1000 байт на моей машине за раз):
SRC=0x20000000
- Напишите в SRC (позвоните на соответствующую страницу
page1
). - Системный вызов для копирования SRC в 0x30000000 в процессе назначения. Теперь адрес процесса src 0x20000000 и адрес процесса назначения 0x30000000 указывают на одну и ту же страницу (
page1
). - Напишите что-нибудь другое в SRC (это должно вызвать ошибку страницы для обработки COW). Предположим, исходный адрес теперь указывает на
page2
. - Системный вызов для копирования SRC в 0x30001000 в процессе назначения.
На данный момент должны существовать две отдельные страницы: SRC 0x20000000 page2
DST 0x30000000 page1
DST 0x30001000 page2
Я обнаружил, что на шаге 3, когда я записываю что-то другое в src 0x20000000, ошибка страницы не генерируется. После проверки фактические сопоставления страниц следующие: SRC 0x20000000 page1
DST 0x30000000 page1
DST 0x30001000 page1
В моем коде, если я вызываю flush_tlb_page
и передаю исходный адрес, пользовательский код работает, как и ожидалось, с правильными сопоставлениями страниц. Поэтому я убежден, что неправильно поддерживаю TLB. В copy_page_range
ядро вызывает mmu_notifier_invalidate_range_start/end
до и после изменения таблиц страниц. Я делаю то же самое и дважды проверил, действительно ли я передаю правильный struct_mm и адреса в mmu_notifier_invalidate_range_start/end
. Эта функция не справляется с очисткой tlb?
Итак, буквально, когда я закончил набирать это, я проверил dup_mmap
и понял, что основной вызывающий объект copy_page_range
, dup_mmap
(kernel/fork.c), вызывает flush_tlb_mm
. Я предполагаю, что должен вызывать flush_cache_range
и flush_tlb_range
до и после моего кода ядра. Это правильно? Что именно делает mmu_notifier_invalidate_range_start/end
?