binder驅(qū)動-------之內(nèi)存映射篇

轉(zhuǎn)自 https://blog.csdn.net/xiaojsj111/article/details/31422175

1:binder內(nèi)存管理概述

binder一次跨進程通訊,只需要一次拷貝(原因后面會解析)嚼吞,而一般的像socket通訊則需要兩次拷貝盒件;參與binder通訊的進程,無論是client還是服務(wù)器端舱禽,他們都會通過調(diào)用ProcessState::self()函數(shù)來建立自己的初步映射炒刁,為什么說是初步影射呢,因為binder_mmap為我們分配了指定長度的虛擬地址誊稚,但卻只是建立一個物理頁的影射翔始,其他的虛擬地址都還暫未建立虛擬到物理的影射罗心。下面我們的講述從ProcessState::self()函數(shù)開始,該函數(shù)使用典型的單列模式:如已創(chuàng)建該實例則直接返回城瞎,如果沒有創(chuàng)建渤闷,則創(chuàng)建返回這個實例,這里需要鎖來防止創(chuàng)建兩個同類型的實例脖镀,該函數(shù)還是static類型的飒箭,所以可以在系統(tǒng)的任何地方調(diào)用。

sp<ProcessState> ProcessState::self()

{

? ? Mutex::Autolock _l(gProcessMutex);

? ? if (gProcess != NULL) {

? ? ? ? return gProcess;

? ? }

? ? gProcess = new ProcessState;

? ? return gProcess;

}

展開構(gòu)建函數(shù):ProcessState蜒灰,如下:

ProcessState::ProcessState()

? ? : mDriverFD(open_driver())//打開/dev/binder設(shè)備驅(qū)動

? ? , mVMStart(MAP_FAILED)

? ? , mManagesContexts(false)

? ? , mBinderContextCheckFunc(NULL)

? ? , mBinderContextUserData(NULL)

? ? , mThreadPoolStarted(false)

? ? , mThreadPoolSeq(1)

{

? ? if (mDriverFD >= 0) {

? ? ? ? // XXX Ideally, there should be a specific define for whether we

? ? ? ? // have mmap (or whether we could possibly have the kernel module

? ? ? ? // availabla).

#if !defined(HAVE_WIN32_IPC)

? ? ? ? // 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);//建立影射补憾,直接調(diào)用到驅(qū)動的binder_mmap函數(shù)

? ? ? ? if (mVMStart == MAP_FAILED) {

? ? ? ? ? ? // *sigh*

? ? ? ? ? ? ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");

? ? ? ? ? ? close(mDriverFD);

? ? ? ? ? ? mDriverFD = -1;

? ? ? ? }

#else

? ? ? ? mDriverFD = -1;

#endif

? ? }

? ? LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.? Terminating.");

}

以上函數(shù):打開了/dev/binder設(shè)備節(jié)點,并且建立了初步的映射卷员,binder driver中的mmap函數(shù)具體分析見下一節(jié)。

先大概說下腾务,整個函數(shù)執(zhí)行完后毕骡,都做了些什么事情:

為進程的用戶和內(nèi)核空間分配了一段虛擬地址,這段虛擬地址將用于binder內(nèi)存的訪問岩瘦。binder的物理內(nèi)存頁由binder驅(qū)動負責分配未巫,然后這些物理頁的訪問,將分為進程的用戶空間和進程內(nèi)核空間启昧。由于進程的用戶空間和內(nèi)核空間的虛擬地址范圍是不一樣的叙凡,所以這里分配的一段虛擬地址將包括進程的用戶空間地址和內(nèi)核空間地址。

并且在映射建立初始階段密末,只是映射了一個物理頁握爷,而不是為整個虛擬地址空間都建立相應(yīng)的物理影射,目的是充分高效的利用內(nèi)存严里,只是到需要用到的時候才建立映射新啼,用完后馬上釋放映射,這樣可以充分高效的利用系統(tǒng)的物理內(nèi)存頁刹碾。

