本文參考《Android系统源代码情景分析》,作者罗升阳

一、service manager代码:

       ~/Android/frameworks/base/cmd/servicemanager
       ----binder.c
       ----service_manager.c
       ----binder.h

驱动层代码:

~/Android//kernel/goldfish/drivers/staging/android

----binder.c

----binder.h

二、源代码分析

从Android Binder进程间通信---Service Manager进程。处理BC_TRANSACTION,返回BR_TRANSACTIONhttp://blog.csdn.net/jltxgcy/article/details/26151113,我们已经知道Service Manager成功地将一个Service组件注冊到内部的Service组件列表所svclist中之后,接着就会调用函数binder_send_reply将Service组件注冊结果返回给Binder驱动程序,Binder驱动程序再将该结果返回给请求注冊Service组件的进程。

~/Android/frameworks/base/cmd/servicemanager

----binder.c

void binder_send_reply(struct binder_state *bs,
struct binder_io *reply,
void *buffer_to_free,
int status) //status为0。注冊成功代码0写入binder_io结构体reply中
{
struct {
uint32_t cmd_free;
void *buffer;
uint32_t cmd_reply;
struct binder_txn txn;
} __attribute__((packed)) data; data.cmd_free = BC_FREE_BUFFER;//BC_FREE_BUFFER后面跟的通信数据是一个内核缓冲区的用户空间地址
data.buffer = buffer_to_free;//一个用户空间地址,指向一块用来传输进程间通信数据的内核缓冲区
data.cmd_reply = BC_REPLY;//BC_REPLY后面跟的通信数据是一个binder_transaction_data结构体。即一个binder_txn结构体
data.txn.target = 0;
data.txn.cookie = 0;
data.txn.code = 0;
if (status) {//status为0
data.txn.flags = TF_STATUS_CODE;
data.txn.data_size = sizeof(int);
data.txn.offs_size = 0;
data.txn.data = &status;
data.txn.offs = 0;
} else {
data.txn.flags = 0;
data.txn.data_size = reply->data - reply->data0;//0的大小,由于做为do_add_service成功,reply结构体放入0
data.txn.offs_size = ((char*) reply->offs) - ((char*) reply->offs0);//0
data.txn.data = reply->data0;//指向了0
data.txn.offs = reply->offs0;//无
}
binder_write(bs, &data, sizeof(data));
}

首先定义了一个匿名结构体data,用来描写叙述一个BC_FREE_BUFFER和一个BC_REPLY命令协议。分别用成员变量cmd_free和cmd_reply来表示。命令协议BC_FREE_BUFFER后面跟的通信数据是一个内核缓冲区的用户空间地址,它就保存在成员变量buffer中;而命令协议BC_REPLY后面跟的通信数据是一个binder_transaction_data结构体,即一个binder_txn结构体。它就保存在成员变量txn中。

然后调用binder_write将匿名结构体data中BC_FREE_BUFFER和BC_REPLY命令协议发送给Binder驱动程序。

实现例如以下:

~/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;//匿名结构体data指针
bwr.read_size = 0;
bwr.read_consumed = 0;
bwr.read_buffer = 0;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
fprintf(stderr,"binder_write: ioctl failed (%s)\n",
strerror(errno));
}
return res;
}

函数binder_write是通过IO控制命令BINDER_WRITE_READ来将BC_FREE_BUFFER和BC_REPLY命令协议发送给Binder驱动程序的,映射到驱动程序binder_thread_write。

~/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))
return -EFAULT;
ptr += sizeof(uint32_t);
......
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr; if (copy_from_user(&tr, ptr, sizeof(tr)))//上面刚提到的binder_txn结构体data.txn
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr, cmd == BC_REPLY);//tr为上面已经赋值的data.txn
break;
}
........
default:
printk(KERN_ERR "binder: %d:%d unknown command %d\n", proc->pid, thread->pid, cmd);
return -EINVAL;
}
*consumed = ptr - buffer;
}
return 0;
}

我们临时不分析BC_FREE_BUFFER命令,仅仅分析BC_REPLY,while第二次循环会运行到这里。
      tr就是上面已经赋值的data.txn。然后调用binder_transaction函数。实现例如以下:

