在Android系统中,通过如下两个ioctl操作实现Ashmem的锁定和解锁操作。
ASHMEM_PIN
ASHMEM_UNPIN
ASHMEM_PIN和ASHMEM_UNPIN在文件kernel/common/include/linux/ashmem.h中定义,对应代码如下。
再看函数ashmem_ioctl,在其实现代码中和ASHMEM_PIN和ASHMEM_UNPIN这两个操作相关的代码如下。
在上述代码中,调用函数ashmem_pin_unpin处理控制命令ASHMEM_PIN和ASHMEM_UNPIN。函数ashmem_pin_unpin的实现流程如下。
获取传递到用户空间的参数,并将获取值保存在本地变量pin中。这是一个ashmem_pin类型的结构体类型,在里面包括了要pin/unpin的内存块的起始地址和大小。
因为起始地址和大小的单位都是字节,所以通过转换,处理为以页面为单位,并保存在本地变量pgstart和pgend中。
不但对参数进行安全性检查,并且确保只要从用户空间传进来的内块的大小值为0,就认为是要pin/unpin整个Ashmem。
判断当前要执行操作的类别,根据ASHMEM_PIN操作和ASHMEM_UNPIN操作分别执行ashmem_pin和ashmem_unpin。
当创建Ashmem时,所有默认的内存都是pinned状态的,只有用户告诉Ashmem驱动程序要unpin某一块内存时,Ashmem驱动程序才会把这块内存unpin。
用户告知Ashmem驱动程序重新pin某一块前面被unpin过的内块,这样能够将此内存从unpinned(被释放)状态改转换pinned(被绑定)状态。
函数ashmem_pin_unpin在文件kernel/goldfish/ashmem.c中定义,具体的实现代码如下。
由此可见,执行ASHMEM_PIN操作的目标对象必须是一块处于Ashmem状态的内存块。
函数ashmem_unpin的功能是对某一块Ashmem进行unpin操作,具体处理流程如下。
在遍历asma->unpinned_list列表时,查找当前处于unpinned状态的内存块是否与将要unpin的内存块[pgstart,pgend]是否相交,如果相交则通过执行合并操作调整pgstart和pgend的大小。
调用函数range_del删除原来的已经被unpinned过的内存块。
调用函数range_alloc重新unpinned调整过后的内存块[pgstart,pgend],此时新的内存块[pgstart,pgend]已经包含了刚才所有被删掉的unpinned状态的内存。
如果找到相交的内存块,并且调整了pgstart和pgend的大小之后,需要重新扫描asma->unpinned_list列表。原因是新的内存块[pgstart,pgend]可能与前后的处于unpinned状态的内存块发生相交。(www.daowen.com)
函数ashmem_unpin在文件kernel/goldfish/ashmem.c中定义,具体的实现代码如下。
range_before_page的操作是一个宏定义,功能是判断range描述的内存块是否在page页面之前,如果是则表示结束整个描述。asma->unpinned_list列表是按照页面号从大到小进行排列的,并且每一块被unpin的内存都是不相交的。range_before_pag的定义代码如下。
page_range_subsumed_by_range的操作也是一个宏定义,功能是判断内存块是不是包含了[start,end]这个内存块,如果包含则说明当前要unpin的内存块已经处于unpinned状态。如果什么也不用操作则直接返回。page_range_subsumed_by_range的定义代码如下。
page_range_in_range的操作也是一个宏定义,功能是判断内存块[start,end]是否互相包或者相交。page_range_in_range的定义代码如下。
page_range_subsumed_by_range的操作也是一个宏定义,功能是判断内存块range是否包含内存块[start,end]。page_range_subsumed_by_range的定义代码如下。
range_in_range的操作也是一个宏定义,功能是判断内存块地址page是否包含在内存块range中。range_in_range的定义代码如下。
再看函数range_del,功能是从asma->unpinned_list中删掉内存块,并判断它是否在lru列表中。函数range_del的具体实现代码如下。
再看函数lru_del,内存块的状态purged值为ASHMEM_NOT_PURGED,表示现在没有收回对应的物理页面,那么内存块就位于lru列表中,则使用函数lru_del删除这个内存块。函数lru_del的具体实现代码如下。
再看在函数ashmem_unpin中调用的range_alloc函数,其功能是从slab缓冲区ashmem_range_cachep中分配一个ashmem_range,并进行相应的初始化处理。然后放在对应的列表ashmem_area->unpinned_list中,并判断这个range的purged是否处于ASHMEM_NOT_PURGED状态,如果是则要把它放在lru列表中。函数range_alloc在文件kernel/goldfish/ashmem.c中实现,具体的实现代码如下。
再看函数lru_add,功能是将未被回收的unpinned内存块添加到全局列表ashmem_lru_list中。函数lru_add在文件kernel/goldfish/ashmem.c中实现,具体的实现代码如下。
再看函数ashmem_pin,功能是pin一块Ashmem区域。被pin的内存块肯定被保存在unpinned_list列表中,如果不在则什么都不用做。要想判断在unpinned_list列表中是否存在pin的内存块,需要通过遍历asma->unpinned_list列表的方式找出与之相交的内存块。函数ashmem_pin在文件kernel/goldfish/ashmem.c中实现,具体的实现代码如下。
在上述代码中对重新锁定内存块操作实现了判断,通过if语句处理了如下的四种情形。
指定要锁定的内存块[start,end]包含了解锁状态的内存块range,此时只要将解锁状态的内存块range从其宿主Ashmem的解锁内存块列表unpinned_list中删除即可。
合并要锁定内存块[pgstart,pgend]后部分和解锁状态内存块range的前半部分,此时将解锁状态内存块range的开始地址设置为要锁定内存块的末尾地址的下一个页面地址。
合并要锁定内存块[pgstart,pgend]前部分和解锁状态内存块range的后半部分,此时将解锁状态内存块range的末尾地址设置为要锁定内存块的开始地址的下一个页面地址。
设置要锁定内存块[pgstart,pgend]包含在解锁状态内存块range中。
再看函数range_shrink,功能是设置range描述的内存块的起始页面号,如果还存在于lru列表中,则需要调整在lru列表中的总页面数大小。函数range_shrink在文件kernel/goldfish/ashmem.c中定义,具体的实现代码如下。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。