Ответ, данный в 2014 году, правильный. Все, что знает процессор, это то, что страница отсутствует, а иногда и то, что у нее неправильное разрешение (например, запись на страницу, доступную только для чтения). В этот момент процессор генерирует исключение "ошибка страницы", которое ядро получает и теперь должно обрабатывать.
В некоторых случаях эту ошибку страницы нужно будет передать приложению на всем пути, в Linux как сигнал SIGSEGV («нарушение сегментации»), например, когда пользователь использует нулевой указатель. Но, как вы сказали, чаще всего ядро должно и может обрабатывать ошибку страницы. Ядро хранит в своих собственных таблицах (а не в таблице страниц, которая представляет собой структуру с определенным форматом, определяемым процессором) информацию о том, что должна содержать каждая страница виртуальной памяти. Ниже приведены некоторые вещи, которые ядро может понять о странице ошибки, обращаясь к своим собственным таблицам. Это не исчерпывающий список.
Это может быть страница, созданная mmap() с диска. Этот случай включает в себя явное использование приложением mmap(), но также происходит, когда вы запускаете исполняемый файл или используете общие библиотеки — они также отображаются с диска — поэтому ошибка страницы также может произойти, когда процессор выполняет инструкции, а не только при чтении и писать. Ядро хранит список этих сопоставлений, поэтому, когда оно получает ошибку страницы, оно может выяснить, где на диске ему нужно прочитать, чтобы получить недостающую страницу. Таким образом, он читает с диска, а при получении данных помещает их в новую страницу в памяти и устанавливает запись в таблице страниц (PTE) так, чтобы она указывала на эту новую страницу с данными, и возобновляет поток приложения — где неисправная инструкция повторяется и теперь успешно.
Возможно, это была страница, выгруженная на диск. Опять же, ядро хранит таблицу того, какие страницы были выгружены, и где в разделе подкачки (или файле подкачки, или где-то еще) эта страница теперь находится.
Возможно, это была попытка записи на страницу «копирование при записи». Ядру необходимо сделать копию исходной страницы и изменить адрес в таблице страниц, чтобы он указывал на новую копию, а затем разрешить запись. Например, когда вы выделяете большую область памяти, она может указывать на существующую «нулевую» страницу и выделяться только при первой записи на страницы. Другой пример: после fork() все страницы нового процесса являются страницами копирования при записи, указывающими на страницы исходного процесса, и будут фактически скопированы только при первой записи (любым процессом).
Однако, поскольку вы ищете достоверные источники, возможно, вы захотите прочитать объяснение того, как именно ядро Linux делает это, например, в: https://vistech.net/~champ/online-docs/books/linuxkernel2/060.htm.
person
Nadav Har'El
schedule
13.04.2019