没有合适的资源?快使用搜索试试~ 我知道了~
linux用户进程内存映射
需积分: 9 10 下载量 198 浏览量
2013-08-28
21:27:20
上传
评论
收藏 37KB DOCX 举报
温馨提示
试读
16页
详解mmap、malloc在内核态的实现,原理是什么,有详细的描述和加的内核源码注释,可完全理解用户进程申请内存是怎么一回事
资源推荐
资源详情
资源评论
如果已经看了博客中本系列文档的前面的几篇文章,应该就已经对
malloc 、 mmap
大
致了解了,它们就是在堆中创建 ( 或合并 ) 所需虚拟地址的
vma
线性区,换句话说,就是达
到进程地址空间中要有满足要求的
vma ,但不会给
vma
映射物理页 ( 除非一定要求,即
vma
的
ags
标识了页锁定标志
VM_LOCKED) ,这是
linux
的对用户进程物理页分配的推后原则,
把握这个原则有助于分析
malloc/mmap
乃至理解
linux
的用户进程内存管理。
库函数
malloc
在
linux
内核的实现是典型的匿名映射,关于匿名映射可以参考前面的
缺页异常处理的文章, IPC
方式中的共享内存也是匿名映射,绝大多数的
mmap
应用场合
是文件映射,而内核中的处理,对于
malloc
的处理函数是
do_brk ,这是因为
malloc
操作的
是进程地址空间的堆段,而函数
do_brk
就是针对堆段的处理, mmap
包括
IPC
的共享内存
都是由函数
do_mmap
处理;不论哪种处理其实操作的都是进程地址空间的线性区 ;
下面首先看下函数 do_mmap,源码如下:
stac inline unsigned long do_mmap(struct le *le, unsigned long addr,
unsigned long len, unsigned long prot,
unsigned long ag, unsigned long oset)
{
unsigned long ret = -EINVAL;
if ((oset + PAGE_ALIGN(len)) < oset)
goto out;
if (!(oset & ~PAGE_MASK))
ret = do_mmap_pgo(le, addr, len, prot, ag, oset >> PAGE_SHIFT);
out:
return ret;
}
它的核心函数是 do_mmap_pgo",这里主要关注下 do_mmap 的参数情况:
#le: 如果新的线性区将要把一个文件映射到内存,则要用文件描述符 #le 和文件偏移
o"set,如不需要,则 #le 和 o"set 不考虑都为空;
addr: 指定从哪里开始查找空闲区间,一般都是 NULL 即由内核指定;
len: 要求的线性地址空间长度;
prot: 指定线性区下的页的访问权限;
ag: 指定线性区的其他标志;
初步有个印象即可,接下来关注函数 do_mmap_pgo",源码如下:
unsigned long do_mmap_pgo(struct le *le, unsigned long addr,
unsigned long len, unsigned long prot,
unsigned long ags, unsigned long pgo)
{
/*当前进程的 mm*/
struct mm_struct * mm = current->mm;
struct inode *inode;
unsigned int vm_ags;
int error;
unsigned long reqprot = prot;
/*
* Does the applicaon expect PROT_READ to imply PROT_EXEC?
*
* (the excepon is when the underlying lesystem is noexec
* mounted, in which case we dont add PROT_EXEC.)
*/
/*是否隐藏了可执行属性*/
if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
if (!(le && (le->f_path.mnt->mnt_ags & MNT_NOEXEC)))
prot |= PROT_EXEC;
if (!len)
return -EINVAL;
/*判断输入的欲映射的起始地址是否小于最小映射地址,如果小于,将 addr 修改为
最小地址,不过前提是 MAP_FIXED 旗标没有设置*/
if (!(ags & MAP_FIXED))
addr = round_hint_to_min(addr);
/* Careful about overows.. */
/*检测 len 是否为 0*/
len = PAGE_ALIGN(len);
if (!len)
return -ENOMEM;
/* oset overow? */
/*再次检测是否越界*/
if ((pgo + (len >> PAGE_SHIFT)) < pgo)
return -EOVERFLOW;
/* Too many mappings? */
/*在一个进程中对于 mmap 个数是有限制的。超出了还是 nomem 的错误*/
if (mm->map_count > sysctl_max_map_count)
return -ENOMEM;
/* Obtain the address to map to. we verify (or select) it and ensure
* that it represents a valid secon of the address space.
*/
/*创建新的 vma 区域之前先要寻找一块足够大小的空闲区域,本函数就是用于查找没
有映射过的空洞内存区,返回值 addr 就是这段空洞的起始地址*/
addr = get_unmapped_area(le, addr, len, pgo, ags);
if (addr & ~PAGE_MASK)
return addr;
/* Do simple checking here so the lower-level rounes 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.
*/
/*设置 vm_ags,根据传入的 port 和 ags 以及 mm 本身自有的旗标来设置*/
vm_ags = calc_vm_prot_bits(prot) | calc_vm_ag_bits(ags) |
mm->def_ags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
/*下面两个 if 是关于锁定的内存的内容,暂不关注*/
if (ags & MAP_LOCKED)
if (!can_do_mlock())
return -EPERM;
/* mlock MCL_FUTURE? */
if (vm_ags & VM_LOCKED) {
unsigned long locked, lock_limit;
locked = len >> PAGE_SHIFT;
locked += mm->locked_vm;
lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
lock_limit >>= PAGE_SHIFT;
if (locked > lock_limit && !capable(CAP_IPC_LOCK))
return -EAGAIN;
}
/*判断是文件映射还是匿名映射,如果是文件映射则赋值 inode*/
inode = le ? le->f_path.dentry->d_inode : NULL;
/*对 vm_ags 进行设置,由参数 ags 确定 vma 线性区的 ags,是共享还是私有*/
/*文件映射*/
if (le) {
switch (ags & MAP_TYPE) {
/*共享*/
case MAP_SHARED:
if ((prot&PROT_WRITE) && !(le->f_mode&FMODE_WRITE))
return -EACCES;
/*
* Make sure we don't allow wring to an append-only
* le..
*/
if (IS_APPEND(inode) && (le->f_mode & FMODE_WRITE))
return -EACCES;
/*
* Make sure there are no mandatory locks on the le.
*/
if (locks_verify_locked(inode))
return -EAGAIN;
vm_ags |= VM_SHARED | VM_MAYSHARE;
if (!(le->f_mode & FMODE_WRITE))
vm_ags &= ~(VM_MAYWRITE | VM_SHARED);
/* fall through */
/*私有*/
case MAP_PRIVATE:
if (!(le->f_mode & FMODE_READ))
return -EACCES;
if (le->f_path.mnt->mnt_ags & MNT_NOEXEC) {
if (vm_ags & VM_EXEC)
return -EPERM;
vm_ags &= ~VM_MAYEXEC;
}
if (!le->f_op || !le->f_op->mmap)
return -ENODEV;
break;
default:
return -EINVAL;
}
}
/*匿名映射*/
else {
switch (ags & MAP_TYPE) {
/*共享,对应共享内存*/
case MAP_SHARED:
/*
* Ignore pgo.
*/
pgo = 0;
vm_ags |= VM_SHARED | VM_MAYSHARE;
break;
/*私有*/
case MAP_PRIVATE:
/*
* Set pgo according to addr for anon_vma.
*/
pgo = addr >> PAGE_SHIFT;
break;
default:
return -EINVAL;
}
}
error = security_le_mmap(le, reqprot, prot, ags, addr, 0);
if (error)
return error;
error = ima_le_mmap(le, prot);
if (error)
return error;
/*实际创建 vma*/
return mmap_region(le, addr, len, ags, vm_ags, pgo);
}
这个函数由三部分组成:
1、 找到能否创建符合要求的
vma ,应该在哪里创建?
这部分主要通过函数
get_unmapped_area
实现,我们需要一段虚拟空间,范围是
[addr , addr+len] ,用户进程一般不会指定
addr( 对应
ags
含义标志
MAP_FIXED
的
情 况 ) , 也 就 是 由 内 核 指 定 这 个 虚 拟 空 间 的 首 地 址
addr
在 哪 里 , 在 函 数
剩余15页未读,继续阅读
资源评论
FSak47
- 粉丝: 235
- 资源: 12
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功