在Android進程間通信——Binder通信相關系統(tǒng)進程(二)這篇中我們知道滤灯,手機系統(tǒng)服務需要先注冊到servicemanager中才能被服務端找到和使用纺座,而一個普通應用的運行過程中需要頻繁用到系統(tǒng)服務,比如AMS,WMS等恬叹,這些系統(tǒng)服務都要在系統(tǒng)啟動時就注冊到servicemanager進程中,之后才能被其他進程調(diào)用迂烁。所以進程間通信中最底層和最核心的進程就是servicemanager恭陡,沒有它可以說進程間通信是無法正常工作的,所以我們就先從servicemanager這個進程開始坦报,通過分析源碼了解一步一步了解進Android進程間通信的原理库说。源碼github地址
servicemanager的源碼
servicemanager是一個c語言編寫的程序,其源碼位于系統(tǒng)源碼/frameworks/native/cmds/servicemanager/service_manager.c
首先我們看下main方法做了什么,這里我省略了一些與核心邏輯無關代碼
int main(int argc, char** argv)
{
char *driver;
if (argc > 1) {
driver = argv[1];
} else {
driver = "/dev/binder";
}
bs = binder_open(driver, 128*1024);
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
binder_loop(bs, svcmgr_handler);
return 0;
}
binder_open
我們看到片择,在main方法中潜的,調(diào)用了binder_open方法,同時傳入得數(shù)據(jù)是一個driver字符傳表示binder驅(qū)動得路徑字管,另外一個是128*1026也就是128k啰挪。
binder_open的源碼位于service_manager.c同目錄下的binder.c中,注意這個不是真正的binder驅(qū)動代碼嘲叔。
struct binder_state //binder_open方法的返回數(shù)據(jù)結(jié)構(gòu)體
{
int fd;
void *mapped;
size_t mapsize;
};
//注意其中忽略了核心邏輯無關代碼
struct binder_state *binder_open(size_t mapsize)
{
struct binder_state *bs;
bs = malloc(sizeof(*bs));
bs->fd = open("/dev/binder", O_RDWR);
bs->mapsize = mapsize;
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
return bs;
}
1.首先為創(chuàng)建了一個binder_state結(jié)構(gòu)體變量bs并分配了內(nèi)存
2.通過open("/dev/binder",O_RDWR)方法打開binder驅(qū)動亡呵,并返回fd文件句柄保存到bs中
open方法會調(diào)用驅(qū)動kernel/drivers/staging/android/binder.c
binder是一種虛擬的字符設備,注冊在/dev/binder中,字符(char)設備是個能夠像字節(jié)流(類似文件)一樣被訪問的設備借跪,由字符設備驅(qū)動程序來實現(xiàn)這種特性政己。字符設備驅(qū)動程序通常至少要實現(xiàn)open,close,read和write系統(tǒng)調(diào)用。大多數(shù)字符設備是一個個只能順序訪問的數(shù)據(jù)通道
// 每個使用Binder的進程都會在使用前打開binder驅(qū)動,驅(qū)動在open的時候會創(chuàng)建結(jié)構(gòu)體binder_proc歇由,并將它的指針記錄到文件結(jié)構(gòu)體private_data中卵牍,以后的驅(qū)動操作就能通過文件結(jié)構(gòu)體來獲取該進程的信息。
// 驅(qū)動里都是以binder_proc為單元來分配和記錄資源的沦泌,驅(qū)動會給每個使用binder的進程創(chuàng)建一個/proc/binder/proc/pid文件糊昙,用來展示進程相關的binder信息。
//proc/binder/stats和/proc/binder/proc/pid能查看到相關信息
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;//進程信息
struct binder_device *binder_dev;//
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
get_task_struct(current);
proc->tsk = current;
INIT_LIST_HEAD(&proc->todo);//注意初始化todo鏈表谢谦,表示要執(zhí)行的任務
init_waitqueue_head(&proc->wait);//初始化等待隊列释牺,表示掛起任務
proc->default_priority = task_nice(current);
binder_dev = container_of(filp->private_data, struct binder_device,
miscdev);/、
proc->context = &binder_dev->context回挽;//context上下文信息没咙,保存有servicemanager 進程相關信息
binder_stats_created(BINDER_STAT_PROC);
hlist_add_head(&proc->proc_node, &binder_procs);
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
filp->private_data = proc;
}
3.通過mmap方法給打開的文件句柄fd映射一塊內(nèi)存,對這個映射的內(nèi)存讀寫就可以實現(xiàn)對文件的讀寫
這里是一個重點千劈,也就是通常說binder只需要一次拷貝來實現(xiàn)進程間通信祭刚。
這里需要我們補充一下Linux的背景知識,知道為啥需要mmap墙牌,可以參考這篇博客Android Binder通信一次拷貝你真的理解了嗎涡驮?
我們需要了解以下背景
1、為啥要有跨進程通信機制喜滨?
因為Linux的內(nèi)存模型是進程間內(nèi)存隔離的捉捅,2個進程不能直接通信,也就是進程之間不能直接訪問對方的內(nèi)存虽风, 這肯定是為了安全
2棒口、Android為啥不用Linux原生自帶的進程間通信機制可參考Android Bander設計與實現(xiàn) - 設計篇
為了性能和安全等因素
3、了解什么是內(nèi)核空間和什么是用戶空間
普通應用運行在內(nèi)核空間焰情,系統(tǒng)應用運行在內(nèi)核空間陌凳,普通應用不能直接訪問內(nèi)核空間,可以通過系統(tǒng)調(diào)用訪問內(nèi)核空間
4内舟、傳統(tǒng)跨進程通信方式合敦,或不用mmap的跨進程通信方式
也就是需要進程A先把數(shù)據(jù)拷貝到內(nèi)核空間,然后再從內(nèi)核空間拷貝到進程B 這樣才能實現(xiàn)跨進程通信验游,但是2次拷貝的開銷很大
5充岛、Linux的虛擬內(nèi)存——用戶空間程序使用的內(nèi)存地址都是系統(tǒng)分配的虛擬內(nèi)存地址,虛擬內(nèi)存是對物理內(nèi)存的映射
mmap
這里調(diào)用mmap最終會調(diào)用到binder驅(qū)動中的binder_mmap方法具體流程可參考Android binder中的mmap到binder_mmap調(diào)用流程 在理解binder_mmap函數(shù)前耕蝉,我們要了解一些基本的結(jié)構(gòu)體的作用崔梗。
struct file
代表一個打開的文件描述符
vm_area_struct *vma
表示一個虛擬內(nèi)存,因為實際物理內(nèi)存并不是連續(xù)的垒在,所以程序的虛擬內(nèi)存是一個鏈表
vm_area_struct結(jié)構(gòu)體中幾個重要的變量
struct vm_area_struct {
unsigned long vm_start;//虛擬內(nèi)存起始位置
unsigned long vm_end;//虛擬內(nèi)存結(jié)束位置
struct vm_area_struct* vm_next, * vm_prev;//虛擬內(nèi)存的前后節(jié)點指針
unsigned long vm_flags;//主要保存VMA標志位
const struct vm_operations_struct* vm_ops;//VMA操作函數(shù)合集蒜魄,常用于文件映射
struct file* vm_file;//描述一個被映射的文件
void* vm_private_data;
};
binder_proc
binder_proc是一個非常重要的結(jié)構(gòu)體,其中保存了進程的很多信息
struct binder_proc {
// 進程打開設備文件/dev/binder時,Binder驅(qū)動會為它創(chuàng)建一個binder_proc結(jié)構(gòu)體谈为,并將它
// 保存在全局hash列表中旅挤,proc_node是該hash列表的節(jié)點。
struct hlist_node proc_node;
//每個使用了Binder機制的進程都有一個Binder線程池伞鲫,用來處理進程間通信請求粘茄。threads以
// 線程ID作為key來組織進程的Binder線程池。進程可以調(diào)用ioctl將線程注冊到Binder驅(qū)動中
// 當沒有足夠的空閑線程處理進程間通信請求時秕脓,驅(qū)動可以要求進程注冊更多的線程到Binder線程
// 池中
struct rb_root threads;
struct rb_root nodes;//組織Binder實體對象柒瓣,它以成員ptr作為key
struct rb_root refs_by_desc;//進程中的rb_root紅黑樹的根,用于查找handle對應的node 組織Binder引用對象吠架,它以成員desc作為key
struct rb_root refs_by_node;//組織Binder引用對象芙贫,它以成員node作為key
int pid;
struct vm_area_struct *vma; // 內(nèi)核緩沖區(qū)的用戶空間地址,供應用程序使用
struct mm_struct *vma_vm_mm;
struct task_struct *tsk;
struct files_struct *files;
struct hlist_node deferred_work_node;
int deferred_work;
void *buffer;// 內(nèi)核緩沖區(qū)的內(nèi)核空間地址诵肛,供驅(qū)動程序使用
ptrdiff_t user_buffer_offset;// vma和buffer之間的差值
// buffer指向一塊大的內(nèi)核緩沖區(qū)屹培,驅(qū)動程序為方便管理,將它劃分成若干小塊怔檩,這些小塊的內(nèi)核緩
// 沖區(qū)用binder_buffer描述保存在列表中,按地址從小到大排列蓄诽。buffers指向該列表的頭部薛训。
struct list_head buffers;
struct rb_root free_buffers// buffers中的小塊有的正在使用,被保存在此紅黑樹
struct rb_root allocated_buffers;;// buffers中的空閑小塊被保存在此紅黑樹
size_t free_async_space;
struct page **pages;// buffer和vma都是虛擬地址仑氛,它們對應的物理頁面保存在page中乙埃,這是一個數(shù)組,每個元素指向一個物理頁面
size_t buffer_size;
uint32_t buffer_free;
struct list_head todo;//// 當進程接收到一個進程間通信請求時锯岖,Binder驅(qū)動就將該請求封
// 裝成一個工作項介袜,并且加入到進程的待處理工作向隊列中,該隊列
// 使用成員變量todo來描述出吹。
wait_queue_head_t wait;// 線程池中空閑Binder線程會睡眠在由該成員所描述的等待隊列中
// 當宿主進程的待處理工作項隊列增加新工作項后遇伞,驅(qū)動會喚醒這
// 些線程,以便處理新的工作項
struct binder_stats stats;
struct list_head delivered_death;
int max_threads;// 驅(qū)動程序最多可以主動請求進程注冊的線程數(shù)
int requested_threads;
int requested_threads_started;
int ready_threads;// 進程當前的空閑Binder線程數(shù)
long default_priority;
struct dentry *debugfs_entry;
struct binder_context *context;//保存binder的上下文信息
};
binder_buffer
該結(jié)構(gòu)體用來描述一個內(nèi)核緩沖區(qū)捶牢,該緩沖區(qū)用于在進程間傳輸數(shù)據(jù)鸠珠。
struct binder_buffer {
// 每一個使用Binder機制的進程在Binder驅(qū)動中都有一個內(nèi)核緩沖區(qū)列表,用來保存Binder驅(qū)動
// 程序為它分配的內(nèi)核緩沖區(qū)秋麸,entry是該列表的一個節(jié)點
struct list_head entry; /* free and allocated entries by address */
// 進程使用兩個紅黑樹分別保存使用中以及空閑的內(nèi)核緩沖區(qū)渐排。如果空閑,free=1灸蟆,
//rb_node就是空閑內(nèi)核緩沖區(qū)紅黑樹中的節(jié)點驯耻,否則是使用中內(nèi)核緩沖區(qū)紅黑樹中的節(jié)點
struct rb_node rb_node; /* free entry by size or allocated entry */
/* by address */
unsigned free:1;
unsigned allow_user_free:1;
unsigned async_transaction:1;
unsigned debug_id:29;
struct binder_transaction *transaction;
struct binder_node *target_node;
size_t data_size;
size_t offsets_size;
size_t extra_buffers_size;
// 保存通信數(shù)據(jù),分兩種類型:普通數(shù)據(jù)、Binder對象可缚。驅(qū)動程序不關心普通數(shù)據(jù)霎迫,但必須知道里面
// 的Binder對象,因為要根據(jù)它們來維護內(nèi)核中Binder實體對象和Binder引用對象的生命周期城看。
uint8_t data[0];
};
下面開始看下binder_mmap的流程
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;//在binder_open驅(qū)動打開時,filp的private_data指向binder_proc的地址filp->private_data = proc;具體可參考kernel/drivers/staging/android/binder.c
const char *failure_string;
//內(nèi)核進入這個函數(shù)時女气,就已經(jīng)預先為此次映射分配好了調(diào)用進程在用戶空間的虛擬地址范圍
//(vma->vm_start,vma->vm_end)
struct binder_buffer *buffer;//
// // 有效性檢查:映射的內(nèi)存不能大于4M
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;
vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
//為進程所在的內(nèi)核空間申請與用戶空間同樣長度的虛擬地址空間测柠,這段空間用于內(nèi)核來訪問和管理binder內(nèi)存區(qū)域
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
//將內(nèi)核空間地址賦值給proc->buffer炼鞠,即保存到進程上下文中,對應內(nèi)核虛擬地址的開始即為binder內(nèi)存的開始地址
proc->buffer = area->addr;
// 計算 "內(nèi)核空間地址" 和 "進程虛擬地址" 的偏移
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
// 為proc->pages分配內(nèi)存轰胁,用于存放內(nèi)核分配的物理頁的頁描述指針:
//struct page谒主,每個物理頁對應這樣一個struct page結(jié)構(gòu)
proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
// 內(nèi)核空間的內(nèi)存大小 = 進程虛擬地址區(qū)域(用戶空間)的內(nèi)存大小
proc->buffer_size = vma->vm_end - vma->vm_start;
vma->vm_ops = &binder_vm_ops;
// 將 proc(進程上下文信息) 賦值給vma私有數(shù)據(jù)
vma->vm_private_data = proc;
// 通過調(diào)用binder_update_page_range()來分配物理頁面。即赃阀,將物理內(nèi)存映射到內(nèi)核空間 以及 用戶空間
buffer = proc->buffer;
INIT_LIST_HEAD(&proc->buffers);
// 將物理內(nèi)存添加到proc->buffers鏈表中進行管理霎肯。
list_add(&buffer->entry, &proc->buffers);
buffer->free = 1;
// 把分配好內(nèi)存插入到對應的表中(空閑內(nèi)存表)
binder_insert_free_buffer(proc, buffer);
//異步傳輸大小只有一半
proc->free_async_space = proc->buffer_size / 2;
barrier();
proc->files = get_files_struct(current);
// 將用戶空間地址信息保存到proc中
proc->vma = vma;
proc->vma_vm_mm = vma->vm_mm;
/*pr_info("binder_mmap: %d %lx-%lx maps %p\n",
proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
return 0;
}
binder_update_page_range
......
for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
int ret;
page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
BUG_ON(*page);
*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
if (*page == NULL) {
pr_err("%d: binder_alloc_buf failed for page at %p\n",
proc->pid, page_addr);
goto err_alloc_page_failed;
}
//內(nèi)核空間的虛擬內(nèi)存映射到物理內(nèi)存
ret = map_kernel_range_noflush((unsigned long)page_addr,
PAGE_SIZE, PAGE_KERNEL, page);
flush_cache_vmap((unsigned long)page_addr,
(unsigned long)page_addr + PAGE_SIZE);
if (ret != 1) {
pr_err("%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)存映射到物理內(nèi)存
ret = vm_insert_page(vma, user_page_addr, page[0]);
if (ret) {
pr_err("%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 */
}
....
binder_mmap中關鍵的代碼
1.area = get_vm_area(size) 這個是分配了一塊內(nèi)存空間
2.proc->buffer = area->addr proc->buffer是內(nèi)核空間的數(shù)據(jù)緩沖區(qū),也就是把剛剛分配的內(nèi)存空間地址給了內(nèi)核空間的buffer
- proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;計算 "內(nèi)核空間地址" 和 "用戶空間地址" 的偏移
所以我們可以得出榛斯,如果用戶空間想要訪問剛剛分配的內(nèi)核空間內(nèi)存buffer,那么只需要通過proc的buffer指針+user_buffer_offset就可以訪問到vma->vm_start=proc->user_buffer_offset+proc_buffer观游。
總結(jié)
binder_mmap給當前進程分配了一塊物理內(nèi)存,并將這塊物理內(nèi)存的應用保存到proc進程的上下文中驮俗。
到目前為止懂缕,終于走完了main方法中的binder_open方法。servicermanager進程打開了binder驅(qū)動王凑,并在進程中創(chuàng)建了一塊內(nèi)存搪柑。
binder_become_context_manager
在open_binder方法執(zhí)行完成后,開始將該進程servicemanager注冊為上下文管理器索烹,也就是在binder驅(qū)動中工碾,記錄servicemanager的進程信息,之后這個進程就會管理系統(tǒng)服務百姓,其他系統(tǒng)服務都要在這個進程中注冊后才能使用
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
看起來代碼很簡單渊额,只調(diào)用了一個ioctl,傳入fd瓣戚,BINDER_SET_CONTEXT_MGR命令端圈,和一個0
ioctl 是設備驅(qū)動程序中設備控制接口函數(shù),一個字符設備驅(qū)動通常會實現(xiàn)設備打開子库、關閉舱权、讀、寫等功能仑嗅,在一些需要細分的情境下宴倍,如果需要擴展新的功能张症,通常以增設 ioctl() 命令的方式實現(xiàn)。
接收參數(shù)
fd 文件描述符
cmd 交互協(xié)議鸵贬,設備驅(qū)動將根據(jù) cmd 執(zhí)行對應操作
… 可變參數(shù) arg俗他,依賴 cmd 指定長度以及類型
這個ioctl最終會調(diào)用到binder驅(qū)動中的binder_ioctl
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;//獲取進程信息
struct binder_thread *thread;//進程的線程
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;//用戶空間的數(shù)據(jù)指針
thread = binder_get_thread(proc);//從proc信息中獲取線程
switch (cmd) {
case BINDER_WRITE_READ://binder讀寫數(shù)據(jù) arg是數(shù)據(jù)指針 對于smgr binder_write_read 是 arg cmd為 filp為驅(qū)動的句柄,thread為進程中獲取的線程
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS:
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR://用戶空間ioctl傳過來命令,BINDER_SET_CONTEXT_MGR
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
goto err;
break;
case BINDER_THREAD_EXIT:
binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
proc->pid, thread->pid);
binder_free_thread(proc, thread);
thread = NULL;
break;
case BINDER_VERSION: {
struct binder_version __user *ver = ubuf;
if (size != sizeof(struct binder_version)) {
ret = -EINVAL;
goto err;
}
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
break;
}
}
在匹配到BINDER_SET_CONTEXT_MGR項后阔逼,驅(qū)動調(diào)用了binder_ioctl_set_ctx_mgr方法
static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;//從filp中讀取進程信息
struct binder_context *context = proc->context;// binder_context 保存binder上下文管理者的信息兆衅。通過binder context可以找到service manager對應的bind node。
kuid_t curr_euid = current_euid();
if (context->binder_context_mgr_node) {//已經(jīng)注冊了MGR
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto out;
}
ret = security_binder_set_context_mgr(proc->tsk);
if (ret < 0)
goto out;
if (uid_valid(context->binder_context_mgr_uid)) {
if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) {
pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
from_kuid(&init_user_ns, curr_euid),
from_kuid(&init_user_ns,
context->binder_context_mgr_uid));
ret = -EPERM;
goto out;
}
} else {
context->binder_context_mgr_uid = curr_euid;
}
//創(chuàng)建一個binder_node
context->binder_context_mgr_node = binder_new_node(proc, 0, 0);
context->binder_context_mgr_node->local_weak_refs++;
context->binder_context_mgr_node->local_strong_refs++;
context->binder_context_mgr_node->has_strong_ref = 1;
context->binder_context_mgr_node->has_weak_ref = 1;
out:
return ret;
}
我們看下binder_context結(jié)構(gòu)體
//binder_context是全局唯一的嗜浮,其中包含了一個binder_context_mgr_node也就是節(jié)點信息羡亩,可以通過這個節(jié)點找到對應的進程
struct binder_context {
struct binder_node *binder_context_mgr_node;
kuid_t binder_context_mgr_uid;
const char *name;
};
到目前為止servicemanager已經(jīng)在binder驅(qū)動中注冊為一個MGR節(jié)點,也就是成為了系統(tǒng)的服務管家進程危融。其他進程都可以通過proc->context->binder_node來獲取到servicemanager來將自己注冊畏铆。
binder_loop
binder_loop(bs, svcmgr_handler)
從字面意思上我們看到,servicemanager進程開啟了一個循環(huán)吉殃,成為了一個守護進程辞居,開始了作為系統(tǒng)服務管家的工作。我們看到接收的參數(shù)為一個binder_state結(jié)構(gòu)蛋勺,和一個svcmgr_handler瓦灶,這個是一個回調(diào)函數(shù),用于處理其他進程發(fā)送給servicemanager進程的命令
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE抱完;
case SVC_MGR_ADD_SERVICE://注意倚搬,這個是注冊服務命令,也就是AMS通過跨進程調(diào)用最終調(diào)用到servicemanager的方法乾蛤,也是我們經(jīng)過大量的源碼分析后最ServerManager.addService的終點
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
handle = bio_get_ref(msg);
allow_isolated = bio_get_uint32(msg) ? 1 : 0;
if (do_add_service(bs, s, len, handle, txn->sender_euid,
allow_isolated, txn->sender_pid))
return -1;
break;
case SVC_MGR_LIST_SERVICES: {
return 0;
}
binder_loop命令調(diào)用了驅(qū)動的binder_loop命令,先看下相關的結(jié)構(gòu)體binder_write_read 這個結(jié)構(gòu)體的作用其實就是對binder通信數(shù)據(jù)的封裝
struct binder_write_read {
binder_size_t write_size; /* bytes to write */寫數(shù)據(jù)大小
binder_size_t write_consumed; /* bytes consumed by driver */已經(jīng)讀取的寫入數(shù)據(jù)
binder_uintptr_t write_buffer;//寫入數(shù)據(jù)的buffer地址
binder_size_t read_size; /* bytes to read *///讀數(shù)據(jù)大小
binder_size_t read_consumed; /* bytes consumed by driver *已經(jīng)讀取了多少數(shù)據(jù)
binder_uintptr_t read_buffer;//讀取數(shù)據(jù)的buffer
};
調(diào)用binder_looper
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;//內(nèi)核中創(chuàng)建一個binder_write_read結(jié)構(gòu)體指針
uint32_t readbuf[32];//分配讀取buffer內(nèi)存
、////設置寫入數(shù)據(jù)為0捅僵,代表沒有寫入數(shù)據(jù)
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(uint32_t));//設置servicemanager進程中的thread標志位為BINDER_LOOPER_STATE_ENTERED
for (;;) {//無限循環(huán)
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
//掛起家卖,等待喚醒
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//用戶空間發(fā)送bwr數(shù)據(jù)到內(nèi)核空間
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
if (res == 0) {
ALOGE("binder_loop: unexpected reply?!\n");
break;
}
if (res < 0) {
ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
break;
}
}
}
關鍵的是進入無限循環(huán)后,bwr的read_buffer內(nèi)容是 readbuf ,read_size不等于0庙楚,通過通過ioctl發(fā)送BINDER_WRITE_REA命令給驅(qū)動層上荡,其實驅(qū)動層接收到這個命令后,會根據(jù)bwr中的write_size和read_size來判斷是讀數(shù)據(jù)還是寫數(shù)據(jù)馒闷,write_size不等0表示進程寫入數(shù)據(jù)到binder驅(qū)動中,read_siez不等于0表示驅(qū)動讀取數(shù)據(jù)到進程下面我們看ioctl的驅(qū)動層實現(xiàn)binder_ioctrl做了什么
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;//獲取進程信息
struct binder_thread *thread;//進程的線程
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;//用戶空間的數(shù)據(jù)指針
thread = binder_get_thread(proc);//從proc信息中根據(jù)pid查找線程
switch (cmd) {
case BINDER_WRITE_READ://binder讀寫數(shù)據(jù) arg是數(shù)據(jù)指針,filp為驅(qū)動的句柄,thread為進程中獲取的線程
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
}
binder_ioctl_write_read
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{ int ret = 0;
struct binder_proc *proc = filp->private_data;//
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;//內(nèi)核空間數(shù)據(jù)封裝
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {//從用戶空間拷貝數(shù)據(jù)到內(nèi)核空間酪捡,第一次拷貝數(shù)據(jù)
ret = -EFAULT;
goto out;
}
這里我們發(fā)現(xiàn)了一次拷貝,我們知道binder_write_read就是一個對讀寫數(shù)據(jù)進程封裝的結(jié)構(gòu)體纳账,
這樣用戶空間的數(shù)據(jù)封裝結(jié)構(gòu)體內(nèi)容被拷貝到了內(nèi)核空間逛薇,繼續(xù)看代碼
if (bwr.write_size > 0) {//bwr是從用戶空間拷貝過來的數(shù)據(jù)封裝,如果發(fā)現(xiàn)數(shù)據(jù)封裝中寫入數(shù)據(jù)大于0疏虫,則進入binder_thread_write方法永罚,發(fā)送數(shù)據(jù)
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
trace_binder_write_done(ret);
if (ret < 0) {
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
if (bwr.read_size > 0) {//如果發(fā)現(xiàn)啤呼,讀取數(shù)據(jù)大于0,則進入binder_thread_read來接收數(shù)據(jù)
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
if (!list_empty(&proc->todo))
wake_up_interruptible(&proc->wait);
if (ret < 0) {
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
由于我們知道呢袱,bwr結(jié)構(gòu)體中封裝的數(shù)據(jù)read_size是大于0官扣,write_size是小于0的,所以進入binder_thread_read方法
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;//讀取數(shù)據(jù)buffer指針
void __user *ptr = buffer + *consumed;//未讀取數(shù)據(jù)位置
void __user *end = buffer + size;//讀取數(shù)據(jù)的結(jié)束位置
int wait_for_proc_work;//進程是否掛起標志
wait_for_proc_work = thread->transaction_stack == NULL &&list_empty(&thread->todo);//wait_for_proc_work目前為1羞福,表示沒有要處理的任務
if (wait_for_proc_work)//ready_threads為1,進程多了一個空閑線程
proc->ready_threads++;
if (wait_for_proc_work) {
binder_set_nice(proc->default_priority);;//把當前線程的優(yōu)先級設置為它所屬進程的優(yōu)先級
if (non_block) {//非阻塞要立刻返回處理結(jié)果
if (!binder_has_proc_work(proc, thread))
ret = -EAGAIN;
} else
ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
} else {
if (non_block) {//非阻塞要立刻返回處理結(jié)果
if (!binder_has_thread_work(thread))
ret = -EAGAIN;
} else
ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));////睡眠等待直到線程有新的未處理項為止 然后發(fā)送端進程就進入了休眠惕蹄。目的是為了等待目標進程處理完請求后給予它響應。
}
//線程被喚醒了
if (wait_for_proc_work)
proc->ready_threads--;
thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
if (ret)
return ret;
while (1) {
uint32_t cmd;
struct binder_transaction_data tr;
struct binder_work *w;
struct binder_transaction *t = NULL;
.......
從代碼可以看到servicemanager進程啟動時 thread->transaction_stack 肯定未空并且thread->todo也為空治专,所以wait_for_proc_work為真proc->ready_threads增加1卖陵,之后根據(jù)線程同步的還是異步的來確定時直接返回還是掛起,binder通信默認時同步的看靠,所以最終會執(zhí)行到wait_event_freezable_exclusive掛起線程赶促,等待接收喚醒。至此挟炬,servicemanager進程終于啟動完成了鸥滨,并且進入了休眠,等待后面其他進程來喚醒了