wule
这个其实不一定,你先用一个小的size去完成置1,然后后面申请一个大的size内存,让这个大内存横跨两个page,自然就可以down_write了。后面的这个page就是要分配的,好像和shrink没 ...
shrink的问题再解释下。
新内核(pixel2) binder_update_page_range 里的代码:
for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
if (!page->page_ptr) {
need_mm = true;
break;
}
}
/* Same as mmget_not_zero() in later kernel versions */
if (need_mm && atomic_inc_not_zero(&alloc->vma_vm_mm->mm_users))
mm = alloc->vma_vm_mm;
if (mm) {
down_write(&mm->mmap_sem);
vma = alloc->vma;
}
只要page->page_ptr还存在,就不需要走mm那套流程,而是走lru内存管理机制那套流程。
只有第一次申请页面时,page->page_ptr才是为空,之后这个页就算废弃了,也没有真正释放,当然也不会清空,伪释放流程的代码同样是在binder_update_page_range的末尾:
free_range:
for (page_addr = end - PAGE_SIZE; page_addr >= start;
page_addr -= PAGE_SIZE) {
bool ret;
size_t index;
index = (page_addr - alloc->buffer) / PAGE_SIZE;
page = &alloc->pages[index];
trace_binder_free_lru_start(alloc, index);
ret = list_lru_add(&binder_alloc_lru, &page->lru);
WARN_ON(!ret);
trace_binder_free_lru_end(alloc, index);
continue;
err_vm_insert_page_failed:
unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
err_map_kernel_failed:
__free_page(page->page_ptr);
page->page_ptr = NULL;
err_alloc_page_failed:
err_page_ptr_cleared:
;
}
err_no_vma:
if (mm) {
up_write(&mm->mmap_sem);
mmput(mm);
}
return vma ? -ENOMEM : -ESRCH;
注意那个continue 这导致代码不会走到之后的释放,清空流程。
所以不论是临界page,还是横跨page,只要这个页面被用了一次,就没法真正清空了,下次用就没法走mm信号量流程了。
要清空的话,只有系统判断内存不足或者别的什么情况,这就是shrink了,binder是注册了这个机制的,就是刚才说的那个lru链表,在这里注册的:
void binder_alloc_shrinker_init(void)
{
list_lru_init(&binder_alloc_lru);
register_shrinker(&binder_shrinker);
}