Как процесс fork() помечает родительские PTE только для чтения?

Я перерыл много ресурсов, но ничего конкретного по этому поводу не нашел:

Я знаю, что в некоторых Linux-системах системный вызов fork() работает с копированием при записи; то есть родительский и дочерний элементы используют одно и то же адресное пространство, но PTE теперь помечен как только для чтения и будет использоваться позже COW. когда любой из них пытается получить доступ к странице, происходит PAGE_FAULT, и страница копируется в другое место, где ее можно изменить.

Однако я не могу понять, как ОС достигает общих PTE, чтобы пометить их как «прочитанные». Я предположил, что когда происходит системный вызов fork(), ОС выполняет "прогулку по странице" в родительской таблице страниц и помечает их как доступные только для чтения, но я не нахожу ни подтверждения этому, ни какой-либо информации относительно процесс.

Кто-нибудь знает, как страницы помечаются как только для чтения? Будем признательны за любую помощь. Спасибо!


person ITz    schedule 23.02.2020    source источник
comment
ОС Линуса Linux работает на VMA внутри реализации системного вызова fork: do_fork -› copy_process -› copy_mm -› dup_mm -› dup_mmap ... Здесь мне не удалось получить точную строку, поэтому поисковая машина Интернета дает подсказку для fork+COW+dup_mm как gist.github.com/cwshu/7d52bc993525c1bb7df1 - так что настоящая работа находится в строке retval = copy_page_range(mm, oldmm, mpnt); - проверьте mm/memory.c#L1005   -  person osgx    schedule 24.02.2020


Ответы (1)


ОС Linux реализует форк системных вызовов с итерацией по всем диапазонам памяти (mmaps, стек и куча) родительского процесса. Копирование этих диапазонов (VMA - Области виртуальной памяти находятся в функции copy_page_range (mn/memory.c), который имеет цикл по записям таблицы страниц:

    /*
     * If it's a COW mapping, write protect it both
     * in the parent and the child
     */
    if (is_cow_mapping(vm_flags)) {
        ptep_set_wrprotect(src_mm, addr, src_pte);
        pte = pte_wrprotect(pte);
    }

где is_cow_mapping будет истинным для частных и потенциально доступных для записи страниц (флаги битового поля проверяются для общих и vm-write-and-vm-maywrite-are-needed">maywrite bits и должен иметь только установленный бит maywrite)

#define VM_SHARED   0x00000008
#define VM_MAYWRITE 0x00000020

static inline bool is_cow_mapping(vm_flags_t flags)
{
    return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
}

PUD, PMD и PTE описаны в таких книгах, как https://www.kernel.org/doc/gorman/html/understand/understand006.html и в таких статьях, как LWN 2005: "Четырехуровневые таблицы страниц объединены".

Как реализация форка вызывает copy_page_range:

  • реализация системного вызова fork (sys_fork? или syscall_define0(fork)) — это do_fork (kernel/fork.c), который вызовет
  • copy_process, который вызовет множество функций copy_*, включая
  • copy_mm, который вызывает
  • dup_mm для выделения и заполнения новой структуры mm, где большая часть работы выполняется
  • dup_mmap (все еще ядро/fork.c) который проверит, что было mmaped и как. (Здесь мне не удалось получить точный путь к реализации COW, поэтому я использовал поисковую машину в Интернете с чем-то вроде «fork+COW+dup_mm», чтобы получить подсказки типа [1] или [2] или [ 3]). После проверки типов mmap есть строка retval = copy_page_range(mm, oldmm, mpnt); для реальной работы.
person osgx    schedule 24.02.2020
comment
Хорошая детективная работа, хотя ваши ссылки довольно старые. В последних версиях ядра происходит следующее: copy_page_range()copy_p4d_range()copy_pud_range()copy_pmd_range()copy_pte_range()copy_one_pte(). Вы пропустили промежуточный вызов copy_p4d_range() от copy_page_range(), который больше не вызывает copy_pud_range() напрямую. - person Marco Bonelli; 24.02.2020
comment
Спасибо большое вам обоим! Понятно все объясняет. Однако последний вопрос для уточнения: с чего начинается итерация? Упоминается, что итерация начинается через copy_page_range func. Глядя на прилагаемый код, я увидел, что он работает и с другими структурами данных (такими как TLB). Начинается ли итерация самих таблиц страниц с PTBR (следовательно, итерация действует как обход страницы)? И еще одна вещь, которую я заметил в коде: кажется, что указанная выше функция активна на PTE. Это та же самая функция, которая определяет, будет ли страница считаться активной или неактивной? - person ITz; 24.02.2020
comment
@itaz это объясняется в ответе, это dup_mmap() выполняет эту работу и решает позвонить copy_page_range() - person Marco Bonelli; 24.02.2020
comment
@itaz, итерация начинается с dup_mm -> dup_mmap, и она итерирует не аппаратные таблицы страниц, а программные структуры ядра Linux, которые описывают области памяти процесса. Так что это не прогулка по странице, это прогулка по mmap. dup_mmap выполняет итерацию по областям с mmap, где некоторые mmap могут быть общими или файловыми, а другие являются частными. И некоторые страницы уже могут быть отключены для записи из-за процесса COW. Не уверен, где вы нашли активный, у copy_one_pte есть ветка для отсутствующих страниц (!pte_present(pte)) (например, поменялась местами). - person osgx; 25.02.2020