zoukankan      html  css  js  c++  java
  • mm/vmalloc.c

    /*
     *  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) ", 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) ", 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('', 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;
    }

  • 相关阅读:
    Fidder4 顶部提示 “The system proxy was changed,click to reenable fiddler capture”。
    redis 哨兵 sentinel master slave 连接建立过程
    虚拟点赞浏览功能的大数据量测试
    python基础练习题(题目 字母识词)
    python基础练习题(题目 回文数)
    python基础练习题(题目 递归求等差数列)
    python基础练习题(题目 递归输出)
    python基础练习题(题目 递归求阶乘)
    python基础练习题(题目 阶乘求和)
    python基础练习题(题目 斐波那契数列II)
  • 原文地址:https://www.cnblogs.com/xiaofengwei/p/3773993.html
Copyright © 2011-2022 走看看