內(nèi)核剛開始只是分配了一個物理頁燥撞,并且分別將這個物理頁映射到進程的內(nèi)核虛擬地址空間V1(修改內(nèi)核空間的頁表映射)和進程的用戶虛擬地址空間V2(修改用戶空間的頁表映射)。在用戶空間訪問V1和在內(nèi)核空間訪問V2迷帜,其實都是訪問的是同一個物理內(nèi)存塊物舒,從而實現(xiàn)進程的內(nèi)核和用戶空間共享同一塊物理內(nèi)存的目的。這樣binder驅(qū)動在內(nèi)核空間戏锹,將一段數(shù)據(jù)拷貝到這個物理頁冠胯,則該進程的用戶空間則不需要copy_to_user()即可以同步看到內(nèi)核空間的修改,并能夠訪問這段物理內(nèi)存景用。

每個進程都有屬于自己的一塊binder內(nèi)存涵叮,這塊內(nèi)存的大小就是在進程調(diào)用mmap系統(tǒng)調(diào)用時分配的惭蹂,binder_proc->buffer指向這塊內(nèi)存在內(nèi)核空間的首地址,這塊內(nèi)存塊在進程的用戶空間的首地址為:ProcessState::mVMStart,他們的差值存儲在binder_proc->user_buffer_offset變量中割粮,這樣在已知內(nèi)核地址情況下盾碗,在用戶空間只需要加上這個user_buffer_offset偏移值,即可以得到在用戶空間訪問的地址舀瓢,從而達到訪問同一物理內(nèi)存地址的目的廷雅。

每個進程的binder內(nèi)存,組織在以下三個列表中:

struct list_head buffers;//這個列表連接所有的內(nèi)存塊京髓,以地址的大小為順序航缀,各內(nèi)存塊首尾相連

struct rb_root free_buffers;//連接所有的已建立映射的虛擬內(nèi)存塊,以內(nèi)存的大小為index組織在以該節(jié)點為根的紅黑樹下

struct rb_root allocated_buffers;//連接所有已經(jīng)分配的虛擬內(nèi)存塊堰怨,以內(nèi)存塊的開始地址為index組織在以該節(jié)點為根的紅黑樹下

如果在進程的binder事物傳遞過程中灿巧,需要為target 進程分配內(nèi)存揽涮,則調(diào)用binder_alloc_buf(struct binder_proc *proc,?size_t data_size,

size_t offsets_size, int is_async)函數(shù)為target進程分配需要的內(nèi)存抠藕。

binder_alloc_buf函數(shù)首先從target進程的binder_proc->free_buffers樹中查找size(=data_size+offsets_size)大小的內(nèi)存塊,如果能夠精確匹配到蒋困,則返回該內(nèi)存塊盾似,并將該內(nèi)存從binder_proc->free_buffers樹中刪除,并將它插入到binder_proc->allocated_buffers樹中

如果不能找到精確的匹配的大小塊雪标,則從binder_proc->free_buffers樹中查找最接近size汰聋,但又大于size的內(nèi)存门粪,找到后返回該內(nèi)存塊,并將該內(nèi)存從binder_proc->free_buffers樹中刪除烹困,并將它插入到binder_proc->allocated_buffers樹中

如果在binder_proc->free_buffers樹中找不任何一塊比size大的空閑內(nèi)存塊拟蜻,則通過binder_update_page_range函數(shù)分配更多地物理內(nèi)存,并建立相應(yīng)的虛擬到物理地址的映射枯饿,由于映射的最小單元是一個物理頁酝锅,如果所需的size又比一個page size小,則將這塊內(nèi)存分為兩部分奢方,前一部分就是大小為size大小的內(nèi)存塊搔扁,返回該該內(nèi)存塊爸舒,并將它插入到binder_proc->allocated_buffers樹中,而剩余的內(nèi)存塊則直接插入到binder_proc->free_buffers樹中

