/*
 *  linux/mm/vmalloc.c
 *
 *  Copyright (C) 1993  Linus Torvalds
 */

#include <asm/system.h>
#include <linux/config.h>

#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/malloc.h>
#include <asm/segment.h>

//虚拟内存结构体定义
struct vm_struct {
    unsigned long flags;          //标志
    void * addr;                  //地址
    unsigned long size;           //长度
    struct vm_struct * next;      //下一个指针
};

static struct vm_struct * vmlist = NULL;

/* Just any arbitrary offset to the start of the vmalloc VM area: the
 * current 8MB value just means that there will be a 8MB "hole" after the
 * physical memory until the kernel virtual memory starts.  That means that
 * any out-of-bounds memory accesses will hopefully be caught.
 * The vmalloc() routines leaves a hole of 4kB between each vmalloced
 * area for the same reason. ;)
 */
#define VMALLOC_OFFSET    (8*1024*1024)

//设置页目录,实际上每个任务都知道其它任务的页目录
static inline void set_pgdir(unsigned long dindex, unsigned long value)
{
    struct task_struct * p;
    //取init任务
    p = &init_task;
    //每个任务中的指定索引设置为指定值
    do {
        ((unsigned long *) p->tss.cr3)[dindex] = value;
        p = p->next_task;
    } while (p != &init_task);
}

//释放区域页面
static int free_area_pages(unsigned long dindex, unsigned long index, unsigned long nr)
{
    unsigned long page, *pte;
    
    //找到页表
    if (!(PAGE_PRESENT & (page = swapper_pg_dir[dindex])))
        return 0;
    //
    page &= PAGE_MASK;
    //页表中索引
    pte = index + (unsigned long *) page;
    //遍历释放内存页
    do {
        unsigned long pg = *pte;
        *pte = 0;
        if (pg & PAGE_PRESENT)
            free_page(pg);
        pte++;
    } while (--nr);
    //指向页表
    pte = (unsigned long *) page;
    //页表中
    for (nr = 0 ; nr < 1024 ; nr++, pte++)
        if (*pte)
            return 0;
    //设置页目录表项
    set_pgdir(dindex,0);
    mem_map[MAP_NR(page)] = 1;
    //释放页表所在页面
    free_page(page);
    //刷新
    invalidate();
    return 0;
}

//分配区域页面
static int alloc_area_pages(unsigned long dindex, unsigned long index, unsigned long nr)
{
    unsigned long page, *pte;
    
    //根据索引从页目录中找到页表
    page = swapper_pg_dir[dindex];
    //如果页面不存在
    if (!page) {
        //从核心内存中申请一页内存
        page = get_free_page(GFP_KERNEL);
        //如果申请不到,则退出
        if (!page)
            return -ENOMEM;
        //如果能够获取到指定页面
        if (swapper_pg_dir[dindex]) {
            //则释放刚刚申请的页面
            free_page(page);
            //使用索引到的页面
            page = swapper_pg_dir[dindex];
        } else {
            //设置页面属性
            mem_map[MAP_NR(page)] = MAP_PAGE_RESERVED;
            set_pgdir(dindex, page | PAGE_SHARED);
        }
    }
    //已经获取到页面了
    //页面边界对齐
    page &= PAGE_MASK;
    //指定内存
    pte = index + (unsigned long *) page;
    //从竞争中移除,设置为共享
    *pte = PAGE_SHARED;        /* remove a race with vfree() */
    //循环申请内存,共计nr页
    do {
        //申请一页内存
        unsigned long pg = get_free_page(GFP_KERNEL);
        
        //申请失败,返回错误
        if (!pg)
            return -ENOMEM;
        //申请成功,设置属性
        *pte = pg | PAGE_SHARED;
        pte++;
    } while (--nr);
    invalidate();
    return 0;
}

