Подтвердить что ты не робот

Есть ли API для определения физического адреса с виртуального адреса в Linux?

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

4b9b3361

Ответ 1

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

Устройства 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 пользовательской земли, я не знаю о нем.

Ответ 2

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

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

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

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

Ответ 3

/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

Как упоминалось в разделе: fooobar.com/questions/218637/... Linux распределяет таблицы страниц лениво, поэтому убедитесь, что вы прочитали и записали байт по этому адресу из тестовой программы, прежде чем использовать 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 - это простой способ преодолеть это.

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

Посмотрите это, чтобы сбросить все страницы: Как декодировать записи /proc/pid/pagemap в Linux?

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

Создать дамп всех страниц процесса с помощью /proc/<pid>/maps

/proc/<pid>/maps перечисляет все диапазоны адресов процесса, поэтому мы можем перейти к этому, чтобы перевести все страницы: fooobar.com/questions/218681/...

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

Из модуля ядра, virt_to_phys, было упомянуто.

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

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

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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

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

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