Как copy_from_user из ядра Linux работает внутри?

Как именно copy_from_user() работает внутри? Использует ли он какие-либо буферы или выполняется какое-либо отображение памяти, учитывая тот факт, что ядро имеет право доступа к области памяти пользователя?

Ответ 1

Реализация copy_from_user() сильно зависит от архитектуры.

В x86 и x86-64 он просто выполняет прямое чтение из адреса пользовательского пространства и записывает на адрес ядра, а временно отключает SMAP (Supervisor Mode Access Prevention), если он настроен. Его сложная часть состоит в том, что код copy_from_user() помещается в специальную область, чтобы обработчик ошибок страницы мог распознавать, когда в нем возникает ошибка. Ошибка защиты памяти, возникающая в copy_from_user(), не убивает процесс, как если бы он был вызван каким-либо другим кодом процесса-контекста, или паника, как это было бы в случае прерывания, это просто возобновление выполнения путь кода, который возвращает -EFAULT вызывающему.

Ответ 2

относительно "как bout copy_to_user, так как ядро ​​проходит по адресу пространства ядра, как может процесс доступа пользователя к нему"

Процесс пользовательского пространства может попытаться получить доступ к любому адресу. Однако, если адрес не отображается в этом пользовательском пространстве процесса (т.е. В таблицах страниц этого процесса), или если существует проблема с доступом, подобным попытке записи, в место только для чтения, создается сбой страницы. Обратите внимание, что по крайней мере на x86 каждый процесс имеет все пространство ядра, отображаемое в нижнем 1 гигабайте виртуального адресного пространства этого процесса, а 3 верхних гигабайта общего адресного пространства 4 Гб (я использую здесь 32-битную классическую case) используются для текста процесса (т.е. кода) и данных. Копия на или из пользовательского пространства выполняется кодом ядра, который выполняется от имени процесса и фактически является отображением памяти (т.е. Таблицами страниц) этого процесса, которые используются во время копирования. Это происходит, когда выполнение выполняется в режиме ядра - то есть режим привилегированного/супервизора на языке x86. Предполагая, что код пользовательского пространства прошел законное целевое местоположение (т.е. Адрес, правильно отображаемый в этом адресном пространстве процесса), чтобы скопировать данные, copy_to_user, запустить из контекста ядра, мог бы нормально записывать этот адрес/область без вывода проблемы и после того, как элемент управления вернется к пользователю, пользовательское пространство также может считывать из этой настройки местоположения сам процесс для начала. Более интересные подробности можно найти в главах 9 и 10 "Понимание ядра Linux", 3-е издание, "Daniel P. Bovet", "Marco Cesati". В частности, access_ok() является необходимой, но не достаточной проверкой действительности. Пользователь по-прежнему может передавать адреса, не принадлежащие адресному пространству процесса. В этом случае возникает ошибка Page Fault, когда код ядра выполняет копию. Наиболее интересная часть заключается в том, как обработчик ошибок на странице ядра определяет, что ошибка страницы в этом случае связана не с ошибкой в ​​коде ядра, а с плохим адресом пользователя (особенно, если рассматриваемый код ядра является модулем ядра загружен).

Ответ 3

Реализация системного вызова copy_from_user() выполняется с использованием двух буферов из разных адресных пространств:

  • Буфер пользовательского пространства в пользовательском виртуальном адресном пространстве.
  • Буфер пространства ядра в виртуальном адресном пространстве ядра.

Когда copy_from_user() системный вызов copy_from_user(), данные копируются из пользовательского буфера в буфер ядра.

Часть (операция записи) кода драйвера символьного устройства, где используется copy_from_user(), приведена ниже:

ssize_t cdev_fops_write(struct file *flip, const char __user *ubuf,
                        size_t count, loff_t *f_pos) 
{     
    unsigned int *kbuf;
    copy_from_user(kbuf, ubuf, count);
    printk(KERN_INFO "Data: %d",*kbuf); 
}

Ответ 4

В лучшем ответе есть что-то не так: copy_ (from | to) пользователь не может использоваться в контексте прерывания, он может спать, копировать (от | до) пользовательскую функцию можно использовать только в контексте процесса, таблица страниц процесса включает всю информацию, которую ядро ​​должно получить к ней, поэтому ядро ​​может напрямую обращаться к адресу пространства пользователя, если мы можем убедиться, что адрес страницы находится в памяти, используйте функцию copy (from | to) _user, потому что они могут проверять это для нас, и если адресная страница, адресованная пользовательскому пространству, не является резидентной, она будет исправлять ее для нас напрямую.