//执行
static int do_area(void * addr, unsigned long size,
    int (*area_fn)(unsigned long,unsigned long,unsigned long))
{
    unsigned long nr, dindex, index;
    
    //内存页数
    nr = size >> PAGE_SHIFT;
    //页目录索引
    dindex = (TASK_SIZE + (unsigned long) addr) >> 22;
    //页表索引
    index = (((unsigned long) addr) >> PAGE_SHIFT) & (PTRS_PER_PAGE-1);
    //遍历处理,执行area_fn功能
    while (nr > 0) {
        unsigned long i = PTRS_PER_PAGE - index;

if (i > nr)
            i = nr;
        nr -= i;
        if (area_fn(dindex, index, i))
            return -1;
        index = 0;
        dindex++;
    }
    return 0;
}

//释放虚拟内存
void vfree(void * addr)
{
    struct vm_struct **p, *tmp;

if (!addr)
        return;
    if ((PAGE_SIZE-1) & (unsigned long) addr) {
        printk("Trying to vfree() bad address (%p)\n", addr);
        return;
    }
    for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
        if (tmp->addr == addr) {
            *p = tmp->next;
            do_area(tmp->addr, tmp->size, free_area_pages);
            kfree(tmp);
            return;
        }
    }
    printk("Trying to vfree() nonexistent vm area (%p)\n", addr);
}

//分配虚拟内存
void * vmalloc(unsigned long size)
{
    void * addr;
    struct vm_struct **p, *tmp, *area;

size = PAGE_ALIGN(size);
    if (!size || size > high_memory)
        return NULL;
    area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
    if (!area)
        return NULL;
    addr = (void *) ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1));
    area->size = size + PAGE_SIZE;
    area->next = NULL;
    for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
        if (size + (unsigned long) addr < (unsigned long) tmp->addr)
            break;
        addr = (void *) (tmp->size + (unsigned long) tmp->addr);
    }
    area->addr = addr;
    area->next = *p;
    *p = area;
    if (do_area(addr, size, alloc_area_pages)) {
        vfree(addr);
        return NULL;
    }
    return addr;
}

//读取
int vread(char *buf, char *addr, int count)
{
    struct vm_struct **p, *tmp;
    char *vaddr, *buf_start = buf;
    int n;

for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
        vaddr = (char *) tmp->addr;
        while (addr < vaddr) {
            if (count == 0)
                goto finished;
            put_fs_byte('\0', buf++), addr++, count--;
        }
        n = tmp->size - PAGE_SIZE;
        if (addr > vaddr)
            n -= addr - vaddr;
        while (--n >= 0) {
            if (count == 0)
                goto finished;
            put_fs_byte(*addr++, buf++), count--;
        }
    }
finished:
    return buf - buf_start;
}

