Linux内存管理之vmalloc和kmalloc

了解linux操作系统的,我们都知道内存管理对于linux操作系统的重要性,当然其他操作系统内存管理也占有举足轻重的角色。

在内核里分配内存可不像在其他地方分配内存那么容易。造成这种局面的因素有很多,从根本上讲,是因为内核本身不能像用户空间那样奢侈地使用内存。内核与用户空间不同,它不具备这种能力,它不支持简单便捷的内存分配方式。比如,内核一般不能睡眠。此外,处理聂村分配错误对内核来说也绝非易事。正是由于这些限制,再加上内存分配机制不能太复杂,所以在内核中获取内存要比在用户空间复杂的多。不过从程序开发者的角度来看,也不是说内核的分配就困难的不得了,只是和用户空间中的内存分配不太一样而已。


linux的内存管理是一块比较复杂的内容,我们对其各个部分分别进行总结,而后再结合linux源代码进行理解,这样就能更好的掌握linux内存管理这一块。

今天先学习了vmalloc和kmalloc的部分,那我们就先介绍这两个函数的功能。

vmalloc()函数的工作方式类似于kmalloc(),只不过前者分配的内存虚拟地址是连续的,而物理地址则无需连续。这也是用户空间分配函数的工作方式:由malloc()(这个函数我们后续还会介绍,这也是我们在用户空间进行开发的时候经常会碰到的一个内存分配函数)返回的页在进程的虚拟地址空间内是连续的,但是,这并不保证它们在物理RAM中也是连续的。kmalloc()函数确保页在物理地址上是连续的(虚拟地址自然也是连续的)。vmalloc()函数只确保页在虚拟地址空间内是连续的。它通过分配非连续的物理内存块,再"修正"页表,把内存映射到逻辑地址空间的连续区域中,就能做到这点。


大多数情况下,只有硬件设备需要得到物理地址连续的内存。在很多体系结构中,硬件设备存在于内存管理单元以外,它根本不需要理解什么是虚拟地址。因此硬件设备用到的任何内存区域都必须是物理上连续的块,而不仅仅是虚拟地址连续的块。而仅供软件使用的内存块(例如与进程相关的缓冲区)就可以使用只有虚拟地址连续的内存块。但是在你编程中,根本觉察不到这种差异。对内核而言,所有内存看起来是逻辑上连续的。


尽管在某些情况下才需要物理上连续的内存块,但是,很多内核代码都用kmalloc()来获得内存,而不是vmalloc(),这是为什么呢?


这主要是出于性能的考虑。vmalloc函数为了把物理上不连续的页转换为虚拟地址连续的页,必须专门建立页表项。糟糕的是,通过vmalloc获得的页必须一个一个的进行映射(因为它们物理上不连续),这就会导致比直接内存映射大的多的TLB抖动。因为这些原因,vmalloc仅在不得已时才会使用--典型的就是为了获得大块内存时,例如,当模块被动态插入到内核中时,就把模块装载到由vmalloc分配的内存上。


vmalloc函数声明在<linux/vmalloc.h>中,定义在<mm/vmalloc.c>中,用法与用户空间的malloc相同:

void *vmalloc(unsigned long size)

该函数返回一个指针,指向逻辑上连续的一块内存区,其大小至少为size。在发生错误时,函数返回NULL。函数可能睡眠,因此不能在中断上下文进行调用,也不能从其他不允许阻塞的情况下进行调用。

要释放通过vmalloc所获得的内存,使用下面的函数:

void vfree(const void *addr)

这个函数会释放从addr开始的内存块,其中addr是以前由vmalloc分配的内存块的地址。这个函数也可以睡眠,因此不能从中断上下文中调用。它没有返回值


这个函数用起来比较简单:

char *buf;

buf = vmalloc(16*PAGE_SIZE);/*获取16个页*/

if(!buf)

/*出错了!不能分配内存*/

/*buf现在指向虚拟地址连续的一块内存区,其大小至少为16个PAGE_SIZE*/

在分配内存之后,一定要释放它:

vfree(buf);

猜你喜欢

转载自blog.csdn.net/yu_xiaofei/article/details/70036899
今日推荐