~/Android//kernel/goldfish/drivers/staging/android

----binder.c

static void
binder_transaction(struct binder_proc *proc, struct binder_thread *thread,
struct binder_transaction_data *tr, int reply)
{
struct binder_transaction *t;
struct binder_work *tcomplete;
......
struct binder_proc *target_proc;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
struct list_head *target_list;
wait_queue_head_t *target_wait;
struct binder_transaction *in_reply_to = NULL;
........
uint32_t return_error; ........ if (reply) {
in_reply_to = thread->transaction_stack;//首先从线程thread的事务堆栈中将该binder_transaction结构体取出来,而且保存在变量in_reply_to中
if (in_reply_to == NULL) {
......
return_error = BR_FAILED_REPLY;
goto err_empty_call_stack;
}
binder_set_nice(in_reply_to->saved_priority);
if (in_reply_to->to_thread != thread) {//在上节中刚设置的
........
return_error = BR_FAILED_REPLY;
in_reply_to = NULL;
goto err_bad_call_stack;
}
thread->transaction_stack = in_reply_to->to_parent;//Server Manager进程的主线程transaction_stack为NULL
target_thread = in_reply_to->from;//找到目标线程
if (target_thread == NULL) {
return_error = BR_DEAD_REPLY;
goto err_dead_binder;
}
if (target_thread->transaction_stack != in_reply_to) {//FregServer进程的主线程的transation_stack就是这个in_reply_to
.........
return_error = BR_FAILED_REPLY;
in_reply_to = NULL;
target_thread = NULL;
goto err_dead_binder;
}
target_proc = target_thread->proc;//找到了目标进程
} else {
........
}
if (target_thread) {
.........
target_list = &target_thread->todo;//分别将它的todo队列和wait等待队列作为目标todo队列target_list和目标wait等待队列target_wait
target_wait = &target_thread->wait;//分别将它的todo队列和wait等待队列作为目标todo队列target_list和目标wait等待队列target_wait
} else {
.........
}
......... /* TODO: reuse incoming transaction for reply */
t = kzalloc(sizeof(*t), GFP_KERNEL);//分配了binder_transaction结构体
........ tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);//分配了binder_work结构体
if (tcomplete == NULL) {
return_error = BR_FAILED_REPLY;
goto err_alloc_tcomplete_failed;
}
....... if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;//service_manager的主线程
else
t->from = NULL;
t->sender_euid = proc->tsk->cred->euid;//service_manager进程号
t->to_proc = target_proc;//目标进程
t->to_thread = target_thread;//目标线程
t->code = tr->code;//0
t->flags = tr->flags;//0
t->priority = task_nice(current);
t->buffer = binder_alloc_buf(target_proc, tr->data_size,
tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));//分配了binder_buffer结构体
if (t->buffer == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_alloc_buf_failed;
}
t->buffer->allow_user_free = 0;//不同意释放
.......
t->buffer->transaction = t;
t->buffer->target_node = target_node;//NULL
if (target_node)
binder_inc_node(target_node, 1, 0, NULL);//增加目标Binder实体对象的强引用计数 offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));//偏移数组在data中起始位置,位于数据缓冲区之后 if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {//数据缓冲区复制到data中
binder_user_error("binder: %d:%d got transaction with invalid "
"data ptr\n", proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {//偏移数组复制到data中,偏移数组位于数据缓冲区之后
binder_user_error("binder: %d:%d got transaction with invalid "
"offsets ptr\n", proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
if (!IS_ALIGNED(tr->offsets_size, sizeof(size_t))) {
binder_user_error("binder: %d:%d got transaction with "
"invalid offsets size, %zd\n",
proc->pid, thread->pid, tr->offsets_size);
return_error = BR_FAILED_REPLY;
goto err_bad_offset;
}
off_end = (void *)offp + tr->offsets_size;
for (; offp < off_end; offp++) {//偏移数组里面没有内容
.....
}
if (reply) {
BUG_ON(t->buffer->async_transaction != 0);
binder_pop_transaction(target_thread, in_reply_to);//FregServer进程的主线程thread->transaction_stack为NULL
} else if (!(t->flags & TF_ONE_WAY)) {
.........
} else {
.........
}
t->work.type = BINDER_WORK_TRANSACTION;
list_add_tail(&t->work.entry, target_list);//增加到目标线程的todo
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
list_add_tail(&tcomplete->entry, &thread->todo);//增加到本线程的todo
if (target_wait)
wake_up_interruptible(target_wait);//唤醒目标线程
return;
}

当Binder驱动程序分发一个进程间通信请求给一个线程处理时,就会将一个binder_transaction结构体压入到它的事务堆栈中。因此首先从线程thread的事务堆栈中将该binder_transaction结构体取出来。而且保存在变量in_reply_to中。

binder_transaction结构体in_reply_to成员变量from指向了之前请求与thread进行进程间通信的线程。因此紧接着获取了目标线程target_thread。

找到目标线程target_thread之后,分别将它的todo队列和wait等待队列作为目标todo队列target_list和目标wait等待队列target_wait。
       然后使用初始化binder_transaction结构体t,增加到目标线程的todo。

又初始化了binder_work结构体,增加到本线程(service_manager主线程)的todo队列。最后唤醒目标线程。

我们如果本线程继续运行。运行完成后再运行被唤醒的目标线程。

service_manager主线程继续运行,运行完binder_transaction,一层一层的返回,终于返回到binder_loop中。继续运行for循环。ioctl映射到binder_ioctl,由于仅仅有read_size大于0,所以运行binder_thread_read,实现例如以下:

~/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眼下为0,表示线程有要处理的任务 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)//为0
proc->ready_threads++;
mutex_unlock(&binder_lock);
if (wait_for_proc_work) {//为0
........
} 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)//为0
proc->ready_threads--;
thread->looper &= ~BINDER_LOOPER_STATE_WAITING;//looper为BINDER_LOOPER_STATE_ENTERED if (ret)
return ret; while (1) {
case BINDER_WORK_TRANSACTION_COMPLETE: {
cmd = BR_TRANSACTION_COMPLETE;
if (put_user(cmd, (uint32_t __user *)ptr))//将一个BR_TRANSACTION_COMPLETE返回协议写入到用户提供的缓冲区。 return -EFAULT;
ptr += sizeof(uint32_t); binder_stat_br(proc, thread, cmd);
if (binder_debug_mask & BINDER_DEBUG_TRANSACTION_COMPLETE)
printk(KERN_INFO "binder: %d:%d BR_TRANSACTION_COMPLETE\n",
proc->pid, thread->pid); list_del(&w->entry);//删除todo上的工作项
kfree(w);//释放结构体
binder_stats.obj_deleted[BINDER_STAT_TRANSACTION_COMPLETE]++;
} break;
} done: *consumed = ptr - buffer;//消耗的大小
..........
return 0;
}

