关闭

Linux驱动学习12(初步认识内存管理)

标签: linux内核内存分配内存管理
990人阅读 评论(0) 收藏 举报
分类:
一、MMU的基本知识

1.内存管理单元简称mmu,她负责虚拟地址到真实物理地址的转换,并且提供了硬件机制以检查内存

访问权限,同时对Cache缓存进行控制。

2.现代计算机多进程操作系统通过mmu使得各个进程用户都拥有独立的地址空间,从一个进程来看。

都拥有4G(32位cpu)的地址空间,其中0-3G 属于用户空间,3-4G 属于内核空间。

3.各个进程得以正常运行的原因就是mmu提供的内存访问权限检查的硬件机制,她可以保护进程不受

破坏。

4.TTB(Translation  Table Base):表示存放在存储器上的页表的地址。

5.TLB(Translation LookasideBuffer):他缓存了少量的虚拟地址到物理地址的转换关系,是地址转

换表的缓存,也称为快表。 

6.TTW(Translation  Tablewalk):当上面提到的TLB中没有转换关系的时候,那么就需要通过TTW来遍

历存储器上面存储的页表TTB(Translation  Table Base).当TTW访问成功后,应该把该结果放入TLB中。


上面这张图很好的阐述了处理器、mmu、存储器之间的框架关系。

下面还有张图,是代码流程图。


二、内存分配函数
1、static inline void *kmalloc(size_t size,gfp_t flags)
/*
size :表示分配的内存大小
通常size大小为一些固定的值,linux内核为size大小提供了如下的取值
32B、64B、128B、256B、512B、1KB、2KB、4KB、8KB、16KB、32KB、64KB、128KB
flags:表示分配标志
通常使用如下标志:
进程上下文,可以睡眠          GFP_KERNEL
进程上下文,不可以睡眠        GFP_ATOMIC
中断处理程序                  GFP_ATOMIC
软中断                        GFP_ATOMIC
Tasklet                       GFP_ATOMIC
用于DMA的内存,可以睡眠       GFP_DMA | GFP_KERNEL
用于DMA的内存,不可以睡眠     GFP_DMA | GFP_ATOMIC
*/
1.保证分配的内存在物理上是连续的,虚拟地址上自然也是连续的;
2.当使用DMA访问内存的时候,是要求该片内存在物理上是连续的,所以应该使用该种分配内存方式;
3.void kfree(const void *ptr)释放由kmalloc()分配出来的内存块;
调用流程:


static inline void *kmalloc(size_t size,gfp_t flags)
                    ▼
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);
该函数可分配多个页并返回分配内存的首地址,分配的页数为 2的order次幂,分配的页不清零
                    ▼                    
struct page * alloc_pages(int gfp_mask, unsigned long order);
该函数返回了分配的第一个页的描述符而非首地址


2、
void *vmalloc(size_t size)


1.保证分配的内存在虚拟上是连续的,物理上不能保证;
2.void vfree(void *addr),这个函数可以睡眠,因此不能从中断上下文调用。 
3.vmalloc不能用在原子上下文中, 因为它的内部实现使用了标志为 GFP_KERNEL 的
kmalloc()。
4.vmalloc比 kmalloc要慢 ,因为他调用了kmalloc。
调用流程:
    void *vmalloc(size_t size)
                    ▼
static inline void *kmalloc(size_t size,gfp_t flags)
                    ▼
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);
该函数可分配多个页并返回分配内存的首地址,分配的页数为 2的order次幂,分配的页不清零
                    ▼                    
struct page * alloc_pages(int gfp_mask, unsigned long order);


3、获取某一页在内核中的虚拟地址函数
/**
 * page_address - get the mapped virtual address of a page
 * @page: &struct page to get the virtual address of
 *
 * Returns the page's virtual address.
 */
void *page_address(struct page *page)




4、kmalloc、vmalloc和malloc的区别:

[*]kmalloc和 vmalloc 是分配的是内核的内存,malloc分配的是用户的内存 


[*]kmalloc保证分配的内存在物理上是连续的,vmalloc保证的是在虚拟地址空间上的连续,malloc不保 
证任何东西 (这点是自己猜测的,不一定正确) 


