Android Binder框架实现之Native层addService详解之请求的处理

2024-04-13 00:58

本文主要是介绍Android Binder框架实现之Native层addService详解之请求的处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android Binder框架实现之Native层addService详解之请求的处理


Android Binder框架实现目录:

Android Binder框架实现之Binder的设计思想
Android Binder框架实现之何为匿名/实名Binder
Android Binder框架实现之Binder中的数据结构
Android Binder框架实现之Binder相关的接口和类
Android Binder框架实现之Parcel详解之基本数据的读写
Android Binder框架实现之Parcel read/writeStrongBinder实现
Android Binder框架实现之servicemanager守护进程
Android Binder框架实现之defaultServiceManager()的实现
Android Binder框架实现之Native层addService详解之请求的发送
Android Binder框架实现之Native层addService详解之请求的处理
Android Binder框架实现之Native层addService详解之请求的反馈
Android Binder框架实现之Binder服务的消息循环
Android Binder框架实现之Native层getService详解之请求的发送
Android Binder框架实现之Native层getService详解之请求的处理
Android Binder框架实现之Native层getService详解之请求的反馈
Android Binder框架实现之Binder Native Service的Java调用流程
Android Binder框架实现之Java层Binder整体框架设计
Android Binder框架实现之Framework层Binder服务注册过程源码分析
Android Binder框架实现之Java层Binder服务跨进程调用源码分析
Android Binder框架实现之Java层获取Binder服务源码分析


引言

   在Android Binder框架实现之Native层addService详解之请求的发送 中介绍了addService得请求发送部分,Binder驱动在处理addService请求时,有将一个待处理事务添加到ServiceManager中,然后将ServiceManager唤醒。在 Android Binder框架实现之ServiceManager守护进程篇章的末尾,我们说过ServiceManager启动之后,由于没有事务处理,就进入了等待状态。这里从ServiceManager被唤醒收开始讲解。

注意:本文是基于Android 7.xx版本进行介绍的,其中涉及的源码路径如下:

frameworks/native/libs/binder/IServiceManager.cpp
frameworks/native/libs/binder/Static.cpp
frameworks/native/libs/binder/ProcessState.cpp
kernel/drivers/staging/android/binder.c
kernel/include/linux/list.h
kernel/drivers/staging/android/uapi/binder.h
external/kernel-headers/original/uapi/linux/android/binder.h
frameworks/native/include/binder/ProcessState.h
frameworks/native/include/binder/BpBinder.h
frameworks/native/libs/binder/BpBinder.cpp
frameworks/native/include/binder/IPCThreadState.h
frameworks/native/libs/binder/IPCThreadState.cpp
frameworks/native/include/binder/IInterface.h
frameworks/native/libs/binder/IInterface.cpp
frameworks/native/include/binder/IBinder.h
frameworks/native/libs/binder/Binder.cpp
frameworks/av/media/mediaserver/main_mediaserver.cpp
frameworks/native/cmds/servicemanager/service_manager.c
  • 在正式开始进行源码分析前,先看下Native Binder服务注册整体示意图:

在这里插入图片描述

  • 接着在正式开始进行源码分析前,先看看Native Binder类图如下:

在这里插入图片描述



一.addService请求的处理流程

   下面,就接着Android Binder框架实现之ServiceManager守护进程中得的休眠部分被唤醒后进行讲解,看看Service Manager被唤醒后,会干些什么。在前面的篇章里面我们知道ServiceManager成为服务大管家以后,会等待在驱动函数binder_thread_read里面,那么我就从该函数被唤醒说起。