运行完binder_thread_read,返回binder_ioctl。最后返回binder_loop函数。開始运行binder_parse,实现例如以下:

~/Android/frameworks/base/cmd/servicemanager

----binder.c

int binder_parse(struct binder_state *bs, struct binder_io *bio,
uint32_t *ptr, uint32_t size, binder_handler func)//ptr为BR_TRANSACTION_COMPLETE的指针。size为它的大小
{
int r = 1;
uint32_t *end = ptr + (size / 4); while (ptr < end) {
uint32_t cmd = *ptr++;
.......
switch(cmd) {//cmd为BR_TRANSACTION_COMPLETE
......
 case BR_TRANSACTION_COMPLETE:
            break;
......}
} return r;
}

运行完binder_parse后。继续运行binder_loop的for循环,重新睡眠等待直到其所属的进程有新的未处理项为止,停留在以下的代码:

wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));//睡眠等待,直到有一个新的进程属于未经加工的项目到目前为止

版权声明:本文博主原创文章,博客,未经同意不得转载。

Service Manager流程,派BC_REPLY,唤醒FregServer流程,返回BR_TRANSACTION_COMPLETE,睡眠等待proc-&gt;wait的更多相关文章

  1. 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6621566 上一篇文章Android进程间通信 ...

  2. android 进程间通信---Service Manager(1)

    Bind机制由4个部分组成.bind驱动,Client,ServiceManager &Service 1.Bind其实是一个基于linux系统的驱动,目的是为了实现内存共享. bind驱动的 ...

  3. 【干货分享】流程DEMO-固定资产转移流程

    流程名: 固定资产转移  业务描述: 固定资产从某员工转移至另一员工,转出人与转入人必须不同  流程相关文件: 流程包.xml  流程说明: 直接导入流程包文件,即可使用本流程  表单:  流程:  ...

  4. 部署Service Manager 2012遇到的2个问题

    上周装了个Service Manager 2012学习,以便完善System Center整个解决方案,在部署期间遇到2个问题,花了我不少时间解决.一.安装时提示“执行自定义操作时失败”每当到了安装的 ...

  5. android 进程间通信---Service Manager(2)

    关于servicemanager的设计: 还是这张结构图,由于ProcessState & IPCThreadState是与binder deriver交互的, 所以对于client端来说Bp ...

  6. Android 核心分析 之六 IPC框架分析 Binder,Service,Service manager

    IPC框架分析 Binder,Service,Service manager 我首先从宏观的角度观察Binder,Service,Service Manager,并阐述各自的概念.从Linux的概念空 ...

  7. 浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6627260 在前面一篇文章浅谈Service ...

  8. Apache HttpServer Installing the apache2.2 service <OS 5>拒绝访问. :Failed to open the WinNT service manager

    Installing the apache2.2 service<OS 5>拒绝访问.  :Failed to open the WinNT service manager 只需要于管理员 ...

  9. Service Manager 2012

    部署Service Manager 2012遇到的2个问题 上周装了个Service Manager 2012学习,以便完善System Center整个解决方案,在部署期间遇到2个问题,花了我不少时 ...

