linux 1.0 内核注解 linux/mm/mmap.c
时间:2009-03-17 来源:taozhijiangscu
/********************************************
*Created By: 陶治江
*Date: 2009-3-16
********************************************/
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h> #include <asm/segment.h>
#include <asm/system.h> static int anon_map(struct inode *, struct file *,
unsigned long, size_t, int,
unsigned long);
/*
* description of effects of mapping type and prot in current implementation.
* this is due to the current handling of page faults in memory.c. the expected
* behavior is in parens:
*
* map_type prot
* PROT_NONE PROT_READ PROT_WRITE PROT_EXEC
* MAP_SHARED r: (no) yes r: (yes) yes r: (no) yes r: (no) no
* w: (no) yes w: (no) copy w: (yes) yes w: (no) no
* x: (no) no x: (no) no x: (no) no x: (yes) no
*
* MAP_PRIVATE r: (no) yes r: (yes) yes r: (no) yes r: (no) no
* w: (no) copy w: (no) copy w: (copy) copy w: (no) no
* x: (no) no x: (no) no x: (no) no x: (yes) no
*
*/ #define CODE_SPACE(addr) \
(PAGE_ALIGN(addr) < current->start_code + current->end_code) int do_mmap(struct file * file, unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long off)
{
int mask, error; if ((len = PAGE_ALIGN(len)) == 0) //PAGE_ALIGN,将指针对齐到下一个页面的起始地址
return addr; //TASK_SIZE,用户进程空间:3G,这里是检查起始地址addr以及长度等信息是否合法
if (addr > TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE-len)
return -EINVAL; if (file != NULL)
switch (flags & MAP_TYPE)
{
//MAP_SHARED表示执行线性区中的页可以被几个进程所共享
//MAP_PRIVATE 含义相反
case MAP_SHARED:
//如果映射页面要求可被写,但是文件访问模式不允许写,出错
if ((prot & PROT_WRITE) && !(file->f_mode & 2))
return -EACCES;
/* fall through */
case MAP_PRIVATE: //文件不可被读,返回,好像读是至少的权限了
if (!(file->f_mode & 1))
return -EACCES;
break; default:
return -EINVAL;
}
/*
* obtain the address to map to. we verify (or select) it and ensure
* that it represents a valid section of the address space.
*/ if (flags & MAP_FIXED) {
//表示区间的起始地址必须由addr所指定
//那么这样来说,addr必须是页的边界才合法了
if (addr & ~PAGE_MASK)
return -EINVAL;
if (len > TASK_SIZE || addr > TASK_SIZE - len)
return -EINVAL;
} else {
struct vm_area_struct * vmm; /* Maybe this works.. Ugly it is. */
//这里的1G-1.5G可能是对共享内存的一个硬限制了~~~
addr = SHM_RANGE_START; /*1G*/
while (addr+len < SHM_RANGE_END) /*1.5G*/
{
//这里是进行寻找,至少是寻找vm_start vm_end之间有交集
for (vmm = current->mmap ; vmm ; vmm = vmm->vm_next)
{
if (addr >= vmm->vm_end)
continue;
if (addr + len <= vmm->vm_start)
continue;
// addr + len >= vm_start && addr <= vm_end
addr = PAGE_ALIGN(vmm->vm_end);
break;
}
if (!vmm)
break;
}
if (addr+len >= SHM_RANGE_END)
return -ENOMEM;
} /*
* determine the object being mapped and call the appropriate
* specific mapper. the address has already been validated, but
* not unmapped, but the maps are removed from the list.
*/
if (file && (!file->f_op || !file->f_op->mmap)) //文件
return -ENODEV;
mask = 0;
if (prot & (PROT_READ | PROT_EXEC))
mask |= PAGE_READONLY;
if (prot & PROT_WRITE)
if ((flags & MAP_TYPE) == MAP_PRIVATE) //独有的
mask |= PAGE_COPY;
else
mask |= PAGE_SHARED;
if (!mask)
return -EINVAL; do_munmap(addr, len); /* Clear old maps */ if (file)
error = file->f_op->mmap(file->f_inode, file, addr, len, mask, off);
else //匿名映射
error = anon_map(NULL, NULL, addr, len, mask, off);
if (!error)
return addr; //success if (!current->errno)
current->errno = -error; //设置错误标号
return -1;
} asmlinkage int sys_mmap(unsigned long *buffer)
{
int error;
unsigned long flags;
struct file * file = NULL; error = verify_area(VERIFY_READ, buffer, 6*4);
if (error)
return error;
flags = get_fs_long(buffer+3);
if (!(flags & MAP_ANONYMOUS)) //MAP_ANONYMOUS表示没有文件同这个线性区相关
{
//!MAP_ANONYMOUS
unsigned long fd = get_fs_long(buffer+4);
if (fd >= NR_OPEN || !(file = current->filp[fd]))
return -EBADF;
}
//int do_mmap(struct file * file, unsigned long addr, unsigned long len,
//unsigned long prot, unsigned long flags, unsigned long off)
//从此就可以看出了buffer的参数的结构了吧
return do_mmap(file, get_fs_long(buffer), get_fs_long(buffer+1),
get_fs_long(buffer+2), flags, get_fs_long(buffer+5));
} //应该这个函数是一个通用的umap函数
//实际是对区域(addr,addr+len)以及area的范围来调用特定的函数的
//要删除的区域就是(addr,addr+len)
void unmap_fixup(struct vm_area_struct *area,unsigned long addr, size_t len)
{
struct vm_area_struct *mpnt;
unsigned long end = addr + len; //不能进行增加的哦
if (addr < area->vm_start || addr >= area->vm_end ||
end <= area->vm_start || end > area->vm_end ||
end < addr)
{
printk("unmap_fixup: area=%lx-%lx, unmap %lx-%lx!!\n",
area->vm_start, area->vm_end, addr, end);
return;
} //整个区域的删除,就直接调用了close进行删除线性区了
if (addr == area->vm_start && end == area->vm_end) {
if (area->vm_ops && area->vm_ops->close)
area->vm_ops->close(area);
return;
} //(addr,addr+len) //对于一个方向的缩减
if (addr >= area->vm_start && end == area->vm_end)
area->vm_end = addr;
if (addr == area->vm_start && end <= area->vm_end)
{
area->vm_offset += (end - area->vm_start); //截取的大小照成的偏移吧
area->vm_start = end;
} /* Unmapping a hole */
//中间的截取,两边剩余,而中间将产生空洞
//实际是创建了两个小的区域了
if (addr > area->vm_start && end < area->vm_end)
{
/* Add end mapping -- leave beginning for below */
mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL); *mpnt = *area;
mpnt->vm_offset += (end - area->vm_start);
mpnt->vm_start = end; //后半个~~~
if (mpnt->vm_inode)
mpnt->vm_inode->i_count++;
insert_vm_struct(current, mpnt);
area->vm_end = addr; /* Truncate area */
}
//will fall through
/* construct whatever mapping is needed */
mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
*mpnt = *area; //值复制
insert_vm_struct(current, mpnt);
}
asmlinkage int sys_mprotect(unsigned long addr, size_t len, unsigned long prot)
{
return -EINVAL; /* Not implemented yet */
} asmlinkage int sys_munmap(unsigned long addr, size_t len)
{
return do_munmap(addr, len);
} /*
* Munmap is split into 2 main parts -- this part which finds
* what needs doing, and the areas themselves, which do the
* work. This now handles partial unmappings.
* Jeremy Fitzhardine <[email protected]>
*/
//addr必须页对齐
int do_munmap(unsigned long addr, size_t len)
{
struct vm_area_struct *mpnt, **npp, *free; if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr)
return -EINVAL; if ((len = PAGE_ALIGN(len)) == 0)
return 0; /*
* Check if this memory area is ok - put it on the temporary
* list if so.. The checks here are pretty simple --
* every area affected in some way (by any overlap) is put
* on the list. If nothing is put on, nothing is affected.
*/
npp = ¤t->mmap; //一个链表作用
free = NULL;
for (mpnt = *npp; mpnt != NULL; mpnt = *npp)
{
unsigned long end = addr+len; //对于这个检查,是没有任何交汇的,当然没有什么要释放的了~~~
//难道可以addr<end不是一定的啊?,呃或许这里允许len<0吧
if ((addr < mpnt->vm_start && end <= mpnt->vm_start) ||
(addr >= mpnt->vm_end && end > mpnt->vm_end))
{
npp = &mpnt->vm_next;
continue;
} *npp = mpnt->vm_next;
mpnt->vm_next = free;
free = mpnt; //这里需要注意的是 free是指向的一个临时链表的结构!!
} if (free == NULL)
return 0; /*
* Ok - we have the memory areas we should free on the 'free' list,
* so release them, and unmap the page range..
* If the one of the segments is only being partially unmapped,
* it will put new vm_area_struct(s) into the address space.
*/
while (free) {
unsigned long st, end; mpnt = free;
free = free->vm_next; st = addr < mpnt->vm_start ? mpnt->vm_start : addr;
end = addr+len;
end = end > mpnt->vm_end ? mpnt->vm_end : end; if (mpnt->vm_ops && mpnt->vm_ops->unmap) //本身函数的调用,如果没有就调用下面的通用函数
mpnt->vm_ops->unmap(mpnt, st, end-st);
else
unmap_fixup(mpnt, st, end-st); kfree(mpnt);
} unmap_page_range(addr, len);
return 0;
} /* This is used for a general mmap of a disk file */
//对于普通磁盘文件的映射
int generic_mmap(struct inode * inode, struct file * file,
unsigned long addr, size_t len, int prot, unsigned long off)
{
struct vm_area_struct * mpnt;
extern struct vm_operations_struct file_mmap;
struct buffer_head * bh; if (prot & PAGE_RW) /* only PAGE_COW or read-only supported right now */
return -EINVAL;
if (off & (inode->i_sb->s_blocksize - 1))
return -EINVAL;
if (!inode->i_sb || !S_ISREG(inode->i_mode))
return -EACCES;
if (!inode->i_op || !inode->i_op->bmap)
return -ENOEXEC;
if (!(bh = bread(inode->i_dev,bmap(inode,0),inode->i_sb->s_blocksize)))
return -EACCES;
//记录被访问时间,设置脏了
if (!IS_RDONLY(inode)) {
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
}
brelse(bh); mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
if (!mpnt)
return -ENOMEM; unmap_page_range(addr, len);
mpnt->vm_task = current;
mpnt->vm_start = addr;
mpnt->vm_end = addr + len;
mpnt->vm_page_prot = prot;
mpnt->vm_share = NULL;
mpnt->vm_inode = inode;
inode->i_count++;
mpnt->vm_offset = off;
mpnt->vm_ops = &file_mmap;
insert_vm_struct(current, mpnt);
merge_segments(current->mmap, NULL, NULL);
return 0;
} //将vm插入到进程队列中,进行的插入是有地址顺序的,
//并进行一些重叠性的检查
void insert_vm_struct(struct task_struct *t, struct vm_area_struct *vmp)
{
struct vm_area_struct **nxtpp, *mpnt; nxtpp = &t->mmap;
for(mpnt = t->mmap; mpnt != NULL; mpnt = mpnt->vm_next)
{
//这里找到了,因为nxtpp保存了前一个地址,所以不用担心啊
if (mpnt->vm_start > vmp->vm_start)
break;
nxtpp = &mpnt->vm_next; //这里表示有重叠的区域了
if ((vmp->vm_start >= mpnt->vm_start && vmp->vm_start < mpnt->vm_end) ||
(vmp->vm_end >= mpnt->vm_start && vmp->vm_end < mpnt->vm_end))
printk("insert_vm_struct: ins area %lx-%lx in area %lx-%lx\n",
vmp->vm_start, vmp->vm_end,
mpnt->vm_start, vmp->vm_end);
}
vmp->vm_next = mpnt; *nxtpp = vmp; //->mmap=vmp
} //进行区域的合并,冗余的vm_area_structs将被删除
void merge_segments(struct vm_area_struct *mpnt,map_mergep_fnp mergep, void *mpd)
{
struct vm_area_struct *prev, *next; if (mpnt == NULL)
return;
for(prev = mpnt, mpnt = mpnt->vm_next;
mpnt != NULL;
prev = mpnt, mpnt = next)
{
int mp; next = mpnt->vm_next;
if (mergep == NULL)
{
unsigned long psz = prev->vm_end - prev->vm_start;
mp = prev->vm_offset + psz == mpnt->vm_offset;
}
else //指明了调用特定的合并函数
mp = (*mergep)(prev, mpnt, mpd); /*
* Check they are compatible.
* and the like...
* What does the share pointer mean?
*/
if (prev->vm_ops != mpnt->vm_ops ||
prev->vm_page_prot != mpnt->vm_page_prot || //线性区域的页匡的访问权限
prev->vm_inode != mpnt->vm_inode ||
prev->vm_end != mpnt->vm_start || //合并的条件啊!!
!mp ||
prev->vm_share != mpnt->vm_share || /* ?? */
prev->vm_next != mpnt) /* !!! */ //顺序排放的,应该没有什么问题吧
continue; prev->vm_end = mpnt->vm_end;
prev->vm_next = mpnt->vm_next;
kfree_s(mpnt, sizeof(*mpnt));
mpnt = prev;
}
} /*
* Map memory not associated with any file into a process
* address space. Adjecent memory is merged.
*/
//匿名文件关联的内存映射
static int anon_map(struct inode *ino, struct file * file,
unsigned long addr, size_t len, int mask,
unsigned long off)
{
struct vm_area_struct * mpnt; if (zeromap_page_range(addr, len, mask))
return -ENOMEM; mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
if (!mpnt)
return -ENOMEM; mpnt->vm_task = current;
mpnt->vm_start = addr;
mpnt->vm_end = addr + len;
mpnt->vm_page_prot = mask;
mpnt->vm_share = NULL;
mpnt->vm_inode = NULL; //空
mpnt->vm_offset = 0;
mpnt->vm_ops = NULL;
insert_vm_struct(current, mpnt);
merge_segments(current->mmap, ignoff_mergep, NULL); return 0;
} /* Merge, ignoring offsets */ //??
int ignoff_mergep(const struct vm_area_struct *m1,
const struct vm_area_struct *m2,
void *data)
{
if (m1->vm_inode != m2->vm_inode) /* Just to be sure */
return 0; return (struct inode *)data == m1->vm_inode;
}
文档地址:http://blogimg.chinaunix.net/blog/upfile2/090317145723.pdf
*Created By: 陶治江
*Date: 2009-3-16
********************************************/
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h> #include <asm/segment.h>
#include <asm/system.h> static int anon_map(struct inode *, struct file *,
unsigned long, size_t, int,
unsigned long);
/*
* description of effects of mapping type and prot in current implementation.
* this is due to the current handling of page faults in memory.c. the expected
* behavior is in parens:
*
* map_type prot
* PROT_NONE PROT_READ PROT_WRITE PROT_EXEC
* MAP_SHARED r: (no) yes r: (yes) yes r: (no) yes r: (no) no
* w: (no) yes w: (no) copy w: (yes) yes w: (no) no
* x: (no) no x: (no) no x: (no) no x: (yes) no
*
* MAP_PRIVATE r: (no) yes r: (yes) yes r: (no) yes r: (no) no
* w: (no) copy w: (no) copy w: (copy) copy w: (no) no
* x: (no) no x: (no) no x: (no) no x: (yes) no
*
*/ #define CODE_SPACE(addr) \
(PAGE_ALIGN(addr) < current->start_code + current->end_code) int do_mmap(struct file * file, unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long off)
{
int mask, error; if ((len = PAGE_ALIGN(len)) == 0) //PAGE_ALIGN,将指针对齐到下一个页面的起始地址
return addr; //TASK_SIZE,用户进程空间:3G,这里是检查起始地址addr以及长度等信息是否合法
if (addr > TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE-len)
return -EINVAL; if (file != NULL)
switch (flags & MAP_TYPE)
{
//MAP_SHARED表示执行线性区中的页可以被几个进程所共享
//MAP_PRIVATE 含义相反
case MAP_SHARED:
//如果映射页面要求可被写,但是文件访问模式不允许写,出错
if ((prot & PROT_WRITE) && !(file->f_mode & 2))
return -EACCES;
/* fall through */
case MAP_PRIVATE: //文件不可被读,返回,好像读是至少的权限了
if (!(file->f_mode & 1))
return -EACCES;
break; default:
return -EINVAL;
}
/*
* obtain the address to map to. we verify (or select) it and ensure
* that it represents a valid section of the address space.
*/ if (flags & MAP_FIXED) {
//表示区间的起始地址必须由addr所指定
//那么这样来说,addr必须是页的边界才合法了
if (addr & ~PAGE_MASK)
return -EINVAL;
if (len > TASK_SIZE || addr > TASK_SIZE - len)
return -EINVAL;
} else {
struct vm_area_struct * vmm; /* Maybe this works.. Ugly it is. */
//这里的1G-1.5G可能是对共享内存的一个硬限制了~~~
addr = SHM_RANGE_START; /*1G*/
while (addr+len < SHM_RANGE_END) /*1.5G*/
{
//这里是进行寻找,至少是寻找vm_start vm_end之间有交集
for (vmm = current->mmap ; vmm ; vmm = vmm->vm_next)
{
if (addr >= vmm->vm_end)
continue;
if (addr + len <= vmm->vm_start)
continue;
// addr + len >= vm_start && addr <= vm_end
addr = PAGE_ALIGN(vmm->vm_end);
break;
}
if (!vmm)
break;
}
if (addr+len >= SHM_RANGE_END)
return -ENOMEM;
} /*
* determine the object being mapped and call the appropriate
* specific mapper. the address has already been validated, but
* not unmapped, but the maps are removed from the list.
*/
if (file && (!file->f_op || !file->f_op->mmap)) //文件
return -ENODEV;
mask = 0;
if (prot & (PROT_READ | PROT_EXEC))
mask |= PAGE_READONLY;
if (prot & PROT_WRITE)
if ((flags & MAP_TYPE) == MAP_PRIVATE) //独有的
mask |= PAGE_COPY;
else
mask |= PAGE_SHARED;
if (!mask)
return -EINVAL; do_munmap(addr, len); /* Clear old maps */ if (file)
error = file->f_op->mmap(file->f_inode, file, addr, len, mask, off);
else //匿名映射
error = anon_map(NULL, NULL, addr, len, mask, off);
if (!error)
return addr; //success if (!current->errno)
current->errno = -error; //设置错误标号
return -1;
} asmlinkage int sys_mmap(unsigned long *buffer)
{
int error;
unsigned long flags;
struct file * file = NULL; error = verify_area(VERIFY_READ, buffer, 6*4);
if (error)
return error;
flags = get_fs_long(buffer+3);
if (!(flags & MAP_ANONYMOUS)) //MAP_ANONYMOUS表示没有文件同这个线性区相关
{
//!MAP_ANONYMOUS
unsigned long fd = get_fs_long(buffer+4);
if (fd >= NR_OPEN || !(file = current->filp[fd]))
return -EBADF;
}
//int do_mmap(struct file * file, unsigned long addr, unsigned long len,
//unsigned long prot, unsigned long flags, unsigned long off)
//从此就可以看出了buffer的参数的结构了吧
return do_mmap(file, get_fs_long(buffer), get_fs_long(buffer+1),
get_fs_long(buffer+2), flags, get_fs_long(buffer+5));
} //应该这个函数是一个通用的umap函数
//实际是对区域(addr,addr+len)以及area的范围来调用特定的函数的
//要删除的区域就是(addr,addr+len)
void unmap_fixup(struct vm_area_struct *area,unsigned long addr, size_t len)
{
struct vm_area_struct *mpnt;
unsigned long end = addr + len; //不能进行增加的哦
if (addr < area->vm_start || addr >= area->vm_end ||
end <= area->vm_start || end > area->vm_end ||
end < addr)
{
printk("unmap_fixup: area=%lx-%lx, unmap %lx-%lx!!\n",
area->vm_start, area->vm_end, addr, end);
return;
} //整个区域的删除,就直接调用了close进行删除线性区了
if (addr == area->vm_start && end == area->vm_end) {
if (area->vm_ops && area->vm_ops->close)
area->vm_ops->close(area);
return;
} //(addr,addr+len) //对于一个方向的缩减
if (addr >= area->vm_start && end == area->vm_end)
area->vm_end = addr;
if (addr == area->vm_start && end <= area->vm_end)
{
area->vm_offset += (end - area->vm_start); //截取的大小照成的偏移吧
area->vm_start = end;
} /* Unmapping a hole */
//中间的截取,两边剩余,而中间将产生空洞
//实际是创建了两个小的区域了
if (addr > area->vm_start && end < area->vm_end)
{
/* Add end mapping -- leave beginning for below */
mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL); *mpnt = *area;
mpnt->vm_offset += (end - area->vm_start);
mpnt->vm_start = end; //后半个~~~
if (mpnt->vm_inode)
mpnt->vm_inode->i_count++;
insert_vm_struct(current, mpnt);
area->vm_end = addr; /* Truncate area */
}
//will fall through
/* construct whatever mapping is needed */
mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
*mpnt = *area; //值复制
insert_vm_struct(current, mpnt);
}
asmlinkage int sys_mprotect(unsigned long addr, size_t len, unsigned long prot)
{
return -EINVAL; /* Not implemented yet */
} asmlinkage int sys_munmap(unsigned long addr, size_t len)
{
return do_munmap(addr, len);
} /*
* Munmap is split into 2 main parts -- this part which finds
* what needs doing, and the areas themselves, which do the
* work. This now handles partial unmappings.
* Jeremy Fitzhardine <[email protected]>
*/
//addr必须页对齐
int do_munmap(unsigned long addr, size_t len)
{
struct vm_area_struct *mpnt, **npp, *free; if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr)
return -EINVAL; if ((len = PAGE_ALIGN(len)) == 0)
return 0; /*
* Check if this memory area is ok - put it on the temporary
* list if so.. The checks here are pretty simple --
* every area affected in some way (by any overlap) is put
* on the list. If nothing is put on, nothing is affected.
*/
npp = ¤t->mmap; //一个链表作用
free = NULL;
for (mpnt = *npp; mpnt != NULL; mpnt = *npp)
{
unsigned long end = addr+len; //对于这个检查,是没有任何交汇的,当然没有什么要释放的了~~~
//难道可以addr<end不是一定的啊?,呃或许这里允许len<0吧
if ((addr < mpnt->vm_start && end <= mpnt->vm_start) ||
(addr >= mpnt->vm_end && end > mpnt->vm_end))
{
npp = &mpnt->vm_next;
continue;
} *npp = mpnt->vm_next;
mpnt->vm_next = free;
free = mpnt; //这里需要注意的是 free是指向的一个临时链表的结构!!
} if (free == NULL)
return 0; /*
* Ok - we have the memory areas we should free on the 'free' list,
* so release them, and unmap the page range..
* If the one of the segments is only being partially unmapped,
* it will put new vm_area_struct(s) into the address space.
*/
while (free) {
unsigned long st, end; mpnt = free;
free = free->vm_next; st = addr < mpnt->vm_start ? mpnt->vm_start : addr;
end = addr+len;
end = end > mpnt->vm_end ? mpnt->vm_end : end; if (mpnt->vm_ops && mpnt->vm_ops->unmap) //本身函数的调用,如果没有就调用下面的通用函数
mpnt->vm_ops->unmap(mpnt, st, end-st);
else
unmap_fixup(mpnt, st, end-st); kfree(mpnt);
} unmap_page_range(addr, len);
return 0;
} /* This is used for a general mmap of a disk file */
//对于普通磁盘文件的映射
int generic_mmap(struct inode * inode, struct file * file,
unsigned long addr, size_t len, int prot, unsigned long off)
{
struct vm_area_struct * mpnt;
extern struct vm_operations_struct file_mmap;
struct buffer_head * bh; if (prot & PAGE_RW) /* only PAGE_COW or read-only supported right now */
return -EINVAL;
if (off & (inode->i_sb->s_blocksize - 1))
return -EINVAL;
if (!inode->i_sb || !S_ISREG(inode->i_mode))
return -EACCES;
if (!inode->i_op || !inode->i_op->bmap)
return -ENOEXEC;
if (!(bh = bread(inode->i_dev,bmap(inode,0),inode->i_sb->s_blocksize)))
return -EACCES;
//记录被访问时间,设置脏了
if (!IS_RDONLY(inode)) {
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
}
brelse(bh); mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
if (!mpnt)
return -ENOMEM; unmap_page_range(addr, len);
mpnt->vm_task = current;
mpnt->vm_start = addr;
mpnt->vm_end = addr + len;
mpnt->vm_page_prot = prot;
mpnt->vm_share = NULL;
mpnt->vm_inode = inode;
inode->i_count++;
mpnt->vm_offset = off;
mpnt->vm_ops = &file_mmap;
insert_vm_struct(current, mpnt);
merge_segments(current->mmap, NULL, NULL);
return 0;
} //将vm插入到进程队列中,进行的插入是有地址顺序的,
//并进行一些重叠性的检查
void insert_vm_struct(struct task_struct *t, struct vm_area_struct *vmp)
{
struct vm_area_struct **nxtpp, *mpnt; nxtpp = &t->mmap;
for(mpnt = t->mmap; mpnt != NULL; mpnt = mpnt->vm_next)
{
//这里找到了,因为nxtpp保存了前一个地址,所以不用担心啊
if (mpnt->vm_start > vmp->vm_start)
break;
nxtpp = &mpnt->vm_next; //这里表示有重叠的区域了
if ((vmp->vm_start >= mpnt->vm_start && vmp->vm_start < mpnt->vm_end) ||
(vmp->vm_end >= mpnt->vm_start && vmp->vm_end < mpnt->vm_end))
printk("insert_vm_struct: ins area %lx-%lx in area %lx-%lx\n",
vmp->vm_start, vmp->vm_end,
mpnt->vm_start, vmp->vm_end);
}
vmp->vm_next = mpnt; *nxtpp = vmp; //->mmap=vmp
} //进行区域的合并,冗余的vm_area_structs将被删除
void merge_segments(struct vm_area_struct *mpnt,map_mergep_fnp mergep, void *mpd)
{
struct vm_area_struct *prev, *next; if (mpnt == NULL)
return;
for(prev = mpnt, mpnt = mpnt->vm_next;
mpnt != NULL;
prev = mpnt, mpnt = next)
{
int mp; next = mpnt->vm_next;
if (mergep == NULL)
{
unsigned long psz = prev->vm_end - prev->vm_start;
mp = prev->vm_offset + psz == mpnt->vm_offset;
}
else //指明了调用特定的合并函数
mp = (*mergep)(prev, mpnt, mpd); /*
* Check they are compatible.
* and the like...
* What does the share pointer mean?
*/
if (prev->vm_ops != mpnt->vm_ops ||
prev->vm_page_prot != mpnt->vm_page_prot || //线性区域的页匡的访问权限
prev->vm_inode != mpnt->vm_inode ||
prev->vm_end != mpnt->vm_start || //合并的条件啊!!
!mp ||
prev->vm_share != mpnt->vm_share || /* ?? */
prev->vm_next != mpnt) /* !!! */ //顺序排放的,应该没有什么问题吧
continue; prev->vm_end = mpnt->vm_end;
prev->vm_next = mpnt->vm_next;
kfree_s(mpnt, sizeof(*mpnt));
mpnt = prev;
}
} /*
* Map memory not associated with any file into a process
* address space. Adjecent memory is merged.
*/
//匿名文件关联的内存映射
static int anon_map(struct inode *ino, struct file * file,
unsigned long addr, size_t len, int mask,
unsigned long off)
{
struct vm_area_struct * mpnt; if (zeromap_page_range(addr, len, mask))
return -ENOMEM; mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
if (!mpnt)
return -ENOMEM; mpnt->vm_task = current;
mpnt->vm_start = addr;
mpnt->vm_end = addr + len;
mpnt->vm_page_prot = mask;
mpnt->vm_share = NULL;
mpnt->vm_inode = NULL; //空
mpnt->vm_offset = 0;
mpnt->vm_ops = NULL;
insert_vm_struct(current, mpnt);
merge_segments(current->mmap, ignoff_mergep, NULL); return 0;
} /* Merge, ignoring offsets */ //??
int ignoff_mergep(const struct vm_area_struct *m1,
const struct vm_area_struct *m2,
void *data)
{
if (m1->vm_inode != m2->vm_inode) /* Just to be sure */
return 0; return (struct inode *)data == m1->vm_inode;
}
文档地址:http://blogimg.chinaunix.net/blog/upfile2/090317145723.pdf
相关阅读 更多 +