1.唤醒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)
{...if (wait_for_proc_work) {...binder_set_nice(proc->default_priority);if (non_block) {//不阻塞...} else//阻塞式的读取,阻塞等待事务的发生。ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));} else {...}...while (1) {uint32_t cmd;struct binder_transaction_data tr;struct binder_work *w;struct binder_transaction *t = NULL;//如果当前线程的“待完成工作”不为空,则取出待完成工作if (!list_empty(&thread->todo))w = list_first_entry(&thread->todo, struct binder_work, entry);else if (!list_empty(&proc->todo) && wait_for_proc_work)...else {...}if (end - ptr < sizeof(tr) + 4)break;switch (w->type) {case BINDER_WORK_TRANSACTION: {t = container_of(w, struct binder_transaction, work);} break;}if (!t)continue;//t->buffer->target_node是目标节点//这里,addService请求的目标是ServiceManager,因此target_node是ServiceManager对应的节点;if (t->buffer->target_node) {// 事务目标对应的Binder实体(即,ServiceManager对应的Binder实体)struct binder_node *target_node = t->buffer->target_node;//Binder实体在用户空间的地址(ServiceManager的ptr为NULL)tr.target.ptr = target_node->ptr;//Binder实体在用户空间的其它数据(ServiceManager的cookie为NULL),至于为什么取值都为NULL参见binder_context_mgr_node创建tr.cookie =  target_node->cookie;t->saved_priority = task_nice(current);if (t->priority < target_node->min_priority &&!(t->flags & TF_ONE_WAY))binder_set_nice(t->priority);else if (!(t->flags & TF_ONE_WAY) ||t->saved_priority > target_node->min_priority)binder_set_nice(target_node->min_priority);cmd = BR_TRANSACTION;} else {tr.target.ptr = 0;tr.cookie = 0;cmd = BR_REPLY;}//交易码tr.code = t->code;tr.flags = t->flags;tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);if (t->from) {struct task_struct *sender = t->from->proc->tsk;tr.sender_pid = task_tgid_nr_ns(sender,task_active_pid_ns(current));} else {tr.sender_pid = 0;}//数据大小tr.data_size = t->buffer->data_size;//数据中对象的偏移数组的大小(即对象的个数)tr.offsets_size = t->buffer->offsets_size;//数据tr.data.ptr.buffer = (binder_uintptr_t)((uintptr_t)t->buffer->data +proc->user_buffer_offset);//数据中对象的偏移数组tr.data.ptr.offsets = tr.data.ptr.buffer +ALIGN(t->buffer->data_size,sizeof(void *));//将cmd指令写入到ptr,即传递到用户空if (put_user(cmd, (uint32_t __user *)ptr))return -EFAULT;ptr += sizeof(uint32_t);//将tr数据拷贝到用户空间if (copy_to_user(ptr, &tr, sizeof(tr)))return -EFAULT;ptr += sizeof(tr);...//删除已处理的事务list_del(&t->work.entry);t->buffer->allow_user_free = 1;//设置回复信息if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {//该事务会发送给ServiceManager守护进程处理//ServiceManager处理之后,还需要给Binder驱动回复处理结果//这里设置Binder驱动回复信息t->to_parent = thread->transaction_stack;// to_thread表示Service Manager反馈后,将反馈结果交给当前thread进行处理t->to_thread = thread;// transaction_stack交易栈保存当前事务。用来记录反馈是针对哪个事务的。thread->transaction_stack = t;} else {...}break;}done://更新bwr.read_sonsumed的值*consumed = ptr - buffer;...return 0;
}

ServiceManager进程在调用 wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread))进入中断等待之后,被MediaPlayerService进程添加服务唤醒。唤醒之后,binder_has_thread_work()为true,因为ServiceManager的待处理事务队列中有个待处理事务(即,MediaPlayerService添加服务的请求)。下面我们看看唤醒之后相关的代码处理逻辑:
(1) 进入while循环后,首先取出待处理事务。
(2) 事务的类型是BINDER_WORK_TRANSACTION,得到对应的binder_transaction*类型指针t之后,跳出switch语句。很显然,此时t不为NULL,因此继续往下执行。下面的工作的目的,是将t中的数据转移到tr中(tr是事务交互数据包结构体binder_transaction_data对应的指针),然后将指令和tr数据都拷贝到用户空间,让ServiceManager读取后进行处理。
(3) 数据的转移及数据在内核态和用户态之间的拷贝

    // 数据大小tr.data_size = t->buffer->data_size;// 数据中对象的偏移数组的大小(即对象的个数)tr.offsets_size = t->buffer->offsets_size;// 数据tr.data.ptr.buffer = (void *)t->buffer->data +proc->user_buffer_offset;// 数据中对象的偏移数组tr.data.ptr.offsets = tr.data.ptr.buffer +ALIGN(t->buffer->data_size,sizeof(void *));

