以下是基于Android 7.0,在關(guān)鍵的地方寫了備注野揪》梅蓿看著備注就可以知道在講什么了。
關(guān)于用戶空間斯稳,內(nèi)核空間海铆,用戶空間地址,內(nèi)核地址挣惰,虛存游添,物理內(nèi)存系草,頁等操作系統(tǒng)的基礎(chǔ)知識就不展開了,需要自己事先去學(xué)習(xí)掌握唆涝。
首先要了解比較多的linux的知識了找都。
- struct vm_area_struct:該結(jié)構(gòu)體表示用戶空間的內(nèi)存地址
- struct vm_struct:該結(jié)構(gòu)體表示內(nèi)核空間的內(nèi)存地址
- struct page:表示一個物理頁的映射
- struct vm_struct * get_vm_area(unsigned long size, unsigned long flags):尋找一塊空間內(nèi)存區(qū)域,創(chuàng)建一個vm_struct結(jié)構(gòu)
- void *kmalloc(size_t size, gfp_t flags):申請內(nèi)存廊酣,內(nèi)核調(diào)用
- struct page * alloc_pages(gfp_t gfp_mask, unsigned int order):分配頁并且返回struct page實例
- int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages):完成物理頁到虛擬內(nèi)存的映射
- int vm_insert_page(struct vm_area_struct *, unsigned long addr, struct page *):把分配的物理頁插到用戶vm能耻,可以理解為產(chǎn)生物理頁的映射
//在ProcessState中會調(diào)用mmap(),其中的fd是binder_open()后返回的亡驰,因此后面會根據(jù)binder注冊的file_operation調(diào)用到binder_mmap, 這里的vm_area_struct也是因為mmap()中的參數(shù)設(shè)置為NULL后由操作系統(tǒng)自動完成晓猛。
static int binder_mmap(struct file *filp, struct vm_area_struct *vm){
struct vm_struct *area;
//在binder_open的時候,binder_proc就會保存在private_data中
struct binder_proc *proc = filp->private_data;
struct binder_buffer *buffer;
//映射的內(nèi)存不超過4MB
if((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;
//根據(jù)用戶空間的vm_area_struct大小在內(nèi)核空間也尋找對應(yīng)大小的內(nèi)存vm_struct
area = get_vma_area(vm->vm_end - vma->vm_start, VM_IOREMAP);
//用binder_proc->buffer保存內(nèi)核空間映射的首地址
proc->buffer = area->addr;
//用binder_proc->user_buffer_offset保存用戶空間地址和內(nèi)核空間地址的差凡辱,這樣方便相互之間的轉(zhuǎn)換運算
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
//頁內(nèi)存對齊戒职,為pages分配內(nèi)存大小對應(yīng)的頁數(shù)量的結(jié)構(gòu)體page
proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
//分配并且映射物理內(nèi)存
binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma);
...
}
//在本例中,proc表示當(dāng)前進程binder_proc數(shù)據(jù)結(jié)構(gòu)透乾,allocate為1洪燥,start為對應(yīng)的內(nèi)核空間的首地址,end為首地址后一個page頁的地址乳乌,vma對應(yīng)用戶空間的地址捧韵。
static int binder_update_page_range(struct binder_proc *proc, int allocate, void *start, void *end, struct vm_area_struct *vma){
void *page_addr;
unsigned long user_page_addr;
struct page **page;
struct mm_struct *mm;
//本例只分配一個PAGE
for(page_addr = start; page_addr < end; page_addr += PAGE_SIZE){
struct page **page_array_ptr;
//本次循環(huán)操作的是第N頁, 本例中是0
page = &proc->pages[([page_addr - proc->buffer) / PAGE_SIZE];
//分配實際的物理內(nèi)存
*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEN | __GFP_ZERO);
tmp_area.addr = page_addr;
//這里我也不清楚汉操,為什么是2個PAGE_SIZE
tmp_area.size = PAGE_SIZE + PAGE_SIZE;/* guard page */
page_array_ptr = page;
//映射物理頁到內(nèi)核虛存
map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);
//開始映射物理頁到用戶內(nèi)存
user_page_addr = (uintptr_t)page_addr + proc->user_buffer_offset;
vm_insert_page(vma, user_page_addr, page[0]);
}
}
從上面可以看到再来,Binder利用mmap將用戶空間內(nèi)存和內(nèi)核空間內(nèi)存映射到同一塊物理內(nèi)存,這樣就避免了數(shù)據(jù)在空間空間和內(nèi)核空間的拷貝過程磷瘤,使得用戶空間/內(nèi)核空間在物理頁的數(shù)據(jù)修改在另一方直接可見芒篷。
同時物理頁也不是直接要多少分配多少,而是使用多少分配多少采缚。
當(dāng)然這里也涉及到了內(nèi)存不足時的交換空間针炉,缺頁錯誤等操作系統(tǒng)的知識,因此要提高計算機能力仰担,基礎(chǔ)知識不可謂不重要糊识。