binder_free_buf函數(shù):(to be continued)

2:binder的mmap

用戶空間的ProcessState函數(shù)中的mmap系統(tǒng)調(diào)用會調(diào)用到binder驅(qū)動中的binder_mmap函數(shù)稿蹲。先看下系統(tǒng)調(diào)用mmap的原型:

void *mmap(void *addr, size_t length int " prot ", int " flags ,?int fd, off_t offset);

mmap() creates a new mapping in the virtual address space of the calling process. The starting address for the new mapping is specified inaddr. Thelengthargument specifies the length of the mapping.Ifaddris NULL, then the kernel chooses the address at which to create the mapping; this is the most portable method of creating a new mapping. Ifaddris not NULL, then the kernel takes it as a hint about where to place the mapping; on Linux, the mapping will be created at a nearby page boundary. The address of the new mapping is returned as the result of the call.

一般在調(diào)用的時候addr都是給的null值扭勉,即由內(nèi)核來分配調(diào)用進程的此次映射塊的開始地址(該地址是調(diào)用進程的用戶空間的虛擬地址),并將開始該地址作為mmap系統(tǒng)調(diào)用的返回值苛聘,而參數(shù)length則指定要映射的地址的長度涂炎,flags參數(shù)指定被映射空間的訪問屬性,此處設(shè)置為PROT_READ屬性设哗,就是說用戶空間的進程對內(nèi)核分配的這個物理內(nèi)存的內(nèi)容只有讀的權(quán)限唱捣,沒有修改的權(quán)限。

我們看servicemanager進程的maps圖



可以看到mmap的長度是128k网梢。我們通過如下代碼:frameworks/base/cmds/servicemanager/service_manager.c


int main(int argc, char **argv)

{

? ? struct binder_state *bs;

? ? void *svcmgr = BINDER_SERVICE_MANAGER;

? ? bs = binder_open(128*1024);

? ? if (binder_become_context_manager(bs)) {

? ? ? ? ALOGE("cannot become context manager (%s)\n", strerror(errno));

? ? ? ? return -1;

? ? }

? ? svcmgr_handle = svcmgr;

? ? binder_loop(bs, svcmgr_handler);

? ? return 0;

}

可以看到他mmap系統(tǒng)調(diào)用規(guī)定的地址長度就是128K

而我們看藍牙apk的進程中的maps:

他映射的虛擬地址長度為:0xFE000震缭,即為frameworks/native/libs/binder/processState.cpp中的#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))的大小。從上面兩個列子我們知道战虏,進程所屬的binder內(nèi)存蛀序,也就是系統(tǒng)調(diào)用mmap所分配的地址空間,并可以通過cat proc/pid/maps命令查看到活烙。

我們再看/drivers/staging/android/binder.c驅(qū)動中static int binder_mmap(struct file *filp, struct vm_area_struct *vma)函數(shù)的含義:

vma->vm_start和vma->vm_end即為此次映射內(nèi)核為我們分配的開始地址和結(jié)束地址,他們差值就是系統(tǒng)調(diào)用mmap中的length的值遣鼓。而vma->vm_start的則是系統(tǒng)調(diào)用mmap調(diào)用的返回值啸盏。需要注意的是vma->vm_start和vma->vm_end都是調(diào)用進程的用戶空間的虛擬地址,他們地址范圍可以通過如下命令:cat /proc/pid/maps | grep "/dev/binder"看到骑祟,如上面兩個圖回懦,針對bluetooth.apk進程,他們vma->vm_start和vma->vm_end的值分別為:0x5c525000和0x5c623000

進程在調(diào)用mmap系統(tǒng)調(diào)用時次企,就會調(diào)用到binder驅(qū)動對應(yīng)的file_operations->mmap()成員函數(shù)怯晕,即binder_mmap函數(shù)。

static int binder_mmap(struct file *filp, struct vm_area_struct *vma)

{

int ret;

struct vm_struct *area;

struct binder_proc *proc = filp->private_data;

const char *failure_string;

struct binder_buffer *buffer;//內(nèi)核進入這個函數(shù)時缸棵,就已經(jīng)預(yù)先為此次映射分配好了調(diào)用進程在用戶空間的虛擬地址范圍(vma->vm_start舟茶,vma->vm_end)

? if ((vma->vm_end - vma->vm_start) > SZ_4M)

vma->vm_end = vma->vm_start + SZ_4M;

binder_debug(BINDER_DEBUG_OPEN_CLOSE,

? ? "binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",

? ? proc->pid, vma->vm_start, vma->vm_end,

? ? (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,

? ? (unsigned long)pgprot_val(vma->vm_page_prot));

if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {

ret = -EPERM;

failure_string = "bad vm_flags";

goto err_bad_arg;

}

vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;

mutex_lock(&binder_mmap_lock);

if (proc->buffer) {

ret = -EBUSY;

failure_string = "already mapped";

goto err_already_mapped;

}

? ? ? ? //為進程所在的內(nèi)核空間申請與用戶空間同樣長度的虛擬地址空間,這段空間用于內(nèi)核來訪問和管理binder內(nèi)存區(qū)域

area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);

if (area == NULL) {

ret = -ENOMEM;

failure_string = "get_vm_area";

goto err_get_vm_area_failed;

}//對應(yīng)內(nèi)核虛擬地址的開始堵第,即為binder內(nèi)存的開始地址

proc->buffer = area->addr;

proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;//同一塊物理內(nèi)存吧凉,內(nèi)核的虛擬地址同應(yīng)用空間的虛擬地址的差

mutex_unlock(&binder_mmap_lock);

#ifdef CONFIG_CPU_CACHE_VIPT

if (cache_is_vipt_aliasing()) {

while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {

printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);

vma->vm_start += PAGE_SIZE;

}

}

#endif//用于存放內(nèi)核分配的物理頁的頁描述指針:struct page,每個物理頁對應(yīng)這樣一個struct page結(jié)構(gòu)

proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);

