写点什么

Android- 面试中常问的 MMAP 到底是啥东东?原理及应用解析

用户头像
Android架构
关注
发布于: 2021 年 11 月 05 日


unsigned longarch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,const unsigned long len, const unsigned long pgoff,const unsigned long flags){struct vm_area_struct *vma;struct mm_struct *mm = current->mm;unsigned long addr = addr0;int do_align = 0;int aliasing = cache_is_vipt_aliasing();struct vm_unmapped_area_info info;


...


addr = vm_unmapped_area(&info);...return addr;}


先找到合适的虚拟内存(用户空间),几经周转后,调用相应文件或者设备驱动中的 mmap 函数,完成该设备文件的 mmap,至于如何处理处理虚拟空间,要看每个文件的自己的操作了。



这里有个很关键的结构体


const struct file_operations *f_op;


它是文件驱动操作的入口,在 open 的时候,完成 file_operations 的绑定,open 流程跟 mmap 类似







先通过 get_unused_fd_flags 获取个未使用的 fd,再通过 do_file_open 完成 file 结构体的创建及初始化,最后通过 fd_install 完成 fd 与 file 的绑定。



重点看下


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


path_openat:


static struct file *path_openat(int dfd, struct filename *pathname,struct nameidata *nd, const struct open_flags *op, int flags){struct file *base = NULL;struct file *file;struct path path;int opened = 0;int error;


file = get_empty_filp();if (IS_ERR(file))return file;


file->f_flags = op->open_flag;


error = path_init(dfd, pathname->name, flags | LOOKUP_PARENT, nd, &base);if (unlikely(error))goto out;


current->total_link_count = 0;error = link_path_walk(pathname->name, nd);if (unlikely(error))goto out;


error = do_last(nd, &path, file, op, &opened, pathname);while (unlikely(error > 0)) { /* trailing symlink */struct path link = path;void *cookie;if (!(nd->flags & LOOKUP_FOLLOW)) {path_put_conditional(&path, nd);path_put(&nd->path);error = -ELOOP;break;}error = may_follow_link(&link, nd);if (unlikely(error))break;nd->flags |= LOOKUP_PARENT;nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);error = follow_link(&link, nd, &cookie);if (unlikely(error))break;error = do_last(nd, &path, file, op, &opened, pathname);put_link(nd, &link, cookie);}out:if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT))path_put(&nd->root);if (base)fput(base);if (!(opened & FILE_OPENED)) {BUG_ON(!error);put_filp(file);}if (unlikely(error)) {if (error == -EOPENSTALE) {if (flags & LOOKUP_RCU)error = -ECHILD;elseerror = -ESTALE;}file = ERR_PTR(error);}return file;}


拿 Binder 设备文件为例子,在注册该设备驱动的时候,对应的 file_operations 已经注册好了,




open 的时候,只需要根根 inode 节点,获取到 file_operations 既可,并且,在 open 成功后,要回调 file_operations 中的 open 函数



open 后,就可以利用 fd 找到 file,之后利用 file 中的 file_operations *f_op 调用相应驱动函数,接着看 mmap。

Binder mmap 的作用及原理(一次拷贝)

Binder 机制中 mmap 的最大特点是一次拷贝即可完成进程间通信。Android 应用在进程启动之初会创建一个单例的 ProcessState 对象,其构造函数执行时会同时完成 binder mmap,为进程分配一块内存,专门用于 Binder 通信,如下。


ProcessState::ProcessState(const char *driver): mDriverName(String8(driver)), mDriverFD(open_driver(driver))...{if (mDriverFD >= 0) {// mmap the binder, providing a chunk of virtual address space to receive transactions.mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);...}}


第一个参数是分配地址,为 0 意味着让系统自动分配,流程跟之前分子类似,先在用户空间找到一块合适的虚拟内存,之后,在内核空间也找到一块合适的虚拟内存,修改两个控件的页表,使得两者映射到同一块物力内存。


Linux 的内存分用户空间跟内核空间,同时页表有也分两类,用户空间页表跟内核空间页表,每个进程有一个用户空间页表,但是系统只有一个内核空间页表。而 Binder mmap 的关键是:也更新用户空间对应的页表的同时也同步映射内核页表,让两个页表都指向同一块地址,这样一来,数据只需要从 A 进程的用户空间,直接拷贝拷贝到 B 所对应的内核空间,而 B 多对应的内核空间在 B 进程的用户空间也有相应的映射,这样就无需从内核拷贝到用户空间了。


binder_update_page_range 完成了内存分配、页表修改等关键操作:


用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android-面试中常问的MMAP到底是啥东东?原理及应用解析