Есть ли API для определения физического адреса по виртуальному адресу в Linux? - PullRequest
26 голосов
/ 21 апреля 2011

Существует ли какой-либо API для определения физического адреса по виртуальному адресу в операционной системе Linux?

Ответы [ 5 ]

32 голосов
/ 21 апреля 2011

Ядро и пространство пользователя работают с виртуальными адресами (также называемыми линейными адресами), которые отображаются на физические адреса аппаратным обеспечением управления памятью. Это отображение определяется таблицами страниц, настроенными операционной системой.

Устройства DMA используют адреса шины. На компьютере i386 адреса шины совпадают с физическими адресами, но другие архитектуры могут иметь специальное оборудование для преобразования адресов для преобразования адресов шины в физические адреса.

В Linux вы можете использовать эти функции с asm/io.h:

  • virt_to_phys (virt_addr);
  • phys_to_virt (phys_addr);
  • virt_to_bus (virt_addr);
  • bus_to_virt (bus_addr);

Все это касается доступа к обычной памяти. Существует также «общая память» на шине PCI или ISA. Его можно отобразить в 32-битном адресном пространстве с помощью ioremap (), а затем использовать через функции readb (), writeb () (и т. Д.).

Жизнь осложняется тем фактом, что существуют различные кэши, поэтому разные способы доступа к одному и тому же физическому адресу не обязательно дают один и тот же результат.

Кроме того, реальный физический адрес за виртуальным адресом может измениться. Более того, не будет никакого адреса, связанного с виртуальным адресом, пока вы не получите доступ к этой памяти.

Что касается API пользовательского пространства, мне не известно ни одного.

18 голосов
/ 19 декабря 2012

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

Для этой цели ядро ​​Linux предоставляет доступ к пользовательскому интерфейсу через набор файлов в /proc. Документацию можно найти здесь . Краткое резюме:

  1. /proc/$pid/maps предоставляет список сопоставлений виртуальных адресов вместе с дополнительной информацией, такой как соответствующий файл для сопоставленных файлов.
  2. /proc/$pid/pagemap предоставляет дополнительную информацию о каждой отображаемой странице, включая физический адрес, если он существует.

Этот веб-сайт предоставляет программу на C, которая создает дамп сопоставлений всех запущенных процессов с использованием этого интерфейса, и объяснение того, что он делает.

11 голосов

/proc/<pid>/pagemap пример минимального запуска для пользователя

virt_to_phys_user.c:

#define _XOPEN_SOURCE 700
#include <fcntl.h> /* open */
#include <stdint.h> /* uint64_t  */
#include <stdio.h> /* printf */
#include <stdlib.h> /* size_t */
#include <unistd.h> /* pread, sysconf */

typedef struct {
    uint64_t pfn : 54;
    unsigned int soft_dirty : 1;
    unsigned int file_page : 1;
    unsigned int swapped : 1;
    unsigned int present : 1;
} PagemapEntry;

/* Parse the pagemap entry for the given virtual address.
 *
 * @param[out] entry      the parsed entry
 * @param[in]  pagemap_fd file descriptor to an open /proc/pid/pagemap file
 * @param[in]  vaddr      virtual address to get entry for
 * @return 0 for success, 1 for failure
 */
int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr)
{
    size_t nread;
    ssize_t ret;
    uint64_t data;
    uintptr_t vpn;

    vpn = vaddr / sysconf(_SC_PAGE_SIZE);
    nread = 0;
    while (nread < sizeof(data)) {
        ret = pread(pagemap_fd, &data, sizeof(data) - nread,
                vpn * sizeof(data) + nread);
        nread += ret;
        if (ret <= 0) {
            return 1;
        }
    }
    entry->pfn = data & (((uint64_t)1 << 54) - 1);
    entry->soft_dirty = (data >> 54) & 1;
    entry->file_page = (data >> 61) & 1;
    entry->swapped = (data >> 62) & 1;
    entry->present = (data >> 63) & 1;
    return 0;
}

/* Convert the given virtual address to physical using /proc/PID/pagemap.
 *
 * @param[out] paddr physical address
 * @param[in]  pid   process to convert for
 * @param[in] vaddr virtual address to get entry for
 * @return 0 for success, 1 for failure
 */
int virt_to_phys_user(uintptr_t *paddr, pid_t pid, uintptr_t vaddr)
{
    char pagemap_file[BUFSIZ];
    int pagemap_fd;

    snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
    pagemap_fd = open(pagemap_file, O_RDONLY);
    if (pagemap_fd < 0) {
        return 1;
    }
    PagemapEntry entry;
    if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) {
        return 1;
    }
    close(pagemap_fd);
    *paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE));
    return 0;
}

int main(int argc, char **argv)
{
    pid_t pid;
    uintptr_t vaddr, paddr = 0;

    if (argc < 3) {
        printf("Usage: %s pid vaddr\n", argv[0]);
        return EXIT_FAILURE;
    }
    pid = strtoull(argv[1], NULL, 0);
    vaddr = strtoull(argv[2], NULL, 0);
    if (virt_to_phys_user(&paddr, pid, vaddr)) {
        fprintf(stderr, "error: virt_to_phys_user\n");
        return EXIT_FAILURE;
    };
    printf("0x%jx\n", (uintmax_t)paddr);
    return EXIT_SUCCESS;
}

