Как ядро ​​Linux определяет, был ли изменен адрес памяти для реализации COW?

Исходный код Здесь:

 #include <stdio.h>
 #include <stdlib.h>

 void main() {
     int *a = malloc(sizeof(int));
     *a = 11;
     int b = 22;//on the stack

     int pid = fork();

     if (pid == 0) {
         printf("pid=%d, a = %d, &a=%p\n", getpid(), *a, a);
         printf("pid=%d, b = %d, &b=%p\n", getpid(), b, &b);
         getchar();
         *a = 33;// ===========cow=========happend here=====
         b = 44;
         printf("pid=%d, a = %d, &a=%p\n", getpid(), *a, a);
         printf("pid=%d, b = %d, &b=%p\n", getpid(), b, &b);
     } else {
         printf("pid=%d, a = %d, &a=%p\n", getpid(), *a, a);
         printf("pid=%d, b = %d, &b=%p\n", getpid(), b, &b);
     }
     pause();
 }

Вот дизассемблированный gdb той строки, которая записывает 33 в a, здесь я установил точку останова. и запустить эту программу. Затем используйте crash, чтобы увидеть физический адрес a.

  >│0x40073a <main+154>     movl   $0x2c,-0x20(%rbp)  //copy on write happend here                                                                                                                              │
   │0x400741 <main+161>     mov    -0x18(%rbp),%rax                                                                                                                                 │
   │0x400745 <main+165>     mov    (%rax),%ebx

Линейный адрес a0x602010, поэтому с помощью vtop я получил следующее:

Мы видим, что они указывают на один и тот же физический адрес 2a683010.

    PID: 6468
COMMAND: "a.out"
   TASK: ffff88007c317300  [THREAD_INFO: ffff880016728000]
    CPU: 0
  STATE: TASK_TRACED|TASK_WAKEKILL
crash> vtop 0x602010
VIRTUAL     PHYSICAL
602010      2a683010

   PML: 24e6f000 => 2409c067
   PUD: 2409c000 => 7144067
   PMD: 7144018 => 19847067
   PTE: 19847010 => 800000002a683065
  PAGE: 2a683000



    PID: 6464
COMMAND: "a.out"
   TASK: ffff880036992280  [THREAD_INFO: ffff880014e38000]
    CPU: 0
  STATE: TASK_TRACED|TASK_WAKEKILL
crash> vtop 0x602010
VIRTUAL     PHYSICAL
602010      2a683010

   PML: 36df9000 => 3a654067
   PUD: 3a654000 => 1a71a067
   PMD: 1a71a018 => 18f2a067
   PTE: 18f2a010 => 800000002a683065
  PAGE: 2a683000

После ввода ni в gdb (что изменило значение a на 33), снова используйте vtop. Я вижу, что один из физических адресов процесса изменился.

crash> vtop 0x602010
VIRTUAL     PHYSICAL
602010      5d755010

   PML: 24e6f000 => 2409c067
   PUD: 2409c000 => 7144067
   PMD: 7144018 => 19847067
   PTE: 19847010 => 800000005d755067
  PAGE: 5d755000



crash> vtop 0x602010
VIRTUAL     PHYSICAL
602010      2a683010

   PML: 36df9000 => 3a654067
   PUD: 3a654000 => 1a71a067
   PMD: 1a71a018 => 18f2a067
   PTE: 18f2a010 => 800000002a683065
  PAGE: 2a683000

Мой вопрос в том, что произошло, когда процессор выполнился

movl   $0x2c,-0x20(%rbp)

Как ядро ​​​​узнало, что оно изменяет разделяемую память, поэтому перед записью необходимо выполнить копирование? Я предполагаю, что он использует что-то вроде прерывания по ошибке страницы. Но я не нашел никакого прерывания, связанного с этим.

Пожалуйста, предоставьте исходный код ядра, если за это отвечает ядро.


