Linux 中 mmap() 函数的内存映射问题理解?
深入理解Linux中mmap()函数的内存映射机制
在Linux的世界里,mmap()函数就像一把神秘的钥匙,解锁了内存与文件之间的直接连接。让我们一起揭开这个功能的神秘面纱,看看它是如何在内核层面上运作的。
首先,让我们摒弃虚拟内存这个商业化的概念,它在开发中并没有实际意义。在开发者的视野里,我们关注的是虚拟空间,即进程所见的所有地址构成的广袤区域。虚拟空间是对进程分配到的所有物理地址(已分配和将要分配的)的一种重新映射,它为我们提供了程序的运行舞台。
mmap的主要作用是让应用程序能够像操作内存一样直接访问文件。虽然其底层实现和性能优化是由内核和驱动器负责的,但作为开发者,你只需关注其功能,并根据需求灵活运用即可。不必过于纠结那些技术细节,让它在你的代码中发挥威力即可。
现在,我们来看看mmap的工作原理。当你调用mmap时,内核会在你的虚拟空间中预留一块区域,但不会立即分配物理内存。当你试图访问这片空间时,处理器会触发异常,内核会在此时动态分配物理内存,并填充文件内容,然后返回给你的程序,这时,你才会察觉到数据的存在。
关于驱动的页面读取策略和内存分配算法,这些都是内核的内部决策,不作为编程依赖。记住,mmap的核心是提供便利,而非详尽的细节,所以不要过多幻想。
至于swap分区,它在内存管理中的角色可以通过深入理解《Linux内存管理的奥秘》一文来掌握。理解了这些,你就能更好地理解我为何建议你以虚拟空间的概念替代虚拟内存。
内存,作为计算机的核心组件,由DRAM组成,它存储着数据。为了访问内存,我们为它分配地址,这些地址的集合就是内存空间。物理地址空间包括内存和IO,其大小受到地址总线长度的限制,可以远大于实际的DRAM容量。
CPU在访问内存时,MMU(内存管理单元)会进行地址转换,将虚拟地址映射到物理地址。虚拟地址空间与指令集地址长度可能不一致,例如,64位处理器可以访问64位地址,但地址总线可能只有48位。这就意味着,虚拟空间可以很大,但物理内存并不一定需要匹配。
每个进程都有独立的虚拟空间,这些空间可以映射到物理内存的不同位置。当Linux执行程序时,使用mmap将程序加载到内存中,使虚拟空间与文件内容相关联,就像这样:
这个过程看似直接,但文件内容实际存储在磁盘上,CPU无法直接访问。当执行操作时,内核会触发异常,将文件内容从磁盘加载到物理内存,形成动态的内存映射。
你可以在/proc/pid/maps文件中查看进程的内存映射状态,如init进程的映射情况。这些映射中,有些区域是有文件关联的,被称为backlog文件,它们在内存不足时从磁盘加载数据;而没有文件关联的内存,如通过brk或malloc动态分配的,称为匿名内存,它们可能与swap文件关联,提供扩展内存的可能。
最后,如果你希望释放缓存,可以使用/proc/sys/vm/drop_caches命令,或者在mount时指定sync或dirsync参数来控制。但请务必谨慎操作,以免影响系统性能。
通过以上深入解析,你对Linux的mmap函数及其内存映射机制应该有了更清晰的认识。现在,你可以信心满满地在代码中运用这一强大工具,实现高效的文件映射操作了。
狐强三七: mmap系统调用并不是完全为了用于共享内存而设计的.它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作. 用open系统调用打开文件, 并返回描述符fd. 用mmap建立内存映射, 并返回映射首地址指针start. 对映射(文件)进行各种操作, 显示(printf), 修改(sprintf). 用munmap(void *start, size_t lenght)关闭内存映射. 用close系统调用关闭文件fd. 推荐你一本《linux就该这么学》书,看看吧会对你有用的
七星区17265821391: linux中ioremap和mmap的区别 - ?
狐强三七: 你不是已经说了区别 ioremap是将物理地址转换为虚拟地址mmap是将设备内存线性地址映射到用户地址空间linux的线程只能访问虚拟地址,不管是不是内核,ioremap应用,比如有个寄存器地址是0xe8000000 你要用ioremap映射后,才能访问地址0xe8000000.这两个地址是不同的,mmu会帮你搞定,对你是透明的mmap在内核我用过ops中的mmap方法 我写过一个例程,见附件.里面还有用户态的测试程序.
七星区17265821391: 使用mmap(linux系统调用)追加文件内容 - ?
狐强三七: mmap 是做内存映射,mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存.普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作. void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset); PROT_WRITE //页可以被写入 //这个参数你设置没有?
七星区17265821391: linux mmap() 函数出现 Permission denied - ?
狐强三七: int fd=open("./a.txt",O_WRONLY); //这个地方错了.你应该用读写方式打开.而不是只写方式. 比如:int fd=open("./a.txt",O_RDWR); 这样改后就不会报错了.
七星区17265821391: imx6q linux bsp中怎么读取一个寄存器的值 - ?
狐强三七: 这一问题来自项目中一个实际的需求: 我需要在Linux启动之后,确认我指定的芯片寄存器是e79fa5e98193e59b9ee7ad9431333339663334否与我在uboot的配置一致. 举个例子: 寄存器地址:0x20000010负责对DDR2的时序配置,该寄存器...
七星区17265821391: linux 中 kill() 与 signal() 函数 - ?
狐强三七: 虽然子进程将父进程的函数重新拷贝了一份,子进程和父进程共享同一段内存空间,但不能被共享.可以通过共享内存解决这个问题.使用这个函数void* mmap(void * addr, size_t len, int prot, int flags, int fd, off_t offset)把进程地址空间...
七星区17265821391: Java字段名映射成别的名称 - ?
狐强三七: 假设我们有A B 两个类 分别是 a1 a2 b1 b2一人两个属性 B2 是你接收的类 在A类里面放一个构造函数 即 class A{ int a1;inta2; public void A(B b){ this.a1=b.b1;this.a2=b.b2; } } 你在使用的时候就是 A a=new A(b);//b 即为我们接收到数据的类 完美解决
七星区17265821391: linux下使用 socket mmap 方法,而不用send , recv等函数能跨机器通讯吗? - ?
狐强三七: 用socket肯定能,但是你不用向send recv 什么 read write等函数 你怎么通信? mmap是内存映射,共享内存用它, 是进程间通信,跨机器用不了
七星区17265821391: 关于linux系统下的物理内存映射 - ?
狐强三七: int video_qsb_mmap(struct file *file,struct vm_area_struct *vma) { int ret; u32 size = vma->vm_end - vma->vm_start; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_flags |= VM_RESERVED; if(frm_numvm_start,align_...