if (proc->pages == NULL) {

ret = -ENOMEM;

failure_string = "alloc page array";

goto err_alloc_pages_failed;

}

proc->buffer_size = vma->vm_end - vma->vm_start;//映射的長度即為binder內(nèi)存的大小

vma->vm_ops = &binder_vm_ops;//設(shè)定

vma->vm_private_data = proc;//為binder內(nèi)存的最開始的一個頁的地址建立虛擬到物理頁的映射踏志,其他的虛擬地址都還沒有建立相應(yīng)的映射阀捅,沒有映射也就還不能夠被訪問

if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {

ret = -ENOMEM;

failure_string = "alloc small buf";

goto err_alloc_small_buf_failed;

}

buffer = proc->buffer;

INIT_LIST_HEAD(&proc->buffers);//proc->buffers用來連接所有已建立映射的binder內(nèi)存塊

list_add(&buffer->entry, &proc->buffers);

buffer->free = 1;

binder_insert_free_buffer(proc, buffer);//將剛分配的一個page大小的binder內(nèi)存塊插入到proc->free_buffers紅黑樹中

proc->free_async_space = proc->buffer_size / 2;

barrier();

proc->files = get_files_struct(proc->tsk);

proc->vma = vma;

proc->vma_vm_mm = vma->vm_mm;