person Jason Young    schedule 01.06.2016    source источник
comment
пожалуйста, предоставьте исходный код ядра, если за это отвечает ядро. github.com /торвальдс/линукс   -  person Andrew Henle    schedule 01.06.2016
comment
@AndrewHenle хороший :-)   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 01.06.2016
comment
Я думаю, что этот лучше, чем github code.woboq.org/linux/linux   -  person Jason Young    schedule 01.06.2016
comment
Я думаю, что в википедии есть ответ: en.wikipedia.org/wiki / Копирование при записи может быть реализовано путем уведомления MMU о том, что определенные страницы в адресном пространстве процесса доступны только для чтения. Когда данные записываются на эти страницы, MMU вызывает исключение, которое обрабатывается ядром, которое выделяет новое пространство в физической памяти и заставляет записываемую страницу соответствовать этому новому местоположению в физической памяти. Блестящая штука.   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 01.06.2016
comment
Также я бы дал ссылку на people.redhat.com/anderson/crash_whitepaper по вопросу, многие вроде меня могут этого не знать :-)   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 01.06.2016
comment
Итак: books.google.com.hk/   -  person Jason Young    schedule 01.06.2016
comment
Связанные stackoverflow.com/questions/13392931/copy-on- запись в Linux   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 01.06.2016
comment
Как насчет этого: code.woboq.org/linux/linux /mm/memory.c.html#3196   -  person Jason Young    schedule 01.06.2016


Ответы (1)


Мой вопрос в том, что произошло, когда процессор выполнился

movl $0x2c,-0x20(%rbp)

Как ядро ​​​​узнало, что оно изменяет разделяемую память, поэтому перед записью необходимо выполнить копирование? Я предполагаю, что он использует что-то вроде прерывания по ошибке страницы. Но я не нашел никакого прерывания, связанного с этим.

Это достигается совместными усилиями процессора и ОС.

Сторона процессора:

Когда CPU выполняет такую ​​инструкцию:

movl $0x2c,-0x20(%rbp)

то есть извлечение адреса, хранящегося в регистре% rbp, и добавление к нему смещения -x20, а затем выдача доступа к памяти (movl).

После совершения доступа к памяти процессор просматривает таблицу аппаратных страниц (ну, в большинстве случаев это можно сделать, посетив TLB, но я просто говорю здесь о фундаментальном принципе). Таблицы страниц, конечно же, должны быть предварительно настроены ОС.

Предположим, процессор пробирается к таблице страниц последнего уровня и просто обнаруживает, что соответствующая запись таблицы страниц (будет называть ее pte для краткости в остальной части этого ответа) для этого самого адреса предлагает страницу содержащий этот адрес, НЕ находится в памяти! (Он просто сверяется с флагом конкретной страницы этого pte), затем, в зависимости от архитектуры процессора, возникает аппаратное исключение! Согласно терминологии Intel, такие исключения классифицируются как ошибки, и вы должны часто слышать термин 'ошибка страницы' (вид исключения, которое может быть исправлено и выполнение может быть возобновлено, как будто такого исключения вообще не было!)

На стороне ОС:

Затем мы перемещаемся вверх по стеку в домен ОС. В процессе загрузки ОС установит таблицу обработки исключений и прерываний (на жаргоне x86 мы называем ее IDT) и зарегистрирует ее в процессоре.

Затем, при возникновении этой ошибки страницы, обработчик предварительной настройки выполняется процессором (технически процессор должен сначала сохранить контекст ЦП, например, нажать регистры cs и rip, регистр rflags и т. д.).

Обработчик можно разделить на часть, относящуюся к архитектуре (где ОС в дальнейшем будет выполнять некоторые задачи, связанные с аппаратным обеспечением, такие как сохранение большего количества регистров, вызов хука, специфичного для архитектуры, определение того, разрешена ли ошибка страницы? и т. д.) и часть, независимая от архитектуры. (логика ошибки страницы), поэтому неудивительно, что точка входа обработчика зависит от арки.

Для Linux на x86 часть, относящаяся к архитектуре, находится в arch/x86/entry/entry_64.S (для 64-разрядной версии), а функция C do_page_fault() находится в arch/x86/mm. /fault.c. Затем в do_page_fault() вызывается независимая от архитектуры функция C handle_mm_fault(), которая располагается в основном коде MM по адресу mm/memory.c.

И для этого вопроса в handle_mm_fault() do_wp_page() обрабатывает логику COW. По сути, handle_mm_fault() просто просматривает таблицу страниц ошибочного адреса и обнаруживает, что это страница с защитой от записи (присутствует, но флаг записи не установлен), поэтому вызывает do_wp_page() для выделения новой страницы.

person larmbr    schedule 07.06.2016