# 内存的虚与实
在学习《计算机操作系统》课时,谈到在操作系统下,各个程序都是认为自己是系统的全部,即独享着系统全部资源。这样的好处十分明显:程序开发的时候并不需要关心其它程序,更不用关心其它程序对硬件资源的占用。与此作对比,在嵌入式系统MCU上进行开发时,则必须要考虑其它进程的情况。否则,要么饿死别人,要么被别人饿死。而两者的区分也非常简单,即看硬件资源有无MMU模块。今天给大家带来的分享是内存管理中的虚与实。也就是MMU与Linux Kernel如何管理内存的策略。
在64位系统中,低地址空间256TB范围为用户空间,高256TB范围为内核空间。Linux操作系统对于不同应用程序分配相同的用户空间地址,而自己的内核空间则独占式管理。
## **地址空间分布**
上面所说,各个程序分配了相同的地址空间,这也“欺骗”了应用程序,让其认为一直在运行。对于物理内存来说,同一个地址肯定不会存放着两个程序所需要使用的数据。这个很现实,那么前面从应用程序看到的地址就是虚拟内存地址,由MMU提供。真实的内存物理地址则由系统与MMU共同管理,对应用程序无感。换句话说,应用程序0看到的0x20000000的内容与应用程序1看到的0x20000000的内容不同,两者虽然“内存地址”相同,但物理内存地址并不同,其内容不同也就不难理解了。
我们再来看一下ARM64的地址空间分布:
再来细化一下用户空间地址:
## **内存申请函数**
接合实际开发应用,我们再看一下程序中常用的四个内存空间申请接口函数:kmalloc(), vmalloc(), malloc(), mmap()。
先说一下四个函数的相同点:都是用来申请一段内存。而不同点就是申请下来的这段内存所在的地址空间位置,地址是否物理连续了。我们分开来具体说明一下:
- kmalloc()函数。kmalloc()用于在内核空间中分配一定数量的物理连续页。注意它分配出来的内存是连续的物理内存,这一点有别于下面要说的vmalloc()。
- vmalloc()函数。其也是在Linux 内核中的分配大块内存,与 kmalloc() 不同,vmalloc() 通常用于需要连续内存但不需要物理连续性的场景。它分配的内存区域是通过页表映射到内核的虚拟地址空间的。这意味着这些区域可以使用内核的任何虚拟地址,包括直接映射的区域和高端内存区域。
- malloc()函数。它是C语言的标准库函数,用于在堆区动态分配内存。注意:它是在用户空间中使用的,而不是内核空间。
- mmap()函数。它是一种内存映射文件的方法。允许一个进程将一个文件或者其他对象映射进内存。一旦一个文件被映射,进程就可以像访问内存一样访问该文件。mmap() 用于实现共享内存,这对于多进程间通信和数据共享非常有用,同时,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。
下面这和图片展示了mmap()的映射关系。
## **总结**
内存的知识,内存管理的知识十分宏大。短短几天的阅读,坦白讲,也只是了解了一个皮毛,知道其应用的场景,而要明白其背后的策略,设计的目的,解决的问题则需要更多的时间,结合实际应用来不断的提出问题,不断的理解。
不过,通过本章的总结,我发现,其实malloc()函数调用返回的内存地址已经是对齐了~~
虚虚实实,各自发挥着各自的作用,在操作系统中相得益彰。充分利用它们各自的优点,让我们的程序性能优异,稳定可靠。