ホームページ  >  記事  >  運用・保守  >  Linux ドライバー IO の記事 - mmap 操作

Linux ドライバー IO の記事 - mmap 操作

嵌入式Linux充电站
嵌入式Linux充电站転載
2023-07-31 15:55:071121ブラウズ

まえがき

通常、Linux ドライバーを作成してユーザー空間を操作するときは、常に copy_from_user を使用します。 ユーザー空間から転送されたデータをコピーします。なぜこれを行うのですか?

ユーザー空間はカーネル空間データに直接アクセスできないため、異なるアドレス空間をマップし、データは最初にコピーしてから操作することしかできません。

ユーザー空間が数 MB のデータをカーネルに転送する必要がある場合、元のコピー方法は明らかに非効率で非現実的です。では、どうすればよいでしょうか?

よく考えてみると、コピーする理由はユーザー空間からカーネル空間に直接アクセスできないからで、カーネル空間のバッファに直接アクセスできれば解決するのでしょうか?

簡単に言えば、物理メモリには 2 つのマッピングがあります。つまり、物理メモリには 2 つの仮想アドレスがあり、1 つはカーネル空間に、もう 1 つはユーザー空間にあります。 関係は次のとおりです:

Linux ドライバー IO の記事 - mmap 操作

mmap マッピングを通じて実現できます。

#アプリケーション層

アプリケーション層のコードは、主に

#mmap# を介して非常にシンプルです。 # #システム コールMap を実行すると、返されたアドレスを操作できます。

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);

mmap

の最初のパラメータは、マップする開始アドレスです。通常は NULL に設定されます。 は、カーネルが開始アドレスを決定することを意味します

2 番目のパラメータは、マップされる メモリ空間のサイズです。

3 番目のパラメータ

PROT_READ | PROT_WRITE は、マップされたスペースが 読み取り可能および書き込み可能であることを示します。

4 番目のパラメータは、

MAP_SHARED または MAP_PRIVATE: に入力できます。

  • MAP_SHARED: 複数の APPmmap を呼び出して同じメモリをマップすると、全員がメモリへの変更を確認できます。つまり、複数の APP とドライバーが実際には 同じメモリ にアクセスします。
  • MAP_PRIVATE: copy on write プライベート マッピングを作成します。 APP がこのメモリに変更を加えると、他のプログラムはこれらの変更を認識できなくなります。つまり、APP がメモリに書き込むとき、カーネルは最初にこの APP のコピーを作成します。このコピーはこの APP## に対してプライベートです。 #、その他のAPPおよびドライバーにアクセスできません。

驱动层

驱动层主要是实现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);

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,
};

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

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

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

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

#

以上がLinux ドライバー IO の記事 - mmap 操作の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事は嵌入式Linux充电站で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。