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

Каков правильный способ сопоставления в пространстве пользовательского пространства буфера, выделенного с помощью kmalloc? Возможно, я еще не понял карты памяти... Я пишу модуль ядра, который выделяет этот буфер (например, 120 байт), и я буду читать и писать его в процессе пользовательского пространства. Очевидно, я создал устройство char и реализовал метод mmap в структуре file_operations. Мой метод:

static int my_mmap(struct file *filp, struct vm_area_struct *vma)
{
  //printk(KERN_INFO "Allocated virtual memory length = %d", vma->vm_end - vma->vm_start);

  long unsigned int size = vma->vm_end - vma->vm_start;

  if (remap_pfn_range(vma, vma->vm_start,
                      __pa(mem_area) >> PAGE_SHIFT,  //what about vma->vm_pgoff?
                      size,
                      vma->vm_page_prot) < 0)
    return -EAGAIN;

  vma->vm_flags |= VM_LOCKED;
  vma->vm_ops = &vmops;
  vma->vm_flags |= VM_RESERVED;

  my_vm_open(vma);

  return 0;
}

где mem_area указывает на область памяти, выделенную kmalloc в модуле init. Область заполнена тем же значением (например, 0x10). Все работает, но я думаю, что в этом коде есть что-то не так:

  • kmalloc может возвращать указатель, который не выравнивается по странице, и в этом случае я не считаю правильным значение третьего параметра remap_pfn_range на самом деле в пользовательском пространстве, я читаю Неверное значение. Вместо этого все работает, если я использую __get_free_page (потому что функция всегда возвращает указатель, выравниваемый по страницам) или когда kmalloc возвращает указатель с выравниванием по странице. Картирование памяти работает с областями памяти, которые являются мультиплексированными PAGE_SIZE, поэтому, если я должен выделить целую страницу вместо использования kmalloc?

  • Когда вызывается my_mmap, ядро ​​еще выделило некоторые страницы? Я спрашиваю об этом, потому что я нашел некоторые реализации пользовательского метода mmap, который вызывает remap_pfn_range с vma->vm_pgoff как третий параметр... как это может быть полезно? Является ли это страничным фреймом первой новой выделенной страницы? Если я передаю в качестве третьего параметра кадр страницы, как я делаю в my_mmap, я должен освобождать страницы, начиная со страницы в vma->vm_pgoff?

  • Однако я нашел реализацию метода mmap, который отображает буфер, выделенный с помощью kmalloc. Чтобы правильно отобразить буфер, перед remap_pfn_range выполняется операция (которую я не ожидаю). Предположим, что mem - это указатель, возвращаемый kmalloc, mem_area инициализируется следующим образом:

    mem_area=(int *)(((unsigned long)mem + PAGE_SIZE - 1) & PAGE_MASK);
    

Итак, mem_area содержит одно и то же значение mem, только если mem выравнивается по страницам, иначе указатель должен содержать указатель в начале следующей страницы. Однако с этой операцией, если я передаю третий параметр remap_pfn_range, отображение значения __pa(mem_area) >> PAGE_SHIFT работает хорошо. Почему?

Спасибо всем!

Ответ 1

  • Да, вы должны выделять целые числа страниц.

  • Нет, ядро ​​не выделило ни одной страницы. vm->vm_pgoff - это запрошенное смещение в отображаемом устройстве - это последний параметр для вызова пользовательского пространства mmap(), переведенный с байтов на страницы. Вероятно, вы смотрите на реализации mem или kmem mmap, и в этом случае смещение представляет физическую или линейную страницу, которую хочет отобразить пользовательское пространство.

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

Вы должны проверить, что отображаемый размер не превышает ваш буфер.