Linux 드라이버 IO 문서 - mmap 작업

풀어 주다: 2023-07-31 15:55:07
앞으로
1101명이 탐색했습니다.

머리말

보통 Linux 드라이버를 작성하고 사용자 공간과 상호작용할 때 copy_from_user사용자 공간에서 전달된 데이터를 복사합니다. 왜 이러나요? copy_from_user把用户空间传过来的数据进行拷贝,为什么要这么做呢?

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

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

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

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

Linux 드라이버 IO 문서 - 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);
로그인 후 복사

mmap的第一个参数是想要映射的起始地址,通常设置为NULL사용자 공간은 커널 공간 데이터에 직접 접근할 수 없기 때문에 서로 다른 주소 공간을 매핑하므로 데이터를 먼저 복사한 다음 작동할 수 있습니다.

사용자 공간에서 몇 MB의 데이터를 커널로 전송해야 한다면 원본 복사 방법은 분명히 매우 비효율적이고 비현실적이므로 어떻게 해야 할까요? 🎜🎜생각해 보세요. 복사하는 이유는 사용자 공간이 커널 공간에 직접 접근할 수 없기 때문입니다. 그러면 커널 공간의 버퍼에 직접 접근할 수 있다면 해결될까요? 🎜🎜🎜간단히 말하면 물리적 메모리 조각에 두 개의 매핑이 있다는 의미입니다. 즉, 커널 공간에 하나, 사용자 공간에 하나, 총 두 개의 가상 주소가 있다는 의미입니다. 🎜관계는 다음과 같습니다: 🎜
Linux 드라이버 IO 문서 - mmap 작업
🎜Bymmap 매핑이 가능합니다. 🎜🎜🎜🎜🎜애플리케이션 레이어🎜🎜 🎜🎜🎜애플리케이션 레이어 코드는 매우 간단합니다. 주로 mmap🎜 시스템 호출🎜을 사용하여 매핑을 수행한 다음 반환된 주소에 대해 작업을 수행할 수 있습니다. 🎜
int remap_pfn_range(
  struct vm_area_struct *vma, 
  unsigned long addr, 
  unsigned long pfn, 
  unsigned long size, 
  pgprot_t prot);
로그인 후 복사
로그인 후 복사
🎜mmap의 첫 번째 매개변수는 매핑하려는 시작 주소. 일반적으로 NULL < /code>, 🎜는 커널이 시작 주소 🎜를 결정한다는 의미입니다. 🎜<p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">두 번째 매개변수는 <strong>매핑할 메모리 공간의 크기</strong>입니다. </p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">세 번째 매개변수<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);">PROT_READ | PROT_WRITE 표시 매핑된 공간은 PROT_READ | PROT_WRITE表示映射后的空间是可读可写的。

第四个参数可填MAP_SHAREDMAP_PRIVATE읽고 쓸 수 있습니다

. 🎜🎜네 번째 매개변수는 MAP_SHARED</code > 또는 <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) ; 글꼴 계열: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">MAP_PRIVATE:🎜
  • <코드 스타일="글꼴 크기: 14px;패딩: 2px 4px;국경 반경: 4px;마진 오른쪽: 2px;마진 왼쪽: 2px;배경 색상: rgba(27, 31, 35, 0.05 );font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">MAP_SHARED: 다중<코드 스타일 ="글꼴 크기: 14px; 패딩: 2px 4px; 테두리 반경: 4px; 여백 오른쪽: 2px; 여백 왼쪽: 2px; 배경 색상: rgba(27, 31, 35, 0.05); 글꼴 가족: "Operator Mono", Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">APP 모두 mmap 동일한 메모리를 매핑하면 모든 사람이 메모리에 대한 수정 사항을 볼 수 있습니다. 즉, 여러 APP, 드라이버 실제 모두 동일한 메모리에 액세스합니다.
  • MAP_SHARED:多个APP都调用mmap映射同一块内存时, 对内存的修改大家都可以看到。就是说多个APP、驱动程序实际上访问的都是同一块内存
  • MAP_PRIVATE:创建一个copy on write的私有映射。当APP对该内存进行修改时,其他程序是看不到这些修改的。就是当APP写内存时, 内核会先创建一个拷贝给这个APP,这个拷贝是这个APP🎜MAP_PRIVATE</ 코드>: <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);">쓰기 시 복사 비공개 매핑. 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    관련 라벨:
    원천:嵌入式Linux充电站
    본 웹사이트의 성명
    본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
    인기 튜토리얼
    더>
    최신 다운로드
    더>
    웹 효과
    웹사이트 소스 코드
    웹사이트 자료
    프론트엔드 템플릿
    회사 소개 부인 성명 Sitemap
    PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!