(4) t->buffer是在binder_transaction()中,通过binder_alloc_buf()分配的内核空间地址。现在要将此数据返回给Service Manager守护进程,需要将内核空间的数据拷贝到用户空间。读者朋友们如果有仔细阅读前面的篇章的话,在前面Android Binder框架实现之ServiceManager守护进程中讲解mmap()的时候,我们已经通过此方法将内核虚拟地址和用户进程虚拟地址映射到同一个物理存储区了。现在,已知内核虚拟地址(即t->buffer->data)。那么,只需要将t->buffer->data加上proc->user_buffer_offset(内核虚拟地址和进程虚拟地址的偏移)即可得到在用户空间的地址。这个就是一些面试Binder的考官口里所说的,Binder是通过什么机制实现内核态和用户态数据交互的,就是这里所说的内存映射。

(5) 在tr赋值完毕以后,就将完整数据拷贝到用户空间。此时,该事务已经在Binder驱动中被处理,于是将事务从Service Manager的待处理事务队列中删除。Binder驱动随后会将该事务发送给Service Manager守护进程,Service Manager守护进程在处理完事务之后,需要反馈结果给Binder驱动。因此,接下来会设置t->to_thread和t->transaction_stack等成员。最后,修改*consumed的值,即bwr.read_consumed的值,表示待读取内容的大小。执行完binder_thread_read()之后,回到binder_ioctl()中,执行copy_to_user()将数据拷贝到用户空间。此时ServiceManager进程已经从驱动层返回回来,进入用户空间中执行,即回到binder_loop()中,那么我们继续分析binder_loop()。



2.返回binder_loop()