随机推荐

  1. poj 1221 UNIMODAL PALINDROMIC DECOMPOSITIONS (母函数)

    /* 给出一个数n,把它拆分成若干个数的和,要求最大的数在中间并向两边非递增.问拆法有多少种. 母函数.枚举中间的那一个数.由于左右对称.所以仅仅须要求左边部分的方案就可以. 注意,左右两部分的取数必 ...

  2. HdU 4046 Panda 段树

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=4046 意甲冠军:到了bw组成的长度为n的字符串(n<=50000).有m次操作(m<=1000 ...

  3. 安卓SDK更新host文件地址

    之前在安装jdk时,安装进度一直很缓慢,在更新这个(两个任选,都有效)host文件地址后,瞬间就可以了. 203.208.46.146 dl.google.com 203.208.46.146 dl- ...

  4. ConcurrentHashMap中的2的n次方幂上舍入方法(转)

    最近看JDK中的concurrentHashMap类的源码,其中有那么一个函数: /** * Returns a power of two table size for the given desir ...

  5. Cracking the coding interview--问题与解答

    http://www.hawstein.com/posts/ctci-solutions-contents.html 作者:Hawstein出处:http://hawstein.com/posts/c ...

  6. 在iOS7中改动状态栏字体的颜色

    状态栏的字体为黑色:UIStatusBarStyleDefault 状态栏的字体为白色:UIStatusBarStyleLightContent 一.在info.plist中,将View contro ...

  7. php判断变量是否存在

    isset— 检测变量是否设置, isset() 只能用于变量,因为传递任何其它参数都将造成解析错误.若想检测常量是否已设置,可使用 defined() 函数. 如果已经使用 unset() 释放了一 ...

  8. How to import the www.googleapis.com SSL CA certification to the jks store file?

    Assumed that you have installed JDK and configured JAVA_HOME for your current operation system. (1)  ...

  9. 从零開始学android&lt;RelativeLayout相对布局.十六.&gt;

    相对布局管理器指的是參考某一其它控件进行摆放,能够通过控制,将组件摆放在一个指定參考组件的上.下.左.右等位置,这些能够直接通过各个组件提供的属性完毕. 以下介绍一下各个方法的基本使用 No. 属性名 ...

  10. 怎么样MyEclipse配置Tomcat?

    1.下载tomcat免安装版.tomcat路径不包含空格 http://download.csdn.net/detail/u014112584/7549191 2.windows -preferenc ...