Linux驱动IO篇——mmap操作

简介: Linux驱动IO篇——mmap操作

前言

平时我们写Linux驱动和用户空间交互时,都是通过copy_from_user把用户空间传过来的数据进行拷贝,为什么要这么做呢?

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

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

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

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

通过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表示由内核来决定该起始地址

第二参数是要映射的内存空间的大小

第三个参数PROT_READ | PROT_WRITE表示映射后的空间是可读可写的。

第四个参数可填MAP_SHAREDMAP_PRIVATE

  • MAP_SHARED:多个APP都调用mmap映射同一块内存时, 对内存的修改大家都可以看到。就是说多个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了。

end

往期推荐

入职Linux驱动工程师后,我才知道的真相......

机遇:我是如何走向Linux驱动的...

当我用几道题考了一遍做Linux驱动的同事......

“不是所有的驱动岗,都值得你去”

Linux驱动面试高频考点

相关文章
|
23天前
|
存储 缓存 Linux
Linux IO的奥秘:深入探索数据流动的魔法
Linux I/O(输入/输出)系统是其核心功能之一,负责处理数据在系统内部及与外界之间的流动。为了优化这一流程,Linux进行了一系列努力和抽象化,以提高效率、灵活性和易用性。🚀
Linux IO的奥秘:深入探索数据流动的魔法
|
24天前
|
存储 安全 数据管理
探索Linux的挂载操作🌈
在Linux这个强大的操作系统中,挂载操作是一个基本而重要的概念。它涉及到文件系统、设备和数据访问,对于理解Linux的工作方式至关重要。那么,挂载操作究竟是什么,为什么我们需要它,如果没有它,我们将面临什么问题呢?让我们一起深入探讨。
探索Linux的挂载操作🌈
|
2月前
|
Linux Windows
Linux之基本指令操作
Linux之基本指令操作
|
2月前
|
Linux API 调度
Linux系统驱动跟裸机驱动的区别
Linux系统驱动跟裸机驱动的区别
31 0
|
2月前
|
存储 缓存 Linux
【Shell 命令集合 磁盘维护 】Linux 设置和查看硬盘驱动器参数 hdparm命令使用教程
【Shell 命令集合 磁盘维护 】Linux 设置和查看硬盘驱动器参数 hdparm命令使用教程
38 0
|
2月前
|
存储 算法 Shell
【Shell 命令集合 磁盘维护 】Linux 对软盘进行格式化操作 fdformat命令使用指南
【Shell 命令集合 磁盘维护 】Linux 对软盘进行格式化操作 fdformat命令使用指南
34 0
|
2月前
|
Shell Linux C语言
【Shell 命令集合 磁盘管理 】Linux 控制光驱或可移动媒体设备的弹出和关闭操作 eject命令使用教程
【Shell 命令集合 磁盘管理 】Linux 控制光驱或可移动媒体设备的弹出和关闭操作 eject命令使用教程
37 1
|
10天前
|
机器学习/深度学习 缓存 监控
linux查看CPU、内存、网络、磁盘IO命令
`Linux`系统中,使用`top`命令查看CPU状态,要查看CPU详细信息,可利用`cat /proc/cpuinfo`相关命令。`free`命令用于查看内存使用情况。网络相关命令包括`ifconfig`(查看网卡状态)、`ifdown/ifup`(禁用/启用网卡)、`netstat`(列出网络连接,如`-tuln`组合)以及`nslookup`、`ping`、`telnet`、`traceroute`等。磁盘IO方面,`iostat`(如`-k -p ALL`)显示磁盘IO统计,`iotop`(如`-o -d 1`)则用于查看磁盘IO瓶颈。
|
21天前
|
Linux
Linux操作系统调优相关工具(三)查看IO运行状态相关工具 查看哪个磁盘或分区最繁忙?
Linux操作系统调优相关工具(三)查看IO运行状态相关工具 查看哪个磁盘或分区最繁忙?
22 0
|
6天前
|
Ubuntu Linux
Linux(Ubuntu)系统临时IP以及静态IP配置(关闭、启动网卡等操作)
请注意,以上步骤是在临时基础上进行配置的。如果要永久保存静态IP地址,通常还需要修改 `/etc/network/interfaces`文件,以便在系统重启后保持配置。同时,确保备份相关配置文件以防止出现问题。
23 1

热门文章

最新文章