mm/vmalloc.c的更多相关文章

  1. Memory Allocation API In Linux Kernel && Linux Userspace、kmalloc vmalloc Difference、Kernel Large Section Memory Allocation

    目录 . 内核态(ring0)内存申请和用户态(ring3)内存申请 . 内核态(ring0)内存申请:kmalloc/kfree.vmalloc/vfree . 用户态(ring3)内存申请:mal ...

  2. 高端内存映射之vmalloc分配内存中不连续的页--Linux内存管理(十九)

    1 内存中不连续的页的分配 根据上文的讲述, 我们知道物理上连续的映射对内核是最好的, 但并不总能成功地使用. 在分配一大块内存时, 可能竭尽全力也无法找到连续的内存块. 在用户空间中这不是问题,因为 ...

  3. vmalloc详解

    vmalloc是一个接口函数, 内核代码使用它来分配在虚拟内存中连续但在物理内存中不一定连续的内存. 只需要一个参数,以字节为单位. 使用vmalloc的最著名的实例是内核对模块的实现. 因为模块可能 ...

  4. xenomai安装

    一.Linux内核打实时补丁 1.将下载的Linux和xenomai安装包放在/usr/src目录下,并解压文件包,命令如下 tar xjf  Linux-3.8.13.tar.bz2   tar x ...

  5. Linux内核内存管理

    <Linux内核设计与实现>读书笔记(十二)- 内存管理   内核的内存使用不像用户空间那样随意,内核的内存出现错误时也只有靠自己来解决(用户空间的内存错误可以抛给内核来解决). 所有内核 ...

  6. 《Linux内核设计与实现》读书笔记(十二)- 内存管理【转】

    转自:http://www.cnblogs.com/wang_yb/archive/2013/05/23/3095907.html 内核的内存使用不像用户空间那样随意,内核的内存出现错误时也只有靠自己 ...

  7. systemtap 列出所有linux 内核模块与相关函数0

    diskiohttp://blog.163.com/digoal%40126/blog/static/16387704020131015105532435/ [root@localhost linux ...

  8. Linux内核入门到放弃-内存管理-《深入Linux内核架构》笔记

    概述 内存管理的实现涵盖了许多领域: 内存中的物理内存页管理 分配大块内存的伙伴系统 分配较小内存块的slab.slub和slob分配器 分配非连续内存块的vmalloc机制 进程的地址空间 在IA- ...

  9. 一个未完成的2.6.32-220内核踩内存crash分析记录

    遇到一个crash,log如下: BUG: unable to handle kernel NULL pointer dereference at (null) IP: [<ffffffff81 ...

随机推荐

  1. sublime 3

    主题: Theme: Flatland 着色:todo Blue Dawn.tmTheme {     "theme": "Flatland Dark.sublime-t ...

  2. win7下的ipython没有的问题

    在笔记本上安装python2.7后,执行python是可以的,但是ipython却不行. 一.问题排查 在网上搜索了看到python与ipython的区别: 例如:ipython有tab补全功能,然后 ...

  3. STM32学习笔记(十) CAN通讯测试(环回模式)

    1.CAN通讯的理解 想学习CAN通讯,那么要对通讯协议有一定的认知.通讯协议是指通信双方对数据传送控制的一种约定.约定中包括对数据格式,同步方式,传输速度,传送步骤,检纠错方式以及控制字符定义等问题 ...

  4. JQuery_简单选择器

    jQuery 最核心的组成部分就是:选择器引擎.它继承了 CSS 的语法,可以对 DOM 元素的标签名.属性名.状态等进行快速准确的选择,并且不必担心浏览器的兼容性. jQuery选择器实现了 CSS ...

  5. 远程实时调试手机上的Web页面

    1. 安装    需要Node.js平台, 先安装好后, 打开Node.js command prompt, 通过NPM来安装 weinre npm -g install weinre 2. 启动   ...

  6. postgres 查询数据库所有表名

    select relname as TABLE_NAME ,col_description(c.oid, 0) as COMMENTS from pg_class cwhere relkind = ' ...

  7. 云端 Linux下安装 Java

    根据需要在给云端的机器安装Java 问题的分解为2个一个是Java源文件的传输.一个是Linux下Java的安装. Java 传输到云端: 可以通过SSH Secure File Transfer C ...

  8. IOS中的动画菜单

    SvpplyTable(可折叠可张开的菜单动画) 允许你简单地创建可折叠可张开的菜单动画效果,灵感来自于Svpply app.不同表格项目使用JSON定义,你可以定义每个菜单项和任何子菜单,为每个项目 ...

  9. 局域网单机部署双tomcat内外网不能访问防火墙问题查出来

    tomcat部署的项目内网访问不了 win7  64 位 控制面板 - 安全 -防火墙-入站规则- 新建规则 选中“端口”按钮,点选“下一步”: 选择“TCP”按钮,在“特定本地端口”输入tomcat ...

  10. 使用excel快速制表 拒绝粗心

    办公室打印个表格 使用了word打印后 发现 id重复很多 只好网上找了点excel 2003资料 学习小 快速制作表格 新建一个excel文件. 在新建excel中,用鼠标选中需要的表格行数列数,然 ...