void binder_loop(struct binder_state *bs, binder_handler func)
{int res;struct binder_write_read bwr;uint32_t readbuf[32];bwr.write_size = 0;bwr.write_consumed = 0;bwr.write_buffer = 0;readbuf[0] = BC_ENTER_LOOPER;binder_write(bs, readbuf, sizeof(uint32_t));for (;;) {bwr.read_size = sizeof(readbuf);bwr.read_consumed = 0;bwr.read_buffer = (uintptr_t) readbuf;//向Kernel中发送消息(先写后读)//先将消息传递给Kernel,然后再从Kernel读取消息反馈res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);...//接续读取的消息反馈res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);...}
}

此时binder_loop会从ioctl中返回回来,然后将反馈的数据作为参数传递给binder_parse()进行解析,下面让我们一起分析分析binder_parse()做了那些工作。



3.binder_parse()

int binder_parse(struct binder_state *bs, struct binder_io *bio,uintptr_t ptr, size_t size, binder_handler func)
{int r = 1;uintptr_t end = ptr + (uintptr_t) size;while (ptr < end) {uint32_t cmd = *(uint32_t *) ptr;ptr += sizeof(uint32_t);switch(cmd) {case BR_NOOP:break;...case BR_TRANSACTION: {struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;if ((end - ptr) < sizeof(*txn)) {ALOGE("parse: txn too small!\n");return -1;}binder_dump_txn(txn);if (func) {unsigned rdata[256/4];struct binder_io msg;//用户保存"Binder驱动反馈的信息"struct binder_io reply;//用来保存"回复给Binder驱动的信息"int res;//初始化replaybio_init(&reply, rdata, sizeof(rdata), 4);//根据txn(Binder驱动反馈的信息)初始化msgbio_init_from_txn(&msg, txn);//消息处理,此处是一个函数指针调用,最后跳转到svcmgr_handlerres = func(bs, txn, &msg, &reply);if (txn->flags & TF_ONE_WAY) {binder_free_buffer(bs, txn->data.ptr.buffer);} else {//反馈消息给Binder驱动binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);}}ptr += sizeof(*txn);break;}...}}return r;
}

此处的cmd是bwr.read_buffer指针里面的数据。而在Binder驱动的binder_thread_read()中,反馈的第一个指令是BR_NOOP;因此这里的cmd=BR_NOOP,不执行任何动作,继续取出下一个指令cmd=BR_TRANSACTION。在BR_TRANSACTION中,会先取出消息,在对消息处理之后,再将反馈信息发送给Binder驱动。下面我们详细分析分支BR_TRANSACTION的内容。
(1) 首先,将ptr转换成binder_transaction_data结构体指针。binder_transaction_data对应的结构体,在Android Binder框架实现之Binder中常见的数据结构中有它的详细介绍。
(2) 此处的func是函数指针svcmgr_handler,不为空;因此,先调用bio_init()初始化reply,再调用bio_init_from_txn()来初始化msg。
(3) 初始化完毕之后,就调用svcmgr_handler()对消息进行处理。
(4) 消息处理完毕,就通过binder_send_reply()将处理结果反馈给Binder驱动。



4.bio_init()

void bio_init(struct binder_io *bio, void *data,size_t maxdata, size_t maxoffs)
{size_t n = maxoffs * sizeof(size_t);if (n > maxdata) {bio->flags = BIO_F_OVERFLOW;bio->data_avail = 0;bio->offs_avail = 0;return;}bio->data = bio->data0 = (char *) data + n;bio->offs = bio->offs0 = data;bio->data_avail = maxdata - n;bio->offs_avail = maxoffs;bio->flags = 0;
}

该函数比较简单,就是对struct binder_io的各个成员赋值,初始化处理。



5.bio_init_from_txn()

void bio_init_from_txn(struct binder_io *bio, struct binder_transaction_data *txn)
{bio->data = bio->data0 = (char *)(intptr_t)txn->data.ptr.buffer;	//数据的起始地址bio->offs = bio->offs0 = (binder_size_t *)(intptr_t)txn->data.ptr.offsets;	// 数据中对象的偏移数组的起始地址bio->data_avail = txn->data_size;	//数据大小bio->offs_avail = txn->offsets_size / sizeof(size_t);	//对象个数bio->flags = BIO_F_SHARED;
}

bio_init_from_txn()就是根据已有的数据txn初始化struct binder_io的各个成员值。



6.svcmgr_handler()

int svcmgr_handler(struct binder_state *bs,struct binder_transaction_data *txn,struct binder_io *msg,struct binder_io *reply)
{struct svcinfo *si;uint16_t *s;size_t len;uint32_t handle;uint32_t strict_policy;int allow_isolated;if (txn->target.ptr != BINDER_SERVICE_MANAGER)return -1;if (txn->code == PING_TRANSACTION)return 0;strict_policy = bio_get_uint32(msg);s = bio_get_string16(msg, &len);if (s == NULL) {return -1;}if ((len != (sizeof(svcmgr_id) / 2)) ||memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {...}...switch(txn->code) {case SVC_MGR_GET_SERVICE:case SVC_MGR_CHECK_SERVICE:...case SVC_MGR_ADD_SERVICE: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: {...}bio_put_uint32(reply, 0);return 0;
}

在章节3.binder_parse()我们可以知道,func(bs, txn, &msg, &reply)最终会调用到此,那么我们来一步步的分解该函数。
(1) txn->target.ptr是Binder驱动在binder_thread_read()中赋值的,它指向Service Manager的Binder实体在用户空间的句柄,是NULL。让我们来看看其定义如下,#define BINDER_SERVICE_MANAGER 0U。所以很显然的,txn->target.ptr=BINDER_SERVICE_MANAGER成立。
(2) 接下来,先通过bio_get_uint32(msg)和bio_get_string16(msg, &len)进行有效性检测。通过bio_get_uint32()从msg中取出32位的整型数,就是MediaPlayerService请求数据中的STRICT_MODE_PENALTY_GATHER。然后,通过bio_get_string16(msg, &len)获取数据中字符串,也就是"android.os.IServiceManager"。接着,将该字符串和svcmgr_id进行比较(依次比较长度和内容);很显然,这里是相等的。
(3) 在通过有效性检测之后,就根据相应的事务编码进行处理。这里txt->code的值是SVC_MGR_ADD_SERVICE。先通过bio_get_string16()获取MediaPlayerService的名称,也就是s=“media.player”,然后就通过bio_get_ref()获取MediaPlayerService对象的引用。


6.1 bio_get_ref()
uint32_t bio_get_ref(struct binder_io *bio)
{struct flat_binder_object *obj;obj = _bio_get_obj(bio);if (!obj)return 0;if (obj->type == BINDER_TYPE_HANDLE)return obj->handle;return 0;
}

flat_binder_object对应的结构体,关于它的详细介绍可以参考Android Binder框架实现之Binder中常见的数据结构的章节,细心的读者是不是在前面那个地方也见过这个数据结构呢,对了就是在addService里面将MediaPlayerService扁平化的时候有使用到。
(1) _bio_get_obj(bio)的代码就不展开了,它是根据bio创建binder_object对象。实际上,obj就是MediaPlayerService打包成的flat_binder_object对象。
(2) obj->type的值是BINDER_TYPE_HANDLE。原来MediaPlayerService对应的type是BINDER_TYPE_BINDER,但在Binder驱动的binder_transaction()中,将type修改成了BINDER_TYPE_HANDLE。因此,返回obj->pointer,而obj->pointer实际上是flat_binder_object中的handle,而该handle在Binder驱动中被赋值为"MediaPlayerService对应的Binder引用的描述,即binder_ref->desc"。根据该引用描述,可以在Binder驱动中找到MediaPlayerService对应的Binder实体以及MediaPlayerService对应的进程上下文信息,进而可以给MediaPlayerService发送消息。


6.2 do_add_service()

接下来继续回到svcmgr_handler()中,继续执行do_add_service(),看看主要做了那些操作。

int do_add_service(struct binder_state *bs,const uint16_t *s, size_t len,uint32_t handle, uid_t uid, int allow_isolated,pid_t spid)
{struct svcinfo *si;...if (!svc_can_register(s, len, spid, uid)) {...}si = find_svc(s, len);if (si) {...} else {si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));if (!si) {...}si->handle = handle;si->len = len;memcpy(si->name, s, (len + 1) * sizeof(uint16_t));si->name[len] = '\0';si->death.func = (void*) svcinfo_death;si->death.ptr = si;si->allow_isolated = allow_isolated;si->next = svclist;svclist = si;}binder_acquire(bs, handle);binder_link_to_death(bs, handle, &si->death);return 0;
}

do_add_service()在正式分析前,让我们来看看相关的参数,bs是struct binder_state类型,它在保存了打开"/dev/binder"文件的相关信息。s是IBinder对象的名称,即"media.player"。len是s的长度。handle是MediaPlayerService在Binder驱动中的引用描述。uid是MediaPlayerService的uid。allow_isolated是flase。下面来开始正式分析:
(1) svc_can_register()是检测能否将uid线程的信息注册到Service Manager中。这里,返回true。
(2) find_svc(s, len)是在Service Manager的服务队列svclist中,查找是否有名称为s的服务。由于之前没有将MediaPlayerService注册到Service Manager中,这里返回的si=null;接下来,就将MediaPlayerService的信息保存到si中,然后再将si注册到svclist中。
这样,MediaPlayerService就注册到Service Manager中了。


6.3 bio_put_uint32()

接下来,继续回到svcmgr_handler()中,调用bio_put_uint32(reply, 0)。这里就不对bio_put_uint32()的代码进行展开了,bio_put_uint32(reply, val)的作用是将val写入到reply中。但是,当val=0时,不会写入任何数据;也就是说bio_put_uint32(reply, 0)不会写入任何数据到reply中!

int svcmgr_handler(struct binder_state *bs,struct binder_txn *txn,struct binder_io *msg,struct binder_io *reply)
{...switch(txn->code) {case SVC_MGR_ADD_SERVICE:s = bio_get_string16(msg, &len);ptr = bio_get_ref(msg);allow_isolated = bio_get_uint32(msg) ? 1 : 0;if (do_add_service(bs, s, len, ptr, txn->sender_euid, allow_isolated))return -1;break;...}bio_put_uint32(reply, 0);return 0;
}

接着,回到binder_parse()中,调用binder_send_reply()写入到即将发送Binder的缓冲区中。下面我们继续分析binder_send_reply()函数。



7.binder_send_reply()

void binder_send_reply(struct binder_state *bs,struct binder_io *reply,binder_uintptr_t buffer_to_free,int status)
{struct {uint32_t cmd_free;binder_uintptr_t buffer;uint32_t cmd_reply;struct binder_transaction_data txn;} __attribute__((packed)) data;data.cmd_free = BC_FREE_BUFFER;data.buffer = buffer_to_free;data.cmd_reply = BC_REPLY;data.txn.target.ptr = 0;data.txn.cookie = 0;data.txn.code = 0;if (status) {data.txn.flags = TF_STATUS_CODE;data.txn.data_size = sizeof(int);data.txn.offsets_size = 0;data.txn.data.ptr.buffer = (uintptr_t)&status;data.txn.data.ptr.offsets = 0;} else {data.txn.flags = 0;data.txn.data_size = reply->data - reply->data0;data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);data.txn.data.ptr.buffer = (uintptr_t)reply->data0;data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;}binder_write(bs, &data, sizeof(data));
}

先分析下该函数的入参,bs是struct binder_state,它保存了打开"/dev/binder"文件的相关信息。reply没有任何数据。buffer_to_free是对应binder_transaction_data中保存请求数据的buffer缓冲区,它是在Binder驱动的binder_transaction()中分配的。status_t=0。下面来细化一下该函数的相关逻辑:
(1) 对私有结构体data进行赋值,该函数中的私有结构体struct是用来描述返回给Binder驱动的数据。我们知道,Binder机制的交互数据的格式是"指令+数据"。这里,返回的指令有两个BC_FREE_BUFFER和BC_REPLY,BC_FREE_BUFFER是告诉Binder驱动,请求处理完毕,让Binder驱动释放数据缓冲;而BC_REPLY是告诉Binder驱动,这是回复,回复的内容是data.txt.data,实际上,这里的回复内容是空!
(2) 最后调用binder_write()将数据进行打包.


7.1 binder_write()
int binder_write(struct binder_state *bs, void *data, size_t len)
{struct binder_write_read bwr;int res;bwr.write_size = len;			//数据长度bwr.write_consumed = 0;bwr.write_buffer = (uintptr_t) data;	// 数据是BINDER_WRITE_READbwr.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;
}

可以看到此处的read_size = 0而write_size > 0,所以此时只是向Binder驱动发送一个信息,而不会读取返回的信息。



8.重返servicemanager进程Binder驱动层处理

   兜兜转转又回到了Binder驱动层,我想有了前面知识的铺垫大家应该对BInder里面一时用户层一时内核层,不会感到陌生了,下面让我们一起重返内核层分析,我们来了。


8.1 binder_ioctl()
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{...switch (cmd) {case BINDER_WRITE_READ: {struct binder_write_read bwr;...// 将binder_write_read从"用户空间" 拷贝到 "内核空间"if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {...}// 如果write_size>0,则进行写操作if (bwr.write_size > 0) {ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);...}// 如果read_size>0,则进行读操作if (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 (copy_to_user(ubuf, &bwr, sizeof(bwr))) {ret = -EFAULT;goto err;}break;}...}ret = 0;...return ret;
}

下面让我们看看Binder驱动对应的部分,此时bwr.write_size>0,而bwr.read_size=0;因此,只会执行写动作,而不会进行读取动作。所以我们只需关注binder_thread_write()到底写了些什么。


8.2 binder_thread_write()分析
static int binder_thread_write(struct binder_proc *proc,struct binder_thread *thread,binder_uintptr_t binder_buffer, size_t size,binder_size_t *consumed)
{uint32_t cmd;void __user *buffer = (void __user *)(uintptr_t)binder_buffer;void __user *ptr = buffer + *consumed;void __user *end = buffer + size;// 读取binder_write_read.write_buffer中的内容。// 每次读取32bit(即4个字节)while (ptr < end && thread->return_error == BR_OK) {if (get_user(cmd, (uint32_t __user *)ptr))return -EFAULT;ptr += sizeof(uint32_t);...switch (cmd) {case BC_FREE_BUFFER: {binder_uintptr_t data_ptr;struct binder_buffer *buffer;// 获取要释放的内存地址if (get_user(data_ptr, (binder_uintptr_t __user *)ptr))return -EFAULT;ptr += sizeof(binder_uintptr_t);// 根据用户空间地址,得到进程空间地址;// 再根据进程空间地址,在proc->allocated_buffers红黑树中进行查找该地址对应的binder_buffer对象。buffer = binder_buffer_lookup(proc, data_ptr);...// 释放内存trace_binder_transaction_buffer_release(buffer);binder_transaction_buffer_release(proc, buffer, NULL);binder_free_buf(proc, buffer);break;}case BC_TRANSACTION:case BC_REPLY: {struct binder_transaction_data tr;if (copy_from_user(&tr, ptr, sizeof(tr)))return -EFAULT;ptr += sizeof(tr);binder_transaction(proc, thread, &tr, cmd == BC_REPLY);break;break;}...}// 更新bwr.write_consumed的值*consumed = ptr - buffer;}return 0;
}

通过前面的章节中,我们知道ServiceMananger进程返回给Binder驱动的指令有两个,分别是BC_FREE_BUFFER和BC_REPLY。下面来分析一下对该两个指令的处理:
(1) binder_write_read()先读出BC_FREE_BUFFER指令,然后释放内存。代码中给出了相应的注释,这里就不再详细说明了。
(2) 接着,读出BC_REPLY指令,将数据拷贝到内核空间之后,便执行binder_transaction()对数据进行处理。


8.3 binder_transaction()分析
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;binder_size_t *offp, *off_end;binder_size_t off_min;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;struct binder_transaction_log_entry *e;uint32_t return_error;...if (reply) {//当处理的事务为BC_REPLY,target_node并没有赋值,此时为初始值NULL,这点很重要在binder_thread_read//中有体现,即在addservice的反馈中客户端进程binder_thread_read的时候有特殊处理,根据target_node为NULL//事务栈in_reply_to = thread->transaction_stack;...//设置优先级binder_set_nice(in_reply_to->saved_priority);...thread->transaction_stack = in_reply_to->to_parent;//发起请求的线程,即MediaPlayerService所在线程//from的值,是MediaPlayerService发起请求时所在binder_transaction()中赋值的。target_thread = in_reply_to->from;...//MediaPlayerSerice对应的进程target_proc = target_thread->proc;} else {...}if (target_thread) {e->to_thread = target_thread->pid;target_list = &target_thread->todo;target_wait = &target_thread->wait;} else {...}e->to_proc = target_proc->pid;// 分配一个待处理的事务t,t是binder事务(binder_transaction对象)t = kzalloc(sizeof(*t), GFP_KERNEL);if (t == NULL) {return_error = BR_FAILED_REPLY;goto err_alloc_t_failed;}// 分配一个待完成的工作tcomplete,tcomplete是binder_work对象。tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);if (tcomplete == NULL) {return_error = BR_FAILED_REPLY;goto err_alloc_tcomplete_failed;}binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);t->debug_id = ++binder_last_id;e->debug_id = t->debug_id;if (!reply && !(tr->flags & TF_ONE_WAY))t->from = thread;elset->from = NULL;// 下面的一些赋值是初始化事务tt->sender_euid = proc->tsk->cred->euid;// 事务将交给target_proc进程进行处理t->to_proc = target_proc;// 事务将交给target_thread线程进行处理t->to_thread = target_thread;//事务编码t->code = tr->code;//事务标志t->flags = tr->flags;//事务优先级t->priority = task_nice(current);//分配空间t->buffer = binder_alloc_buf(target_proc, tr->data_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;t->buffer->target_node = target_node;trace_binder_transaction_alloc_buf(t->buffer);if (target_node)binder_inc_node(target_node, 1, 0, NULL);offp = (binder_size_t *)(t->buffer->data +ALIGN(tr->data_size, sizeof(void *)));// 将"用户传入的数据"保存到事务中if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)tr->data.ptr.buffer, tr->data_size)) {...}// 将"用户传入的数据偏移地址"保存到事务中if (copy_from_user(offp, (const void __user *)(uintptr_t)tr->data.ptr.offsets, tr->offsets_size)) {...}...off_end = (void *)offp + tr->offsets_size;off_min = 0;for (; offp < off_end; offp++) {...}if (reply) {BUG_ON(t->buffer->async_transaction != 0);binder_pop_transaction(target_thread, in_reply_to);} else if (!(t->flags & TF_ONE_WAY)) {...} else {...}//设置事务的类型为BINDER_WORK_TRANSACTIONt->work.type = BINDER_WORK_TRANSACTION;//将事务添加到target_list队列中,即target_list的待处理事务中list_add_tail(&t->work.entry, target_list);//设置待完成工作的类型为BINDER_WORK_TRANSACTION_COMPLETEtcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;//将待完成工作添加到thread->todo队列中,即当前线程的待完成工作中。list_add_tail(&tcomplete->entry, &thread->todo);if (target_wait)wake_up_interruptible(target_wait);return;...
}

在正式分析该函数前,先看看函数的入参reply为1,所以此时进入reply的处理部分,下面一步步讲解:
(1) target_thread被赋值为MediaPlayerService所在的binder_thread,而target_proc则是MediaPlayerService对应的进程。
(2) 接着,会新建一个待处理事务t和待完成的工作tcomplete,并对它们进行初始化。这部分前面已经介绍过了;这里就不再重复说明了。从Service Manager反馈的信息中,仅仅包含了数据0,而没有flat_binder_object对象;因此,off_end=offp,不会执行for循环。
(3) 此时,MediaPlayerService已经成功的添加到了Server Manager守护进程中,接下来便调用binder_pop_transaction(target_thread, in_reply_to)将事务从"target_thread的事务栈"中删除,即从MediaPlayerService线程的事务栈中删除该事务。
(4) 之后,便是设置事务的类型为BINDER_WORK_TRANSACTION,然后将其添加到target_list队列中。即,将事务添加到MediaPlayerService的待处理事务队列中。
(5) 设置待完成工作的类型为BINDER_WORK_TRANSACTION_COMPLETE,然后将其添加到thread->todo中。即,将其添加到当前线程(Service Manager守护进程的线程)的待处理事务队列中。
(6) 最后,调用wake_up_interruptible()唤醒MediaPlayerService进程。

下面,还是先说完ServiceManager的流程,然后再来看MediaPlayerService被唤醒后做了什么,其大概步骤如下:

  • ServiceManager执行完binder_transaction()后,回到binder_thread_write()中;此时,数据已经处理完毕,便返回到binder_ioctl()中。binder_ioctl()将数据拷贝到用户空间后,Binder驱动的工作就结束了。
  • 于是,又回到ServiceManager守护进程中,binder_write()执行完ioctl()后,返回到binder_send_reply()中,binder_send_reply()则进一步返回到binder_parse()。binder_parse()已经解析完请求数据,于是进一步返回到binder_loop()中。而binder_loop()会再次开始循环,调用ioctl(,BINDER_WRITE_READ,)到Binder驱动执行读操作。
  • 当ServiceManager再次进入到Binder驱动,并通过binder_ioctl()调用到binder_thread_read()时。由于此时的ServiceManager线程中有一个类型为BINDER_WORK_TRANSACTION_COMPLETE的待处理事务;于是,便取出该事务进行执行。执行完毕之后,将该事务从Service Manager的待处理事务队列中删除,并反馈cmd=BR_TRANSACTION_COMPLETE信息给ServiceManager守护进程。ServiceManager守护进程收到Binder驱动的反馈后,解析出BR_TRANSACTION_COMPLETE,该指令什么也不做;它的目的是让ServiceManager知道,此次addService的反馈已经顺利完成!
  • 于是,ServiceManager继续它的循环;当它再次调用ioctl(),进而进入到Binder驱动中读取请求时;由于此时的待处理事务队列为空,因此,ServiceManager会再次进入中断等待状态,等待Client的请求。

至此,MediaPlayerService进程的addService的请求处理部分就讲解完了。在继续了解请求的反馈之前,先回顾一下本部分的内容。
在这里插入图片描述



二. 请求的处理流程总结

   好了写到这里,本篇已经完结了。那么让我们总结一下本篇到底讲了那些东西,来个概括归纳看看你是否都已经get到了这些点。
(1) MediaPlayerService将addService请求发送到Binder驱动,Binder驱动将addService转换成一个待处理事务并添加到ServiceManager的事务队列中,并将ServiceManager唤醒。ServiceManager被唤醒后,取出并处理。
(2) 接着,Binder驱动将BR_TRANSACTION发送到ServiceManager守护进程中。ServiceManager通过BR_TRANSACTION解析出addService请求;在从请求数据中解析出MediaPlayerService的相关信息后,并将这些信息存储在一个链表中。接着,ServiceManager守护进程反馈BC_FREE_BUFFER和BC_REPLY给Binder驱动。
(3) Binder驱动收到BC_FREE_BUFFER后,释放保存事务数据的内存;在收到BC_REPLY之后,得知ServiceManager已经处理完addService请求。于是,将一个待处理事务添加到MediaPlayerService的事务队列中;然后将MediaPlayerService唤醒。目的是告诉MediaPlayerService,它已经处理完了addService请求。
(4) 最后,Binder驱动还需要反馈一个BR_TRANSACTION_COMPLETE给ServiceManager进程,目的是告诉ServiceManager,Binder驱动已经收到了它的回复。

此时小伙们心里一定还有一个疑问,那就是MediaPlayerService进程被唤醒后会如何处理servicemanager进程反馈的结果呢,这个就是我们下个篇章Android Binder框架实现之Native层addService详解之请求的反馈中重点分析的内容了。



写在最后

   至此,Android Binder框架实现之Native层addService详解之请求的处理就介绍完了。如果小伙们对整个过程还是很迷糊或者困惑,也不要感到伤心和难过,因为Binder不是一蹴而就的事情,这个是一个考验耐心耐力的的知识点学习,容不得半点马虎的,多看源码,多分析我相信终有一天你会突然开窍的。好了就到这里了,欢迎小伙伴们评论和点赞!本篇博客到这里真的要说再见了。

这篇关于Android Binder框架实现之Native层addService详解之请求的处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/898719

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影