文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>非连续内存空间

非连续内存空间

时间:2006-03-06  来源:ChinaE_OS

Linux在多种情况下使用非连续内存空间,如:激活交换区域的数据结构,某块空间,I/O驱动的缓存,使用高内存页面帧。

 

1:非连续空间的线性地址

VMALLOC_START和VMALLOC_END宏定义了非连续空间的开始和结束位置。

2:vm_struct

vm_struct

Type

Name

Description

void *

addr

第一块内存块的线性地址

unsigned long

size

加4096后的空间大小

unsigned long

flags

非连续地址空间的映射方式

struct page **

pages

页描述的数组指针

unsigned int

nr_pages

内存区域中的页数

unsigned long

phys_addr

如果不是硬件设备的I/O共享空间,设置为0

struct vm_struct *

next

指向下一块vm_struct的指针。

Flags的选项:

(1)       VM_ALLOC:通过vmalloc分配的页。

(2)       VM_MAP:通过vmap映射的已经分配的页。

(3)       VM_IOREMAP:通过ioremap映射的硬件设备的板上地址。

3:get_vm_area

查找一块从VMALLOC_START开始VMALLOC_END结束的线性地址。需要两个参数,size,需要创建的空间大小,flag:指定区域的类型。

直接调用__get_vm_area

struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)

{

    return __get_vm_area(size, flags, VMALLOC_START, VMALLOC_END);

}

 

__get_vm_area执行的步骤如下:

(1)    调用kalloc获取vm_struct描述符的地址。

(2)       获取vmlist_lock,遍历vm_struct链表,找到一块至少为size+4096大小的空地址空间。

(3)    如果找到,初始化描述符域,释放vmlist_lock,并且返回初始化的非连续空间的的地址。否则,释放描述符域,释放vmlist_lock,并且返回NULL.

4:vmalloc

分配一块size大小的非连续地址空间,如果分配失败,返回NULL.该函数调用了内部实现函数__vmalloc。

void *vmalloc(unsigned long size)

{

       return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);

}

__vmalloc的步骤如下:

(1)       检查参数size的合法性。

size = PAGE_ALIGN(size);

    if (!size || (size >> PAGE_SHIFT) > num_physpages)

        return NULL;

 

(2)       调用get_vm_area获取一块空的地址空间。如果不成功,返回NULL,并且退出执行。

    area = get_vm_area(size, VM_ALLOC);

    if (!area)

        return NULL;

 

(3)       然后调用内部__vmalloc_area分配页面,并且直接返回分配结果。

    return __vmalloc_area(area, gfp_mask, prot);

__vmalloc_area的步骤如下:

(1)       获取页数,以及所有页数据结构需要的空间大小。

nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT;

    array_size = (nr_pages * sizeof(struct page *));

 

    area->nr_pages = nr_pages;

(2)       检查array_size是否大于PAGE_SIZE,如果是,递归调用__vmalloc,否则调用kmalloc分配页目录。如果分配的页目录为空,释放已经获取得vm_struct,并且退出。 

    if (array_size > PAGE_SIZE)

        pages = __vmalloc(array_size, gfp_mask, PAGE_KERNEL);

    else

        pages = kmalloc(array_size, (gfp_mask & ~__GFP_HIGHMEM));

    area->pages = pages;

    if (!area->pages) {

        remove_vm_area(area->addr);

        kfree(area);

        return NULL;

    }

(3)       分配页面。

    memset(area->pages, 0, array_size);

 

    for (i = 0; i < area->nr_pages; i++) {

        area->pages[i] = alloc_page(gfp_mask);

        if (unlikely(!area->pages[i])) {

            /* Successfully allocated i pages, free them in __vunmap() */

            area->nr_pages = i;

            goto fail;

        }

    }

(4)       调用map_vm_area,建立内核使用的页表入口,标示已经分配的非连续内存空间的每个页面已经和vmalloc分配的连续线性地址联系起来。

    if (map_vm_area(area, prot, &pages))

        goto fail;

    return area->addr;

 

5:vfree

Vfree释放vmalloc 和vmalloc_32分配的非线性地址空间,vunmap释放vmap创建的地址空间。不过这两个函数都之间调用了_vunmap实现。

void vfree(void *addr)

{

    BUG_ON(in_interrupt());

    __vunmap(addr, 1);

}

void vunmap(void *addr)

{

    BUG_ON(in_interrupt());

    __vunmap(addr, 0);

}

_vunmap的实现如下:

(1)       调用remove_vm_area获取vm_struct描述符的地址,清除非线性地址空间线性地址对应的内核页表入口。

area = remove_vm_area(addr);

    if (unlikely(!area)) {

        printk(KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n",

                addr);

        WARN_ON(1);

        return;

    }

(2)    检查deallocate_pages,如果设置为1,察看指向页描述符指针的area->pages数组,对于数组的每个元素,调用__free_page释放每个页帧到zone页帧分配器中;执行kfree(area->pages)释放数组本身。

if (deallocate_pages) {

       int i;

 

       for (i = 0; i < area->nr_pages; i++) {

           if (unlikely(!area->pages[i]))

              BUG();

           __free_page(area->pages[i]);

       }

 

       if (area->nr_pages > PAGE_SIZE/sizeof(struct page *))

           vfree(area->pages);

       else

           kfree(area->pages);

    }

(3)       调用kfree(area)释放vm_struct.

 

5: remove_vm_area

查找并且删除连续的内核虚拟空间。

struct vm_struct *remove_vm_area(void *addr)

{

    struct vm_struct *v;

    write_lock(&vmlist_lock);

    v = __remove_vm_area(addr);

    write_unlock(&vmlist_lock);

    return v;

}

 

__remove_vm_area步骤如下:

(1)       根据地址查找要删除的描述符vm_struct,如果找到,跳到2,否则返回NULL.

for (p = &vmlist ; (tmp = *p) != NULL ;p = &tmp->next) {

         if (tmp->addr == addr)

             goto found;

    }

    return NULL;

(2)       调用unmap_vm_area释放该块。

found:

    unmap_vm_area(tmp);

    *p = tmp->next;

 

    /*

     * Remove the guard page.

     */

    tmp->size -= PAGE_SIZE;

    return tmp;

}

相关阅读 更多 +
排行榜 更多 +
试着飞手游下载

试着飞手游下载

休闲益智 下载
血染小镇(功能菜单)中文版下载

血染小镇(功能菜单)中文版下载

飞行射击 下载
泰坦之旅高爆版下载

泰坦之旅高爆版下载

角色扮演 下载