mm/mmap.c
/*
* linux/mm/mmap.c
*
* Written by obz.
*/
#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)
return addr;
//校验参数
if (addr > TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE-len)
return -EINVAL;
/*
* do simple checking here so the lower-level routines won't have
* to. we assume access permissions have been handled by the open
* of the memory object, so we don't do any here.
*/
//文件存在
if (file != NULL)
switch (flags & MAP_TYPE) {
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) {
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. */
addr = SHM_RANGE_START;
while (addr+len < SHM_RANGE_END) {
for (vmm = current->mmap ; vmm ; vmm = vmm->vm_next) {
if (addr >= vmm->vm_end)
continue;
if (addr + len <= vmm->vm_start)
continue;
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;
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)) {
unsigned long fd = get_fs_long(buffer+4);
if (fd >= NR_OPEN || !(file = current->filp[fd]))
return -EBADF;
}
return do_mmap(file, get_fs_long(buffer), get_fs_long(buffer+1),
get_fs_long(buffer+2), flags, get_fs_long(buffer+5));
}
/*
* Normal function to fix up a mapping
* This function is the default for when an area has no specific
* function. This may be used as part of a more specific routine.
* This function works out what part of an area is affected and
* adjusts the mapping information. Since the actual page
* manipulation is done in do_mmap(), none need be done here,
* though it would probably be more appropriate.
*
* By the time this function is called, the area struct has been
* removed from the process mapping list, so it needs to be
* reinserted if necessary.
*
* The 4 main cases are:
* Unmapping the whole area
* Unmapping from the start of the segment to a point in it
* Unmapping from an intermediate point to the end
* Unmapping between to intermediate points, making a hole.
*
* Case 4 involves the creation of 2 new areas, for each side of
* the hole.
*/
//
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;
}
/* Unmapping the whole area */
if (addr == area->vm_start && end == area->vm_end) {
if (area->vm_ops && area->vm_ops->close)
area->vm_ops->close(area);
return;
}
/* Work out to one of the ends */
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 */
}
/* 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 <jeremy@sw.oz.au>
*/
//执行
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;
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;
}
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;
}
/*
* Insert vm structure into process list
* This makes sure the list is sorted by start address, and
* some some simple overlap checking.
* JSGF
*/
//插入虚拟内存结构到进程链表中
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)
{
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;
}
/*
* Merge a list of memory segments if possible.
* Redundant vm_area_structs are freed.
* This assumes that the list is ordered by address.
*/
//合并内存段
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;
/*
* merge prev with mpnt and set up pointers so the new
* big segment can possibly merge with the next one.
* The old unused mpnt is freed.
*/
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;
}
mm/mmap.c的更多相关文章
- Linux内存分配小结--malloc、brk、mmap【转】
转自:https://blog.csdn.net/gfgdsg/article/details/42709943 http://blog.163.com/xychenbaihu@yeah/blog/s ...
- 内存控制函数(1)-mmap() 建立内存映射
示例1: 1.首先建立一个文本文件,名字为tmp,内容为hello world 2.编写mmap.c #include <sys/types.h> #include <sys/sta ...
- Linux中brk()系统调用,sbrk(),mmap(),malloc(),calloc()的异同【转】
转自:http://blog.csdn.net/kobbee9/article/details/7397010 brk和sbrk主要的工作是实现虚拟内存到内存的映射.在GNUC中,内存分配是这样的: ...
- mmap vs read
先放个结论: 内存映射通常比随机访问更快,尤其访问的对象是分离的和不可预测的. 内存映射会持续占用pages, 直到完成访问. 这意味当长时间重度使用一个文件很久之前, 然后你关闭了它, 然后再重新打 ...
- Linux进程通信之mmap
mmap()函数: void *mmap(void* addr,size_t length,int port,int flags,int fd,off_t offset); 返回:成功:返回创建的映射 ...
- PROC 文件系统调节参数介绍(netstat -us)
转自:http://www.cnblogs.com/super-king/p/3296333.html /proc/net/* snmp文件 Ip: ip项 Forwarding : 是 ...
- LInux_System_Call_INT_80h
Int 80h Website (Copy from Linux-System-Call) List of Linux/i386 system calls Copyright (C) 1999-200 ...
- Linux Process Virtual Memory
目录 . 简介 . 进程虚拟地址空间 . 内存映射的原理 . 数据结构 . 对区域的操作 . 地址空间 . 内存映射 . 反向映射 .堆的管理 . 缺页异常的处理 . 用户空间缺页异常的校正 . 内核 ...
- Linux内存管理基本概念
1. 基本概念 1.1 地址 (1)逻辑地址:指由程序产生的与段相关的偏移地址部分.在C语言指针中,读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址.( ...
随机推荐
- (一)kafka修改topic分区的位置
(一)kafka修改topic分区的位置 环境:kafka_2.10-0.8.2.1 + JDK1.7.0_80 1. 查看分区topic的分区分布 $ le-kafka-topics.sh --de ...
- C#位运算讲解与示例2
在C#中可以对整型运算对象按位进行逻辑运算.按位进行逻辑运算的意义是:依次取被运算对象的每个位,进行逻辑运算,每个位的逻辑运算结果是结果值的每个位.C#支持的位逻辑运算符如表2.9所示. 运算符号 意 ...
- XMLHttpRequest upload属性
一.新版本的XMLHttpRequest对象,传送数据的时候,有一个progress事件,用来返回进度信息. 它分成上传和下载两种情况 1)下载的progress事件属于XMLHttpRequest对 ...
- Java安装与环境配置
Java安装 第一步 安装jdk-8u111-windows(根据操作系统的位数选择安装32位或64位) 注:路径用英文,不要出现中文和空格等.jdk和jre放在同一个文件夹下,便于管理 本例的路径为 ...
- Oracle重做日志文件
一.联机重做日志的规划管理 1.联机重做日志 记录了数据的所有变化(DML,DDL或管理员对数据所作的结构性更改等) 提供恢复机制(对于意外删除或宕机利用日志文件实现数据恢复) 可以被分组管理 11 ...
- android音视频点/直播模块开发
音视频 版权声明:本文为博主原创文章,未经博主允许不得转载. 前言 随着音视频领域的火热,在很多领域(教育,游戏,娱乐,体育,跑步,餐饮,音乐等)尝试做音视频直播/点播功能,那么作为开发一个小白, ...
- Element is not currently visible and so may not be interacted with错误
用selenium定位时,碰到这种错误的原因: 此种问题的关键是在于用className和id都不唯一所以找不到对象 所以,碰到这种问题就换一种定位方式
- javasrcipt中的for in 循环
function myFunction(){ var x; //声明变量x: var txt=""; //声明变量txt并赋值为空: var person={fname:&qu ...
- Selenium 获取隐藏元素的内容
第一种 先获取元素通过 属性获取 WebElement webElemt= webElement.findElement(By.xpath("//*[@class='xxxxxx]/a&qu ...
- spring mvc 注解 学习笔记(一)
以前接触过spring,但是没有接触spring mvc 以及注解的应用,特习之,记之: 注解了解 @Component 是通用标注, @Controller 标注web控制器, @Service 标 ...