/*printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p\n",

proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/

return 0;

err_alloc_small_buf_failed:

kfree(proc->pages);

proc->pages = NULL;

err_alloc_pages_failed:

mutex_lock(&binder_mmap_lock);

vfree(proc->buffer);

proc->buffer = NULL;

err_get_vm_area_failed:

err_already_mapped:

mutex_unlock(&binder_mmap_lock);

err_bad_arg:

printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n",

? ? ? proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);

return ret;

}

上面binder_update_page_range函數(shù)就是為進程的內(nèi)核空間和進程的用戶空間針對同一塊物理內(nèi)存建立映射,這樣進程的用戶空間和內(nèi)核空間就可以共享該物理內(nèi)存了针余。

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 vm_struct tmp_area;

struct page **page;

struct mm_struct *mm;

binder_debug(BINDER_DEBUG_BUFFER_ALLOC,

? ? "binder: %d: %s pages %p-%p\n", proc->pid,

? ? allocate ? "allocate" : "free", start, end);

if (end <= start)//表示已經(jīng)建立過映射饲鄙,不需再映射

return 0;

trace_binder_update_page_range(proc, allocate, start, end);

if (vma)

mm = NULL;

else

mm = get_task_mm(proc->tsk);

if (mm) {

down_write(&mm->mmap_sem);

vma = proc->vma;

if (vma && mm != proc->vma_vm_mm) {

pr_err("binder: %d: vma mm and task mm mismatch\n",

proc->pid);

vma = NULL;

}

}

if (allocate == 0)//表示拆除已經(jīng)建立的映射

goto free_range;

if (vma == NULL) {

printk(KERN_ERR "binder: %d: binder_alloc_buf failed to "

? ? ? "map pages in userspace, no vma\n", proc->pid);

goto err_no_vma;

}

for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {

int ret;

struct page **page_array_ptr;

page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];

BUG_ON(*page);

*page = alloc_page(GFP_KERNEL | __GFP_ZERO);//分配一個物理頁凄诞,并將該物理頁的struct page指針值存放在proc->pages二維數(shù)組中

if (*page == NULL) {

printk(KERN_ERR "binder: %d: binder_alloc_buf failed "

? ? ? "for page at %p\n", proc->pid, page_addr);

goto err_alloc_page_failed;

}

tmp_area.addr = page_addr;//映射對應(yīng)的內(nèi)核虛擬地址

tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;//映射對應(yīng)的大小

page_array_ptr = page;//該物理頁對應(yīng)的struct page結(jié)構(gòu)體,用這個結(jié)構(gòu)體可以完全的描述這個物理頁

ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);//為內(nèi)核的這段虛擬地址建立虛擬到物理頁的映射

if (ret) {

printk(KERN_ERR "binder: %d: binder_alloc_buf failed "

? ? ? "to map page at %p in kernel\n",

? ? ? proc->pid, page_addr);

goto err_map_kernel_failed;

}

user_page_addr =

(uintptr_t)page_addr + proc->user_buffer_offset;//由內(nèi)核的虛擬地址得到用戶空間的虛擬地址

ret = vm_insert_page(vma, user_page_addr, page[0]);//為應(yīng)用空間的這段虛擬地址建立虛擬到物理的映射

if (ret) {? ? ? ? ? ? ? ? ? ? //至此應(yīng)用空間和內(nèi)核空間都映射到了同一塊物理頁內(nèi)存

printk(KERN_ERR "binder: %d: binder_alloc_buf failed "

? ? ? "to map page at %lx in userspace\n",

? ? ? proc->pid, user_page_addr);

goto err_vm_insert_page_failed;

}

/* vm_insert_page does not seem to increment the refcount */

}

if (mm) {

up_write(&mm->mmap_sem);

mmput(mm);

}

return 0;

free_range://釋放影射

for (page_addr = end - PAGE_SIZE; page_addr >= start;

? ? page_addr -= PAGE_SIZE) {

page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];//得到該段虛擬地址對應(yīng)的struct page結(jié)構(gòu)體

if (vma)

zap_page_range(vma, (uintptr_t)page_addr +//拆除應(yīng)用空間的映射表

proc->user_buffer_offset, PAGE_SIZE, NULL);

err_vm_insert_page_failed://查出內(nèi)核空間的映射表

unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);

err_map_kernel_failed://釋放對應(yīng)的物理內(nèi)存頁忍级,這樣這塊物理內(nèi)存頁又可以被系統(tǒng)其他地方使用了

__free_page(*page);

*page = NULL;

err_alloc_page_failed:

;

}

err_no_vma:

if (mm) {

up_write(&mm->mmap_sem);

mmput(mm);

}

return -ENOMEM;

}

剛開始binder_mmap只是為進程映射一個物理頁帆谍,當后續(xù)如果映射的內(nèi)存不夠用的時候,根據(jù)需要增加建立相應(yīng)的新的物理頁映射颤练,用于滿足接下來的物理內(nèi)存分配需求既忆。而這個動作就是在binder_alloc_buf函數(shù)中實現(xiàn)的。就是當系統(tǒng)的物理內(nèi)存不夠用的時候嗦玖,通過binder_alloc_buf動態(tài)的建立需要的物理內(nèi)存的映射(但最少是一個物理頁患雇,因為最少的映射單元就是一個物理頁)

在binder_mmap函數(shù)中,建立內(nèi)存分配和映射的操作函數(shù)集(binder_vm_ops)時宇挫,并沒有像通用的mmap驅(qū)動那樣去設(shè)置fault()函數(shù)苛吱,讓系統(tǒng)如果發(fā)生缺頁異常時,調(diào)用這個fault函數(shù)來分配物理內(nèi)存器瘪,并且建立相應(yīng)的虛擬地址到物理地址的映射翠储,而是在binder_alloc_buf函數(shù)中去建立動態(tài)的物理內(nèi)存的分配和虛擬地址到物理地址的映射,這樣做的好處就是:減少了系統(tǒng)中斷的次數(shù)橡疼,加速binder內(nèi)存發(fā)生缺頁時的處理速度援所。因為fault()函數(shù)是在異常處理被調(diào)用的,即中斷處理中被調(diào)用的欣除,過多的這種處理會讓系統(tǒng)對用戶的輸入反應(yīng)遲鈍住拭。

3:共享內(nèi)存在binder通訊中的使用

3.1 共享內(nèi)存的分配

在通過進行binder事物的傳遞時,如果一個binder事物(用struct binder_transaction結(jié)構(gòu)體表示)需要使用到內(nèi)存历帚,就會調(diào)用binder_alloc_buf函數(shù)分配此次binder事物需要的內(nèi)存空間滔岳。需要注意的是:從目標進程所在binder內(nèi)存空間分配所需的內(nèi)存:(drivers/staging/android/binder.c)

t->sender_euid = proc->tsk->cred->euid;

t->to_proc = target_proc;//事物的目標進程

t->to_thread = target_thread;//事物的目標線程

t->code = tr->code;//事物代碼

t->flags = tr->flags;

t->priority = task_nice(current);//線程的優(yōu)先級的遷移

trace_binder_transaction(reply, t, target_node);

t->buffer = binder_alloc_buf(target_proc, tr->data_size,//從target進程的binder內(nèi)存空間分配所需的內(nèi)存大小(tr->data_size+tr->offsets_size)

tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));

if (t->buffer == NULL) {

return_error = BR_FAILED_REPLY;

goto err_binder_alloc_buf_failed;

}

t->buffer->allow_user_free = 0;

t->buffer->debug_id = t->debug_id;

t->buffer->transaction = t;//該binder_buffer對應(yīng)的事務(wù)

t->buffer->target_node = target_node;//該事物對應(yīng)的目標binder實體

至此已經(jīng)為事務(wù)傳輸分配了所需的內(nèi)存挽牢,分配的binder內(nèi)存的開始地址存放在binder_buffer->data域中谱煤,binder_buffer->data_size存放此次事務(wù)對應(yīng)的數(shù)據(jù)大小,binder_buffer->offsets_size表示數(shù)據(jù)中包含的binder對象的數(shù)目禽拔。該事務(wù)初始化好刘离,會被放到target線程所在的todo列表中齿拂,被喚醒target線程闹获。

3.2 共享內(nèi)存的訪問

