編輯:關於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沒有要處理的數據,進程上也沒有要處理的數據,那麼睡眠等待直到其所屬的進程有新的未處理項為止。
Andriod實現刮刮卡的效果
思想:將一個View設計成多層,內層(包括中獎信息)和外層(用於刮獎),外層的圖層用Canvas與一個Bitmap關聯,用這個關聯的Bitmap來處理手勢的滑動,類似於刮
Android中Toast的使用
Toast簡介 Toast是一種沒有交點,顯示時間有限,不能與用戶進行交互,用於顯示提示信息的顯示機制,我們可以把它叫做提示框。Toast是沒有依賴性的,大家可能比較了
Android 自定義View背景動畫 流程簡讀 (2)
這一篇主要根據上一篇的大致說明,我相信如果看完這一篇,對開發自定義View將會有很大的幫助,先介紹ColorStateList和StateListDrawable兩個類:
Android應用《撕開美女衣服》的實現過程及源代碼
現在很多Android市場中都能找到關於美女的應用,比如 撕開美女衣服、吹裙子等。 這些應用的下載量挺大的,作為Android的開發人員或者一名技術人員我們不能只局限在欣