linux内核之情景分析mmap操作
进程可以通过mmap把一个已打开文件映射到用户空间.
mmap(void*start,size_t length,int prot,int flags,int fd,off_t offset)
asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,unsigned long prot, unsigned long flags,unsigned long fd, unsigned long pgoff){return do_mmap2(addr, len, prot, flags, fd, pgoff);}
/* common code for old and new mmaps */static inline long do_mmap2(unsigned long addr, unsigned long len,unsigned long prot, unsigned long flags,unsigned long fd, unsigned long pgoff){int error = -EBADF;struct file * file = NULL;flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);if (!(flags & MAP_ANONYMOUS)) {//map_anonymous表示没有文件,只是在指定位置分配内存file = fget(fd);//上一条表示,没有文件,就跳过if以下,有文件则打开文件if (!file)//如果文件不存在,直接返回错误goto out;}down(¤t->mm->mmap_sem);//信号量down操作error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);//mmap主体操作还是这个up(¤t->mm->mmap_sem);//信号量up操作if (file)fput(file);out:return error;}
do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned long len,unsigned long prot, unsigned long flags, unsigned long pgoff){struct mm_struct * mm = current->mm;//获取当前进程的内存描述符struct vm_area_struct * vma;int correct_wcount = 0;int error;//file非0表示是文件,其对应一定有相关操作函数.if (file && (!file->f_op || !file->f_op->mmap))return -ENODEV;//长度对齐,如果为0,直接返回if ((len = PAGE_ALIGN(len)) == 0)return addr;//长度大于3g或者addr+len映射区域超过用户空间,返回错误if (len > TASK_SIZE || addr > TASK_SIZE-len)return -EINVAL;// 偏移量是否超过了长度/* offset overflow? */if ((pgoff + (len >> PAGE_SHIFT)) < pgoff)return -EINVAL;//映射次数是否超过了限定/* Too many mappings? */if (mm->map_count > MAX_MAP_COUNT)return -ENOMEM;//是否加锁?这里不知道了/* mlock MCL_FUTURE? */if (mm->def_flags & VM_LOCKED) {unsigned long locked = mm->locked_vm << PAGE_SHIFT;locked += len;if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)return -EAGAIN;}/* 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 & FMODE_WRITE))return -EACCES;//确保我们不被允许写在一个只可追加的文件/* Make sure we don't allow writing to an append-only file.. */if (IS_APPEND(file->f_dentry->d_inode) && (file->f_mode & FMODE_WRITE))return -EACCES;//确保我们的文件没有锁/* make sure there are no mandatory locks on the file. */if (locks_verify_locked(file->f_dentry->d_inode))return -EAGAIN;/* fall through */case MAP_PRIVATE://私有映射if (!(file->f_mode & FMODE_READ))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) {//如果参数flag的标志位map_fixed为0表示,指定映射位置只是一个参考值if (addr & ~PAGE_MASK)return -EINVAL;} else {//不满足由内核从空洞执行分配一个区域addr = get_unmapped_area(addr, len);if (!addr)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.*/vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);//从slab获取一个vma结构if (!vma)return -ENOMEM;vma->vm_mm = mm;//指向内存描述符vma->vm_start = addr;//vma的起始地址指向映射的起始地址vma->vm_end = addr + len;//同上vma->vm_flags = vm_flags(prot,flags) | mm->def_flags;//设置vma属性if (file) {//如果file为0,表示匿名映射,仅仅是为了创建虚拟区间,或者仅在于建立从物理空间到虚存空间映射,而非文件映射VM_ClearReadHint(vma);//以下代码设置一堆属性vma->vm_raend = 0;if (file->f_mode & FMODE_READ)vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;if (flags & MAP_SHARED) {vma->vm_flags |= VM_SHARED | VM_MAYSHARE;/* This looks strange, but when we don't have the file open* for writing, we can demote the shared mapping to a simpler* private mapping. That also takes care of a security hole* with ptrace() writing to a shared mapping without write* permissions.** We leave the VM_MAYSHARE bit on, just to get correct output* from /proc/xxx/maps..*/if (!(file->f_mode & FMODE_WRITE))vma->vm_flags &= ~(VM_MAYWRITE | VM_SHARED);}} else {vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;if (flags & MAP_SHARED)vma->vm_flags |= VM_SHARED | VM_MAYSHARE;}vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f];vma->vm_ops = NULL;vma->vm_pgoff = pgoff;//表示所映射内容在文件的起点,此值用于发生缺页异常根据虚存地址计算出相应页面的文件位置vma->vm_file = NULL;vma->vm_private_data = NULL;/* Clear old maps */error = -ENOMEM;if (do_munmap(mm, addr, len))//检查目的地址的vma空间是否已经使用(如果map_fixed设置为1的话)goto free_vma;//已经使用则释放free_vma//检查是否超过了限制/* Check against address space limit. */if ((mm->total_vm << PAGE_SHIFT) + len> current->rlim[RLIMIT_AS].rlim_cur)goto free_vma;//检查当前进程专用的可写区间而物理页面不足的情况/* Private writable mapping? Check memory availability.. */if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE &&!(flags & MAP_NORESERVE) &&!vm_enough_memory(len >> PAGE_SHIFT))goto free_vma;if (file) {//vm_deanwrite职位表示从文件到vma映射,表示不允许同过常规方式读写文件if (vma->vm_flags & VM_DENYWRITE) {error = deny_write_access(file);if (error)goto free_vma;correct_wcount = 1;}vma->vm_file = file;get_file(file);//递增file结构的共享计数error = file->f_op->mmap(file, vma);//一个文件操作必须存在mmap,否则释放vmaif (error)goto unmap_and_free_vma;} else if (flags & MAP_SHARED) {//共享映射error = shmem_zero_setup(vma);if (error)goto free_vma;}/* Can addr have changed??*为了防止flags与addr有变化,再重新设置一遍,* Answer: Yes, several device drivers can do it in their* f_op->mmap method. -DaveM*/flags = vma->vm_flags;addr = vma->vm_start;insert_vm_struct(mm, vma);//插入当前进程的内存描述符if (correct_wcount)atomic_inc(&file->f_dentry->d_inode->i_writecount);mm->total_vm += len >> PAGE_SHIFT;//映射区域+len>>page_shitif (flags & VM_LOCKED) {//需要加锁mm->locked_vm += len >> PAGE_SHIFT;make_pages_present(addr, addr + len);//建立初始映射}return addr;unmap_and_free_vma:if (correct_wcount)atomic_inc(&file->f_dentry->d_inode->i_writecount);vma->vm_file = NULL;fput(file);/* Undo any partial mapping done by a device driver. */flush_cache_range(mm, vma->vm_start, vma->vm_end);zap_page_range(mm, vma->vm_start, vma->vm_end - vma->vm_start);flush_tlb_range(mm, vma->vm_start, vma->vm_end);free_vma:kmem_cache_free(vm_area_cachep, vma);return error;}
linux内核之情景分析mmap操作的更多相关文章
- Linux内核源代码情景分析系列
http://blog.sina.com.cn/s/blog_6b94d5680101vfqv.html Linux内核源代码情景分析---第五章 文件系统 5.1 概述 构成一个操作系统最重要的就 ...
- Linux内核源代码情景分析-fork()
父进程fork子进程: child = fork() fork经过系统调用.来到了sys_fork.具体过程请參考Linux内核源码情景分析-系统调用. asmlinkage int sys_fork ...
- linux 内核源代码情景分析——linux 内核源代码中的C语言代码
linux 内核的主体是以GNU的C语言编写的,GNU为此提供了编译工具gcc.GNU对C语言本身作了不少扩充. 1) gcc 从 C++ 语言中吸收了"inline"和" ...
- linux 内核源代码情景分析——地址映射的全过程
linux 内核采用页式存储管理.虚拟地址空间划分成固定大小的"页面",由MMU在运行时将虚拟地址映射成某个物理内存页面中的地址.页式内存管理比段式内存管理有很多好处,但是由于In ...
- linux 内核源代码情景分析——linux 内存管理的基本框架
386 CPU中的页式存管的基本思路是:通过页面目录和页面表分两个层次实现从线性地址到物理地址的映射.这种映射模式在大多数情况下可以节省页面表所占用的空间.因为大多数进程不会用到整个虚存空间,在虚存空 ...
- linux 内核源代码情景分析——用户堆栈的扩展
上一节中,我们浏览了一次因越界访问而造成映射失败从而引起进程流产的过程,不过有时候,越界访问时正常的.现在我们就来看看当用户堆栈过小,但是因越界访问而"因祸得福"得以伸展的情景. ...
- linux 内核源代码情景分析——越界访问
页式存储管理机制通过页面目录和页面表将每个线性地址转换成物理地址,当遇到下面几种情况就会使CPU产生一次缺页中断,从而执行预定的页面异常处理程序: ① 相应的页面目录或页表项为空,也就是该线性地址与物 ...
- linux 内核源代码情景分析——几个重要的数据结构和函数
页面目录PGD.中间目录PMD和页面表PT分别是由表项pgd_t.pmd_t和pte_t构成的数组,而这些表项都是数据结构 1 /* 2 * These are used to make use of ...
- linux 内核源代码情景分析——linux 内核源码中的汇编语言代码
1. 用汇编语言编写部分核心代码的原因: ① 操作系统内核中的底层程序直接与硬件打交道,需要用到一些专用的指令,而这些指令在C语言中并无对应的语言成分: ② CPU中的一些特殊指令也没有对应的C语言成 ...
随机推荐
- ATM-interface-bank
from lib import commonfrom db import db_handler user_logger = common.get_logger('bank') def check_ba ...
- 【css】cursor鼠标指针光标样式知识整理
在前端开发中,我们经常需要对对象鼠标指针光标进行控制,比如鼠标经过超链接时变成手指形状.在这里整理一下cursor鼠标指针光标样式的知识,记录与方便以后查找. 1.常用cursor光标 div( cu ...
- DeepFaceLab报错,OOM如何解决?
DeepFaceLab出错,虽然错误提示好几个屏幕,但是无非两种情况,一种是驱动没装好,一种是显存配置不够.上一篇文章说了驱动的问题,这一篇就说说配置不够的问题. 这个问题的表现形式,往往是各种OOM ...
- Birthday Paradox
Birthday Paradox Sometimes some mathematical results are hard to believe. One of the common problems ...
- HDU4616 树形DP+三次深搜
这题和之前那个HDU2616有着奇妙的异曲同工之处..都是要求某个点能够到达的最大权重的地方... 但是,这题加了个限制,要求最多只能够踩到C个陷阱,一单无路可走或者命用光了,就地开始清算总共得分之和 ...
- 菜鸟学Linux - Linux文件属性
在Linux中,文件的属性是一个很重要的概念,用户或者用户组对一个文件所拥有的权限,都可以从文件的属性得知. 我们可以通过ls -al命令,列出某个文件夹下面的所有文件(包括以.开头的隐藏文件).下面 ...
- day40--mysql step4 SQLAlchemy
1.unique = True 表示启动唯一索 2.有add 必须有commit这样数据才会提交 3.ORM功能 #!/usr/bin/env python # -*- coding:utf-8 -* ...
- 图说不为人知的IT传奇故事-2-IBM咬不动的胡桃
此系列文章为“图说不为人知的IT传奇故事”,各位大忙人可以在一分钟甚至几秒内了解把握整个内容,真可谓“大忙人的福利”呀!!希望各位IT界的朋友在钻研技术的同时,也能在文学.历史上有所把握.了解这些故事 ...
- 【LeetCode】Remove Duplicates from Sorted List(删除排序链表中的重复元素)
这道题是LeetCode里的第83道题. 题目描述: 给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次. 示例 1: 输入: 1->1->2 输出: 1->2 示例 2: ...
- Leetcode 546.移除盒子
移除盒子 给出一些不同颜色的盒子,盒子的颜色由数字表示,即不同的数字表示不同的颜色.你将经过若干轮操作去去掉盒子,直到所有的盒子都去掉为止.每一轮你可以移除具有相同颜色的连续 k 个盒子(k > ...