Article sur les IO du pilote Linux - fonctionnement mmap

Libérer: 2023-07-31 15:55:07
avant
1102 Les gens l'ont consulté

Avant-propos

Habituellement, lorsque nous écrivons des pilotes Linux et interagissons avec l'espace utilisateur, nous utilisons toujours copy_from_userCopiez les données transmises depuis l'espace utilisateur. Pourquoi faites-vous cela ? copy_from_user把用户空间传过来的数据进行拷贝,为什么要这么做呢?

因为用户空间是不能直接内核空间数据的,他们映射的是不同的地址空间,只能先将数据拷贝过来,然后再操作。

如果用户空间需要传几MB的数据给内核,那么原来的拷贝方式显然效率特别低,也不太现实,那怎么办呢?

想想,之所以要拷贝是因为用户空间不能直接访问内核空间,那如果可以直接访问内核空间的buffer,是不是就解决了。

简单来说,就是让一块物理内存拥有两份映射,即拥有两个虚拟地址,一个在内核空间,一个在用户空间。关系如下:

Article sur les IO du pilote Linux - fonctionnement mmap

通过mmap映射就可以实现。

应用层

应用层代码很简单,主要就是通过mmap系统调用进行映射,然后就可以对返回的地址进行操作。

char * buf;
/* 1. 打开文件 */
 fd = open("/dev/hello", O_RDWR);
 if (fd == -1)
 {
      printf("can not open file /dev/hello\n");
      return -1;
 }

/* 2. mmap
       * MAP_SHARED  : 多个APP都调用mmap映射同一块内存时, 对内存的修改大家都可以看到。
       *               就是说多个APP、驱动程序实际上访问的都是同一块内存
       * MAP_PRIVATE : 创建一个copy on write的私有映射。
       *               当APP对该内存进行修改时,其他程序是看不到这些修改的。
       *               就是当APP写内存时, 内核会先创建一个拷贝给这个APP,
       *               这个拷贝是这个APP私有的, 其他APP、驱动无法访问。
       */
