分析总结机制(优选3篇)
- 总结
- 2024-03-11 11:57:27
- 181
分析总结机制 第1篇
当安卓手机开机后,init进程就会去启动Zygote进程,然后Zygote进程就会去启动xxxtemxxxvice进程,而xxxtemxxxvice进程里就会启动它里面的核心服务。
关于这点不清楚的可以看回
xxxtemxxxver进程则是由Zygote进程启动的,因此xxxviceManager与xxxtemxxxver的关系如下:
xxxviceManager它是用c写的,可以看看所在目录:
里面都是关于Binder的c文件,由此也可推测出xxxviceManager进程主要运行Binder。而xxxtemxxxver则用java写,它持有AMS、PMS等这些核心服务类,将它们统一放在xxxtemxxxver里,但是不对外暴露(为了安全性),所以xxxviceManager管理这些服务类类时实则是通过Binder跨进程通信去管理的,这也是为什么要搞多一个xxxviceManager类去管理这些服务类的原因。
我们来看看PMS的main()方法:
可以看到,PMS调用了xxxviceManager的addxxxvice(),而addxxxvice()方法的具体实现就是在xxxviceManagerNative里:
可以看到调用了 mRemote的transact()方法进行Binder通信。到这里为止,应该明白了xxxviceManager为什么也称为服务管理类了吧,它通过Binder去跟xxxtemxxxver进行通信,进而能拥有核心服务类的引用,从而能管理它们。
对于xxxtemxxxver、PMS和AMS如果有什么疑问可以看回:
分析总结机制 第2篇
为什么会有虚拟内存,以前最早的时候,每个应用程序的内存还很小,而物理内存(内存条)够大,所以这时候全部应用都可以运行在物理内存上,而这时CPU也是直接读取物理内存,但随着数据越来越大(图片和视频的发展等),应用大小也变得越来越大,因此物理内存根本不可能跑得动所有应用程序的,几十个应用加起来内存是非常大的,而常规的物理内存大小顶多就16g,根本不够,那这时就诞生了虚拟内存以及程序的局部性原则。
在没有出现虚拟内存的时候,计算机通过CPU直接向内存存取数据,这样效率很快,但也意味着运行的程序不能超过物理内存的大小,这就限制了运行的程序数量了。因此虚拟内存就出现了,使用虚拟寻址,CPU需要将虚拟地址翻译成物理地址,这样才能访问到真实的物理内存。而虚拟内存空间是在硬盘上的一块连续的字节大小的单元组成的数组,每个字节都有一个唯一的虚拟地址,作为到数组的索引。
可以看到CPU中含有一个内存管理单元(Memory Management Unit, MMU)的硬件,它的功能就是将虚拟地址转换为物理地址。MMU需要借助存放在内存中的页表来动态翻译虚拟地址,该页表由操作系统管理。
页表又是什么呢?操作系统通过将虚拟内存分割为大小固定的块来作为硬盘和内存之间的传输单位,这个块被称为虚拟页,而物理内存也会按照这种方法分割为物理页。
CPU在获得虚拟地址之后,就通过MMU将虚拟地址翻译为物理地址。在这个翻译过程中还需要借助页表,页表就是一个存放在物理内存中的数据结构,它记录了虚拟页与物理页的映射关系:
这里的PTE是页表结构的一个有效位,该有效位代表这个虚拟页是否被缓存在物理内存中,物理页号或硬盘地址如果为空值,则代表这个PTE还没有被分配。现在虚拟页VP0、VP4、VP6和VP7被缓存在物理内存中,有效位为1。虚拟页VP2和VP5被分配在页表中,但并没有缓存在物理内存,有效位为1为0。虚拟页VP1和VP3还没有被分配,有效位为0,地址也为空值。
MMU根据虚拟地址在页表中寻址到了PTE4,该PTE的有效位为1,代表该虚拟页已经被缓存在物理内存中了,最终MMU得到了PTE中的物理内存地址,即指向PP 1。
而在这个表映射过程中,还有个机制,它就是能让几十个应用能同时运行在物理内存中的关键,一个应用它的代码都存在磁盘里,当它运行的时候,只有一部分代码在物理内存中运行,其他部分仍在在磁盘里缓存在交换xxxwap里(可以理解为是磁盘里的一个共享区域,当物理内存不够空间了,就会让用户进程代码运行在交换空间里,等到物理内存有空间了,就再从swap里把代码放进物理内存里运行)。
现在硬盘里的apk要运行,由于程序的局部性原则,首先只运行int a = 1这行代码到物理内存里。当CPU要去访问这个a的值时,肯定要先知道它的内存地址是多少才能访问,所以现在页表第一项有效位还是0,地址为NULL。
然后MMU把磁盘的地址也就是虚拟内存地址记录在页表里,有效位还是0,表示虚拟内存被缓存在页表里:
这时候MMU翻译CPU要操作的内存地址,将物理内存跟表里的虚拟地址映射,将有效位为1,当下一次CPU再访问这个内存里的数据,就直接通过MMU直接定位到物理内存里的内存地址上了:
当该页记录的每一项都满了,就会回收旧的内存地址,把那一项记录进行清除,记录新的数据的内存地址和有效位,如此反复,这就是一个应用程序运行时的过程,当有多个应用程序要运行的时候,也是这样通过虚拟内存机制以及程序的局部性原则,加上CPU的切片时间够快,不停切换来操作每个应用的数据,所以使得整个过程,仿佛就是同一时间内多个应用在一起同时运行着。
这里还要记住,每次存取是按一页的倍数来操作,而一页有4k大小,一页里的每行就是记录一个数据的内存地址。然后页表就按照这一张张页来记录地址和有效位。
分析总结机制 第3篇
现在我们已经知道脚本中会去启动一个xxxviceManager进程,而这个进程会去开启binder通信,因此我们能在文件里的main函数看到它调用了binder_open函数:
通过binder_open函数去打开Binder驱动文件,但这里与打开普通文件不一样,因为驱动文件与普通文件的区别在于,驱动文件相当于是一套代码在运行的,这套代码运行在内核空间里,由linux系统去执行驱动binder,也就是说我们对驱动文件的操作会反映到它的驱动代码里。下面我们来分析一下它的驱动代码,文件:
该函数就是binder驱动的逻辑,可以看到这句misc_register(&binder_miscdev)这句代码,把binder_miscdev的地址传到misc_register函数里,misc_register函数是个注册方法,它注册我们对驱动文件进行什么操作时,会触发驱动层去调用对应的哪个方法。binder_miscdev是个结构体:
它这里有个binder_fops结构体,进去看看:
左边一列就是表示对驱动文件进行相关操作时,会去调用对应的函数,看到这行.open = binder_open代码,意思其实是当对驱动文件Binder进行open函数时,对应的驱动就会执行它的binder_open函数,这也是驱动文件独有的特殊性了,现在对它跟普通文件的区别是不是有了更深的看法了。接下来看这个binder_open函数:
代码很长,先来说说刚刚我们分析了当对binder驱动文件进行open的时候,其实就是相当于触发了binder_open函数,那么也就是说xxxviceManager也会调用这个binder_open函数,既然如此,当其他用户进程(服务端)想要成为服务端时,它去实现binder,自然也要open,进而也会触发到这个驱动xxxinder_open函数:
现在再来看这张图,Binder驱动的核心是维护一个binder_proc类型的链表,该链表里面记录了包括xxxviceManager在内的所有Client信息,当Client去请求得到某个xxxvice时,Binder驱动就去binder _proc中查找相应的xxxvice返回给Client,同时增加当前xxxvice的引用个数,你能看到binder_open()函数里这行代码:
每次当有服务端app应用申请打开binder,就会生成一个节点添加到链表里,用来描述当前这个app进程信息的,然后接着binder_open()函数里的代码:
执行了kzalloc函数,我们都知道malloc函数是声明了一块内存空间,它是相对于用户空间来说的,而kzalloc函数则是内核驱动层里开辟内存区域,也就是说此时这个当前app进程(服务端)在驱动层里开辟了一个内存区域。然后接下去执行get_task_struct(current),得到当前进程,设置给binder驱动里对应的proc,这样binder驱动也就相当于拿到服务端进程的引用了。
接着往下看:
设置锁,防止并发问题,在这段锁住的过程中,执行hlist_add_head函数,该函数就是添加节点binder_procs,把当前这个服务端进程的binder_proc添加到链表proc里,下个服务端如果也要开启binder,则再创建个节点,添加到链表里:
现在再看回native层的binder_open方法:
当驱动里完成了对服务进程的引用的获取和添加到链表中后,接下来native层就继续执行mmap函数,根据之前的映射关系:
这时驱动层也就触发了binder_mmap函数:
传进来的参数filp是binder指针,vma是当前的服务端进程的用户空间,接下来又会去定义一个内核空间area,然后之前保存在flip的当前进程信息赋值给proc。接下来就是映射用户空间(服务端)的内存大小,当大小大于4M,最后也只能是只有4M大小。
接下来继续为刚刚声明的内核空间area划分一块内存空间:
大小跟用户空间(服务端进程)一样,此时这个内核空间是虚拟空间,而上面的服务端应用的用户空间也是虚拟空间,它们互相通信的话,肯定需要拷贝的,那这样不就跟以前传统的进程间通信一样了吗,因此接下来就是分别对它们两个空间进行mmap映射,为它们两映射同一块物理内存空间,这样它们两个通信,就直接在这块共享区域操作就可以免去拷贝了,直接映射到binder驱动里。这也就是为什么整个binder机制下进行通信下,内存拷贝只发生一次,除了客户端要把数据拷贝到内核时,而内核不用拷贝数据到服务端里。
所以可以看到接下来这句代码:
调用了kzalloc函数,分配要申请的物理内存大小,大小用页数表示,然后继续看代码:
binder_update_page_range()函数就是让内核空间与进程空间同时映射到物理内存里,来看看它的详情:
可以看到,这里调用了alloc_page函数,这个函数作用就是划分一页page大小的物理内存区域,得到这个物理内存之后,就调用map_vm_area函数对内核空间进行映射到物理内存上,其实原理就是修改页表,让物理空间映射到用户空间(参考上面讲解的页表机制),即此时Binder内核空间已经映射到这页物理内存。
接着就是调用vm_insert_page函数,原理还是是修改页表,让物理空间映射到用户空间。即此时服务端应用空间已经映射到这页物理内存。以上完整的代码获取可以点这里:
本文由admin于2024-03-11发表在叁佰资料网,如有疑问,请联系我们。
本文链接:http://www.sanbaiyy.com/p/16619.html
上一篇
每周课堂总结(合集23篇)
下一篇
证券总结师(共5篇)