阻塞在binder_thread_read函數(shù)中的thread->wait等待隊列上的目標線程,在被喚醒后蓝丙,從自己的thread->todo雙向列表中取出在binder_transaction()函數(shù)放入到該隊列的事務(wù)(struct binder_transaction)磨淌,并用這個結(jié)構(gòu)體來初始化struct binder_transaction_data結(jié)構(gòu)體:

tr.code = t->code;

tr.flags = t->flags;

tr.sender_euid = t->sender_euid;

if (t->from) {

struct task_struct *sender = t->from->proc->tsk;

tr.sender_pid = task_tgid_nr_ns(sender,

current->nsproxy->pid_ns);

} else {

tr.sender_pid = 0;

}

tr.data_size = t->buffer->data_size;//事務(wù)對應(yīng)的數(shù)據(jù)的大小

tr.offsets_size = t->buffer->offsets_size;

tr.data.ptr.buffer = (void *)t->buffer->data +//得到事物對應(yīng)的內(nèi)核數(shù)據(jù)在用戶空間的訪問地址

proc->user_buffer_offset;

tr.data.ptr.offsets = tr.data.ptr.buffer +//得到事務(wù)對應(yīng)的數(shù)據(jù)在用戶空間的訪問地址

ALIGN(t->buffer->data_size,

? ? sizeof(void *));

if (put_user(cmd, (uint32_t __user *)ptr))//拷貝返回命令

return -EFAULT;

ptr += sizeof(uint32_t);

if (copy_to_user(ptr, &tr, sizeof(tr)))//注意:只是拷貝了命令對應(yīng)的固定大小的tr參數(shù)疲憋,并沒有拷貝tr.data.ptr.buffer指向的內(nèi)容 return -EFAULT;

ptr += sizeof(tr);

最終struct binder_transaction_data結(jié)構(gòu)體會通過ioctl系統(tǒng)調(diào)用以BR_xx命令+參數(shù)(binder_transaction_data結(jié)構(gòu)體)的形式返回給target進程的用戶空間,并被用戶空間所處理梁只。target進程的用戶空間直接使用tr.data.ptr.buffer的值就可以訪問到遠端傳遞過來的數(shù)據(jù)缚柳,而不需要做mem copy的動作將內(nèi)核的數(shù)據(jù)通過copy_to_user來拷貝到用戶空間埃脏;同樣用戶空間直接使用tr.data.ptr.offsets的值就可以直接訪問binder對象的偏移數(shù)組

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市秋忙,隨后出現(xiàn)的幾起案子彩掐,更是在濱河造成了極大的恐慌,老刑警劉巖灰追,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堵幽,死亡現(xiàn)場離奇詭異,居然都是意外死亡弹澎,警方通過查閱死者的電腦和手機朴下,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苦蒿,“玉大人殴胧,你說我怎么就攤上這事∨宄伲” “怎么了团滥?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長报强。 經(jīng)常有香客問我灸姊,道長,這世上最難降的妖魔是什么秉溉? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任厨钻,我火速辦了婚禮,結(jié)果婚禮上坚嗜,老公的妹妹穿的比我還像新娘。我一直安慰自己诗充,他們只是感情好苍蔬,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蝴蜓,像睡著了一般碟绑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上茎匠,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天格仲,我揣著相機與錄音,去河邊找鬼诵冒。 笑死凯肋,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的汽馋。 我是一名探鬼主播侮东,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼圈盔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了悄雅?” 一聲冷哼從身側(cè)響起驱敲,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宽闲,沒想到半個月后众眨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡容诬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年娩梨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片放案。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡姚建,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吱殉,到底是詐尸還是另有隱情掸冤,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布友雳,位于F島的核電站稿湿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏押赊。R本人自食惡果不足惜饺藤,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望流礁。 院中可真熱鬧涕俗,春花似錦、人聲如沸神帅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽找御。三九已至元镀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間霎桅,已是汗流浹背栖疑。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留滔驶,地道東北人遇革。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親澳淑。 傳聞我的和親對象是個殘疾皇子比原,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容