buf =  mmap(NULL, 1024*8, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
Copier après la connexion

mmap的第一个参数是想要映射的起始地址,通常设置为NULLÉtant donné que l'espace utilisateur ne peut pas accéder directement aux données de l'espace noyau, ils mappent différents espaces d'adressage, de sorte que les données ne peuvent être copiées que d'abord, puis exploitées.

Si l'espace utilisateur doit transférer plusieurs Mo de données vers le noyau, alors la méthode de copie originale est évidemment très inefficace et irréaliste, alors que devons-nous faire ? 🎜🎜Pensez-y, la raison de la copie est que l'espace utilisateur ne peut pas accéder directement à l'espace du noyau. Donc, si le tampon dans l'espace du noyau est directement accessible, cela serait-il résolu ? 🎜🎜🎜Pour faire simple, cela signifie qu'un morceau de mémoire physique a deux mappages, c'est-à-dire qu'il a deux adresses virtuelles, une dans l'espace noyau et une dans l'espace utilisateur. 🎜La relation est la suivante : 🎜
Article sur les IO du pilote Linux - fonctionnement mmap
🎜Parmmap peut être réalisé. 🎜🎜🎜🎜🎜Couche d'application🎜🎜 🎜🎜🎜Le code de la couche d'application est très simple, principalement via mmap🎜 appel système🎜 pour le mappage, et vous pouvez ensuite opérer sur l'adresse renvoyée. 🎜
int remap_pfn_range(
  struct vm_area_struct *vma, 
  unsigned long addr, 
  unsigned long pfn, 
  unsigned long size, 
  pgprot_t prot);
Copier après la connexion
Copier après la connexion
🎜le premier paramètre de mmap est l'adresse de départ que vous souhaitez cartographier, généralement définie sur NULL < /code>, 🎜 signifie que le noyau détermine l'adresse de départ 🎜. 🎜<p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Le deuxième paramètre est la <strong>taille de l'espace mémoire à mapper</strong>. </p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Le troisième paramètre<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35 , 0,05);famille de polices : "Operator Mono", Consolas, Monaco, Menlo, monospace;saut de mot : break-all;couleur : rgb(239, 112, 96);">PROT_READ | PROT_WRITE Indique que l'espace cartographié est PROT_READ | PROT_WRITE表示映射后的空间是可读可写的。

第四个参数可填MAP_SHAREDMAP_PRIVATElisible et inscriptible

. 🎜🎜Le quatrième paramètre peut être renseignéMAP_SHARED</code > ou <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05) ; famille de polices : "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">MAP_PRIVATE:🎜
  • MAP_SHARED : MultipleAPP tous appellent mmap Lors du mappage de la même mémoire, tout le monde peut voir les modifications apportées à la mémoire. C'est-à-dire plusieurs APP, pilote les actuels accèdent tous à la même mémoire.
  • MAP_SHARED:多个APP都调用mmap映射同一块内存时, 对内存的修改大家都可以看到。就是说多个APP、驱动程序实际上访问的都是同一块内存
  • MAP_PRIVATE:创建一个copy on write的私有映射。当APP对该内存进行修改时,其他程序是看不到这些修改的。就是当APP写内存时, 内核会先创建一个拷贝给这个APP,这个拷贝是这个APP🎜MAP_PRIVATE</ code> : Créez un <code style="font-size : 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35 , 0.05);font-family : "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">copie en écriture Privé cartographie. QuandLorsque APP modifie cette mémoire, d'autres programmes Je ne peux pas voir ces modifications. C'est alors queAPPLors de l'écriture en mémoire, le noyau créera d'abord une copie pour celaAPP, cette copie est celle-ciAPP est privé et n'est pas accessible par d'autres applications et pilotes.
  • 驱动层

    驱动层主要是实现mmap接口,而mmap接口的实现,主要是调用了remap_pfn_range函数,函数原型如下:

    int remap_pfn_range(
      struct vm_area_struct *vma, 
      unsigned long addr, 
      unsigned long pfn, 
      unsigned long size, 
      pgprot_t prot);
    Copier après la connexion
    Copier après la connexion

    vma:描述一片映射区域的结构体指针

    addr:要映射的虚拟地址起始地址

    pfn:物理内存所对应的页框号,就是将物理地址除以页大小得到的值

    size:映射的大小

    prot:该内存区域的访问权限

    驱动主要步骤:

    1、使用kmalloc或者kzalloc函数分配一块内存kernel_buf,因为这样分配的内存物理地址是连续的,mmap后应用层会对这一个基地址去访问这块内存。

    2、实现mmap函数

    static int hello_drv_mmap(struct file *file, struct vm_area_struct *vma)
    {
     /* 获得物理地址 */
     unsigned long phy = virt_to_phys(kernel_buf);//kernel_buf是内核空间分配的一块虚拟地址空间
        
        /* 设置属性:cache, buffer*/
     vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
        
        /* map */
        if(remap_pfn_range(vma, vma->vm_start, phy>>PAGE_SHFIT,
                          vma->vm_end - vma->start, vma->vm_page_prot)){
     printk("mmap remap_pfn_range failed\n");
        return -ENOBUFS;
     }
     return 0;
    }
    
    static struct file_operations my_fops = {
     .mmap = hello_drv_mmap,
    };
    Copier après la connexion

    1、通过virt_to_phys将虚拟地址转为物理地址,这里的kernel_buf是内核空间的一块虚拟地址空间

    2、设置属性:不使用cache,使用buffer

    3、映射:通过remap_pfn_range函数映射,phy>>PAGE_SHIFT其实就是按page映射,除了这个参数,其他的起始地址、大小和权限都可以由用户在系统调用函数中指定

    当应用层调用mmap后,就会调用到驱动层的mmap函数,最终应用层的虚拟地址和驱动中的物理地址就建立了映射关系,应用层也就可以直接访问驱动的buffer了。

    Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

    Étiquettes associées:
    source:嵌入式Linux充电站
    Déclaration de ce site Web
    Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
    Tutoriels populaires
    Plus>
    Derniers téléchargements
    Plus>
    effets Web
    Code source du site Web
    Matériel du site Web
    Modèle frontal
    À propos de nous Clause de non-responsabilité Sitemap
    Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!