編輯:關於Android編程
本文參考《Android系統源代碼情景分析》,作者羅升陽。
一、~/Android/frameworks/base/cmd/servicemanager
-----binder.h
-----binder.c
-----service_manager.c
~/Android//kernel/goldfish/drivers/staging/android
-----binder.c
-----binder.h
二、源碼分析
1、從service_manager.c的main開始執行。
----~/Android/frameworks/base/cmd/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)) { LOGE("cannot become context manager (%s)\n", strerror(errno)); return -1; } svcmgr_handle = svcmgr;//注意svcmgr_handle與下面的svcmgr_handler不一樣,svcmgr_handle為全局變量,目前為空的函數指針,((void*)0) binder_loop(bs, svcmgr_handler);//svcmgr_handler是一個函數指針,指向具體的函數 return 0; }----~/Android/frameworks/base/cmd/servicemanager/binder.c
struct binder_state { int fd; void *mapped; unsigned mapsize; };----~/Android/frameworks/base/cmd/servicemanager/binder.h
#define BINDER_SERVICE_MANAGER ((void*) 0)ServiceManager的啟動過程由三個步驟組成:第一是調用函數binder_open打開設備文件/dev/binder,以及將它映射到本進程地址空間;第二是調用binder_become_context_manager將自己注冊為Binder進程間通信機制的上下文管理者;第三步是調用函數binder_loop來循環等待和處理Client進程的通信要求。
2、打開和映射Binder設備文件
----~/Android/frameworks/base/cmd/servicemanager/binder.c
struct binder_state *binder_open(unsigned mapsize) { struct binder_state *bs; bs = malloc(sizeof(*bs)); if (!bs) { errno = ENOMEM; return 0; } bs->fd = open("/dev/binder", O_RDWR);//文件描述符保存在fd中 if (bs->fd < 0) { fprintf(stderr,"binder: cannot open device (%s)\n", strerror(errno)); goto fail_open; } bs->mapsize = mapsize;//128*1024 bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);//映射到本進程地址空間 if (bs->mapped == MAP_FAILED) { fprintf(stderr,"binder: cannot map device (%s)\n", strerror(errno)); goto fail_map; } /* TODO: check version */ return bs; fail_map: close(bs->fd); fail_open: free(bs); return 0; }其中open("/dev/binder", O_RDWR)映射到binder驅動程序binder_open方法。
binder_open方法位於~/Android/kernel/goldfish/drivers/staging/android/binder.c
static int binder_open(struct inode *nodp, struct file *filp) { struct binder_proc *proc; if (binder_debug_mask & BINDER_DEBUG_OPEN_CLOSE) printk(KERN_INFO "binder_open: %d:%d\n", current->group_leader->pid, current->pid); proc = kzalloc(sizeof(*proc), GFP_KERNEL);//創建binder_proc結構體 if (proc == NULL) return -ENOMEM; get_task_struct(current); proc->tsk = current; //初始化各個參數 INIT_LIST_HEAD(&proc->todo); init_waitqueue_head(&proc->wait); proc->default_priority = task_nice(current); mutex_lock(&binder_lock); binder_stats.obj_created[BINDER_STAT_PROC]++; hlist_add_head(&proc->proc_node, &binder_procs);//將binder_proc結構體proc加入到一個全局hash隊列binder_procs中 proc->pid = current->group_leader->pid; INIT_LIST_HEAD(&proc->delivered_death); filp->private_data = proc;//將binder_proc結構體proc保存在參數filp的成員變量private_data中 mutex_unlock(&binder_lock); if (binder_proc_dir_entry_proc) {//如果存在/proc/binder/proc目錄 char strbuf[11]; snprintf(strbuf, sizeof(strbuf), "%u", proc->pid); remove_proc_entry(strbuf, binder_proc_dir_entry_proc); create_proc_read_entry(strbuf, S_IRUGO, binder_proc_dir_entry_proc, binder_read_proc_proc, proc);//在/proc/binder/proc目錄下創建一個以進程ID為名稱的只讀文件 } return 0; }創建了binder_proc結構體,分別初始化各個參數,將binder_proc結構體proc加入到一個全局hash隊列binder_procs中。Binder驅動程序將所有打開了設備文件/dev/binder的進程都加入全局hash隊列binder_procs中,因此,通過遍歷這個hash隊列就知道系統當前有多少進程在使用Binder進程間通信機制。然後將binder_proc結構體proc保存在參數filp的成員變量private_data中。最後在/proc/binder/proc目錄下創建一個以進程ID為名稱的只讀文件,並且以函數binder_read_proc_proc作為它的文件內容讀取函數。通過讀取文件/proc/binder/proc/
其中mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0)映射到binder驅動程序binder_mmap方法,暫時先不分析。
3、注冊為Binder上下文管理者
---~/Android/frameworks/base/cmd/servicemanager/binder.c
int binder_become_context_manager(struct binder_state *bs) { return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0); }ioctl方法映射到binder驅動程序binder_ioctl方法。
binder_ioctl位於~/Android/kernel/goldfish/drivers/staging/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int ret; struct binder_proc *proc = filp->private_data;//獲取剛剛在open中創建的binder_proc結構體 struct binder_thread *thread; unsigned int size = _IOC_SIZE(cmd);//命令的大小 void __user *ubuf = (void __user *)arg;//參數地址 ......... mutex_lock(&binder_lock); thread = binder_get_thread(proc);//獲取或者創建一個binder_thread結構體 if (thread == NULL) { ret = -ENOMEM; goto err; } switch (cmd) { ............ case BINDER_SET_CONTEXT_MGR: if (binder_context_mgr_node != NULL) { ......... } if (binder_context_mgr_uid != -1) { ......... } else binder_context_mgr_uid = current->cred->euid;//初始化進程有效用戶ID binder_context_mgr_node = binder_new_node(proc, NULL, NULL);//初始化一個Binder實體對象 if (binder_context_mgr_node == NULL) { ret = -ENOMEM; goto err; } binder_context_mgr_node->local_weak_refs++;//強弱指針,待以後分析 binder_context_mgr_node->local_strong_refs++; binder_context_mgr_node->has_strong_ref = 1; binder_context_mgr_node->has_weak_ref = 1; break; ............. } ret = 0; err: if (thread) thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN; mutex_unlock(&binder_lock); ............. return ret; }binder_get_thread實現如下:
~/Android/kernel/goldfish/drivers/staging/android/binder.c
static struct binder_thread *binder_get_thread(struct binder_proc *proc) { struct binder_thread *thread = NULL; struct rb_node *parent = NULL; struct rb_node **p = &proc->threads.rb_node; while (*p) {//根據當前主線程pid,來查找是否已經分配了binder_thread結構體 parent = *p; thread = rb_entry(parent, struct binder_thread, rb_node); if (current->pid < thread->pid) p = &(*p)->rb_left; else if (current->pid > thread->pid) p = &(*p)->rb_right; else break; } if (*p == NULL) {//如果沒有找到 thread = kzalloc(sizeof(*thread), GFP_KERNEL);//分配binder_thread結構體 if (thread == NULL) return NULL; binder_stats.obj_created[BINDER_STAT_THREAD]++; thread->proc = proc;//初始化各個變量 thread->pid = current->pid; init_waitqueue_head(&thread->wait); INIT_LIST_HEAD(&thread->todo); rb_link_node(&thread->rb_node, parent, p);//根據pid將thread->rb_node插入到proc->threads維護的紅黑樹中 rb_insert_color(&thread->rb_node, &proc->threads); thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; thread->return_error = BR_OK; thread->return_error2 = BR_OK; } return thread; }一個進程會有很多線程,所以thread->rb_node為proc->threads維護的紅黑數中的一個節點。thread按照pid大小將rb_node插入到proc->threads維護的紅黑樹的對應節點處。所以首先根據當前主線程pid,來查找是否已經分配了binder_thread結構體。如果沒有那麼分配binder_thread結構體,初始化各個變量,根據pid將thread->rb_node插入到proc->threads維護的紅黑樹中。將looper狀態設備為BINDER_LOOPER_STATE_NEED_RETURN,表示該線程在完成當前操作之後,需要馬上返回到用戶空間,而不可以去處理進程間的通信請求。
全局變量binder_context_mgr_node用來描述一個Binder實體對象,如果它的值不為NULL,說明已經注冊了Binder進程間通信上下文管理者了。全局變量binder_context_mgr_uid用來描述進程有效用戶ID,如果它的值不等於-1,說明已經注冊了Binder進程間通信上下文管理者了。目前沒有Binder進程間通信上下文管理者,所以binder_context_mgr_uid和binder_context_mgr_node都要初始化。
binder_context_mgr_node實現如下:
-----~/Android/kernel/goldfish/drivers/staging/android/binder.c
static struct binder_node * binder_new_node(struct binder_proc *proc, void __user *ptr, void __user *cookie) { struct rb_node **p = &proc->nodes.rb_node; struct rb_node *parent = NULL; struct binder_node *node; while (*p) {//根據node的ptr,來查找是否已經分配了binder_node結構體 parent = *p; node = rb_entry(parent, struct binder_node, rb_node); if (ptr < node->ptr) p = &(*p)->rb_left; else if (ptr > node->ptr) p = &(*p)->rb_right; else return NULL; } node = kzalloc(sizeof(*node), GFP_KERNEL);//如果沒有找到,分配binder_node結構體 if (node == NULL) return NULL; binder_stats.obj_created[BINDER_STAT_NODE]++; rb_link_node(&node->rb_node, parent, p);//根據ptr將node->rb_node插入proc->nodes中 rb_insert_color(&node->rb_node, &proc->nodes); node->debug_id = ++binder_last_id;//初始化各個變量 node->proc = proc; node->ptr = ptr;//NULL node->cookie = cookie;//NULL node->work.type = BINDER_WORK_NODE; INIT_LIST_HEAD(&node->work.entry); INIT_LIST_HEAD(&node->async_todo); if (binder_debug_mask & BINDER_DEBUG_INTERNAL_REFS) printk(KERN_INFO "binder: %d:%d node %d u%p c%p created\n", proc->pid, current->pid, node->debug_id, node->ptr, node->cookie); return node; }一個進程會有很多實體對象,所以node->rb_node為proc->nodes維護的紅黑數中的一個節點。node按照ptr地址大小將rb_node插入到proc->threads維護的紅黑樹的對應節點處。所以首先根據當前node的ptr,來查找是否已經分配了binder_node結構體。如果沒有那麼分配binder_node結構體,初始化各個變量,根據ptr將thread->rb_node插入到proc->threads維護的紅黑樹中。
4、循環等待Client進程請求
返回用戶空間,main函數開始執行binder_loop,實現如下:
---~/Android/frameworks/base/cmd/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func) { int res; struct binder_write_read bwr; unsigned readbuf[32]; bwr.write_size = 0; bwr.write_consumed = 0; bwr.write_buffer = 0; readbuf[0] = BC_ENTER_LOOPER;//首先將BC_ENTER_LOOPER協議寫入緩沖區readbuf中 binder_write(bs, readbuf, sizeof(unsigned));//調用binder_write將它發送到Binder驅動程序中 for (;;) { bwr.read_size = sizeof(readbuf); bwr.read_consumed = 0; bwr.read_buffer = (unsigned) readbuf; res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//bwr.write_size為0,bwr.read_size不為0 if (res < 0) { LOGE("binder_loop: ioctl failed (%s)\n", strerror(errno)); break; } res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func); if (res == 0) { LOGE("binder_loop: unexpected reply?!\n"); break; } if (res < 0) { LOGE("binder_loop: io error %d %s\n", res, strerror(errno)); break; } } }首先將BC_ENTER_LOOPER協議寫入緩沖區readbuf中,接著調用binder_write將它發送到Binder驅動程序中。函數binder_write的實現如下:
---~/Android/frameworks/base/cmd/servicemanager/binder.c
int binder_write(struct binder_state *bs, void *data, unsigned len) { struct binder_write_read bwr; int res; bwr.write_size = len; bwr.write_consumed = 0; bwr.write_buffer = (unsigned) data; bwr.read_size = 0; bwr.read_consumed = 0; bwr.read_buffer = 0; res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//bwr的write_size不為0,read_size為0 if (res < 0) { fprintf(stderr,"binder_write: ioctl failed (%s)\n", strerror(errno)); } return res; }ioctl方法同樣映射到binder驅動程序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; ......... mutex_lock(&binder_lock); thread = binder_get_thread(proc);//上次獲取的thread,looper為0 if (thread == NULL) { ret = -ENOMEM; goto err; } switch (cmd) {//cmd為上面傳遞過來的BINDER_WRITE_READ case BINDER_WRITE_READ: { struct binder_write_read bwr; if (size != sizeof(struct binder_write_read)) { ret = -EINVAL; goto err; } if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {//從用戶空間傳進來的一個binder_write_read結構體拷貝出來,並且保存在變量bwr中 ret = -EFAULT; goto err; } ......... if (bwr.write_size > 0) {//bwr.write_size大於0,執行這裡 ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed); if (ret < 0) { bwr.read_consumed = 0; if (copy_to_user(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto err; } } if (bwr.read_size > 0) {//bwr.read_size等於0,不執行這裡 ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); if (!list_empty(&proc->todo)) wake_up_interruptible(&proc->wait); if (ret < 0) { if (copy_to_user(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto err; } } ........... if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {//將結果返回用戶空間bwr ret = -EFAULT; goto err; } break; } .......... ret = 0; err: if (thread) thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;//looper為BINDER_LOOPER_STATE_ENTERED,由於對應位已經為0,此時執行此句無效果 mutex_unlock(&binder_lock); ........... return ret; }由於bwr.write_size大於0,開始執行binder_thread_write,實現如下:
binder_ioctl位於~/Android/kernel/goldfish/drivers/staging/android/binder.c
int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, void __user *buffer, int size, signed long *consumed) { uint32_t cmd; void __user *ptr = buffer + *consumed;//起始位置 void __user *end = buffer + size;//結束位置 while (ptr < end && thread->return_error == BR_OK) { if (get_user(cmd, (uint32_t __user *)ptr))//cmd為BC_ENTER_LOOPER return -EFAULT; ptr += sizeof(uint32_t);//由於只有一個cmd,此時ptr已經等於end ............ switch (cmd) { ........... case BC_ENTER_LOOPER: .............. if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {//此時looper為0,不會執行這裡 thread->looper |= BINDER_LOOPER_STATE_INVALID; binder_user_error("binder: %d:%d ERROR:" " BC_ENTER_LOOPER called after " "BC_REGISTER_LOOPER\n", proc->pid, thread->pid); } thread->looper |= BINDER_LOOPER_STATE_ENTERED;//執行本次寫操作,最終的目的居然是looper設置成BINDER_LOOPER_STATE_ENTERED break; ......... *consumed = ptr - buffer;//由於只有一個cmd,consumed為size } return 0; }返回binder_ioctl,bwr.read_size等於0,不會執行,最後將結果返回用戶空間bwr。
返回用戶空間,接著執行binder_loop,從上面我們看出來,驅動程序是否執行讀、寫操作,取決於用戶空間write_size,read_size的大小。此時write_size為0,read_size不為0, ioctl方法同樣映射到binder驅動程序binder_ioctl方法。
binder_ioctl位於~/Android/kernel/goldfish/drivers/staging/android/binder.c
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; ......... mutex_lock(&binder_lock); thread = binder_get_thread(proc);//上次獲取的thread,looper為BINDER_LOOPER_STATE_ENTERED if (thread == NULL) { ret = -ENOMEM; goto err; } switch (cmd) {//cmd為上面傳遞過來的BINDER_WRITE_READ case BINDER_WRITE_READ: { struct binder_write_read bwr; if (size != sizeof(struct binder_write_read)) { ret = -EINVAL; goto err; } if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {//從用戶空間傳進來的一個binder_write_read結構體拷貝出來,並且保存在變量bwr中 ret = -EFAULT; goto err; } ......... if (bwr.write_size > 0) {//bwr.write_size等於0,不執行這裡 ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed); if (ret < 0) { bwr.read_consumed = 0; if (copy_to_user(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto err; } } if (bwr.read_size > 0) {//bwr.read_size大於0,執行這裡 ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); if (!list_empty(&proc->todo)) wake_up_interruptible(&proc->wait); if (ret < 0) { if (copy_to_user(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto err; } } ........... if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {//將結果返回用戶空間bwr ret = -EFAULT; goto err; } break; } .......... ret = 0; err: if (thread) thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN; mutex_unlock(&binder_lock); ........... return ret; }
由於bwr.read_size大於0,開始執行binder_thread_read,實現如下:
binder_ioctl位於~/Android/kernel/goldfish/drivers/staging/android/binder.c
static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, void __user *buffer, int size, signed long *consumed, int non_block) { void __user *ptr = buffer + *consumed;//起始位置 void __user *end = buffer + size;//結束位置 int ret = 0; int wait_for_proc_work; if (*consumed == 0) { if (put_user(BR_NOOP, (uint32_t __user *)ptr))//BR_NOOP存入剛才的局部變量中 return -EFAULT; ptr += sizeof(uint32_t); } retry: wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);//wait_for_proc_work目前為1,表示線程沒有要處理的任務 if (thread->return_error != BR_OK && ptr < end) { .......... } thread->looper |= BINDER_LOOPER_STATE_WAITING;//looper為BINDER_LOOPER_STATE_ENTERED,BINDER_LOOPER_STATE_WAITING if (wait_for_proc_work)//為1 proc->ready_threads++;//ready_threads為1,進程多了一個空閒線程 mutex_unlock(&binder_lock); if (wait_for_proc_work) {//為1 if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED))) { // 此時為false ............... } binder_set_nice(proc->default_priority);//把當前線程的優先級設置為它所屬進程的優先級 if (non_block) {//非阻塞要立刻返回處理結果 if (!binder_has_proc_work(proc, thread))//有任務就接著往下執行,沒有任務就返回 ret = -EAGAIN; } else ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));//睡眠等待直到其所屬的進程有新的未處理項為止 } else { if (non_block) {//非阻塞要立刻返回處理結果 if (!binder_has_thread_work(thread))有任務就接下往下執行,沒有任務就返回 ret = -EAGAIN; } else ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));//睡眠等待直到線程有新的未處理項為止 } mutex_lock(&binder_lock); if (wait_for_proc_work)//為1 proc->ready_threads--;//ready_thread為0 thread->looper &= ~BINDER_LOOPER_STATE_WAITING;//looper為BINDER_LOOPER_STATE_ENTERED if (ret) return ret; while (1) { ........ } done: *consumed = ptr - buffer; .......... return 0; }
static int binder_has_proc_work(struct binder_proc *proc, struct binder_thread *thread) { return !list_empty(&proc->todo) || (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN); } static int binder_has_thread_work(struct binder_thread *thread) { return !list_empty(&thread->todo) || thread->return_error != BR_OK || (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN); }如果線程thread中沒有要處理的數據,那麼就處理進程proc上等待的數據。如果進程上沒有數據要處理,那麼睡眠等待直到其所屬的進程有新的未處理項為止。
如果線程thread中有要處理的數據,那麼就處理線程thread上的數據。如果線程上沒有數據要處理,那麼睡眠等待直到線程有新的未處理項為止。
如果是非阻塞訪問,如果沒有數據,就立刻返回,不會睡眠等待。如果有數據,就繼續往下執行。
目前由於線程thread沒有要處理的數據,進程上也沒有要處理的數據,那麼睡眠等待直到其所屬的進程有新的未處理項為止。
思想:將一個View設計成多層,內層(包括中獎信息)和外層(用於刮獎),外層的圖層用Canvas與一個Bitmap關聯,用這個關聯的Bitmap來處理手勢的滑動,類似於刮
Toast簡介 Toast是一種沒有交點,顯示時間有限,不能與用戶進行交互,用於顯示提示信息的顯示機制,我們可以把它叫做提示框。Toast是沒有依賴性的,大家可能比較了
這一篇主要根據上一篇的大致說明,我相信如果看完這一篇,對開發自定義View將會有很大的幫助,先介紹ColorStateList和StateListDrawable兩個類:
現在很多Android市場中都能找到關於美女的應用,比如 撕開美女衣服、吹裙子等。 這些應用的下載量挺大的,作為Android的開發人員或者一名技術人員我們不能只局限在欣