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)\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的更多相关文章
- 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 ...
- 高端内存映射之vmalloc分配内存中不连续的页--Linux内存管理(十九)
1 内存中不连续的页的分配 根据上文的讲述, 我们知道物理上连续的映射对内核是最好的, 但并不总能成功地使用. 在分配一大块内存时, 可能竭尽全力也无法找到连续的内存块. 在用户空间中这不是问题,因为 ...
- vmalloc详解
vmalloc是一个接口函数, 内核代码使用它来分配在虚拟内存中连续但在物理内存中不一定连续的内存. 只需要一个参数,以字节为单位. 使用vmalloc的最著名的实例是内核对模块的实现. 因为模块可能 ...
- xenomai安装
一.Linux内核打实时补丁 1.将下载的Linux和xenomai安装包放在/usr/src目录下,并解压文件包,命令如下 tar xjf Linux-3.8.13.tar.bz2 tar x ...
- Linux内核内存管理
<Linux内核设计与实现>读书笔记(十二)- 内存管理 内核的内存使用不像用户空间那样随意,内核的内存出现错误时也只有靠自己来解决(用户空间的内存错误可以抛给内核来解决). 所有内核 ...
- 《Linux内核设计与实现》读书笔记(十二)- 内存管理【转】
转自:http://www.cnblogs.com/wang_yb/archive/2013/05/23/3095907.html 内核的内存使用不像用户空间那样随意,内核的内存出现错误时也只有靠自己 ...
- systemtap 列出所有linux 内核模块与相关函数0
diskiohttp://blog.163.com/digoal%40126/blog/static/16387704020131015105532435/ [root@localhost linux ...
- Linux内核入门到放弃-内存管理-《深入Linux内核架构》笔记
概述 内存管理的实现涵盖了许多领域: 内存中的物理内存页管理 分配大块内存的伙伴系统 分配较小内存块的slab.slub和slob分配器 分配非连续内存块的vmalloc机制 进程的地址空间 在IA- ...
- 一个未完成的2.6.32-220内核踩内存crash分析记录
遇到一个crash,log如下: BUG: unable to handle kernel NULL pointer dereference at (null) IP: [<ffffffff81 ...
随机推荐
- HTTP协议的头信息详解
转载地址:http://blog.csdn.net/guoguo1980/article/details/2649658 HTTP(HyperTextTransferProtocol)是超文本传输协议 ...
- Logistic 分类器与 softmax分类器
首先说明啊:logistic分类器是以Bernoulli(伯努利) 分布为模型建模的,它可以用来分两种类别:而softmax分类器以多项式分布(Multinomial Distribution)为模型 ...
- 入坑HttpServletRequest.getParameterMap
在项目开发的时候遇到一个小坑,在发送了异步请求以后,回调的时候传递给我一个参数直接就是HttpServletRequest的请求,下面简称request: 在使用的时候自以为很简单,直接get就好了嘛 ...
- [SoapUI] 同一个Resource不同参数时,在两个step里默认打开总是同一个Resource
当SoapUI里Projects 有两个相同的Resource,只是参数不同时,使用两个Resource创建的step默认打开的总是同一个Resource.我们应当修改method名字为不同,这是So ...
- drupal前端开发的第一点
就是把admin/config/development/performance下面的选项全部去掉,方便作为一个匿名用户来访问,不用每次都drush cc all.
- stage3D之疑问
1.stage3D本身是建立在图形API(如DirectX.Opengl等)之上的一套API,那么在创建stage3D demo时,如何指定使用哪种图形API呢?
- Python学习推荐
1. Python官网 官网想必是最权威的,不仅有Python 2.X和3.X的软件包,还有官方文档Python tutorial (official docs)及社区. 2. 在线阅读免 ...
- windows docker测试二 下载container
安装dockertoolbox,提供了一个docker的界面工具 Kitematic 和字符终端: Docker Quickstart Terminal (这里安装的Kitematic 是Alpha版 ...
- Queue的push和front操作
#include <queue> #include <cstdlib> using namespace std; int main(){ queue<int> qu ...
- hdu 5877/ 2016 ACM/ICPC Dalian Online 1010 Weak Pair
题目链接 分析:树上的节点祖先与儿子的关系,一般就会想到dfs序.正解就是对树先进行dfs序排列,再将问题转化到树状数组统计个数.应该把节点按照权值从大到小排序,这样对于,就是从小到大的顺序.这样更新 ...