[*]kmalloc能分配的大小有限,vmalloc和 malloc 能分配的大小相对较大 


[*]内存只有在要被DMA 访问的时候才需要物理上连续 


[*]vmalloc比kmalloc 要慢 






三、内存分配方法
1.代码实例(按页分配):

#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>


#define SWITCH 1


struct page *p;
char *s;


static int __init mem_test_init(void)
{
    unsigned long VA,PA;
    printk("<1>""module init \n"); 
    #if SWITCH
    p = alloc_pages(GFP_KERNEL,1);  //分配一页
    if(NULL == p)
    {
        printk("<1>""alloc pages error ! \n"); 
        return -ENOMEM;   
    }
    s = page_address(p);            //获取该页的首地址
    
    #else
    s = (char *)__get_free_pages(GFP_KERNEL,1);     //分配一页,返回该页首地址
    if(NULL == s)
    {
        printk("<1>""get free pages error ! \n");
        return -ENOMEM;    
    }
    #endif
    
    PA = __pa((unsigned long)s);            //获取s对应的物理地址
    VA = __va((unsigned long)__va(PA));     //获取s的虚拟地址,注意返回的地址不一定是虚拟地址
    printk("<1>""s is %p \n",s);
    printk("<1>""PA is %x \n",PA);
    printk("<1>""VA is %x \n",VA);  
    return 0;  
}
static void __exit mem_test_exit(void)
{
    #if SWITCH
    __free_pages(p, 1);
    #else
    free_pages((unsigned long)s, 1);
    #endif     
    printk("<1>""module exit \n"); 
}


module_init(mem_test_init);
module_exit(mem_test_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("shopping");

当switch等于0时,结果如下:

module init 
s is f3c58000 
PA is 33c58000 
VA is b3c58000 
当switch等于1时,结果如下:

module init 
s is ea9b8000 
PA is 2a9b8000 
VA is aa9b8000 

很奇怪的是,第一个地址和第三个地址竟然不等?这个问题暂时还没有解决,只能先放一放了。

2.代码实例(kmalloc分配):

#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
char *s;
static int __init mem_test_init(void)
{
    unsigned long VA,PA;
    printk("<1>""module init \n"); 
    s = (char*)kmalloc(2,GFP_KERNEL);
    if(NULL == s)
    {
        printk("<1>""kmalloc error \n");    
        return -ENOMEM;   
    }
    memcpy(s,"this is mem test",30);
    
    printk("<1>""%s \n",s);
    return 0;  
}
static void __exit mem_test_exit(void)
{ 
    kfree(s);
    printk("<1>""module exit \n"); 
}


module_init(mem_test_init);
module_exit(mem_test_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("shopping");

上面我确实在kmalloc中只分配2字节,但是最终写得时候,还是打印成功了

module init 
this is mem test 

这说明该函数确实至少默认分配32字节(这个和架构体系有关)


3.接下来看讨论的最后一种内存分配方式(vmalloc)

#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
char *s;
static int __init mem_test_init(void)
{
    unsigned long VA,PA;
    printk("<1>""module init \n"); 
    s = (char*)vmalloc(30);
    if(NULL == s)
    {
        printk("<1>""kmalloc error \n");    
        return -ENOMEM;   
    }
    memcpy(s,"this is mem test",30);
    
    printk("<1>""%s \n",s);
    return 0;  
}
static void __exit mem_test_exit(void)
{ 
    vfree(s);
    printk("<1>""module exit \n"); 
}

module_init(mem_test_init);
module_exit(mem_test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("shopping");
当指定的size没有真够大的连续空间时,这个函数就会像捡破烂一样,东捡一块西捡一块,凑成满足大
小的物理内存,并连在一起形成连续的虚拟内存再把首地址返回,所以这个函数的操作很麻烦,出于性
能的考虑,能不用的话尽量不用







0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:278313次
    • 积分:4286
    • 等级:
    • 排名:第7052名
    • 原创:132篇
    • 转载:51篇
    • 译文:1篇
    • 评论:72条
    博客专栏
    最新评论