GitHub upstream .

Использование:

sudo ./virt_to_phys_user.out <pid> <physical-address>

sudo требуется для чтения /proc/<pid>/pagemap, даже если у вас есть разрешения на доступ к файлам, как описано в: https://unix.stackexchange.com/questions/345915/how-to-change-permission-of-proc-self-pagemap-file/383838#383838

Как указано в: https://stackoverflow.com/a/46247716/895245Linux распределяет таблицы страниц лениво, поэтому убедитесь, что вы прочитали и записали байт по этому адресу из тестовой программы, прежде чем использовать virt_to_phys_user.

Как проверить это

Тестовая программа:

#define _XOPEN_SOURCE 700
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

enum { I0 = 0x12345678 };

static volatile uint32_t i = I0;

int main(void) {
    printf("vaddr %p\n", (void *)&i);
    printf("pid %ju\n", (uintmax_t)getpid());
    while (i == I0) {
        sleep(1);
    }
    printf("i %jx\n", (uintmax_t)i);
    return EXIT_SUCCESS;
}

Тестовая программа выводит адрес принадлежащей ей переменной и ее PID, например:

vaddr 0x600800
pid 110

, и затем вы можете передать преобразование виртуального адресас помощью:

sudo ./virt_to_phys_user.out 110 0x600800

Наконец, преобразование можно проверить с помощью /dev/mem для наблюдения / изменения памяти, но вы не можете сделать это в Ubuntu 17.04 без перекомпиляции ядра, так как для этого требуется: CONFIG_STRICT_DEVMEM=n см. также: Как получить доступ к физическим адресам из пространства пользователя в Linux? Buildroot - это простой способ чтобы преодолеть это, однако.

В качестве альтернативы, вы можете использовать виртуальную машину, например, команду xp монитора QEMU: Как декодировать записи / proc / pid / pagemap в Linux?

См. Это для вывода всех страниц: Как декодировать записи / proc / pid / pagemap в Linux?

Пользовательское подмножество этого вопроса: Как найтифизический адрес переменной из пространства пользователя в Linux?

Дамп всех страниц процесса с /proc/<pid>/maps

/proc/<pid>/maps перечисляет все диапазоны адресовпроцесс, так что мы можем пройти, чтобы перевести все страницы: / proc / [pid] / pagemaps и / proc / [pid] / maps |linux

Kerneland virt_to_phys работает только для kmalloc адресов

Из модуля ядра, virt_to_phys, упоминалось.

Однако важно подчеркнуть, что у него есть это ограничение.

Например, оно не работает для переменных модуля.arc/x86/include/asm/io.h документация:

Возвращенный физический адрес - это физическое (CPU) отображение для заданного адреса памяти.Допустимо использовать эту функцию только для адресов, напрямую сопоставленных или выделенных с помощью kmalloc.

Вот модуль ядра, который иллюстрирует это вместе с тестом пользовательского пространства .

Так что это не очень общая возможность.См .: Как получить физический адрес из логического адреса в модуле ядра Linux? исключительно для методов модуля ядра.

4 голосов
/ 16 сентября 2017

Предлагаемая выше программа на C обычно работает, но она может возвращать вводящие в заблуждение результаты (как минимум) двумя способами:

  1. Страница отсутствует (но виртуальный адрес сопоставлен со страницей!).Это происходит из-за ленивого сопоставления ОС: оно отображает адреса только тогда, когда к ним фактически обращаются.
  2. Возвращенный PFN указывает на некоторую возможную временную физическую страницу, которая может быть изменена вскоре после копирования при записи.Например: для файлов с отображенной памятью PFN может указывать на копию только для чтения.Для анонимных сопоставлений PFN всех страниц в сопоставлении может быть одной конкретной страницей, доступной только для чтения, заполненной нулями (из которой все анонимные страницы появляются при записи).

Нижняя строка, чтобы обеспечить более надежный результат: для отображений только для чтения, прочитайте с каждой страницы по крайней мере один раз, прежде чем запрашивать ее PFN.Для страниц с поддержкой записи, по крайней мере, один раз запишите на каждую страницу, прежде чем запрашивать ее PFN.

Конечно, теоретически, даже после получения "стабильного" PFN, отображения всегда могут произвольно изменяться во время выполнения (например,перемещение страниц в и из свопа) и на них нельзя полагаться.

2 голосов
/ 29 января 2013

Интересно, почему не существует пользовательского API.

Поскольку физический адрес пользовательской памяти неизвестен.

Linux использует пейджинг по требованию для пользовательской памяти. Ваш пользовательский объект земли не будет иметь физической памяти, пока к нему не будет получен доступ. Когда системе не хватает памяти, ваш пользовательский объект земли может быть заменен и потерять физическую память, если страница не заблокирована для процесса. Когда вы снова получаете доступ к объекту, он переставляется и получает физическую память, но, скорее всего, он отличается от предыдущего. Вы можете сделать снимок отображения страницы, но это не обязательно будет таким же в следующий момент.

Таким образом, поиск физического адреса пользовательского наземного объекта обычно не имеет смысла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...